Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- This program should be installed on a portable computer with a wireless
- modem, to act as a routing beacon in conjunction with managed switches.
- It also serves as the GUI that users of the system interact with.
- ]]--
- local SWITCH_CHANNEL = 45450
- local STATION_BROADCAST_CHANNEL = 45451
- local SERVER_CHANNEL = 45452
- local MY_CHANNEL = 45460
- local g = require("simple-graphics")
- local W, H = term.getSize()
- local modem = peripheral.wrap("back") or error("Missing modem.")
- modem.open(MY_CHANNEL) -- Listen for messages directed to this device.
- modem.open(STATION_BROADCAST_CHANNEL) -- Listen for station broadcasts.
- local function serializeRoutePath(path)
- local str = ""
- for i, segment in pairs(path) do
- str = str .. segment
- if i < #path then str = str .. "," end
- end
- return str
- end
- local function broadcastRoute(route)
- while true do
- modem.transmit(SWITCH_CHANNEL, MY_CHANNEL, route)
- os.sleep(0.5)
- end
- end
- local function isValidStationInfo(msg)
- return msg ~= nil and
- msg.name ~= nil and type(msg.name) == "string" and
- msg.range ~= nil and type(msg.range) == "number" and
- msg.displayName ~= nil and type(msg.displayName) == "string"
- end
- -- Repeats until we are within range of a station that's sending out its info.
- local function waitForStation(stationName)
- while true do
- local event, side, channel, replyChannel, msg, dist = os.pullEvent("modem_message")
- if type(channel) == "number" and channel == STATION_BROADCAST_CHANNEL and isValidStationInfo(msg) and msg.name == stationName and dist ~= nil and msg.range >= dist then
- return
- end
- end
- end
- local function listenForAnyStation()
- while true do
- local event, side, channel, replyChannel, msg, dist = os.pullEvent("modem_message")
- if type(channel) == "number" and channel == STATION_BROADCAST_CHANNEL and isValidStationInfo(msg) and dist ~= nil and msg.range >= dist then
- os.queueEvent("rail_station_nearby", msg, dist)
- end
- end
- end
- local function waitForNoStation(targetName)
- local lastPing = os.epoch()
- while os.epoch() - lastPing < 5000 do
- parallel.waitForAny(
- function ()
- local event, data, dist = os.pullEvent("rail_station_nearby")
- if not targetName or targetName == data.name then
- stationPresent = true
- lastPing = os.epoch()
- end
- end,
- function () os.sleep(3) end
- )
- end
- end
- local function waitForModemMessage(expectedReplyChannel, timeout)
- local data = nil
- parallel.waitForAny(
- function ()
- while true do
- local event, side, channel, replyChannel, msg, dist = os.pullEvent("modem_message")
- if type(replyChannel) == "number" and replyChannel == expectedReplyChannel then
- data = {}
- data.channel = channel
- data.replyChannel = replyChannel
- data.msg = msg
- data.dist = dist
- return
- end
- end
- end,
- function () os.sleep(timeout) end
- )
- return data
- end
- local function drawLookingForStationScreen()
- g.clear(term, colors.white)
- g.drawText(term, 1, 1, "Looking for nearby station", colors.black, colors.yellow)
- g.drawText(term, 1, 2, "Walk near a station to", colors.gray, colors.white)
- g.drawText(term, 1, 3, "see available routes.", colors.gray, colors.white)
- end
- local function drawStationFoundScreen(stationName)
- g.clear(term, colors.white)
- g.drawXLine(term, 1, W, 1, colors.lightBlue)
- g.drawText(term, 1, 1, "Found a station!", colors.black, colors.lightBlue)
- g.drawText(term, 1, 3, stationName, colors.blue, colors.white)
- g.drawText(term, 1, 5, "Fetching routes...", colors.gray, colors.white)
- end
- local function drawDestinationsChoiceScreen(choices)
- g.clear(term, colors.white)
- g.drawXLine(term, 1, W, 1, colors.blue)
- g.drawText(term, 1, 1, "Destinations", colors.white, colors.blue)
- g.drawText(term, W-3, 1, "Quit", colors.white, colors.red)
- for i, choice in pairs(choices) do
- local y = i + 1
- local bg = colors.white
- if i % 2 == 0 then bg = colors.lightGray end
- g.drawXLine(term, 1, W, y, bg)
- g.drawText(term, 1, y, i..". "..choice, colors.black, bg)
- end
- end
- local function drawErrorPage(errorMsg)
- g.clear(term, colors.white)
- g.drawXLine(term, 1, W, 1, colors.red)
- g.drawText(term, 1, 1, "Error", colors.white, colors.red)
- term.setCursorPos(1, 2)
- term.setTextColor(colors.black)
- term.setBackgroundColor(colors.white)
- print(errorMsg)
- local x, y = term.getCursorPos()
- term.setCursorPos(1, y + 1)
- print("Click to dismiss")
- parallel.waitForAny(
- function () os.sleep(5) end,
- function () os.pullEvent("mouse_click") end
- )
- end
- local function drawGoingToSleepScreen()
- g.clear(term, colors.white)
- term.setTextColor(colors.gray)
- term.setCursorPos(1, 1)
- print("Going to sleep. Click again to wake me.")
- os.sleep(2)
- os.shutdown()
- end
- local function handleNearbyStation()
- while true do
- drawLookingForStationScreen()
- local event, stationData, dist = nil
- parallel.waitForAny(
- function () event, stationData, dist = os.pullEvent("rail_station_nearby") end,
- function ()
- os.sleep(10)
- drawGoingToSleepScreen()
- end
- )
- if not stationData then return end
- drawStationFoundScreen(stationData.displayName)
- os.sleep(0.5)
- modem.transmit(SERVER_CHANNEL, MY_CHANNEL, {command = "GET_ROUTES", startNode = stationData.name})
- local response = waitForModemMessage(SERVER_CHANNEL, 3)
- if response and response.msg.success then
- local stations = response.msg.stations
- local stationNames = {}
- local stationIds = {}
- for _, station in pairs(stations) do
- table.insert(stationNames, station.displayName)
- table.insert(stationIds, station.id)
- end
- drawDestinationsChoiceScreen(stationNames)
- local destination = nil
- -- Wait for user to choose destination, quit, or go away from station.
- parallel.waitForAny(
- function ()
- while true do
- local event, button, x, y = os.pullEvent("mouse_click")
- if button == 1 then
- if x >= W-3 and y == 1 then
- return
- elseif y > 1 and y - 1 <= #stationIds then
- destination = stationIds[y-1]
- return
- end
- end
- end
- end,
- function () waitForNoStation(stationData.name) end
- )
- if destination ~= nil then
- -- Fetch the whole route.
- modem.transmit(SERVER_CHANNEL, MY_CHANNEL, {command = "ROUTE", startNode = stationData.name, endNode = destination})
- local routeResponse = waitForModemMessage(SERVER_CHANNEL, 3)
- if routeResponse and routeResponse.msg.success then
- local routeEdgeIds = {}
- for _, segment in pairs(routeResponse.msg.route) do
- if segment.via then
- table.insert(routeEdgeIds, segment.via)
- end
- end
- os.queueEvent("rail_route_selected", {path = routeEdgeIds, destination = destination})
- return
- elseif routeResponse and routeResponse.msg.error then
- drawErrorPage("Failed to get route: "..routeResponse.msg.error)
- else
- drawErrorPage("Failed to get route. Please contact an administrator if the issue persists.")
- end
- end
- elseif response and response.msg.error then
- drawErrorPage(response.msg.error)
- else
- drawErrorPage("Could not get a list of stations. Please contact an administrator if the issue persists.\n"..textutils.serialize(response, {compact=true}))
- end
- end
- end
- local function waitForRouteSelection()
- while true do
- parallel.waitForAny(
- listenForAnyStation,
- handleNearbyStation
- )
- local event, route = os.pullEvent("rail_route_selected")
- if event and route then
- return route
- end
- end
- end
- local args = {...}
- if #args > 1 then
- local route = args
- print("Routing via command-line args:")
- for _, branch in pairs(route) do
- print(" "..branch)
- end
- broadcastRoute(route)
- return
- end
- g.clear(term, colors.white)
- g.drawTextCenter(term, W/2, H/2, "Rail Router", colors.black, colors.white)
- g.drawTextCenter(term, W/2, H/2 + 2, "By Andrew", colors.gray, colors.white)
- os.sleep(0.5)
- while true do
- local route = waitForRouteSelection()
- g.clear(term, colors.white)
- g.drawTextCenter(term, W/2, 2, "Broadcasting route...", colors.black, colors.white)
- g.drawText(term, 1, 4, " Path:", colors.gray, colors.white)
- for i, segment in pairs(route.path) do
- local y = i + 4
- g.drawText(term, 4, y, segment, colors.gray, colors.white)
- end
- g.drawText(term, W-3, 1, "Quit", colors.white, colors.red)
- parallel.waitForAny(
- function() broadcastRoute(route.path) end,
- function() waitForStation(route.destination) end,
- function() -- Listen for user clicks on the "Quit" button.
- while true do
- local event, button, x, y = os.pullEvent("mouse_click")
- if button == 1 and x >= W-3 and y == 1 then
- return
- end
- end
- end
- )
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement