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

parallel API workaround

Started by Milchschokolade, 20 June 2013 - 12:01 PM
Milchschokolade #1
Posted 20 June 2013 - 02:01 PM
I am writing a custom Program to control my entire Base via rednet. To do this, I want to have the main program running useing coroutines to pause and resume different functions while the computer is also waiting for an event to happen. If an evvent occurs, the currently running coroutine is supposed to run until it hits coroutine.yield() before the program reacts to the occured event. I first looked into the parallel API and used that in my Code, but after reading a bit more in the wiki and this Forum, I found out, that the parallel API itsself is useing Coroutines which means, that it would probably mess up with the rest of my program. Now I am looking for a way to work around this. Most important is, not to loose an occuring Event. I have all coroutines, that are not dead in an array co={}.
The loop function is not yet finished, but it will handle my coroutines. I am looking for a way to work around the parallel API so I don't loose either the state of my coroutines nor an event, Please, help me…

--[Main Program]--
local function event()
  e,p1,p2,p3,p4,p5=os.pullEvent()
  occ=="event"
  return e
end

--this function is part of the main program
--it gives the Main loop to control everything
function loop()
while true do
  if co[1]==0 then
	co[2]=coroutine.create(home())
	co[1]=1
  end
  for local coi=1,co[1] do
	coroutine.resume(co[coi+1])


  end
end
occ="loop"
end
while true do
  --checking for any occurance (event or feedback from the loop)
  parallel.waitForAny(event(),loop())
  --the next part of code decides how to handle the occurance
--not yet implemented
end

Complete Code:
Spoiler

--[Milch Operating System Version 0.2]--
--[by Milchschokolade]--
--[Instructions:]--
--[Put 5x4 Advanced Monitors on the Ground, than the Advanced Computer]--
--[with this Program on it 1 block above the Ground and 5x4 advanced Monitors]--
--[on the other side of the Computer: (X is an Advanced Monitor, C the Computer]--
--[X.X.X.X.X. .X.X.X.X.X]--
--[X.X.X.X.X. .X.X.X.X.X]--
--[X.X.X.X.X.C.X.X.X.X.X]--
--[X.X.X.X.X. .X.X.X.X.X]--
--[Make sure, this program is saved as 'startup'. Label the Computer!]--
--[Run the 'id' program on the Computer and put the resulting ID]--
--[(the number only, without '#') as Value behind the 'id=']--
id=
--[reboot the computer]--
--[To add a new Program, make sure to change the Global Variable]--
--['prognumb' to the correct number of programs.]--
--[Color Settings:]--
mtc=265	 --Main Text Color
utc=1	   --Secondary Text Color
stc=32	  --Text Colour to show Sucess
ftc=16384   --Text Color to show faliure
mbc=32768   --Main Background Color
mc=256	  --Menu Color
mct=1	   --Menu Text Color
bc1=18	  --Main Button Color
bc2=32768   --Secondary Button Color

--[Global Variables]--
prog={}			   --List of all programs.
co={0}				 --List of currently running/unfinished coroutines, co[1] is the number of coroutines
selecP=(home)
prognumb=0
version=0.2
lma={}
loGlength=0
loG={}
e,p1,p2,p3,p4,p5=nil --global event variables (see function event)
occ==" "			 --this variable is used to tell the main program, if an event has happened or
					 --if something else happened
button=" "		   --gives the pressed Button					
--[Global Functions]--
--lmonctrl()
--this function updates the left Monitor and handles its graphical interface
local function lmonctrl()
  lmon.clear()
  for local lmonctrli=1,5 do
	lmon.setCursorPos(1,lmonctrli)
	lmon.setBackgroundColor(mc)
	lmon.write("												  ")
  end
  lmon.setCursorPos(1,1)
  lmon.setTextColor(mct)
  lmon.write("Milch OS Version "..tostring(version))
  --reboot button
  lmon.setCursorPos(44,4)
  lmon.setBackgroundColor(bc1)
  lmon.write("	  ")
  lmon.setCursorPos(44,5)
  lmon.setTextColor(bc2)
  lmon.write("Reboot")
  --settings button
  lmon.setCursorPos(35,4)
  lmon.write("		")
  lmon.setCursorPos(35,5)
  lmon.write("Settings")
  --console handling:
  for local lmonctrlk=6,27 do
	lmon.setCurorPos(1,lmonctrlk)
	for local lmonctrlm=1,lma[lmonctrlk-5][1] do
	  lmon.setBackgroundColor(lma[lmonctrlk-5][(lmonctrlm*2)+(lmonctrlm-1)])
	  lmon.setTextColor(lma[lmonctrlk-5][lmonctrlm*2)+(lmonctrlm-1)+1])
	  lmon.write(lma[lmonctrlk-5][lmonctrlm*2)+(lmonctrlm-1)+2])
	end
  end
end
--rmonctrl()
--this function updates the right Monitor and handles its graphical interface
local function rmonctrl()
  rmon.clear()
  for local rmonctrli=1,5 do
	lmon.setCursorPos(1,rmonctrli)
	lmon.setBackgroundColor(mc)
	lmon.write("												  ")
  end
  rmon.setCursorPos(1,1)
  rmon.setTextColor(mct)
  rmon.write(tostring(SelecP))
  --home button
  rmon.setCursorPos(46,4)
  rmon.setBackgroundColor(bc1)
  rmon.seTextColor(bc2)
  rmon.write("	")
  rmon.setCursorPos(46,5)
  rmon.write("Home")
end
--event()
--this function constantly checks if an event is occuring
local function event()
  e,p1,p2,p3,p4,p5=os.pullEvent()
  occ=="event"
  return e
end
local function buttontest(a,b,c)
  button==" "
  if a=="left" then
  --Button on the left Monitor (Control Buttons)
  --'settings' Button
	if b<43 then
	  if b>34 then
		if c<6 then
		  if c>3 then
			button=="settings"
		  end
		end
	  end
	end
  --'reboot' Button
	if b<51 then
	  if b>43 then
		if c<6 then
		  if c>3 then
			button=="reboot"
		  end
		end
	  end
	end
  elseif a=="right" then
  --Button on the right Monitor (functions)

  else
	loGadd(loG,"Error: Wrong Parameter in function buttontest()")
  end
end
local function loGadd(a,B)/>/>/>
  table.insert(a,B)/>/>/>
  loGlength=loGlength+1
end
--[Available Programs]--
--Programs are Managed in an array
--home
--this function is used if no other Program is selected
--It acts like a desktop
--Important: all this Program MUST be the first to get added to the 'prog' array
local function home()
  rmon.setTextColor(bc2)
  rmon.setBackgroundColor(bc1)
  prognumb=(table.maxn(prog))/2
  if prognumb<11 then
	for local homei=2,prognumb do
	  rmon.setCursorPos(1,(homei*2)+3)
	  rmon.write(prog[homei*2])
	end
  else
	local f=math.floor(prognumb/10)
	for  d=1,f do
	  for local homei=((d-1)*10)+2,((d-1)*10)+11 do
		rmon.setCursorPos(1,(homei*2)+3)
		rmon.write(prog[homei*2])
	  end

	  if d==1 then
		rmon.setCursorPos(1,26)
		rmon.write("Next Page")
	  elseif d==f then
		rmon.setCursorPos(1,27)
		rmon.write("Previous Page")
	  else
		rmon.setCursorPos(1,26)
		rmon.write("Next Page")
		rmon.setCursorPos(1,27)
		rmon.write("Previous Page")
	  end
	  rmon.setCursorPos(1,25)
	  rmon.setTextColor(utc)
	  rmon.setBackgroundColor(mbc)
	  rmon.write("Page "..tostring(d).."/"..tostring(f))
	  rmon.setTextColor(bc2)
	  rmon.setBackgroundColor(bc1)
	  coroutine.yield()
	  rmonctrl()
	end
  end
end
table.insert(prog,home())
table.insert(prog,"Home")
--[Main Program]--

--this function is part of the main program
--it gives the Main loop to control everything
function loop()
while true do
  if co[1]==0 then
	co[2]=coroutine.create(home())
	co[1]=1
  end
  for local coi=1,co[1] do
	coroutine.resume(co[coi+1])


  end
end
occ="loop"
end
loGadd(loG,"MilchOS "..tostring(version).."started")
rednet.open("back")
term.clear()
term.setCursorPos(1,1)
term.setTextColor(256)
term.setBackgroundColor(32768)
lmon=peripheral.wrap("left")
rmon=peripheral.wrap("right")
term.write("Welcome to MilchOS Version "..tostring(version))
lmonctrl()
rmonctrl()
while true do
  --checking for any occurance (event or feedback from the loop)
  parallel.waitForAny(event(),loop())
  --the next part of code decides how to handle the occurance
  if occ=="event" then
	if e=="monitor_touch" then
	  buttontest(p1,p2,p3)
	
	elseif e=="rednet_message" then
	
	else
	
	end
  end
  --updating both monitors
  lmonctrl()
  rmonctrl()
end
Lyqyd #2
Posted 20 June 2013 - 02:12 PM
Split into new topic.
ElvishJerricco #3
Posted 20 June 2013 - 02:23 PM
I don't understand what it is you lose by using the parallel API. BTW, in the code posted above, you using parallel.waitForAny wrong. You pass it the functions you want to wait for, you don't call them.


parallel.waitForAny(event, loop) -- right
parallel.waitForAny(event(), loop()) -- wrong
Bomb Bloke #4
Posted 20 June 2013 - 06:22 PM
The idea is that both co-routines run until they want to pull events. When they do, both co-routines end up getting both events.

Speaking as one who doesn't know what you want to do with this program, probably the easiest implementation is to rig things so that neither function ends. Eg:

--[Main Program]--
local function event()
  while true do
    e,p1,p2,p3,p4,p5=os.pullEvent()
    -- You probably want to put some "reactions" to the events here. Eg:
    -- if e == "timer" then waiting = false end
  end
end

--this function is part of the main program
--it IS the Main loop to control everything
local function loop()
  while true do
    -- Do "occurance handling" stuff here based on whatever e,p1,p2,etc are set to at the time.
    -- Feel free to use event-consumers like read() and turtle.forward() etc.
  end
end

local e,p1,p2,p3,p4,p5 = "","","","","",""
parallel.waitForAny(event,loop)
ElvishJerricco #5
Posted 20 June 2013 - 07:23 PM
The idea is that both co-routines run until they want to pull events. When they do, both co-routines end up getting both events.

Speaking as one who doesn't know what you want to do with this program, probably the easiest implementation is to rig things so that neither function ends. Eg:

--[Main Program]--
local function event()
  while true do
    e,p1,p2,p3,p4,p5=os.pullEvent()
    -- You probably want to put some "reactions" to the events here. Eg:
    -- if e == "timer" then waiting = false end
  end
end

--this function is part of the main program
--it IS the Main loop to control everything
local function loop()
  while true do
    -- Do "occurance handling" stuff here based on whatever e,p1,p2,etc are set to at the time.
    -- Feel free to use event-consumers like read() and turtle.forward() etc.
  end
end

local e,p1,p2,p3,p4,p5 = "","","","","",""
parallel.waitForAny(event,loop)

FYI what you have here won't put the event data into the locals declared near the bottom. For a function to use and upper local, it has to be declared before the function.

Anyway the best way to use parallel is to create functions that each individually act as event receivers.


parallel.waitForAny(function()
    while true do
        local eventTable = { os.pullEvent() }
        -- do something with the event
    end
end, function()
    while true do
        local eventTable = { os.pullEvent() }
        -- do something else. Both functions are getting the same event.
    end
end)
theoriginalbit #6
Posted 21 June 2013 - 12:15 AM
I completely agree with Bomb Bloke. I am unsure what you're trying to do exactly, but rigging your functions correctly so they don't end, or using parallel.waitForAll if you do want some to end, is your best bet. If these are not what you want to do then you will have to be a little clearer with what you want.

Anyway the best way to use parallel is to create functions that each individually act as event receivers.


parallel.waitForAny(function()
	while true do
		local eventTable = { os.pullEvent() }
		-- do something with the event
	end
end, function()
	while true do
		local eventTable = { os.pullEvent() }
		-- do something else. Both functions are getting the same event.
	end
end)
That is exactly the same code as what Bomb Bloke posted except that it uses anonymous functions, which can be quite bad for readability….
ElvishJerricco #7
Posted 21 June 2013 - 02:54 AM
That is exactly the same code as what Bomb Bloke posted except that it uses anonymous functions, which can be quite bad for readability….

Right I was just… I don't really know what I was thinking. Sorry bout that.
GopherAtl #8
Posted 21 June 2013 - 07:07 AM
your code had one important fix that seems to be getting overlooked. Bomb's loop function did not ever wait for events; it's important that all coroutines do this, or you'll get failure to yield. They'd don't necessarily have to call pullEvent directly, but it's a bit more than allowed to call functions that eat events; you absolutely must be calling functions that yield and wait for events almost continuously to avoid getting terminated for a failure to yield.