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

Rednet, Wait For X Amount Of Broadcast Messages?

Started by Molo, 28 July 2013 - 06:59 AM
Molo #1
Posted 28 July 2013 - 08:59 AM
Hello,

I'm making my own version of the DW20 mining well system.
I've been writing my own code until i've now ran in to a problem.

When the Mining turtels are done the use the command:
rednet.broadcast("done")

I want the main turtel, that sends out the
rednet.broadcast("start")
command to wait for ALL mining turtels until it proceeds to the next step.

Currently its looking like this: (main turtle)

local times = 0
term.write("How many times would you like to run:")
times = read()
for i = 0, times do
   rednet.broadcast("start")
   place()
   local event, id, msg = os.pullEvent("rednet_message")
   if msg == "done" then
   remove()

So i would like the main turtle to wait for X amount of broadcasted "done" messages, is that possible?
Thanks in advance!

/Molo

EDIT: Check end of thread for solution!
Lyqyd #2
Posted 29 July 2013 - 12:44 PM
Split into new topic.
apemanzilla #3
Posted 29 July 2013 - 12:50 PM
This should work:

local times = 0
term.write("How many times would you like to run:")
times = read()
for i = 0, times do
   rednet.broadcast("start")
   place()
   for i, x do --Change x to the amount you want it to wait for
   local event, id, msg = os.pullEvent("rednet_message")
   if msg ~= "done" then
      i=i-1
   end
   end
   --Add the rest of the code here
end
Not so sure about that i=i-1 but IIRC it works fine.
EDIT: Removed derpiness
immibis #4
Posted 29 July 2013 - 05:14 PM
That doesn't work in Lua, Apemanzilla.

This is how to wait for X messages:

  for i = 1, X do
    -- code to wait for a single message goes here
  end
albrat #5
Posted 29 July 2013 - 06:39 PM

  for i = 1, X do
    loop = true
    while loop do
	  id, message = rednet.receive()
	  if message == "done" then
	   loop = false
	  end
    end
  end

Basically it starts a loop that waits for a rednet message. Checks if it is "done" then if it is… counts it and moves to the next occurance. untill the number you want is reached.
Molo #6
Posted 01 August 2013 - 01:04 PM
Been on a vacation so thats why im kinda late responding.

That doesn't work in Lua, Apemanzilla.

This is how to wait for X messages:

  for i = 1, X do
	-- code to wait for a single message goes here
  end
Well, that was easier then expected!
Thanks alot, working good! :D/>


  for i = 1, X do
	loop = true
	while loop do
	  id, message = rednet.receive()
	  if message == "done" then
	   loop = false
	  end
	end
  end

Basically it starts a loop that waits for a rednet message. Checks if it is "done" then if it is… counts it and moves to the next occurance. untill the number you want is reached.
I started testing with the one that seemed simplier and it worked, didnt test this one but it probably works to!
Thanks to both of you!

If other people are searching for the same problem, they got 2 answers to choose from ;)/>
Molo #7
Posted 01 August 2013 - 01:37 PM
Okey, the fact that the main turtle is waiting for X amounts of broadcasts SEEMS to be working but i ran into a new problem when i added that part.

It seems like the main turtle is running the remove program TWO times instead of ONE now.

The main turtle code: (I've edited the X to the amount of mining turtles I have in-game)

local times = 0
term.write("How many times: ")
times = read()
for i = 0, times do
   rednet.broadcast("start")
   place()
for i = 1, X do
   local event, id, msg = os.pullEvent("rednet_message")
   if msg == "done" then
   remove()
   checkFuel()
end
end
end

The remove function:

function remove()
   turtle.select(2)
   turtle.dig()
   turtle.up()
   turtle.select(1)
   turtle.dig()
   turtle.forward()
end

Sorry for the double post.
TheOddByte #8
Posted 01 August 2013 - 06:01 PM
Okey, the fact that the main turtle is waiting for X amounts of broadcasts SEEMS to be working but i ran into a new problem when i added that part.

It seems like the main turtle is running the remove program TWO times instead of ONE now.

The main turtle code: (I've edited the X to the amount of mining turtles I have in-game)

local times = 0
term.write("How many times: ")
times = read()
for i = 0, times do
   rednet.broadcast("start")
   place()
for i = 1, X do
   local event, id, msg = os.pullEvent("rednet_message")
   if msg == "done" then
   remove()
   checkFuel()
end
end
end

The remove function:

function remove()
   turtle.select(2)
   turtle.dig()
   turtle.up()
   turtle.select(1)
   turtle.dig()
   turtle.forward()
end

Sorry for the double post.
Hey instead of using a for loop for how many turtles you have you could do this
Spoiler

function waitForAllMessages(tIDs,hMsg)
 local tData = {}
while true do
  evt, rID, msg = os.pullEvent("rednet_message") 
   for i = 1, #tIDs do
     if rID == tIDs[i] then
        local add = true

     for k = 1,#tData do
         if rID = tData[k] then
           add = false
        end
      end

  if msg ~= hMsg then add = false end

    if add then
       nData = {}
       nData.id = rID
       nData.msg = msg
       table.insert(tData,nData)
    end
  end
   if #tData == #tIDs then return tData end
  end
 end
end
Now you could simply enter the turtle IDs and it would wait until it received a message from all of them

And now you simply could do this instead

local times = 0
term.write("How many times: ")
times = read()
for i = 0, times do
   rednet.broadcast("start")
      place()
    waitForAllMessages({1,2,3,4},"done") -- 1,2,3,4 are only example IDs
   remove()
   checkFuel()
end

Hope this was of any use ;)/>
Molo #9
Posted 01 August 2013 - 06:31 PM
Okey, the fact that the main turtle is waiting for X amounts of broadcasts SEEMS to be working but i ran into a new problem when i added that part.

It seems like the main turtle is running the remove program TWO times instead of ONE now.

The main turtle code: (I've edited the X to the amount of mining turtles I have in-game)

local times = 0
term.write("How many times: ")
times = read()
for i = 0, times do
   rednet.broadcast("start")
   place()
for i = 1, X do
   local event, id, msg = os.pullEvent("rednet_message")
   if msg == "done" then
   remove()
   checkFuel()
end
end
end

The remove function:

function remove()
   turtle.select(2)
   turtle.dig()
   turtle.up()
   turtle.select(1)
   turtle.dig()
   turtle.forward()
end

Sorry for the double post.
Hey instead of using a for loop for how many turtles you have you could do this
Spoiler

function waitForAllMessages(tIDs,hMsg)
local tData = {}
while true do
  evt, rID, msg = os.pullEven("rednet_message")
   for i = 1, #tIDs do
	 if rID = tIDs[i] then
		local add = true

	 for k = 1,#tData do
		 if rID = tData[k] then
		   add = false
		end
	  end

  if msg ~= hMsg then add = false end

	if add then
	   nData = {}
	   nData.id = rID
	   nData.msg = msg
	   table.insert(tData,nData)
	end
  end
   if #tData == #tIDs then return tData end
  end
end
end
Now you could simply enter the turtle IDs and it would wait until it received a message from all of them

And now you simply could do this instead

local times = 0
term.write("How many times: ")
times = read()
for i = 0, times do
   rednet.broadcast("start")
	  place()
	waitForAllMessages({1,2,3,4},"done") -- 1,2,3,4 are only example IDs
   remove()
   checkFuel()
end

Hope this was of any use ;)/>

i'm gonna give this a try even tho i don't like the fact that i need to enter every turtles ID.
I entered the code into my program but i get a error.
Don't really know what the problem is cause what its complaining about is there.

Error: bios:338: [string "startup"]:19: 'then' expected


function waitForAllMessages(tIDs,hMsg)
local tData = {}
while true do
  evt, rID, msg = os.pullEven("rednet_message")
   for i = 1, #tIDs do
	 if rID = tIDs[i] then <-- THIS IS LINE 19 IN MY CODE
		local add = true

	 for k = 1,#tData do
		 if rID = tData[k] then
		   add = false
		end
	  end

  if msg ~= hMsg then add = false end

	if add then
	   nData = {}
	   nData.id = rID
	   nData.msg = msg
	   table.insert(tData,nData)
	end
  end
   if #tData == #tIDs then return tData end
  end
end
end

I've currently have a working "turtle machine" with a slight "debuff"
I only use one turtle to broadcast the "done" command but it has a delay of "5" so other turtles can catch up if they are after. Would just be smarter and more optimized if i could get it to wait for all "done" then continue directly.

I appreciate all help I'm getting here!
/Molo
TheOddByte #10
Posted 01 August 2013 - 08:17 PM
Sorry about that.. Forgot an '='
Fixed it

function waitForAllMessages(tIDs,hMsg)
local tData = {}
while true do
  evt, rID, msg = os.pullEvent("rednet_message")
   for i = 1, #tIDs do
	 if rID == tIDs[i] then -- Changed '=' to '==' that's how simple the error was..
		local add = true

	 for k = 1,#tData do
		 if rID = tData[k] then
		   add = false
		end
	  end

  if msg ~= hMsg then add = false end

	if add then
	   nData = {}
	   nData.id = rID
	   nData.msg = msg
	   table.insert(tData,nData)
	end
  end
   if #tData == #tIDs then return tData end
  end
end
end

apemanzilla #11
Posted 02 August 2013 - 11:06 AM
That doesn't work in Lua, Apemanzilla.

This is how to wait for X messages:

  for i = 1, X do
    -- code to wait for a single message goes here
  end
I know too many programming languages, getting them mixed up :S
Bubba #12
Posted 02 August 2013 - 11:15 AM
Sorry about that.. Forgot an '='
Fixed it

...snip...
Line 4:  evt, rID, msg = os.pullEven("rednet_message")
...snip...

Check line 4 too. Should be os.pullEvent not os.pullEven
Molo #13
Posted 02 August 2013 - 02:13 PM
Sorry about that.. Forgot an '='
Fixed it

function waitForAllMessages(tIDs,hMsg)
local tData = {}
while true do
  evt, rID, msg = os.pullEven("rednet_message")
   for i = 1, #tIDs do
	 if rID == tIDs[i] then -- Changed '=' to '==' that's how simple the error was..
		local add = true

	 for k = 1,#tData do
		 if rID = tData[k] then
		   add = false
		end
	  end

  if msg ~= hMsg then add = false end

	if add then
	   nData = {}
	   nData.id = rID
	   nData.msg = msg
	   table.insert(tData,nData)
	end
  end
   if #tData == #tIDs then return tData end
  end
end
end

Sorry about that.. Forgot an '='
Fixed it

...snip...
Line 4:  evt, rID, msg = os.pullEven("rednet_message")
...snip...

Check line 4 too. Should be os.pullEvent not os.pullEven


Thanks guys, got it working!
There were some similiar problems but just fixed that with adding another '=' also.
Now working good! :D/>

If anyone got a version where i don't need to enter all mining turtles ID's it would be lovely, I'm thinking of expanding this quite much and it would be kinda annoying to add all computers ID's to the list.
TheOddByte #14
Posted 02 August 2013 - 03:14 PM
Sorry about that.. Forgot an '='
Fixed it

...snip...
Line 4:  evt, rID, msg = os.pullEven("rednet_message")
...snip...

Check line 4 too. Should be os.pullEvent not os.pullEven

Ok I was typing this on my phone so I didn't check it properly, But thx for noticing :D/>
Molo #15
Posted 04 August 2013 - 10:48 AM
I've been trying some of the loops i've been given in this thread.

local times = 0
term.write("How many times: ")
times = read()
for i = 0, times do
   rednet.broadcast("start")
      place()
for i = 1, X do
   loop = true
	  while loop do
		 id, message = rednet.receive()
		 if message == "done" then
		 loop = false
		 remove()
		 checkFuel()
	  end
   end
end
end

That's what I currently got.
The problem is that…
Whenever it receives ONE "done" command, it moves on to the remove() and checkfuel() commands.
This means it sometimes use the remove() and checkfuel() command like 5 times if I have 5 mining turtles.
I want it to wait until it have received all, for example 5 done commands until it does anything.

This is probably possible, and i've probably have a small "error" somewhere, but what am I doing wrong ?
It feels like I'm on the finish line!

/Molo
campicus #16
Posted 04 August 2013 - 12:23 PM
Could you use something like:


local nCount = 0

if message == "done" then
    nCount = nCount + 1
    if nCount == 5 then
        remove()
        checkFuel()
    end
end

Does rednet.broadcast() just send something once or multiple times? If multiple times, you might need variables 'message1', 'message2'… etc for your different turtles.

Hope this helps until a master comes along
Molo #17
Posted 04 August 2013 - 12:48 PM
Could you use something like:


local nCount = 0

if message == "done" then
	nCount = nCount + 1
	if nCount == 5 then
		remove()
		checkFuel()
	end
end

Does rednet.broadcast() just send something once or multiple times? If multiple times, you might need variables 'message1', 'message2'… etc for your different turtles.

Hope this helps until a master comes along

There are 5 turtles each sending one "done" broadcast when they are done.
I want the mainturtle to wait until all 5 broadcasted "done" messages are received.
I didn't get your code to work, it simply kept running the remove() directly after it had used the place() command.
Molo #18
Posted 04 August 2013 - 02:44 PM
I don't know how many different options and small edits in the code until I finally got it working!
I wanna say thanks to all who have taken their time and replied to this thread :)/>

The final, working part of the code to whoever has the same problem:
Change the 2 X's to how many mining turtles/how many 'done' commands you want.


local times = 0
term.write("How many times: ")
times = read()
for i = 0, times do
   rednet.broadcast("start")
   place()
	  for i = 1, X do
	  loop = true
		 while loop do
			id, message = rednet.receive()
			if message == "done" then
			loop = false
			   if i == X then
				  remove()
				  checkFuel()
			   end
			end
	  end
end
end
shaynemk #19
Posted 27 August 2013 - 05:51 PM
For the sake of variety, here is my take on this:

Spoiler
function mine()
   rednet.broadcast("mine") -- broadcast to all turts in range
   x = 0
   while x < 16 do -- when all turtles are done, they send "done"; count and exit when we get enough of these msgs
	  id, msg = rednet.receive()
	  if id > 0 and id < 17 and msg == "done" then -- controller runs on 0, turtles are 1-16.
		 x = x + 1
	  end
   end
   if DEBUG == 1 then print(x," turtles report complete") end -- extra info printed to screen on request
end

This makes it significantly faster and IMO more reliable than just having a general timer on the controller, as it just has to wait until it receives all the msgs from the turtles. That leaves the next time waster in the program on the turtles, so for that I set this up:

Spoiler
-- define the controller ID here
BOSS = 0

-- print extra msgs if enabled
DEBUG = 0

-- place the mining well in front of us
function dropMW()
   if turtle.getItemCount(1) == 0 then
	  print("Error: Place Mining Well in Slot 1.")
   else
	  turtle.select(1)
	  turtle.place()
   end
end

-- speedily clear inventory
function clearInv()
   if DEBUG then print("clearing inventory") end
   for i = 1,10 do
	  turtle.select(i)
	  turtle.dropDown()
   end
   turtle.select(1)
end

-- are there items in my inventory?
function checkInv()
   if DEBUG then print("checking inventory") end
   for i = 1,16 do
	  if turtle.getItemCount(i) > 0 then
		return true
	  end
   end
   turtle.select(1)
   if DEBUG then print("inventory cleared") end
   return false
end

-- fetch Mining Well
function getMW()
   turtle.select(1)
   turtle.dig()
end

------- main()
-- drop the MW so we can scar the earth
dropMW()
sleep(1)

-- dynamically check the inventory, and clear it automatically when items are detected
while checkInv() do
   clearInv()
end

-- nab the MW and tell the boss we are done
getMW()
rednet.send(BOSS,"done")

I'm not going to claim that it is perfect, there are a few optimizations that could still be done, but i think it works rather nicely as it is.

A few notes: the quarry controller runs on #0, whereas the turtles are #s 1-16. The turtles are set to basically execute directly in the shell the command they receive via broadcast, which is why it is imperative to add in security, as simple as ID checking is sufficient for my purposes. This makes it nice, as once I update the code on my server terminal, I can remotely copy updates to all the folders and tell them to reboot if necessary via the controller.

Like any piece of code, there are only a billion and a half different ways one could go about doing it.