This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
connerity's profile picture

Keep a touchscreen interface active while executing other functions?

Started by connerity, 13 August 2013 - 11:57 AM
connerity #1
Posted 13 August 2013 - 01:57 PM
Title: Keep a touchscreen interface active while executing other functions?

Imagine I had a touchscreen interface with 6 buttons, each triggering it's own action, independent from each other, just triggered from the same computer, each starting a function that runs for a specific amount of time.

For example, button1: pump engines for 60 seconds; button2: trigger a lightshow for 60 seconds.

Now, as I have it currently setup, I have a

function1()
--dostuff
os.sleep(60)
end

function2()
--dostuff
os.sleep(60)
end

while true do
 e, s, x, y = os.pullEvent("monitor_touch")
---dostuff, make button1 call function1 that runs for 60 seconds, make button2 call function2 that also lasts 60 seconds, etc
end

structure.

The problem with this is, when I press a button and the corresponding function runs, all the other functions are not available since they are started from the same program.

Is there a way to solve this, or would I need to make a wireless system where the touchscreen interface computer can send the tasks to other computers, so that each computer only has that one task, making all buttons permanently available.
There would be a slight problem with this again, I currently display the progress of the running function on the monitor.
That only works because the program goes to "function1", for example, and that function updates the screen.

Can multiple computers access the same monitor? If so, combining that with the wireless task-execution could work and even update multiple things on the monitor at once.
Bubba #2
Posted 13 August 2013 - 06:37 PM
Split into new topic.
connerity #3
Posted 14 August 2013 - 05:28 AM
That doesn't appear quite to be what I'm looking for, according to the API.
 
parallel.waitForAny(function1, function2, ...) 	Runs all the functions at the same time, and stops when any of them returns.
parallel.waitForAll(function1, function2, ...) 	Runs all the functions at the same time, and stops when all of them have returned.

They both run all associated functions at once.
I need independant access to my functions.

Maybe I wasn't very clear, I don't know, I basically need my

while true do
 e, s, x, y = os.pullEvent("monitor_touch")
---dostuff, make button1 call function1 that runs for 60 seconds, make button2 call function2 that also lasts 60 seconds, etc
end
loop to stay active at all times, while also allowing the functions it triggers to run.
I don't see how I can do that with the parallel functions. They're meant to call multiple functions at once, but the functions triggered by my buttons don't need to be run at once, in fact, that would be disastrous.

For now, I'm gonna solve it with the wireless solution with 1 computer per function and 1 controller, but I hope it is possible to do this with just one computer.
LordIkol #4
Posted 14 August 2013 - 06:31 AM
hi connerity,

if i get it correct you just want to put in a delay for the different Actions.
So that the User only can do every Action once and then has to wait until its finished.

For Example:
If i press Button1 and the Pump starts i can not press 1 again until 60 seconds have passed. But i could start the Firework with Button 2.
if this is what you want then i would go for the timer Event.

I have not so much time now to make an example code but basicly what you wanna do is.

1. Make a table that contains all buttons and a variable for their state and a variable for a timerid
2. when a button is pressed check the state of that button
3. if state its false tell the user to wait
4. if state is true set the state to false, start a timer event, and store the id of that timer in the table
5. listen for the timer event. if a timer fires the event check its ID with the timerids in your button table
6. if it matches with an id set the state of that button back to true and the timerid to 99999999 or sth like that.

hope thats not too confusing if i find some time later i will make an example

greets
Loki
connerity #5
Posted 14 August 2013 - 09:41 AM
I could disable the buttons if I wanted, but that doesn't solve the problem that I can't run multiple functions (with sleep() in them) at once while keeping the os.pullEvent("monitor_touch") up.

I made a working system that does what I want, however, requires 1 computer per function since it works with rednet.

I uploaded enough to fully explain what I want to pastebin (my working code):

Main button control, this registers the buttons and sends a rednet message to the function computers:
http://pastebin.com/1F7wtGGq
The commented-out stuff is from when I tried to make it work on one computer.


Function computer #1 and #2:
http://pastebin.com/6GPvu7Yq
http://pastebin.com/GkifKLuk
I also wonder if I can somehow clear a single line instead of the whole screen, as you can see I made a workaround with setting the background color to black and writing a full line of spaces.

All 3 computers are connected to the same monitor.

What I want is, have those 3 programs run on the same computer, at the same time, while keeping full functionality. (there are of course more programs like #1 and #2, but no need to post them all)
Also keep in mind that everything concerning rednet exists only because of my workaround, only the monitor manipulating part is required.
Lyqyd #6
Posted 14 August 2013 - 12:44 PM
Here's an example of using an event-based model to get around needing to use sleep. This should be pretty self-explanatory, but feel free to ask questions about anything you don't understand it in. Obviously, there are parts of it that are a bit pseudo-codey since they are dependent on specific coordinates of your buttons, etc.


local pumpOffTimer
local lightShowTimer
while true do
  local event = {os.pullEvent()}
  if event[1] == "monitor_touch" then
    if isInAreaOfButtonOne then
      if pumpOffTimer == nil then
        setPumpOn()
        pumpOffTimer = os.startTimer(60)
      end
    elseif isInAreaOfButtonTwo then
      if lightShowTimer == nil then
        startLightShow()
        lightShowTimer = os.startTimer(30)
      end
    end
  elseif event[1] == "timer" then
    if event[2] == pumpOffTimer then
      setPumpOff()
      pumpOffTimer = nil
    elseif event[2] == lightShowTimer then
      stopLightShow()
      lightShowTimer = nil
    end
  end
end
connerity #7
Posted 14 August 2013 - 01:31 PM
Wow this looks nice, wouldn't have thought of that. Or more like, couldn't have, with my current knowledge.

Only problem I'd see on first look is that I can no longer update my monitor with every individual timer every second, since it still needs to loop back to the os.pullEvent() and can't do anything else while doing so.

But still, this would only require 1 additional computer that runs the timers/monitor update instead of one for each function.
Lyqyd #8
Posted 14 August 2013 - 03:29 PM
Uh, you can do all of it from a single computer. You can update the monitor each iteration of the loop if you want, but it makes the most sense to update it only when it needs to change. I'm confused as to how that would require a second computer.
connerity #9
Posted 14 August 2013 - 04:27 PM
The above posted loop by you stops and waits at the local event = {os.pullEvent()} , doesn't it?
Until any event fires, it wouldn't update the remaining time on the monitor because it only goes through the loop when an os.pullEvent() action happens.

I suppose I could make another, constantly running "dummy" timer that only runs for a second and then updates the monitor and starts itself again.
Lyqyd #10
Posted 14 August 2013 - 05:09 PM
Well, yes, you'd need a separate timer for that, and that's exactly what this sort of program structure is good at.
connerity #11
Posted 14 August 2013 - 06:40 PM
Ok, that's quite awesome and easy to expand with more events.

One final question I think:


  local event = {os.pullEvent()}
  if event[1] == "monitor_touch" then
    if isInAreaOfButtonOne then
      if pumpOffTimer == nil then
        setPumpOn()
        pumpOffTimer = os.startTimer(60)
      end

How do I retreive the arguments from the events?
Will they just be event[2], event[3], etc, depending on how many the event gives?

Ah and bonus question, it works but somehow feels clumsy:

      m.setBackgroundColor(colors.black)
      m.write("                                                                          ")
I use this to clear a single line on the screen instead of the whole screen, is there a better way to do that? Full line of spaces feels kinda strange to me.
Lyqyd #12
Posted 14 August 2013 - 08:44 PM
Yeah, the {} around os.pullEvent() means that it puts all of the return values into a table, so you index them with event[2], event[3], etc. like any other table.

You should use term.clearLine() to clear a line, as not every screen is the same size. Or m.clearLine(), I suppose, but you're usually better off redirecting to a monitor if you're drawing to it.
connerity #13
Posted 15 August 2013 - 05:30 AM
Since I draw a lot to monitors and always use m.whatever() I have to ask:
I'm not sure about the redirecting part. I guess you mean redirecting the term.clearLine() (which clears a line on the computerscreen, right?) to a monitor.
How do I do that?
LordIkol #14
Posted 15 August 2013 - 05:49 AM
Lyqyd is talking about this http://computercraft.info/wiki/index.php?title=Term.redirect
That means your Terminal is directly redirected to the monitor instead of wrapping it as perioheral and put things there

greets
Loki