Jump to content




Help with train station project


  • You cannot reply to this topic
46 replies to this topic

#1 pietro.devo

  • Members
  • 25 posts

Posted 30 September 2015 - 11:52 PM

Hi
I'm glad to have found this minecraft mod, I am completely a noob in programming, I had zero knowledge, but little by little I made some steps and I am trying to learn.
I always play minecraft with my favorite mods like industrialcraft, buildcraft, forestry, railcraft and traincraft.
I really love Traincraft and I have build a quite important and long railroad system in my world, so I have decided to take a little challenge, to set up a computercraft-controlled railroad system.

What I'm trying to do is a system as more modular as possible, network-based and with multiple levels of functions, which can be easily adapted to various scenarios, so different kinds of stations, yards, conditions, purposes, and can be easily modified on-the-fly in one or more components.
I'm talking of multiple computers running highly specific tasks to make all system works.
This is what I thought would be the best solution, I'm working on this project, now I think I am in a really early stage, but I would like to share and discuss with all of you
and I'm here exactly to do this, you are far better than me and I would really appreciate any kind of suggestions and help from you.

Let's take a look at a train station, for example starting from the case of a terminal station

What I first made is the definition of the system in its modules, having basically two important levels, one of basic imputs, activators, detectors, peripherals etc etc and one of managers, basically where the "intelligence" of system resides.
All the system works with rednet messages that are commands, requests about states, updates, events etc etc

Here is what I have defined, I will translate and explain:

gestore_annunci
gestore_arrivi
gestore_display
gestore_partenze
gestore_scambi
gestore_segnali
gestore_stati
gestore_tragitti
gestore_transiti

gestore_annunci is "ads_manager", it is the component responsible for listening to all the messages in station network, which are so many, and just separate the ones important for the public from the service messages, commands etc etc and translate from system language to something formal and informative, basically listening to "medium level" and "low level" messages and converting to "high level" messages, that can then be later displayed on station monitors for public or just joining "low level" and "medium level" messages and redirecting to technical consoles .

gestore_arrivi is "arrivals_manager", it is the component responsible for managing all incoming trains and takes in consideration the possibility or not to enter in the station and other things, inquirying sensors and listening to them. It informs the network of all arrivals and its properties in "medium level" messages.

gestore_display is "display_manager", it is the component responsible for managing all different monitors on station, organizing ads and informations in a ordered and graphical way.

gestore_partenze is "departures_manager", it is the component responsible for managing all outgoing trains, analog behaviour as "arrivals_manager".

gestore_scambi is "switch_manager", it is the component which controls all switches, that are some sort of "slave" components. it listens to "medium level" commands and sends "low level" commands.

gestore_segnali is "signposting_manager", it is the component which controls all the "traffic lights" stuff and signs etc etc. Like switch_manager it listens to "medium level" commands from other managers (like for example "gestore_tragitti", really important) and sends "low level" commands to "slave" units which phisically control single lights and so on.

gestore_stati is "states_manager", it is a core component because It is where all states of components are stored (for example if a switch is on or off, if a binary is occupied or not, if a light or a crossing is enabled or disabled and so on). It costantly listen to "low level" or "medium level" (depends on situations, certain kinds of scenarios have a further level of state interpretatation and broadcasting, lower, that then get send in a "medium level" language to "states_manager", for example long binary with multiple detectors to determine if train is just passing or stops there) and keeps updates all variables to responds to all requests of states from other managers , that uses them to elaborate the procedures and send commands or just to determine if a light must be on or off and so on.
It also has a local display, just to have updated visualization of states.

gestore_tragitti is "route_manager", it is very important, the most complex and "intelligent" part beacause it is involved when a train has to arrive, leave or just pass, in fact this component has to collect informations (from states_manager or other detectors) and define the route train has to follow to complete its intent. If a train for example is arriving (and can enter the station) it has to determine at which binary the train can stops and then send instructiond to switch_manager, that sets up all switches in correct way following iys embedded functions, and to signposting_manager, that sets up signs.
It reads and sends "medium level" messages and commands.

gestore_transiti is "passing_manager" and it is intended for non-terminal stations or for yard and switches along railway, where a train doesn't stop but has to pass. It is like gestore_arrivi and gestore_partenze and interacts with gestore_tragitti.

Then there are all the "slave" stuffs, which I don't write, because are simple low-end controllers that manage a single physical component.

I have written different managers and they are working fine in testing stage, sending and receiveing messages and commands, storing values and printing all the stuff.

I need some help with the gestore_tragitti, here is part of my code, it has to be intended as a sample, it is not complete, I need to understand the best way to go on and to write correctly the bottom part.
I want the program to check the state of first binary (all with use of network and inquiryng that involve states_manager) and if it is empty it can execute the function (that I will lately define) that sends commands to switch_manager and sets all switches to make train go to first binary (then the stop and timing etc etc will be apart), else will check next binary, the second one, and do the same, sending train here if possible by setting switches or going on to third switch and so on.
Basically it chooses the first possible arrival/departure/passing mode and makes switch_master and signposting_master execute specific instruction set defined for that route and contained in these.

Spoiler

I probably have made things in a stupid way, I tryed my best, and there is a better way to get the same things with twenty times less lines.
I don't really think it is correct what I've written, beacuse I am not sure if every time system request for a binary state from rednet and the states_master responds this program is still listening and can continue operation
I would appreciate any kind of help.

Sorry for long thread and for my errors, I'm learning english as much as programming.

Edited by pietro.devo, 15 February 2016 - 12:16 PM.


#2 valithor

  • Members
  • 1,053 posts

Posted 01 October 2015 - 09:09 PM

Your assumption about it not listening is correct. LUA code runs from top to bottom, so it is only listening for the message the one time you call os.pullEvent(). With that in mind I am also going to help you shorten your code, while helping you fix this problem.

So... Your individual functions can be shortened to a single function, which accepts a argument.

Example of functions with arguments:
function say(argument) --# the argument name can be anything
  print(argument)
end

say("hi") --# will print hi
say("hello") --# will print hello

So applied to your code:
function configura_tragitto_arrivo(argument)
 rednet.broadcast("configura_tragitto_arrivo_"..argument)
end

function configura_tragitto_partenza(argument)
  rednet.broadcast("configura_tragitto_partenza_"..argument)
end

function configura_tragitto_transito(argument)
  rednet.broadcast("configura_tragitto_transito_"..argument)
end

function stato_binario(argument)
  rednet.broadcast("stato_binario_"..argument)
end

Now since we have changed the functions to accept an argument we can now use a for loop to loop through all of the possible messages.

for i = 1, 9 do --# will loop through the numbers 1-9, and assign the current loop number to i

  event, id, message, distance, protocol = os.pullEvent("rednet_message")

  if message == "binario_00"..i.."_ibero" then
    configura_tragitto_arrivo(i)
    break --# will exit the loop if run, so if the if statement runs
  else
    stato_binario(i)
  end

end

If you need me to explain anything that I posted, or if I missed something obvious, then feel free to ask.

Edited by valithor, 01 October 2015 - 09:12 PM.


#3 pietro.devo

  • Members
  • 25 posts

Posted 02 October 2015 - 01:57 PM

Thank you very much! Everything clear.

Now I'm wrtitting again stuff in a better way.
I have removed all X, Y, Z thing to just use numbers, also all the zeros.

What I've re-worked is gestore_stati, the states manager, this was the first working version:

Spoiler

And here we have the new one, actually there isn't the local printing function, I'll add later or, maybe, much better, I will work on a broadcasting system that let display and updates all values on a remote and dedicated system:

Spoiler

I will translate names for better understanding:

binario ---- > binary
scambio ----> switch
segnale ----> light/signpost

stato ----> state
aggiornamento ----> update

libero ----> free
occupato ----> busy
transito ----> passing

attivo ----> on
inattivo ----> off
temporizzato ----> temporized

So, what this basically does is storing all core variables for the system, constantly listening to rednet for state request (stato_), to which respond broadcasting the actual stored value, and state update (aggiornamento_), to which react writing the new value of variables.

is it written correctly and is there any further optimization to reduce lenght off all the stuff? (I would like to combine all "aggiornamento_" messages of every component, without splitting into "_libero/_occupato/_transito" or "_attivo/_inattivo/_temporizzato" but I am honestly not sure how to do this.

Thanks again valithor for your kind reply

#4 valithor

  • Members
  • 1,053 posts

Posted 02 October 2015 - 05:49 PM

You can shorten all of the variable declarations using tables. Tables are a extremely powerful part of LUA, but they are slightly difficult to understand at first. Essentially they are a variable, which contains a large number of other variables inside of it. Each variable inside of the table is assigned to a "key" or reference value, which makes it to where you can loop through similar to how we did the functions.

tbl = {} --# creating the table
tbl[1] = "hi"
tbl[2] = "hello"

print(tbl[1]) --# prints hi
print(tbl[2]) --# prints hello

Applied to your code:
binario = {}
scambio = {}
segnale = {}

for i = 0, 50 do
  binario[i] = " "
  scambio[i] = " "
  segnale[i] = " "
end

To reference the values in the table you would do tablename[keyname], so for example you could do binario[1] to reference the variable assigned to the "1" key.

A few things you might want to change, but I am not completely sure:

- putting the for loop inside a while loop, so the program is constantly running
while true do
  for i = 1, 50 do
	--# I removed everything from here to make the post take less space
  end
end

- You will need to restructure your code in order for it to work how you want. The way the for loop is setup up currently it is expecting a rednet message before it does the loop each time, so you can only put functions in the loop that are requesting a new rednet message to be sent to the computer.

Applied:
binario = {}
scambio = {}
segnale = {}

for i = 0, 50 do
  binario[i] = " "
  scambio[i] = " "
  segnale[i] = " "
end

function stato_binario(argument)
  rednet.broadcast("binario_"..argument.."_",binario_(argument))
end
function aggiornamento_binario_libero(argument)
  binario[argument] = "libero"
end
function aggiornamento_binario_occupato(argument)
  binario[argument] = "occupato"
end
function aggiornamento_binario_transito(argument)
  binario[argument] = "transito"
end
function stato_scambio(argument)
  rednet.broadcast("scambio_"..argument.."_",scambio_(argument))
end
function aggiornamento_scambio_attivo(argument)
  scambio[argument] = "attivo"
end
function aggiornamento_scambio_inattivo(argument)
  scambio[argument] = "inattivo"
end
function aggiornamento_scambio_temporizzato(argument)
  scambio[argument] = "temporizzato"
end
function stato_segnale(argument)
  rednet.broadcast("segnale_"..argument.."_",segnale_(argument))
end
function aggiornamento_segnale_attivo(argument)
  segnale[argument] = "attivo"
end
function aggiornamento_segnale_inattivo(argument)
  segnale[argument] = "inattivo"
end
function aggiornamento_segnale_temporizzato(argument)
  segnale[argument] = "temporizzato"
end

while true do
  event, id, message, distance, protocol = os.pullEvent("rednet_message")
  local boolean = false --# used to keep track of whether or not the message has been found

  for i = 0, 50 do --# checking the ones that do not require os.pullEvent first

	if message == "aggiornamento_binario_"..i.."_libero" then
	  aggiornamento_binario_libero(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_binario_"..i.."_occupato" then
	  aggiornamento_binario_occupato(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_binario_"..i.."_transito" then
	  aggiornamento_binario_transito(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_scambio_"..i.."_attivo" then
	  aggiornamento_scambio_attivo(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_scambio_"..i.."_inattivo" then
	  aggiornamento_scambio_inattivo(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_scambio_"..i.."_temporizzato" then
	  aggiornamento_scambio_temporizzato(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_segnale_"..i.."_attivo" then
	  aggiornamento_segnale_attivo(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_segnale_"..i.."_inattivo" then
	  aggiornamento_segnale_inattivo(i)
	  boolean = true
	  break
	end

	if message == "aggiornamento_segnale_"..i.."_temporizzato" then
	  boolean = true
	  aggiornamento_segnale_temporizzato(i)
	  break
	end
  end
  if boolean == false then
	for i = 0, 50 do
	  if message == "stato_scambio_"..i.." then
		stato_scambio(i)
		break --# will exit the loop if run, so if the if statement runs
	  end

	  if message == "stato_segnale_"..i.." then
		stato_segnale(i)
		break --# will exit the loop if run, so if the if statement runs
	  end

	  if message == "stato_binario_"..i.." then
		stato_binario(i)
		break --# will exit the loop if run, so if the if statement runs
	  end
	  event, id, message, distance, protocol = os.pullEvent("rednet_message")
	end
  end
end

If you need me to explain anything feel free to ask. I am in class right now, and ran out of time before I could explain everything.

Edited by valithor, 02 October 2015 - 05:53 PM.


#5 pietro.devo

  • Members
  • 25 posts

Posted 05 October 2015 - 07:28 AM

Thanks again for your reply.

I am learning the use of tables, and trying to follow your nice suggestion.
First of all I tried to use your code, I have set up a tester that frequently requests for states and then a displayer of rednet messages to see if the states manager is working correctly.
Program works without errors but doesn't seem to respond to states requests.

What then I did to investigate is putting a local print function just after and update execution and it just doesn't work.
I can't understand if is it actually storing values but not responding or not storing and updating values too.
There is something wrong, but I can't understant what, maybe I'll return to the simpler things

Edited by pietro.devo, 26 October 2015 - 10:01 AM.


#6 pietro.devo

  • Members
  • 25 posts

Posted 26 October 2015 - 10:01 AM

Any idea on what's wrong?

#7 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 28 October 2015 - 12:50 AM

View Postpietro.devo, on 05 October 2015 - 07:28 AM, said:

First of all I tried to use your code, I have set up a tester that frequently requests for states and then a displayer of rednet messages to see if the states manager is working correctly.
Program works without errors but doesn't seem to respond to states requests.

Checking valithor's suggested code, I'd be surprised if you don't get errors - the mis-matching quotes down the bottom should cause it to crash. The script you posted has the same problem; a line like:

if message == "stato_scambio_"..i.." then

... should be:

if message == "stato_scambio_"..i then

... or else you'll be checking if "message" is equal to lots of lines of code!

Assuming you fixed that, and corrected the commas in the rednet.broadcast() calls to something more like rednet.broadcast("binario_"..argument.."_"..binario[argument]), it looks like the extra os.pullEvent() call down the bottom would stop it from reliably responding to anything. It may also have trouble if you're using leading zeros in front of the numbers in your strings (eg, "aggiornamento_scambio_050_inattivo").

You really need to stop using functions for every little thing. A good function can be used from multiple areas of your script. If you don't want to use it more than once, then you probably shouldn't be writing it as a function.

Your code can be reduced dramatically by making better use of tables. Here's another re-write of the same stuff:

rednet.open("back")
rednet.host("stati","gestore_stati")
local display = peripheral.wrap("top")

-- Build structures of tables:
local aggiornamento = { binario = {}, scambio = {}, segnale = {} }
for i = 0, 50 do
	aggiornamento["binario"][i] = " "
	aggiornamento["scambio"][i] = " "
	aggiornamento["segnale"][i] = " "
end

-- So now we can access eg scambio_2 as aggiornamento["scambio"][2]

while true do  -- Run a loop that repeats indefinitely.
	local senderID, message = rednet.receive()
	
	-- Split the message into "words" (using pattern matching), put them into a new table:
	local words = {}
	for word in string.gmatch(message, "%w+") do words[#words + 1] = word end
	
	-- Eg, if the message is "aggiornamento_scambio_050_inattivo",
	-- then words[1] will be "aggiornamento", words[2] will be "scambio", words[3] will be "050", etc...
	
	-- It would be a lot easier to just rednet.send() tables of words in the first place, instead of combining them altogether into single strings!!!
	
	if words[1] == "stato" then
		rednet.broadcast(words[2].."_"..words[3].."_"..aggiornamento[ words[2] ][ tonumber(words[3]) ])
		
		-- So if we got the message "stato_binario_010",
		-- words[2] would be "binario", words[3] would be "010",
		-- so we would send back:
		
		-- "binario" .. "_" .. "010" .. "_" .. aggiornamento[ "binario" ][ 10 ]
	
	elseif words[1] == "aggiornamento" then
		aggiornamento[ words[2] ][ tonumber(words[3]) ] = words[4]
		
		-- So if we got the message "aggiornamento_scambio_050_inattivo",
		-- we would end up doing:
		
		-- aggiornamento[ "scambio" ][ 50 ] = "inattivo"
		
	end
end


#8 pietro.devo

  • Members
  • 25 posts

Posted 28 October 2015 - 03:47 PM

Thank you very much for your response.
You are right, I'm using a long and basic code for my program, I could not write down as you did by myself.
Now I will try to use the form you wrote, I just did prefer to use "stato" instead of "aggiornamento" because of the concept, so this is what I have now, hope is right (Had to add and "end" beacuse got execution error):

Spoiler

Now i get "gestore_stati:9 attempt to index ?" and I can't find out what is wrong.
I'm going on writing the rest of things trying to reduce code as well and planning then to share (translated in english) for everything interested

#9 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 28 October 2015 - 03:54 PM

You haven't defined 'stato' as a table, so you cannot treat it as one.

This line:
local aggiornamento = { binario = {}, scambio = {}, segnale = {} }

...should be changed to this:
local stato = { binario = {}, scambio = {}, segnale = {} }


#10 pietro.devo

  • Members
  • 25 posts

Posted 28 October 2015 - 03:57 PM

Oh, that a really noob error, thank you very much

#11 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 29 October 2015 - 12:05 AM

View Postpietro.devo, on 28 October 2015 - 03:47 PM, said:

(Had to add and "end" beacuse got execution error):

That's because you added a space here:

else if words[1] == "aggiornamento" then

It should work the way you've done it, but it'd be somewhat more tidy to just use "elseif".

Edited by Bomb Bloke, 29 October 2015 - 12:06 AM.


#12 pietro.devo

  • Members
  • 25 posts

Posted 12 November 2015 - 10:00 PM

Hi there.
I took again some time to continue my work, with my stupid knowledge.
Now I am trying to write some generic code for switches, then the same should be done for rail sensors (basically activator rails) and signals.
I have already archieved the thing to work writing ad-hoc code for a single switch, but I'd like to have just a code ready to use to put in every switch and just change a variable number, that identify that specific switch or sensor on the network and all the work is done.

Now, I am not absolutely sure if this is correct, but that's what I have written:

Spoiler

Then I am thinking on writing some sort of code that can make all devices boot with their code, a sort of central core that sends power_on or power_off function on the network, would be possible in a easy way for me?

Edited by pietro.devo, 12 November 2015 - 11:01 PM.


#13 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 13 November 2015 - 12:12 AM

I would just rely on all computers being on all the time. If the server shuts down or their chunks unload, they should boot back up when the chunks are next loaded, automatically.

But, if you really want to turn them on / off manually, and the devices are connected together using wired modems, then you can use peripheral.find() and the computer's peripheral functions to do this pretty easily:

local compList = {peripheral.find("computer")}  -- Hunt down all connected computers, wrap them as peripherals, stick them in a table.

for i = 1, #compList do compList[i].turnOn() end   -- Iterate through the table and turn each computer on.

Without a wired system, it's not so simple. You could probably do something with eg Wireless Redstone (WR-CBE) and an Autonomous Activator to start them, and a simple rednet message to trigger shut downs.

Edited by Bomb Bloke, 13 November 2015 - 04:42 AM.


#14 pietro.devo

  • Members
  • 25 posts

Posted 13 November 2015 - 06:58 AM

Your point is right, I think it's far better to just have computers always execute code. I will just have some core components that need to be always loaded, like the ones that have the control of train schedules, but he rest is fine.

For the switch code I know it is wrong, I am not so sure in the use of arguments

#15 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 13 November 2015 - 12:33 PM

Ah, yes, looking at your code I can see there's an issue or two. You've set "i" to 1 and then you never change it. If strings are incoming with different numbers in the middle of them, then you'd be better off using something like the technique I showed you earlier to break the strings up into separate words so you can inspect them individually.

Scratch that, you'd be better off sending the commands as tables in the first place instead of clumping multiple, separate bits of information together into single strings.

It'd be a lot easier to comment if you also provided the script which is sending the "comando"s, but I'm thinking that all the "numbers with the commands" business could be done away with if you just put that "senderID" data to good use.

#16 pietro.devo

  • Members
  • 25 posts

Posted 13 November 2015 - 01:56 PM

Yes, would be better to archieve that with tables, but for a switch I prefer to do this more friendly to me.
Sorry, I didn't explain well, I would like to install on place my switches, near the rails, then just set the "i" value at the moment, for each of them editing the generic code, on the base on what number I decide to assign to them.
Basically I have then a computer that sends all commands to the correct switches on the base of the route is needed to be set up.

EDIT:

I have solved problem writing the code this way:

i=""

print("inizializzazione")
sleep(1)
print("connessione")
rednet.open("bottom")
rednet.host("scambi","scambio_"..i)
rednet.broadcast("avvio_scambio_"..i)
sleep(1)
print("funzionamento")

while true do
event, id, message, distance, protocol = os.pullEvent("rednet_message")

if message == "comando_scambio_"..i.."_attivo" then
redstone.setOutput("back", true)
rednet.broadcast("aggiornamento_scambio_"..i.."_attivo")
end
if message == "comando_scambio_"..i.."_inattivo" then
redstone.setOutput("back", false)
rednet.broadcast("aggiornamento_scambio_"..i.."_inattivo")
end
if message == "comando_scambio_"..i.."_temporizzato" then
redstone.setOutput("back", true)
rednet.bradcast("aggironamento_scambio_"..i.."_temporizzato")
sleep(10)
redstone.setOutput("back", false)
rednet.broadcast("aggiornamento_scambio_"..i.."_inattivo")
end
end

Edited by pietro.devo, 13 November 2015 - 11:10 PM.


#17 pietro.devo

  • Members
  • 25 posts

Posted 24 November 2015 - 11:31 PM

Hi there.

Here I have written my detector's code.
Basically I am using railcraft detectors under rails, in a future I can use some traincraft possible rail detectors (if there will be), and this is the reason of redstone input from top or back of computer.
Because I have no permanent storage of information on the states manager (so at every server restart all variables need to be defined again) I have simply made every sensor check and send a network update at boot.

i=""

sleep(1)
print("< "..i.." >")
sleep(1)
print("rilevatore")
sleep(1)
print("assegnazione")
sleep(1)
print("inizializzazione")
sleep(1)
print("connessione")
rednet.open("bottom")
rednet.host("rilevatori","rilevatore_"..i)
rednet.broadcast("avvio_rilevatore_"..i)
sleep(1)
print("funzionamento")
sleep(1)
if redstone.getInput("top",true) then
rednet.broadcast("aggiornamento_rilevatore_"..i.."_attivo")
else if redstone.getInput("back",true) then
rednet.broadcast("aggiornamento_rilevatore_"..i.."_attivo")
else
rednet.broadcast("aggiornamento_rilevatore_"..i.."_inattivo")
end
end
while true do
os.pullEvent("redstone")
if redstone.getInput("top",true) then
rednet.broadcast("aggiornamento_rilevatore_"..i.."_attivo")
else if redstone.getInput("back",true) then
  rednet.broadcast("aggiornamento_rilevatore_"..i.."_attivo")
  else
  rednet.broadcast("aggiornamento_rilevatore_"..i.."_inattivo")
  end
end

end

The code is similiar to the switch one and signal one, I just download code from pastebin or from floppy on the fly when setting the computer and change its number at the beginning with a fast edit.

Now, my states manager listen on the network to update its variables, the problem I have found is in the rednet.host function of every single computer on the net.
I am using it to have every controller named in a precise way, and also is useful because if I accidentaly set up a certain controller with a number already in use it just has and error that says me that hostname is already used.
The problem is that with this rednet.host every computer sends a table message over the network and this break my states master, that just stops working because it expects strings.
Do I have to remove the rednet.host function from computers or can I do something on the code of states manager to avoid the issue?

print("< gestore >")
sleep(1)
print("stati")
sleep(1)
print("assegnazione")
sleep(1)
print("inizializzazione")
sleep(1)
print("connessione")
rednet.open("top")
rednet.host("sistemi","gestore_stati")
rednet.broadcast("avvio_gestore_stati")
sleep(1)
print("funzionamento")
local display = peripheral.wrap("top")
local stato = { binario = {}, scambio = {}, segnale = {}, rilevatore = {} }
for i = 0, 50 do
stato["binario"][i] = " "
stato["scambio"][i] = " "
stato["segnale"][i] = " "
stato["rilevatore"][i] = " "
end
while true do
local senderID, message = rednet.receive()
local words = {}

for word in string.gmatch(message, "%w+") do words[#words + 1] = word end
if words[1] == "stato" then
rednet.broadcast(words[2].."_"..words[3].."_"..stato[ words[2] ][ tonumber(words[3]) ])
end

if words[1] == "aggiornamento" then
stato[ words[2] ][ tonumber(words[3]) ] = words[4]
end
end


#18 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 24 November 2015 - 11:45 PM

rednet.host()/lookup()-related messages use a protocol of "dns". By using your own protocol for your own messages, you can ignore the "dns" ones.

rednet.broadcast("avvio_gestore_stati", "trainStation")   -- Broadcast using "trainStation" protocol.

local senderID, message = rednet.receive("trainStation")  -- Receive only messages sent using the "trainStation" protocol.

Edited by Bomb Bloke, 24 November 2015 - 11:45 PM.


#19 pietro.devo

  • Members
  • 25 posts

Posted 22 December 2015 - 04:07 PM

Thank you very much Bomb Bloke, I was planning to keep the DNS for some sort of useful thing in complex stations with many computers for technical monitoring, but It causes more troubles than help for me, so yes, better remove that.
I am very busy and working on this in the little time I get, now I am trying to learn the use of tables, that as you all pointed out, are the better and more polite way of going.
I'd like to drastically reduce code size for the switch master program, simply having some tables defined at the beginning of the code, edited when placing the controller on every station with the correct switch on and off combination for the different routes that station can offers, depending on the number of rails and platforms.

This is what I wrote now:

print("< switches >")
sleep(1)
print("master")
sleep(1)
print("assigning")
sleep(1)
print("initializing")
sleep(1)
print("connecting")
rednet.open("top")
rednet.broadcast("startup_switch_master")
sleep(1)
print("running")

route_0 = {""}
route_1 = {""}
route_2 = {""}
route_3 = {""}
route_4 = {""}
route_5 = {""}
....
route_50_switches_numbers = {"0","1","2","3","4","5"}
route_50_switches_states =  {"on","off","on","on","on","on"}


for i = 0, 50 do
event, id, message, distance, protocol = os.pullEvent("rednet_message")
	  
if message == "configure_route"..i then
rednet.broadcast("command_switch_"..route_[i]_switches_numbers[1].."_"..route_[i]_switches_states[1])
rednet.broadcast("command_switch_"..route_[i]_switches_numbers[2].."_"..route_[i]_switches_states[2])
rednet.broadcast("command_switch_"..route_[i]_switches_numbers[3].."_"..route_[i]_switches_states[3])
rednet.broadcast("command_switch_"..route_[i]_switches_numbers[4].."_"..route_[i]_switches_states[4])
rednet.broadcast("command_switch_"..route_[i]_switches_numbers[5].."_"..route_[i]_switches_states[5])
rednet.broadcast("command_switch_"..route_[i]_switches_numbers[6].."_"..route_[i]_switches_states[6])
end

end

Basically, for every possible route number (numbers can be passengers functions, service ones, cargo skipping station etc etc) there is a table with the numbers of switches that are involved in that route and a table with the states that need to have every switch in the same order.
The switch master, when a superior input is broadcasted in the message form "configure_route_<number> (a specific train is approaching and need to be send somewhere or has to pass and the computer managing trains wants to have everything set up) just sends messages for every involved switch with its number and the state needed, in the form "command_switch_<number>_<state>", and that switches then set theit outputs.
Probably would be better also to merge everything in a unique string, but for me it's already hard this challenge, so, for now I prefer this way.
As it is the program doesn't work, can't figure out how to write correctly, and I also have some doubts about how to manage the fact that I won't have the same number of switches to set in every route, some of them can have a single switch, others over ten of them and so on, I would necessarily to read the number of variables in the table, so number of switches, and broadcast that number of messages sequentially, reading first variable of switch numbers table and first variable of switch states table and broadcasting the commands until the last variable number.
The written form as now is stupid, just with multiple lines with changed numbers, and I don't want to keep as it is, it's just for testing.

Edited by pietro.devo, 22 December 2015 - 04:11 PM.


#20 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 24 December 2015 - 12:24 AM

You can't do this sort of thing:

route_50_switches_numbers = {"0","1","2","3","4","5"}
route_50_switches_states =  {"on","off","on","on","on","on"}

.
.
.

rednet.broadcast("whatever" .. route_[i]_switches_numbers[1] .. "whatever")

The correct format is "tableName[index]". You can't try and split it like "tableName[index]moreTableName". What you're wanting is to build some proper sub-tables; tables put within tables:

route = {}  -- Make a table.

route["switches"] = {}  -- Make another table, in the route table.

route["switches"]["numbers"] = {}  -- Make yet another table, in the route["switches"] table.

route["switches"]["states"] = {}  -- And so on.

-- Now we can do:

route["switches"]["numbers"][50] = {"0","1","2","3","4","5"}
route["switches"]["states"][50] = {"on","off","on","on","on","on"}

-- And later:

rednet.broadcast("whatever"..route["switches"]["numbers"][i][1].."_"..route["switches"]["states"][i][1]).."whatever")

You could alternatively build your table using a single command, though you'd likely still want to spread it out over multiple lines:

route = {
	["switches"] = {
		["numbers"] = {
			[1] = {"0","1","2","3","4","5"},  -- Commas go between entries in the same sub-table.
			[2] = {"0","1","2","3","4","5"},
			.
			.
			.
			[50] = {"0","1","2","3","4","5"}
		},
		
		["states"] = {
			[1] = {"on","off","on","on","on","on"},
			[2] = {"on","off","on","on","on","on"},
			.
			.
			.
			[50] = {"on","off","on","on","on","on"}
		}
	}
}

If a table key is a string (eg "numbers"), then you can use dot notation to refer to it. For example,

route["switches"]["numbers"][i]

... is the same as:

route.switches.numbers[i]

And that for loop you've got there... argh. Fifty times it gets a message, and each time it does, "i" goes up by one. That makes it pretty random as to whether the current message will ever indicate "i"'s value, yeah? If it doesn't match, you're just throwing that message away and getting a new one, by which time "i" has changed again, etc... Ditch that sort of loop!

View Postpietro.devo, on 22 December 2015 - 04:07 PM, said:

Probably would be better also to merge everything in a unique string, but for me it's already hard this challenge, so, for now I prefer this way.

Seriously, you'd be FAR BETTER OFF dropping the idea of transmitting individual strings AT ALL, and just sending tables. Putting strings together and then later trying to pull them apart is far more trouble than it's worth.

For example:

local command = {"configure", "route", 4}  -- command[1] = "configure", command[2] = "route", command[3] = 4

rednet.broadcast(command)

Now at the other end, you might do:

-- Define great big route.switch.whatever table up here

while true do
  local command = rednet.receive()

  if command[1] == "configure" then
    if command[2] == "route" then
      rednet.broadcast( { "command", "switch", route.switches.numbers[ command[3] ], route.switches.states[ command[3] ] } )

    elseif command[2] == "some other word" then
      -- do something else
    end
  end
end

Note the lack of a need to run a loop checking a ton of values of "i" - you just pluck the correct number straight out of the command's table (command[3], which was 4), no muss, no fuss. The info's right there.

Also note that we don't need to go through and send each of the six entries in the route.switches.numbers[ command[3] ] / route.switches.states[ command[3] ] tables individually. We simply send off the entire sub-tables and their whole contents go at once.

And then at the other end you might do:

while true do
  local command = rednet.receive()

  if command[1] == "command" then
    if command[2] == "switch" then
      -- command[3] must be the whole table route.switches.numbers[i] pointed to.
      for i = 1, #command[3] do  -- For each entry in that table, do...
        -- Set number command[3][i] to whatever state is indicated by command[4][i].
      end

    elseif command[2] == "something else" then
      -- do something else
    end

  elseif command[1] == "something else" then
    -- do something else

  elseif etc
  end
end

Edited by Bomb Bloke, 24 December 2015 - 12:27 AM.






1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users