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

Need help recieveing several rednet messages simultaneously

Started by Beans, 20 June 2013 - 03:19 PM
Beans #1
Posted 20 June 2013 - 05:19 PM
I have created a program for monitoring the internal water tanks of combustion engines using the open cc sensors.
The program is designed to run a single bank of 4 engines, shutting them down if the coolant levels fall below a certian level. I currently have 7 sets of engines running on 7 computers all transmiting thier data via rednet. My problem is that I am attempting to code a seperate computer outside my plant to monitor the information from all 7 computers simultaneously. The code I have been using up to this point seems to recieve the messages from 1 or 2 computers ok but bogs down after that. It seems like there are too many messages coming in at once to be processed (but I am a noob so I could be totally wrong). I am new to coding so any pointers would be great.

Here is my code so far. been testing the code on only 3 of the 7 incoming messages.


m = peripheral.wrap("top") -- monitor to view information on
rednet.open("right")

while true do
  event, id, msg, dis = os.pullEvent("rednet_message")

  if id == 114 then
	m.setCursorPos(1,1)
	m.write("Unit 1:")
	m.setCursorPos(3,2)
	m.clearLine()
	m.write(msg)
  end

  if id == 115 then
	m.setCursorPos(1,3)
	m.write("Unit 2:")
	m.setCursorPos(3,4)
	m.clearLine()
	m.write(msg)
  end

  if id == 116 then
	m.setCursorPos(1,5)
	m.write("Unit 3:")
	m.setCursorPos(3,6)
	m.clearLine()
	m.write(msg)
  end

sleep(0.5)
end
Edited by
Lyqyd #2
Posted 20 June 2013 - 10:47 PM
Split into new topic.
Bomb Bloke #3
Posted 20 June 2013 - 11:46 PM
Things will improve if you remove the sleep statement (the pullEvent statement is already yielding, so there's no need for it), but there's still potential to miss messages.

Because it's the monitoring computer that "wants" the data, I'd recommend having it ask each other computer in turn what their statuses are, have them reply, then move on to the next machine. Having many machines trying to spam the one system at once only works if you can afford to drop packets, and there's no reason for that here.

On the monitoring computer, you'd use something like:

local m = peripheral.wrap("top") -- monitor to view information on
rednet.open("right")

local engineComp = {114,115,116}  -- Add computer IDs to this table's list as need be.
-- Eg, "engineComp[1]" is 114, "engineComp[2]" is 115, and so on.

local incomingID,message = 0,""   -- Pre-declare variables as local.

while true do
  for i=1,#engineComp do   -- We'll check each remote computer in turn.
    -- On the first iteration of this loop, "i" is 1.
    -- With each repetition it goes up by one, until it would exceed
    -- the amount of entries in our "engineComp" table, at which 
    -- point the loop ends.

    rednet.send(engineComp[i], "Gimme your status!")
    incomingID,message = rednet.receive(5)  -- Wait up to five seconds for the terminal to answer.

    if incomingID ~= engineComp[i] then  -- Ignore messages from any system other then the one we're asking.
      m.setCursorPos(1,i*2-1)
      m.write("Unit "..i..":")
      m.setCursorPos(3,i*2)
      m.clearLine()
      m.write(message)
    end
  end

  sleep(5) -- Having checked all computers, we might want to pause a moment, maybe.
end

The seven other machines would need to be rigged to respond to these information requests.

rednet.open("whereever")

local status = ""   -- Pre-declare as local to this program.

local function server()
  local incomingID,message = 0,""  -- These variables are only needed in this function.
  while true do
    incomingID,message = rednet.receive()   -- Wait until a message is received.
    sleep(1) -- Wait just a moment, so the requesting machine has time to get ready for our response.

    if message == "Gimme your status!" then
      rednet.send(incomingID,status)
    end

    -- Maybe check for other messages, like an engine shutdown request?
  end
end

local function main()  -- This function does all the rest of the machine's work.
  while true do
    -- Here you monitor the sensor, start/stop the engine accordingly, and update the "status"
    -- variable so that the "server()" function can return it to the requesting machine.
  end
end

parallel.waitForAny(server, main)  -- Causes the system to start running both functions at once.
                                   -- This allows us to get rednet messages while doing something else.

If ALL you want this for is to see if engines are on or off, you might be better off using wireless transmitters.
theoriginalbit #4
Posted 20 June 2013 - 11:56 PM
Before the ninja:
I have a feeling that the main reason would be that you have the sleep in there, so messages are coming in at a quicker rate than they can be processed, now if you remove the sleep the program will not crash because you have the os.pullEvent in the code, so the code will yield to allow other programs and computers to run :)/>

After the ninja:
Also the sleep function does clear the event queue, which means that while the sleep is running you can miss quite a lot of messages!

I agree with Bomb Bloke, requesting the status is probably the best way, there is less rednet spam then. Although I would probably reduce the wait time to 1 second instead of 5.
There is another method of doing what you want, but I think by far, the easiest way is what Bomb Bloke suggested.
Beans #5
Posted 21 June 2013 - 02:28 AM
This is exactly what I was looking for, thank you. One thing I am still unclear on is the perpous of the line

local incomingID,message = 0,"" – Pre-declare variables as local.

and,

local incomingID,message = 0,"" – These variables are only needed in this function.

could you explain what thiese are for exactly?
Bomb Bloke #6
Posted 21 June 2013 - 03:36 AM
When you first make use of a variable, Lua from then on knows that whatever name you gave it has whatever value you assigned to it.

If you just write:

a = 1

… and run it, the variable "a" will be assigned the value of 1.

When the program ends, that doesn't change. Lua is still keeping track of that variable. You can run a completely different program on your ComputerCraft computer, and unless that makes changes to "a", it'll still be 1. It'll remember it until the computer reboots.

When you define a variable as "local", that tells Lua that you only want the variable for a certain amount of time. For example, if your program goes along the lines of:

local a = 1

… then Lua will only track that variable while the program is running. As soon as the program finishes, the variable gets discarded.

You can further isolate variables by declaring them as local in a loop, or function - in these cases, the variables get discarded when those loops or functions end.

Note that functions can (and usually should) also be declared as local - otherwise, they will stay loaded in memory when your programs end, and other programs will be able to call them! This can lead to some very "erratic" results if you accidentally declare a function to use the same name as one of the "default" ones (eg "read()")!

(On the other hand, if there are functions you find yourself writing on a regular basis, you can make use of this feature to make simple APIs for yourself.)

You're probably thinking that it's easier not to worry about this, and usually, it is. But sticking with bad habits while writing simple programs will likely lead to big problems when writing complex programs.

Another benefit to local variables is that they allow you to use the same names multiple times in the same program in order to represent different values. For eg:

local function a()
  for i=1,10 do
    b()
  end
end

local function b()
  for i=1,10 do
    print(i)
  end
end

a()

"for" loops automatically set up their counter variables ("i" in this case) as local to the loop. You can see that the "a()" function sets up a loop using "i" as its counter, which then calls "b()", which does the exact same thing. But because they're local, the two different "i" variables don't interfere with each other.

You'll also notice that you can condense your declarations into a single line by use of commas. For example, this:

x=1
y=1
z=3

… can be shrunk to this:

x,y,z = 1,1,3
GopherAtl #7
Posted 21 June 2013 - 07:01 AM
Things will improve if you remove the sleep statement […], but there's still potential to miss messages.

I'm slightly confused by this claim. If your program or coroutine doesn't call sleep() or any other functions that eats events (sleep, read, http.get, http.post, rednet.receive, os.pullEvent with an event filter parameter (ex, "os.pullEvent('char')"), turtle.basicallyeverything… am I forgetting some?) then you should never miss any events, period. And having it send requests first would increase the total rednet traffic, not decrease it, unless whatever is generating the events was sending them over and over for no reason except on the chance that someone might not have gotten it.
Bomb Bloke #8
Posted 21 June 2013 - 07:56 AM
According to the wiki, rednet.receive() won't catch messages unless you're waiting for them when they're sent - that article makes out that there's no way around this. The event page implies it doesn't have this issue, but I've not tried it that way. Either way, there could stand to be some disambiguation.

In the original example, the engine-controllers simply messaged the server when they felt like it - we don't know how often, but I'll bet it was going to be as often as Beans was checking his sensors, and I'll also bet that was pretty fast.

This'd result in the need to manually time things so that the receipient computer didn't get flooded (even if the event queue is acting as a buffer, it's got its limits). To my mind, having the server machine ask for the information when it needs it is "neater" then that alternative, and it allows for more flexibility in that you can send engine shutdown commands etc to remote systems as well.
GopherAtl #9
Posted 21 June 2013 - 08:08 AM
Ah, ok, I see the source of confusion then. To be clear, if rednet.receive is the only event-pulling function you call in a loop, it won't miss any events, and neigher will os.pullEvent(). It's mixing event-eaters, that wait for a specific event type, which I listed earlier, that can cause events to be missed. Also, receive must have been called before the message was sent, of course; there is no buffer for it to pluck past events out of.

I agree "throttling" the rate of data being sent would be a good idea, but I don't see a reason to do this with explicit server requests, rather than just having the sensor systems send info less frequently. You might want sensors to send critical data without waiting sometimes, so the server should be prepared to handle unexpected messages at any time.
Bomb Bloke #10
Posted 21 June 2013 - 08:30 AM
To be clear, if rednet.receive is the only event-pulling function you call in a loop, it won't miss any events, … Also, receive must have been called before the message was sent, of course; there is no buffer for it to pluck past events out of.
Huh? That's a contradiction. If it relies on events, and shouldn't miss any, then it shouldn't matter if you call it after a message is sent, right?

(Personally I've found it's wont to miss messages even if it is waiting for them when they're sent, every few hundred transmissions or so.)

Edit: So I got around to firing up MineCraft to test which is the case. Turns out, you can indeed call "rednet.receive()" after a message was sent, and retrieve it. The message is buffered within the event queue (rather then being caught and discarded by some inner working of the rednet API), it's just a case of making sure nothing else pulls from that queue before the receive function does - which is the tricky bit.

I agree "throttling" the rate of data being sent would be a good idea, but I don't see a reason to do this with explicit server requests, rather than just having the sensor systems send info less frequently. You might want sensors to send critical data without waiting sometimes, so the server should be prepared to handle unexpected messages at any time.
In some cases, yes. In this case the computers hooked up to the engines are already reacting to the data they're passing on, so I'd say there's no such need: Given this, the pros (remote control of the other systems from one location + all throttling managed by one system instead of seven) outweigh the cons (can't override the monitoring system's throttling remotely). You could turn off the monitoring system and it wouldn't matter in the slightest. It's not a server.

Though I doubt Beans would have much trouble rigging my suggested code to allow any of his computers to accept messages from any of his other computers at any time, if he so wanted. I can only guess as to what suits him.
Beans #11
Posted 21 June 2013 - 09:47 AM
Bomb Bloke is correct in his assumption about my engine monitoring program. As I am new to coding I was not aware that I could run multiple functions at the same time. The way I had set it up is that the monitoring computers would check the water level inside all four engines every two seconds and then place the levels into a rednet message and broadcast it. My reciever was then set up as I posted and it got bogged down with all the randomly incoming messages. the idea of calling for the info works well for my application and, now that I am thinking that way, allows me to have greater control over all 7 engine sets from the main computer.

thanks again for your explinations! They are helping me a great deal.
Bomb Bloke #12
Posted 21 June 2013 - 10:35 AM
Note that Gopher is right in pointing out that the sleep statement was the only thing jamming up your code; I'm fairly sure it'd work as-is if you pulled that one line, especially now you've mentioned the delay on the transmissions. I just happen to think you're better off with the re-write either way.
GopherAtl #13
Posted 21 June 2013 - 11:57 AM
yeah, I wasn't trying to say there weren't ways to improve the code by rewriting it, just that the sleep was the only actual problem in the original code. :)/>
Beans #14
Posted 21 June 2013 - 12:04 PM
Well it's good to know I was on the right track! In answering this you guys actually answered another question I had. I couldnt figure out why people were putting "local" in front of thier codes so cheers for that as well!

ps. the new programs are running smoothly
GopherAtl #15
Posted 21 June 2013 - 12:07 PM
anything not explicitly declared as local is global, which can muck stuff up in unexpected ways. Unless you specifically want a variable to be global, you should usually make them local.
johnnic #16
Posted 23 June 2013 - 12:15 AM
You could use wired modems to hook all these computers together, each computer/wired modem having a different channel.
theoriginalbit #17
Posted 23 June 2013 - 03:49 AM
You could use wired modems to hook all these computers together, each computer/wired modem having a different channel.
This does not at all help with the problem the person was experiencing, no relation to the problem at all actually. Thank you for trying to help, but please make sure you understand the problem at hand before making a suggestion on how to fix it.