-- -- chamber_rtu.lua -- -- Sim-chamber RTU: runs the chamber subsystem, reports status to the coordinator -- over rednet, executes commands it sends, and shows a simple local status -- screen. Keeps polling/serving its subsystem even if the coordinator link drops. -- -- chamber_rtu -- status screen on this terminal -- chamber_rtu monitor_0 -- status screen on a monitor -- -- Needs mimic + facility + chamber installed, and a /config.lua (see -- config.example.lua) with unit_id, unit_type="sim_chamber", modem, etc. -- require("/initenv").init_env() local mimic = require("mimic") local m = require("mimic.elements") local psil = require("scada-common.psil") local facility = require("facility") local chamberlib = require("chamber") local monitor = ... local style = mimic.style local cpair = mimic.cpair local ALIGN = mimic.ALIGN -- -- config + comms + subsystem -- local cfg = facility.load_config() facility.open(cfg.modem) local STATUS_INTERVAL = cfg.status_interval or 2 local LINK_TIMEOUT = 10 -- seconds without a coordinator packet -> link shown down local chamber = chamberlib.new(cfg) local N = chamber.count -- -- command handlers (coordinator -> this RTU). Each returns ok, result_or_err. -- local function argChamber(args) local i = args and tonumber(args.chamber) if not i or i < 1 or i > N then return nil end return i end local handlers = { ping = function () return true, { unit_id = cfg.unit_id } end, list_models = function () return true, { models = chamber.list_models() } end, load_model = function (a) local i = argChamber(a); if not i then return false, "bad chamber" end if not a.model then return false, "no model given" end return chamber.load_model(i, a.model) end, return_model = function (a) local i = argChamber(a); if not i then return false, "bad chamber" end return chamber.return_model(i) end, stock_catalyst = function (a) local i = argChamber(a); if not i then return false, "bad chamber" end return chamber.stock_catalyst(i) end, load_all_empty = function () return chamber.load_all_empty() end, stock_all_low = function () return chamber.stock_all_low() end, } -- -- local status screen (simple, read-only - control comes from the coordinator) -- local ps = psil.create() local root = mimic.init{monitor=monitor, ps=ps} local W, H = root.get_width(), root.get_height() m.TextBox{parent=root, x=1, y=1, width=W-9, text=string.format("CHAMBER RTU #%02d", cfg.unit_id), alignment=ALIGN.CENTER, fg_bg=style.theme.header} m.TextBox{parent=root, x=W-8, y=1, width=8, text="", alignment=ALIGN.RIGHT, fg_bg=style.theme.header, bind="clock"} m.StateIndicator{parent=root, x=2, y=3, min_width=12, value=2, states={ { color=cpair(colors.black, colors.green), text="COORD OK" }, { color=cpair(colors.white, colors.red), text="NO COORD" }, }, bind="link"} m.StateIndicator{parent=root, x=16, y=3, min_width=12, value=1, states={ { color=cpair(colors.black, colors.green), text="ALL READY" }, { color=cpair(colors.black, colors.yellow), text="ATTENTION" }, { color=cpair(colors.white, colors.red), text="NO MODELS" }, }, bind="health"} local function di(y, label, key) m.DataIndicator{parent=root, x=2, y=y, label=label, format="%4.0f", value=0, width=24, fg_bg=style.text_colors, lu_colors=style.lu_colors, bind=key} end di(5, "Chambers:", "s_total") di(6, "Ready:", "s_ready") di(7, "Low catalyst:", "s_low") di(8, "No model:", "s_nomodel") di(10, "Barrel models:", "b_models") di(11, "Barrel catalyst:", "b_catalyst") -- -- loops: poll+broadcast, serve commands, keep link/clock live. Peripheral I/O -- yields, so (as in chamber_gui) it must run outside mimic.run's event loop. -- local last_coord = -1e9 -- os.clock() of the last packet heard from the coordinator local function pollLoop() while true do local st = chamber.collect() facility.broadcast_status(cfg, st) ps.publish("s_total", st.summary.total) ps.publish("s_ready", st.summary.ready) ps.publish("s_low", st.summary.low) ps.publish("s_nomodel", st.summary.no_model) ps.publish("b_models", st.barrel.models) ps.publish("b_catalyst", st.barrel.catalyst) local health = 1 if st.summary.no_model == st.summary.total then health = 3 elseif st.summary.low > 0 or st.summary.no_catalyst > 0 or st.summary.no_model > 0 then health = 2 end ps.publish("health", health) os.sleep(STATUS_INTERVAL) end end local function commandLoop() while true do local id, pkt = facility.receive() if pkt then last_coord = os.clock() facility.handle_command(handlers, id, pkt) end end end local function linkLoop() while true do ps.publish("link", (os.clock() - last_coord) < LINK_TIMEOUT and 1 or 2) ps.publish("clock", os.date("%H:%M:%S")) os.sleep(1) end end parallel.waitForAny(mimic.run, pollLoop, commandLoop, linkLoop)