Added initial scripts and more to readme.
This commit is contained in:
parent
73af82f735
commit
21b9ea1849
14
README.md
14
README.md
|
@ -1,2 +1,12 @@
|
|||
# cc-rail-router
|
||||
Scripts for Computercraft-managed minecart railways.
|
||||
# CC-Rail-Router
|
||||
|
||||
A system for using Computercraft to control minecart junctions and routing. It works by having a traveling portable computer send out messages as it's traveling, so that nearby managed switches are set automatically.
|
||||
|
||||
The portable computer, usually held by the traveling player, will send out a message containing its route as a list of strings, like so:
|
||||
```lua
|
||||
{ "A", "C", "D", "V" }
|
||||
```
|
||||
|
||||
The switch controller will reply then look for any pairs of branch segments that it has control of, and will switch them accordingly. So suppose we have a switch that connects line `C` to line `D`, then upon receiving the above message, it'll change its signal outputs to route trains accordingly from `C` to `D`.
|
||||
|
||||
With this design, the switch controller and portable computer are completely stateless, and no two-way communication is needed.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
--[[
|
||||
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 modem = peripheral.wrap("back") or error("Missing modem.")
|
||||
|
||||
local args = {...}
|
||||
|
||||
local route = args
|
||||
print("Routing via:")
|
||||
for _, branch in pairs(route) do
|
||||
print(" "..branch)
|
||||
end
|
||||
|
||||
local function sendRoute(route)
|
||||
modem.transmit(0, 42, route)
|
||||
end
|
||||
|
||||
while true do
|
||||
sendRoute(route)
|
||||
os.sleep(1)
|
||||
end
|
|
@ -0,0 +1,202 @@
|
|||
--[[
|
||||
This program is to be installed on a computer that controls a single rail
|
||||
junction. As a player with a portable computer approaches, that portable
|
||||
computer will be sending out a signal indicating their preferred switching
|
||||
configuration (the branch they're coming from, and the one they want to go to),
|
||||
and the junction's computer will then send a success reply.
|
||||
]]
|
||||
|
||||
local CONFIG_FILE = "switch_config.tbl"
|
||||
local CHANNEL = 0
|
||||
local config = nil
|
||||
|
||||
local modem = peripheral.wrap("top") or error("Missing modem")
|
||||
if not modem.isWireless() then error("Wireless modem required") end
|
||||
modem.open(CHANNEL)
|
||||
|
||||
term.clear()
|
||||
term.setCursorPos(1, 1)
|
||||
print("Receiving routing commands on channel 0")
|
||||
|
||||
-- Series of guided inputs for building a configuration file from user input.
|
||||
local function configSetupWizard()
|
||||
local cfg = {range = 32, switches = {}}
|
||||
term.clear()
|
||||
term.setCursorPos(1, 1)
|
||||
print("Switch Controller Config Setup Wizard")
|
||||
print("-------------------------------------")
|
||||
print("What range (blocks) does this switch have? Defaults to 32.")
|
||||
local rangeStr = io.read()
|
||||
if rangeStr then
|
||||
local rangeInt = tonumber(rangeStr)
|
||||
if rangeInt == nil or rangeInt < 1 or rangeInt > 128 then
|
||||
print("Invalid range value. Should be a positive integer number. Got "..rangeStr)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
print("How many switch configurations are there? Usually 1 for each possible path of travel.")
|
||||
local switchCountStr = io.read()
|
||||
if not switchCountStr then
|
||||
print("Invalid switch configuration count.")
|
||||
return nil
|
||||
end
|
||||
local switchCount = tonumber(switchCountStr)
|
||||
if switchCount == nil or switchCount < 2 or switchCount > 32 then
|
||||
print("Invalid switch configuration count. Expected a positive integer between 2 and 32.")
|
||||
return nil
|
||||
end
|
||||
for i = 1, switchCount do
|
||||
local sw = {from = nil, to = nil, controls = {}}
|
||||
print("Switch Configuration #"..i..":")
|
||||
print("What is name of the branch traffic comes from?")
|
||||
sw.from = io.read()
|
||||
print("What is the name of the branch traffic goes to?")
|
||||
sw.to = io.read()
|
||||
print("How many control points are needed for this switch configuration?")
|
||||
local controlCountStr = io.read()
|
||||
local controlCount = 0
|
||||
if controlCountStr then controlCount = tonumber(controlCountStr) end
|
||||
if not controlCount or controlCount < 1 then
|
||||
print("Invalid control point count. Expected a positive integer.")
|
||||
return nil
|
||||
end
|
||||
for c = 1, controlCount do
|
||||
local ctl = {type = "redstone", side = "top", state = true}
|
||||
print(" Control Point #"..c..":")
|
||||
print(" What type of control is this?")
|
||||
print(" 1. redstone")
|
||||
print(" 2. redstoneIntegrator")
|
||||
local typeStr = io.read()
|
||||
if not typeStr or typeStr == "1" or typeStr == "redstone" then
|
||||
ctl.type = "redstone"
|
||||
print(" What side is redstone output on?")
|
||||
ctl.side = io.read()
|
||||
print(" What state should the output be? [T/F]")
|
||||
local stateStr = io.read()
|
||||
ctl.state = stateStr == "T" or stateStr == "True" or stateStr == "t" or stateStr == "true"
|
||||
elseif typeStr == "2" or typeStr == "redstoneIntegrator" then
|
||||
ctl.type = "redstoneIntegrator"
|
||||
print(" What is the peripheral name?")
|
||||
ctl.name = io.read()
|
||||
print(" What side is output on?")
|
||||
ctl.side = io.read()
|
||||
print(" What state should the output be? [T/F]")
|
||||
local stateStr = io.read()
|
||||
ctl.state = stateStr == "T" or stateStr == "True" or stateStr == "t" or stateStr == "true"
|
||||
else
|
||||
print(" Unsupported control type.")
|
||||
return nil
|
||||
end
|
||||
table.insert(sw.controls, ctl)
|
||||
end
|
||||
table.insert(config.switches, sw)
|
||||
end
|
||||
local f = io.open(CONFIG_FILE, "w")
|
||||
f:write(textutils.serialize(cfg))
|
||||
f:close()
|
||||
return cfg
|
||||
end
|
||||
|
||||
local function loadConfig()
|
||||
if fs.exists(CONFIG_FILE) then
|
||||
local f = io.open(CONFIG_FILE, "r")
|
||||
local cfg = textutils.unserialize(f:read("*a"))
|
||||
f:close()
|
||||
print("Loaded configuration from file:")
|
||||
print(" "..tostring(#config.switches).." switch configurations.")
|
||||
print(" "..tostring(config.range).." block range.")
|
||||
return cfg
|
||||
else
|
||||
print("File "..CONFIG_FILE.." doesn't exist. Start setup wizard? [y/n]")
|
||||
local response = io.read()
|
||||
if response == "y" or response == "Y" or response == "yes" then
|
||||
return configSetupWizard()
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local config = {range=32, switches={}}
|
||||
if fs.exists(CONFIG_FILE) then
|
||||
local f = io.open(CONFIG_FILE, "r")
|
||||
config = textutils.unserialize(f:read("*a"))
|
||||
f:close()
|
||||
print("Loaded config:")
|
||||
print(" "..tostring(#config.switches).." switch configurations")
|
||||
print(" Range of "..tostring(config.range).." blocks")
|
||||
else
|
||||
local f = io.open(CONFIG_FILE, "w")
|
||||
f:write(textutils.serialize(config))
|
||||
f:close()
|
||||
print("Saved config to "..CONFIG_FILE)
|
||||
end
|
||||
|
||||
local function findSwitchConfiguration(cfg, from, to)
|
||||
if from == nil or to == nil then return nil end
|
||||
for _, sw in pairs(cfg.switches) do
|
||||
if sw.from == from and sw.to == to then return sw end
|
||||
end
|
||||
return nil end
|
||||
end
|
||||
|
||||
local function isSwitchConfigurationActive(sw)
|
||||
for _, control in pairs(sw.controls) do
|
||||
if control.type == "redstone" then
|
||||
local state = redstone.getOutput(control.side)
|
||||
if state ~= control.state then return false end
|
||||
elseif control.type == "redstoneIntegrator" then
|
||||
local state = peripheral.call(control.name, "getOutput", control.side)
|
||||
if state ~= control.state then return false end
|
||||
else
|
||||
error("Invalid control type: "..control.type)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function activateSwitchConfiguration(sw)
|
||||
print("Activating switch: FROM="..sw.from..", TO="..sw.to)
|
||||
for _, control in pairs(sw.controls) do
|
||||
if control.type == "redstone" then
|
||||
redstone.setOutput(control.side, control.state)
|
||||
elseif control.type == "redstoneIntegrator" then
|
||||
peripheral.call(control.name, "setOutput", control.side, control.state)
|
||||
else
|
||||
error("Invalid control type: "..control.type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Handles incoming rail messages that consist of a list of branch names
|
||||
-- that the user would like to traverse.
|
||||
local function handleModemMsg(replyChannel, msg)
|
||||
-- Ignore invalid messages.
|
||||
if not msg or #msg < 2 then return end
|
||||
-- Find the switch configuration(s) that pertain to this route.
|
||||
for i = 1, #msg - 1 do
|
||||
local sw = findSwitchConfiguration(config, msg[i], msg[i+1])
|
||||
if sw and not isSwitchConfigurationActive(sw) then
|
||||
activateSwitchConfiguration(sw)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
config = loadConfig()
|
||||
if not config then
|
||||
print("Unable to load configuration. Exiting.")
|
||||
return
|
||||
end
|
||||
|
||||
while true do
|
||||
local event, p1, p2, p3, p4, p5 = os.pullEvent()
|
||||
if event == "modem_message" then
|
||||
local channel = p2
|
||||
local replyChannel = p3
|
||||
local msg = p4
|
||||
local dist = p5
|
||||
if channel == CHANNEL and dist ~= nil and dist < config.range and msg ~= nil then
|
||||
handleModemMsg(replyChannel, msg)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue