cc-rail-router/router.lua

212 lines
7.4 KiB
Lua

--[[
This program should be installed on a portable computer with a wireless
modem, to act as a routing beacon in conjunction with managed switches.
]]--
local SWITCH_CHANNEL = 45450
local STATION_BROADCAST_CHANNEL = 45451
local STATION_REQUEST_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
-- 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 channel == STATION_BROADCAST_CHANNEL and msg == stationName and dist <= 16 then
return
end
end
end
local function listenForAnyStation()
while true do
local event, side, channel, replyChannel, msg, dist = os.pullEvent("modem_message")
if channel == STATION_BROADCAST_CHANNEL and type(msg) == "string" and dist <= 16 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, name, dist = os.pullEvent("rail_station_nearby")
if not targetName or targetName == 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 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 handleNearbyStation()
while true do
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)
os.sleep(1)
local event, name, dist = os.pullEvent("rail_station_nearby")
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, name, colors.blue, colors.white)
g.drawText(term, 1, 5, "Fetching routes...", colors.gray, colors.white)
os.sleep(1)
modem.transmit(STATION_REQUEST_CHANNEL, MY_CHANNEL, "GET_ROUTES")
local response = waitForModemMessage(STATION_REQUEST_CHANNEL, 1)
if not response or not response.msg or type(response.msg) ~= "table" then
g.clear(term, colors.white)
g.drawXLine(term, 1, W, 1, colors.red)
g.drawText(term, 1, 1, "Error", colors.white, colors.red)
g.drawText(term, 1, 2, "Failed to get routes.", colors.gray, colors.white)
if response then
term.setCursorPos(1, 3)
term.setTextColor(colors.black)
term.setBackgroundColor(colors.lightGray)
print("Response:"..textutils.serialize(response, {compact=true}))
end
os.sleep(5)
else
local routes = response.msg
g.clear(term, colors.white)
g.drawXLine(term, 1, W, 1, colors.blue)
g.drawText(term, 1, 1, "Routes", colors.white, colors.blue)
g.drawText(term, W-3, 1, "Quit", colors.white, colors.red)
for i, route in pairs(routes) 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..". "..route.name, colors.black, bg)
end
-- Either wait for the user to choose a route, or go away from the
-- station transponder.
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
break
elseif y > 1 and y - 1 <= #routes then
local selectedRoute = routes[y-1]
os.queueEvent("rail_route_selected", selectedRoute)
return
end
end
end
end,
function () waitForNoStation(name) end
)
end
end
end
local function waitForRouteSelection()
while true do
parallel.waitForAny(
listenForAnyStation,
handleNearbyStation
)
local event, route = os.pullEvent("rail_route_selected")
if event and type(route) == "table" 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
parallel.waitForAny(
function() broadcastRoute(route) end,
function() waitForStation(route[#route]) end
)
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(1)
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.path[#route.path]) 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