Added WIP driver for computer-craft flavors of devices.
This commit is contained in:
parent
dc9ca7c2af
commit
2771e934c9
|
@ -0,0 +1,235 @@
|
||||||
|
local VERSION = "1.0.2"
|
||||||
|
|
||||||
|
-- Global component states
|
||||||
|
local components = {}
|
||||||
|
|
||||||
|
-- Global signal-indexed state maps.
|
||||||
|
local lastTrainOverheadStates = {}
|
||||||
|
local lastTrainOverheadDataObjs = {}
|
||||||
|
local trainScanTimers = {}
|
||||||
|
|
||||||
|
-- Determines if a train is currently traversing the given signal.
|
||||||
|
local function trainOverhead(signal)
|
||||||
|
return redstone.getInput(signal.redstoneInputSide)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Determines if a train was over the given signal the last time we checked.
|
||||||
|
local function wasTrainOverheadPreviously(signal)
|
||||||
|
return lastTrainOverheadStates[signal.id] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getTrainData(signal)
|
||||||
|
local det = peripheral.wrap(signal.detector)
|
||||||
|
if det == nil then
|
||||||
|
print("Error: Signal " .. signal.id .. "'s detector could not be connected. Please fix the config and restart.")
|
||||||
|
else
|
||||||
|
return det.consist()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns the from, to branches for a signal when a train travels in the given direction.
|
||||||
|
local function getBranches(dir, signal)
|
||||||
|
local branches = signal.branches
|
||||||
|
if string.upper(branches[1].direction) == string.upper(dir) then
|
||||||
|
return branches[2].id, branches[1].id
|
||||||
|
elseif string.upper(branches[2].direction) == string.upper(dir) then
|
||||||
|
return branches[1].id, branches[2].id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sends an update to the RailSignal API.
|
||||||
|
local function sendSignalUpdate(ws, signal, data, msgType)
|
||||||
|
local from, to = getBranches(string.upper(data.direction), signal)
|
||||||
|
local msg = textutils.serializeJSON({
|
||||||
|
signalId = signal.id,
|
||||||
|
fromBranchId = from,
|
||||||
|
toBranchId = to,
|
||||||
|
type = msgType
|
||||||
|
})
|
||||||
|
print("-> S: " .. signal.id .. ", from: " .. from .. ", to: " .. to .. ", T: " .. msgType)
|
||||||
|
ws.send(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateBranchStatusIndicator(branch, status, config)
|
||||||
|
if branch.monitor ~= nil then
|
||||||
|
local mon = peripheral.wrap(branch.monitor)
|
||||||
|
if mon ~= nil then
|
||||||
|
local c = config.statusColors[status]
|
||||||
|
if c == nil then c = config.statusColors.ERROR end
|
||||||
|
mon.setBackgroundColor(c)
|
||||||
|
mon.clear()
|
||||||
|
else
|
||||||
|
print("Error! Could not connect to monitor " .. branch.monitor .. " for branch " .. branch.id .. ". Check and fix config.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateBranchStatus(signal, branchId, status, config)
|
||||||
|
for _, branch in pairs(signal.branches) do
|
||||||
|
if branch.id == branchId then
|
||||||
|
updateBranchStatusIndicator(branch, status, config)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Manually set the status indicator for all branch monitors.
|
||||||
|
local function setAllBranchStatus(config, status)
|
||||||
|
for _, signal in pairs(config.signals) do
|
||||||
|
for _, branch in pairs(signal.branches) do
|
||||||
|
updateBranchStatusIndicator(branch, status, config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function initMonitorColors(config)
|
||||||
|
for _, signal in pairs(config.signals) do
|
||||||
|
for _, branch in pairs(signal.branches) do
|
||||||
|
if branch.monitor ~= nil then
|
||||||
|
local mon = peripheral.wrap(branch.monitor)
|
||||||
|
if mon ~= nil then
|
||||||
|
for c, v in pairs(config.paletteColors) do
|
||||||
|
mon.setPaletteColor(c, v)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print("Error! Could not connect to monitor " .. branch.monitor .. " for branch " .. branch.id .. ". Check and fix config.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Checks if all signals managed by this controller are reporting an "online" status.
|
||||||
|
local function checkSignalOnlineStatus(config)
|
||||||
|
for _, signal in pairs(config.signals) do
|
||||||
|
local resp = http.get(config.apiUrl .. "/railSystems/" .. config.rsId .. "/signals/" .. signal.id)
|
||||||
|
if resp == nil or resp.getResponseCode() ~= 200 then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
local signalData = textutils.unserializeJSON(resp.readAll())
|
||||||
|
if not signalData.online then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handleRedstoneEvent(ws, config)
|
||||||
|
for _, signal in pairs(config.signals) do
|
||||||
|
if trainOverhead(signal) and not wasTrainOverheadPreviously(signal) then
|
||||||
|
local data = getTrainData(signal)
|
||||||
|
if data == nil then
|
||||||
|
print("Got redstone event but could not obtain train data on signal " .. signal.id)
|
||||||
|
else
|
||||||
|
lastTrainOverheadDataObjs[signal.id] = data
|
||||||
|
lastTrainOverheadStates[signal.id] = true
|
||||||
|
sendSignalUpdate(ws, signal, data, "BEGIN")
|
||||||
|
trainScanTimers[signal.id] = os.startTimer(config.trainScanInterval)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handleTrainScanTimerEvent(ws, config, timerId)
|
||||||
|
for k, signal in pairs(config.signals) do
|
||||||
|
if trainScanTimers[signal.id] == timerId then
|
||||||
|
if trainOverhead(signal) then -- The train is still overhead.
|
||||||
|
local data = getTrainData(signal)
|
||||||
|
if data ~= nil then
|
||||||
|
lastTrainOverheadDataObjs[signal.id] = data
|
||||||
|
end
|
||||||
|
trainScanTimers[signal.id] = os.startTimer(config.trainScanInterval)
|
||||||
|
else -- The train has left the signal so send an update.
|
||||||
|
sendSignalUpdate(ws, signal, lastTrainOverheadDataObjs[signal.id], "END")
|
||||||
|
lastTrainOverheadDataObjs[signal.id] = nil
|
||||||
|
lastTrainOverheadStates[signal.id] = nil
|
||||||
|
trainScanTimers[signal.id] = nil
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print("Warn: Train scan timer was ignored.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handleWebSocketMessage(msg, config)
|
||||||
|
local data = textutils.unserializeJSON(msg)
|
||||||
|
local branchId = data["branchId"]
|
||||||
|
local status = data["status"]
|
||||||
|
print("<- B: " .. branchId .. ", Status: " .. status)
|
||||||
|
for _, signal in pairs(config.signals) do
|
||||||
|
updateBranchStatus(signal, branchId, status, config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function loadConfig(file)
|
||||||
|
local f = io.open(file, "r")
|
||||||
|
if f == nil then return createConfig(file) end
|
||||||
|
local text = f:read("*a")
|
||||||
|
f:close()
|
||||||
|
return textutils.unserialize(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Connects to the RailSignal API websocket. Will block indefinitely until a connection can be obtained.
|
||||||
|
local function connectToWebSocket(config)
|
||||||
|
local signalIds = {}
|
||||||
|
for _, signal in pairs(config.signals) do
|
||||||
|
table.insert(signalIds, tostring(signal.id))
|
||||||
|
end
|
||||||
|
local signalIdsStr = table.concat(signalIds, ",")
|
||||||
|
while true do
|
||||||
|
local ws, err = http.websocket(config.wsUrl, {[config.wsHeader] = signalIdsStr})
|
||||||
|
if ws == false then
|
||||||
|
print("Error connecting to RailSignal websocket:\n\tError: " .. err .. "\n\tTrying again in 3 seconds.")
|
||||||
|
os.sleep(3)
|
||||||
|
else
|
||||||
|
print("Successfully connected to RailSignal websocket at " .. config.wsUrl .. " for managing signals: " .. signalIdsStr)
|
||||||
|
return ws
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Main Script
|
||||||
|
term.clear()
|
||||||
|
print("RailSignal Signal Controller V" .. VERSION)
|
||||||
|
print(" By Andrew Lalis. For more info, check here: https://github.com/andrewlalis/RailSignalAPI")
|
||||||
|
print(" To update, execute \"sig update\" in the command line.")
|
||||||
|
local w, h = term.getSize()
|
||||||
|
print(string.rep("-", w))
|
||||||
|
if arg[1] ~= nil and string.lower(arg[1]) == "update" then
|
||||||
|
print("Updating to the latest version of RailSignal signal program.")
|
||||||
|
fs.delete("sig.lua")
|
||||||
|
shell.execute("pastebin", "get", "erA3mSfd", "sig.lua")
|
||||||
|
print("Download complete. Restarting...")
|
||||||
|
os.sleep(1)
|
||||||
|
os.reboot()
|
||||||
|
end
|
||||||
|
local config = loadConfig("sig_config.tbl")
|
||||||
|
initMonitorColors(config)
|
||||||
|
setAllBranchStatus(config, "NONE")
|
||||||
|
local ws = connectToWebSocket(config)
|
||||||
|
local refreshWebSocketAlarm = os.setAlarm((os.time() + math.random(1, 23)) % 24)
|
||||||
|
while true do
|
||||||
|
local eventData = {os.pullEvent()}
|
||||||
|
local event = eventData[1]
|
||||||
|
if event == "redstone" then
|
||||||
|
handleRedstoneEvent(ws, config)
|
||||||
|
elseif event == "timer" then
|
||||||
|
handleTrainScanTimerEvent(ws, config, eventData[2])
|
||||||
|
elseif event == "websocket_message" then
|
||||||
|
handleWebSocketMessage(eventData[3], config)
|
||||||
|
elseif event == "websocket_closed" then
|
||||||
|
setAllBranchStatus(config, "ERROR")
|
||||||
|
print("! RailSignal websocket closed. Attempting to reconnect.")
|
||||||
|
os.sleep(0.5)
|
||||||
|
ws = connectToWebSocket(config)
|
||||||
|
elseif event == "alarm" and eventData[2] == refreshWebSocketAlarm then
|
||||||
|
print("! Checking signal online status.")
|
||||||
|
if not checkSignalOnlineStatus(config) then
|
||||||
|
print("Not all signals are reporting an online status. Reconnecting to the websocket.")
|
||||||
|
ws.close()
|
||||||
|
ws = connectToWebSocket(config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ws.close()
|
Loading…
Reference in New Issue