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

os.pullEvent with multiple loops

Started by Legless, 16 October 2012 - 07:41 AM
Legless #1
Posted 16 October 2012 - 09:41 AM
Hi guys. Just a small question. Need an advice.

There is one room with a lot of Ind.Craft2/Redpower stuff. Two cobblestone generators, recycler, mass-fabricator, filters, retrievers, item detectors, lights and so on. Most of cables does an infinity loop of on/off signals with various time intervals to machines. And I need these being controlled by CC terminal. It should be controlled by pressing a key (on/off).
But I have encountered a problem. As I said, most of a signals are looped. And I can't switch on some machines at the same time.


local keys = {"1", "2", "3", "4", "5", "6"}
local wires = {colors.white, colors.orange, colors.magenta, colors.lightBlue, colors.yellow, colors.lime, colors.pink, colors.gray, colors.lightGray, colors.cyan, colors.purple, colors.blue, colors.brown, colors.green, colors.red}
local num = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384}

local function CobGen1()
rs.setBundledOutput("back", colors.combine(rs.getBundledInput("back"), wires[1]))
sleep(1)
rs.setBundledOutput("back", colors.subtract(rs.getBundledInput("back"), wires[1]))
sleep(1)
end

local function CobGen2()
rs.setBundledOutput("back", colors.combine(rs.getBundledInput("back"), wires[3]))
sleep(1)
rs.setBundledOutput("back", colors.subtract(rs.getBundledInput("back"), wires[3]))
sleep(1)
end

local function Manage()
while true do
  local event, key = os.pullEvent()

   if event == "char" and key = keys[1] then
	rs.setBundledOutput("back", colors.subtract(rs.getBundledInput("back"), wires[2]))
  
	   while true do
	   CobGen1()
	   end
	else
	  rs.setBundledOutput("back", colors.combine(rs.getBundledInput("back"), wires[2]))
	end
  
   if event == "char" and key = keys[2] then
	rs.setBundledOutput("back", colors.subtract(rs.getBundledInput("back"), wires[4]))
  
	   while true do
	   CobGen2()
	   end
	 else
	rs.setBundledOutput("back", colors.combine(rs.getBundledInput("back"), wires[4]))
   end
end
end

Manage()

It is an example of a code. Of course there will be much more functions and conditions.
May be I should make a lot of events and then do a parallel,waitForAny(Event1, Event2,…..)? Is it possible?
Any ideas?
Doyle3694 #2
Posted 16 October 2012 - 01:39 PM
no 1 os.pullEvent() is best. though if you are going to only use char then filter the pullEvent to aonly chars
Legless #3
Posted 16 October 2012 - 02:19 PM
no 1 os.pullEvent() is best. though if you are going to only use char then filter the pullEvent to aonly chars
The poing is that I will use not only chars, but redstone events (item detectors). And as I said above, after a launching a first cobblegenerator for example, I can't switch on the second. Because the cycle is not finished. I don't see any optimal way, except a lot of independed os.pullEvents() with parallel.api (I doubt it will work properly)
Doyle3694 #4
Posted 16 October 2012 - 02:22 PM
it won't work. parallel api waits for the next courutine in the function and does not run them legit parallel. WHen i come home in like 1 h I will see if I can write some sample code to show you
Doyle3694 #5
Posted 16 October 2012 - 02:31 PM

states = {false,false,false,false}
keys = {2,3,4,5}
colors = {colors.white, colors.orange, colors.magenta, colors.lightBlue}

event, num = os.pullEvent()
if event == "key" then
   for i=1,#keys do
	  if num == keys[i] then
		 if state[i] then
			rs.setBundledOutput("back", colors.subtract(rs.setBundledInput("back"), wires[i]))
		    state[i] = false
		 else
			rs.setBundledOutput("back", colors.combine(rs.setBundledInput("back"), wires[i]))
		    state[i] = true
		 end
	  end
   end
end
Havn't tested it, but that's waht I got on the go. will help you more when I get home
Doyle3694 #6
Posted 16 October 2012 - 04:17 PM
oh wait, you want them the turned on wires to pulse once a second… oh..
Doyle3694 #7
Posted 16 October 2012 - 04:19 PM
I think you are better off in that case to use another computer since os.pullEvent freezes the computer…. unless you are going really crazy with timeouts and stuff, but it still would be sick easy to just do another computer for the pulses
Legless #8
Posted 16 October 2012 - 05:24 PM
Yeah, I suspect that I should set another computer.
The first switches on/off os.pullEvent instanly, the second will have while-true-do cycles on parallel api. May be dependent on first one. Anyway, good idea, thanks!
Doyle3694 #9
Posted 16 October 2012 - 05:39 PM
well…
This for the reciever?

local states = {false}
local colors = {colors.white, colors.orange, colors.magenta, colors.lightBlue, colors.yellow, colors.lime, colors.pink, colors.gray, colors.lightGray, colors.cyan, colors.purple, colors.blue, colors.brown, colors.green, colors.red}

while true do
   for i=1,#states do
	  if state[i] then
   	  rs.setBundledOutput("back", colors.combine(colors[i]))
	  end
   end
   sleep(1)
  
   for i=1,#states do
	  if state[i] then
   	  rs.setBundledOutput("back", colors.subtract(colors[i]))
	  end
   end
   sleep(1)
  
   states = {}
   for i=1,#colors do
	  if rs.getBundledOutput("back", colors[i]) then
   	  table.insert(states, true)
	  else
   	  table.insert(states, false)
	  end
   end
end
Untested as well, please give me feedback if you have tested it… I don't feel like saving into appdata and running minecraft and all that :D/>/>
btw, fuck forums destroying my indentation :S
Lyqyd #10
Posted 16 October 2012 - 05:46 PM
You guys are making this waaay too complicated.

You want a key to turn on a pulsing output, and for this output to continue pulsing until another key is pressed to shut it off, correct? That's pretty easy. When you receive a key event, change a control variable (like cobbleGenOneRunning) to true, output the first pulse, and start a timer, saving the return value in something like cobbleGenOneTimer. When you receive a key event for one that's already running, set the timer variable to nil, set the output for it to false, and change the control variable to true. When you receive a timer event, check the first parameter until you find the correct one (so, check it against cobbleGenOneTimer, then cobbleGenTwoTimer, etc. In each of these timer handlers, toggle the output of the signal and start a new timer, saving the return in the same timer variable.

This means it'll keep looping through every time it gets an event. You don't need multiple functions, you don't need the parallel API, you don't even need a second computer. Just clever usage of events. :-)
Doyle3694 #11
Posted 16 October 2012 - 05:48 PM
yeah I'm not that good at timers so I'm not going to use them until some day when I'm forced to. but is that correct syntax for getting redstone inputs? I'm not sure myself even :D/>/>
Lyqyd #12
Posted 17 October 2012 - 02:34 AM
yeah I'm not that good at timers so I'm not going to use them until some day when I'm forced to. but is that correct syntax for getting redstone inputs? I'm not sure myself even :D/>/>

You're already using them. sleep() uses timers.
Fatal_Exception #13
Posted 17 October 2012 - 03:28 AM
They're not really that difficult either.


myTimer = os.startTimer(5) -- timer will fire in 5 seconds

event, arg1, arg2, arg3 = os.pullEvent()
if event == "timer" and arg1 == myTimer then
  print("My Timer fired!")
end
Doyle3694 #14
Posted 17 October 2012 - 10:33 AM
yeah I'm not that good at timers so I'm not going to use them until some day when I'm forced to. but is that correct syntax for getting redstone inputs? I'm not sure myself even :D/>/>

You're already using them. sleep() uses timers.

I realiased that yesterday when looking at how read is built up, I found sleep by misstake :P/>/>
Doyle3694 #15
Posted 17 October 2012 - 10:38 AM
They're not really that difficult either.


myTimer = os.startTimer(5) -- timer will fire in 5 seconds

event, arg1, arg2, arg3 = os.pullEvent()
if event == "timer" and arg1 == myTimer then
  print("My Timer fired!")
end
They are not that complicated for you since you have already learned them, Think abit please.
Fatal_Exception #16
Posted 17 October 2012 - 01:08 PM
If you want to be indignant and play "It's too HARD!!", be my guest. If you take a minute or two and look at the code, and think a bit yourself, you might learn something.

Call os.startTimer with the time in seconds until the timer fires, and save the return value.
Use pullEvent to check for a "timer" event with its first argument equal to the return value you saved.

Any complication with this is purely in your head, I assure you.
Doyle3694 #17
Posted 17 October 2012 - 01:23 PM
yeah I know, but the fact that you put a code without any explaination and say its really easy because you know it. Yes, I'm learning it, and I have read much about it and think I understand it now. :P/>/> But you gotto understand that things are easy when you know them, and hard when you don't know them. You wouldn't make a good teacher would you :D/>/>
Legless #18
Posted 17 October 2012 - 04:51 PM
Sorry, guys, it seems, that I didn't catch Lyqyd's and Fatal_Exception's idea in words yet. May be you have a pieces of code, demonstrating the power of timer events. Just an short example, could you?

It's completely unclear from this part.
When you receive a timer event, check the first parameter until you find the correct one (so, check it against cobbleGenOneTimer, then cobbleGenTwoTimer, etc. In each of these timer handlers, toggle the output of the signal and start a new timer, saving the return in the same timer variable.
Lyqyd #19
Posted 17 October 2012 - 05:54 PM

local cobbleGenControl, cobbleGenTimer =  false, nil
while true do
  e, p1 = os.pullEvent()
  if e == "char" and p1 == "s" then
    --start/stop cobble gen
    if cobbleGenControl then
      cobbleGenControl = false
      cobbleGenTimer = nil
      rs.setOutput("back", false)
    else
      cobbleGenControl = true
      cobbleGenTimer = os.startTimer(1)
      rs.setOutput("back", true)
    end
  elseif e == "timer" and p1 == cobbleGenTimer
    --it is time to toggle the output.
    rs.setOutput("back", not rs.getOutput("back"))
    --and restart the timer
    cobbleGenTimer = os.startTimer(1)
  end
end

This is a highly simplified example. It should be fairly easy to add other keys/timers for different outputs, or to check the current output and set different length timers for on/off durations, or to use bundled outputs, etc. This code should run fine, but I am not currently able to test it. If further explanation is required, please don't hesitate to ask.
Legless #20
Posted 17 October 2012 - 06:34 PM
Awesome! You are genius! I've caught it! There is really no need in extra loops, parallels, another computer. So simple and cool.
And every event is instant. Furthermore, I didn't know that this form is possible
rs.setOutput("back", not rs.getOutput("back"))
One question. I have to make a monitor nearby, which shows various info. Usually I make it renews every second. Could it work with parallel.api?

parallel.waitForAny(%function_with_bunch_of_instant_events%(), %monitor_renew%())
===
And how to invert this line in timer event?
rs.setBundledOutput("back", not rs.testBundledInput("back", colors.white))
It says he expected int, not boolean.
Lyqyd #21
Posted 17 October 2012 - 07:47 PM
Why not handle the monitor stuff in the same loop on its own timer?


rs.setBundledOutput("back", (colors.test(rs.getBundledOutput("back"), colors.blue) and colors.subtract(rs.getBundledOutput("back"), colors.blue) or colors.combine(rs.getBundledOutput("back"), colors.blue)))

That's nasty as a one-liner, but I think it would do the trick.