-- -- dc_rtu.lua -- -- Data-center RTU: one per data center. Reports what's in the IO ports to the -- coordinator, serves load/feed/pull commands, and shows a simple local screen. -- -- dc_rtu -- status screen on this terminal -- dc_rtu monitor_0 -- status screen on a monitor -- -- Needs mimic + facility + dc installed, a /config.lua with unit_type="data_center", -- a modem, and the data center's IO ports on this computer's network. Control -- commands also need the dc_*_port / dc_*_source config fields (CC can't read a -- port's in-world mode, so you name them). -- require("/initenv").init_env() local mimic = require("mimic") local m = require("mimic.elements") local psil = require("scada-common.psil") local facility = require("facility") local dclib = require("dc") local monitor = ... local style = mimic.style local cpair = mimic.cpair local ALIGN = mimic.ALIGN local cfg = facility.load_config() facility.open(cfg.modem) local STATUS_INTERVAL = cfg.status_interval or 2 local LINK_TIMEOUT = 10 local dc = dclib.new(cfg) -- -- command handlers (coordinator -> this RTU) -- local handlers = { ping = function () return true, { unit_id = cfg.unit_id } end, load_model = function (a) return dc.load_model(a and a.model) end, feed_input = function (a) return dc.feed_input(a and a.item, a and a.count) end, pull_output = function () return dc.pull_output() end, } -- -- local status screen -- local ps = psil.create() local root = mimic.init{monitor=monitor, ps=ps} local W = root.get_width() m.TextBox{parent=root, x=1, y=1, width=W-9, text=string.format("DATA CENTER #%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="LOADED" }, { color=cpair(colors.white, colors.gray), text="EMPTY" }, }, 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, "IO ports:", "d_ports") di(6, "Models loaded:", "d_models") di(7, "Items in ports:", "d_items") -- a couple of lines listing which models are loaded (filled in each poll) m.TextBox{parent=root, x=2, y=9, width=W-2, text="", fg_bg=style.label, bind="d_modeltext"} -- -- loops: poll+broadcast, serve commands, keep link/clock live -- local last_coord = -1e9 local function pollLoop() while true do local st = dc.collect() facility.broadcast_status(cfg, st) ps.publish("d_ports", st.totals.ports) ps.publish("d_models", st.totals.models) ps.publish("d_items", st.totals.items) ps.publish("health", st.totals.models > 0 and 1 or 2) -- a compact "Zombie x2, Blaze x1" line local parts = {} for _, mo in ipairs(st.models) do parts[#parts + 1] = mo.disp .. " x" .. mo.count end ps.publish("d_modeltext", #parts > 0 and table.concat(parts, ", ") or "(no models loaded)") 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)