· 8 years ago · Jan 03, 2018, 08:58 AM
1--Station Program
2-- Author: Andrew Lalis, all rights reserved.
3-- This program is not to be distributed without
4-- express permission from the author, by any means.
5
6--Constants
7local configFile = "station_config"
8
9--Protocol for broadcasting station metadata.
10local metadataProt = "STATION_META"
11--Protocol for requesting metadata from all stations.
12local metadataRequestProt = "STATION_META_REQUEST"
13--Protocol for requesting a train to be dispatched from a station.
14local trainDispatchProt = "TRAIN_DISPATCH"
15
16--Config table containing all station information.
17local stationConfig
18
19--Offset if the user has scrolled on the stations list.
20local stationsOffset = 0
21
22--Peripherals.
23local ticketMachine
24local noteBlock
25local monitor
26
27--Returns the number of items in a table.
28function tableSize(tbl)
29 local count = 0
30 for i in pairs(tbl) do count = count + 1 end
31 return count
32end
33
34--Logging function to make output more pretty.
35function logMsg(t, msg)
36 local c = colors.white
37 if (t == "ERROR") then
38 c = colors.red
39 elseif (t == "INFO") then
40 c = colors.blue
41 elseif (t == "INFO_DETAIL") then
42 c = colors.lightBlue
43 term.write(" ")
44 elseif (t == "INFO_HIGH") then
45 c = colors.magenta
46 elseif (t == "WARNING") then
47 c = colors.orange
48 end
49 term.setTextColor(c)
50 print("["..t.."]: "..msg)
51end
52
53--Saves the current station config to a file.
54function saveConfig()
55 local f = fs.open(configFile, "w")
56 f.write(textutils.serialize(stationConfig))
57 f.close()
58 logMsg("INFO_DETAIL", "Station config saved.")
59end
60
61--Draws a horizontal line across the screen.
62function drawLine(y, c)
63 local sizeX,sizeY = monitor.getSize()
64 monitor.setTextColor(colors.black)
65 monitor.setBackgroundColor(c)
66 for i=1,sizeX do
67 monitor.setCursorPos(i,y)
68 monitor.write(" ")
69 end
70end
71
72--Writes to the screen with a certain color, background color, and other parameters.
73function writeToScreen(text, x, y, tc, bc)
74 monitor.setCursorPos(x,y)
75 monitor.setTextColor(tc)
76 monitor.setBackgroundColor(bc)
77 monitor.write(text)
78end
79
80--Writes to the screen centered at some x coordinate.
81function writeCentered(text, x, y, tc, bc)
82 monitor.setTextColor(tc)
83 monitor.setBackgroundColor(bc)
84 monitor.setCursorPos((x - (string.len(text)/2)),y)
85 monitor.write(text)
86end
87
88--Function to draw station monitor with a given offset for the stations list.
89function drawMonitor()
90 local sizeX,sizeY = monitor.getSize()
91 monitor.setBackgroundColor(colors.black)
92 monitor.clear()
93 drawLine(1, colors.blue)
94 writeCentered(stationConfig.name, sizeX/2, 1, colors.white, colors.blue)
95 --Draw list of stations, as found in the station config.
96 local index = 0
97 for i=1,tableSize(stationConfig.otherStations) do
98 local bc = colors.gray
99 if (i % 2 == 0) then
100 bc = colors.lightGray
101 end
102 drawLine(i+1, bc)
103 writeToScreen(stationConfig.otherStations[i][2], 2, i+1, colors.white, bc)
104 end
105end
106
107--Request a train from a station which has more than 1 train.
108function requestTrain()
109 for i=1,tableSize(stationConfig.otherStations) do
110 if (stationConfig.otherStations[i][3] > 1) then
111 rednet.send(stationConfig.otherStations[i][4], stationConfig.dest, trainDispatchProt)
112 logMsg("INFO", "Sent request to "..stationConfig.otherStations[i][1].." for a train.")
113 return
114 end
115 end
116 logMsg("WARNING", "Unable to find a station with an extra train.")
117end
118
119--Dispatches a train from a track, by sending a ticket to it.
120function dispatchTrain(destination, trainCountAtDest)
121 local success = ticketMachine.createTicket(destination,1)
122 if (not success) then
123 logMsg("ERROR", "Unable to create ticket.")
124 return false
125 end
126 --Iterate until an occupied track is found.
127 for i=1,tableSize(stationConfig.tracks) do
128 if (stationConfig.tracks[i][1]) then
129 logMsg("INFO_HIGH", "Train on track "..i.." will be dispatched to "..destination..".")
130 redstone.setBundledOutput("top", stationConfig.tracks[i][2])
131 os.sleep(2)
132 redstone.setBundledOutput("top", 0)
133 if (trainCountAtDest > )
134 return true
135 end
136 end
137 logMsg("ERROR", "Unable to find train to dispatch.")
138 return false
139end
140
141--Broadcasts the station's name, destination, and train count to all other stations.
142function broadcastMetadata()
143 rednet.broadcast({stationConfig.dest,stationConfig.name,stationConfig.trainCount}, metadataProt)
144 logMsg("INFO_DETAIL", "Broadcasted metadata.")
145end
146
147--Scans redstone inputs from each track.
148-- Updates tracks table, and train count, then broadcasts.
149function scanTracks()
150 local trainCount = 0
151 for i=1,tableSize(stationConfig.tracks) do
152 local previous = stationConfig.tracks[i][1]
153 stationConfig.tracks[i][1] = colors.test(redstone.getBundledInput("top"), stationConfig.tracks[i][3])
154 if (stationConfig.tracks[i][1]) then
155 trainCount = trainCount + 1
156 end
157 end
158 stationConfig.trainCount = trainCount
159 if (trainCount < 1) then
160 requestTrain()
161 end
162 broadcastMetadata()
163 saveConfig()
164 logMsg("INFO_DETAIL", "Tracks scanned.")
165end
166
167--Determines if occupancy of the tracks is changed according to config.
168function tracksChanged()
169 for i=1,tableSize(stationConfig.tracks) do
170 local previous = stationConfig.tracks[i][1]
171 local current = colors.test(redstone.getBundledInput("top"), stationConfig.tracks[i][3])
172 if (previous ~= current) then
173 logMsg("INFO_DETAIL", "Track occupancy has changed.")
174 return true
175 end
176 end
177 return false
178end
179
180--What to do when a redstone event is triggered.
181function onRedstoneEvent()
182 if (tracksChanged()) then
183 scanTracks()
184 end
185end
186
187--Saves a station's metadata into the stationConfig file.
188function saveStation(dest, name, trainCount, compID)
189 --First determine if the station already exists in the file.
190 local index = 0
191 for k,v in pairs(stationConfig.otherStations) do
192 if (v[1] == dest) then
193 index = k
194 break
195 end
196 end
197 if (index ~= 0) then
198 stationConfig.otherStations[index] = {dest, name, trainCount, compID}
199 else
200 table.insert(stationConfig.otherStations, {dest, name, trainCount, compID})
201 end
202 saveConfig()
203end
204
205--What to do when a rednet message is received.
206function onRednetMessage(id, msg, prot)
207 --If the computer is receiving metadata from another station.
208 if (prot == metadataProt or (prot == metadataRequestProt and msg ~= "REQUEST")) then
209 saveStation(msg[1], msg[2], msg[3], id)
210 logMsg("INFO", "Received metadata from station: "..msg[1])
211 drawMonitor()
212 --If another computer has requested this computer's metadata.
213 elseif (prot == metadataRequestProt and msg == "REQUEST") then
214 rednet.send(id, {stationConfig.dest,stationConfig.name,stationConfig.trainCount}, metadataRequestProt)
215 --If another computer has requested a train to be dispatched to a location.
216 elseif (prot == trainDispatchProt) then
217 logMsg("INFO", "Train requested at: "..msg)
218 dispatchTrain(msg, 0)
219 end
220end
221
222--What to do when the monitor is touched.
223function onMonitorTouch(x, y)
224 local stationIndex = y - 1 + stationsOffset
225 --Test if the user clicked on a station's name.
226 if (y > 1) then
227 --Make sure that a station exists first.
228 if (stationIndex <= tableSize(stationConfig.otherStations)) then
229 local selectedStation = stationConfig.otherStations[stationIndex]
230 logMsg("INFO", "User selected station: "..selectedStation[1])
231 dispatchTrain(selectedStation[1], selectedStation[3])
232 end
233 end
234end
235
236--Handles all incoming events.
237function eventHandler()
238 local event, p1, p2, p3, p4, p5 = os.pullEvent()
239 --If a redstone signal changes, then do something.
240 if (event == "redstone") then
241 onRedstoneEvent()
242 --If the monitor is touched, pass the x and y coordinates to a function.
243 elseif (event == "monitor_touch") then
244 onMonitorTouch(p2, p3)
245 --If a rednet message is received, pass the ID, message, and protocol to a function.
246 elseif (event == "rednet_message") then
247 onRednetMessage(p1, p2, p3)
248 end
249end
250
251--Sets up the config for the first time, and returns the resulting table.
252function firstTimeSetup()
253 term.setTextColor(colors.lightBlue)
254 logMsg("WARNING", "No config file found. Starting first-time setup...")
255 local config = {
256 tracks={},
257 otherStations={},
258 }
259 term.setTextColor(colors.white)
260 print(" What is the number of the monitor connected to this station?")
261 config.monitor_id = "monitor_"..read()
262 print(" What is the number of the note block connected to this station?")
263 config.note_block_id = "note_block_"..read()
264 print(" What is the cosmetic name of this station?")
265 config.name = read()
266 print(" What is the railcraft destination for this station?")
267 config.dest = read()
268 print(" How many tracks does this station have?")
269 local trackCount = tonumber(read())
270 for i=1,trackCount do
271 config.tracks[i] = {}
272 config.tracks[i][1] = false
273 print(" What is track "..i.."'s dispatch decimal color code?")
274 config.tracks[i][2] = tonumber(read())
275 print(" What is track "..i.."'s detection decimal color code?")
276 config.tracks[i][3] = tonumber(read())
277 end
278 local f = fs.open(configFile, "w")
279 f.write(textutils.serialize(config))
280 f.close()
281 term.setTextColor(colors.lime)
282 print("Setup is complete.")
283 term.setTextColor(colors.white)
284 return config
285end
286
287--Loads config from a file, or if there is none, create it from user input.
288function loadConfig()
289 if (not fs.exists(configFile)) then
290 return firstTimeSetup()
291 else
292 local f = fs.open("station_config", "r")
293 local config = textutils.unserialize(f.readAll())
294 f.close()
295 return config
296 end
297end
298
299--Perform things before the program begins listening for input.
300function setup()
301 stationConfig = loadConfig()
302 --Setup peripherals
303 rednet.open("bottom")
304 ticketMachine = peripheral.wrap("back")
305 noteBlock = peripheral.wrap(stationConfig.note_block_id)
306 monitor = peripheral.wrap(stationConfig.monitor_id)
307 --Broadcast metadata to all other stations.
308 scanTracks()
309 --Broadcast a request message to other stations to get their information.
310 rednet.broadcast("REQUEST", metadataRequestProt)
311 --Draw onto the monitor for the first time.
312 drawMonitor()
313end
314
315--Main loop
316setup()
317while (true) do
318 eventHandler()
319end