diff --git a/supervisor/config/system.lua b/supervisor/config/system.lua index 48867f5..26bd33c 100644 --- a/supervisor/config/system.lua +++ b/supervisor/config/system.lua @@ -62,8 +62,10 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49} local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=49} local net_c_4 = Div{parent=net_cfg,x=2,y=4,width=49} + local net_c_5 = Div{parent=net_cfg,x=2,y=4,width=49} + local net_c_6 = Div{parent=net_cfg,x=2,y=4,width=49} - local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}} + local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4,net_c_5,net_c_6}} TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)} @@ -137,40 +139,54 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - TextBox{parent=net_c_3,x=1,y=1,text="Please set the trusted range below."} - TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg} - TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg} + TextBox{parent=net_c_3,x=1,y=1,text="Please set the modem configuration below."} + TextBox{parent=net_c_3,x=1,y=3,height=3,text="Communications with the coordinator,",fg_bg=g_lg_fg_bg} + -- TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg} - local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg} + local use_wired = Checkbox{parent=net_c_3,x=1,y=12,label="Use Wired Modem",default=ini_cfg.ExtChargeIdling,box_fg_bg=cpair(colors.yellow,colors.black)} - local tr_err = TextBox{parent=net_c_3,x=8,y=14,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + local function submit_modems() + -- tmp_cfg. = use_wired.get_value() + net_pane.set_value(4) + end + + PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_modems,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_5,x=1,y=1,text="Please set the trusted range below."} + TextBox{parent=net_c_5,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg} + TextBox{parent=net_c_5,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg} + + local range = NumberField{parent=net_c_5,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg} + + local tr_err = TextBox{parent=net_c_5,x=8,y=14,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} local function submit_tr() local range_val = tonumber(range.get_value()) if range_val ~= nil then tmp_cfg.TrustedRange = range_val - net_pane.set_value(4) + net_pane.set_value(6) tr_err.hide(true) else tr_err.show() end end - PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_5,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_5,x=44,y=14,text="Next \x1a",callback=submit_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."} - TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra computation (can slow things down).",fg_bg=g_lg_fg_bg} + TextBox{parent=net_c_6,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."} + TextBox{parent=net_c_6,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra computation (can slow things down).",fg_bg=g_lg_fg_bg} - TextBox{parent=net_c_4,x=1,y=11,text="Facility Auth Key"} - local key, _ = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg} + TextBox{parent=net_c_6,x=1,y=11,text="Facility Auth Key"} + local key, _ = TextField{parent=net_c_6,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg} local function censor_key(enable) key.censor(tri(enable, "*", nil)) end - local hide_key = Checkbox{parent=net_c_4,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key} + local hide_key = Checkbox{parent=net_c_6,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key} hide_key.set_value(true) censor_key(true) - local key_err = TextBox{parent=net_c_4,x=8,y=14,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + local key_err = TextBox{parent=net_c_6,x=8,y=14,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} local function submit_auth() local v = key.get_value() @@ -181,8 +197,8 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit else key_err.show() end end - PushButton{parent=net_c_4,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=net_c_4,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_6,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_6,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} --#endregion diff --git a/supervisor/configure.lua b/supervisor/configure.lua index b25f891..fcb1611 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -3,6 +3,7 @@ -- local log = require("scada-common.log") +local ppm = require("scada-common.ppm") local tcd = require("scada-common.tcd") local util = require("scada-common.util") @@ -96,8 +97,11 @@ local tmp_cfg = { RTU_Timeout = nil, ---@type number CRD_Timeout = nil, ---@type number PKT_Timeout = nil, ---@type number + WiredModem = false, ---@type string|false + WirelessModem = false, ---@type boolean TrustedRange = nil, ---@type number AuthKey = nil, ---@type string|nil + PocketTest = true, ---@type boolean LogMode = 0, ---@type LOG_MODE LogPath = "", LogDebug = false, @@ -130,8 +134,11 @@ local fields = { { "RTU_Timeout", "RTU Connection Timeout", 5 }, { "CRD_Timeout", "CRD Connection Timeout", 5 }, { "PKT_Timeout", "PKT Connection Timeout", 5 }, + { "WiredModem", "Wired Modem", false }, + { "WirelessModem", "Pocket Wireless/Ender Modem", true }, { "TrustedRange", "Trusted Range", 0 }, - { "AuthKey", "Facility Auth Key" , ""}, + { "AuthKey", "Facility Auth Key" , "" }, + { "PocketTest", "Pocket Testing Features", true }, { "LogMode", "Log Mode", log.MODE.APPEND }, { "LogPath", "Log Path", "/log.txt" }, { "LogDebug", "Log Debug Messages", false }, @@ -314,6 +321,14 @@ function configurator.configure(ask_config) if k_e then display.handle_key(k_e) end elseif event == "paste" then display.handle_paste(param1) + elseif event == "peripheral_detach" then +---@diagnostic disable-next-line: discard-returns + ppm.handle_unmount(param1) + tool_ctl.gen_modem_list() + elseif event == "peripheral" then +---@diagnostic disable-next-line: discard-returns + ppm.mount(param1) + tool_ctl.gen_modem_list() end if event == "terminate" then return end diff --git a/supervisor/databus.lua b/supervisor/databus.lua index f5daefb..2600afb 100644 --- a/supervisor/databus.lua +++ b/supervisor/databus.lua @@ -27,10 +27,16 @@ function databus.tx_versions(sv_v, comms_v) databus.ps.publish("comms_version", comms_v) end --- transmit hardware status for modem connection state +-- transmit hardware status for the core comms modem connection state ---@param has_modem boolean -function databus.tx_hw_modem(has_modem) - databus.ps.publish("has_modem", has_modem) +function databus.tx_hw_c_modem(has_modem) + databus.ps.publish("has_c_modem", has_modem) +end + +-- transmit hardware status for the pocket modem connection state +---@param has_modem boolean +function databus.tx_hw_p_modem(has_modem) + databus.ps.publish("has_p_modem", has_modem) end -- transmit PLC firmware version and session connection state diff --git a/supervisor/panel/front_panel.lua b/supervisor/panel/front_panel.lua index 786fadf..fb865a3 100644 --- a/supervisor/panel/front_panel.lua +++ b/supervisor/panel/front_panel.lua @@ -34,7 +34,8 @@ local ind_grn = style.ind_grn -- create new front panel view ---@param panel DisplayBox main displaybox -local function init(panel) +---@param wl_modem boolean if there is a separate wireless modem +local function init(panel, wl_modem) local s_hi_box = style.theme.highlight_box local s_hi_bright = style.theme.highlight_box_bright @@ -53,7 +54,7 @@ local function init(panel) local main_page = Div{parent=page_div,x=1,y=1} - local system = Div{parent=main_page,width=14,height=17,x=2,y=2} + local system = Div{parent=main_page,width=18,height=17,x=2,y=2} local on = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)} local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn} @@ -62,14 +63,21 @@ local function init(panel) heartbeat.register(databus.ps, "heartbeat", heartbeat.update) - local modem = LED{parent=system,label="MODEM",colors=ind_grn} + local c_modem = LED{parent=system,label="MODEM"..util.trinary(wl_modem," A",""),colors=ind_grn} system.line_break() - modem.register(databus.ps, "has_modem", modem.update) + c_modem.register(databus.ps, "has_modem_a", c_modem.update) + + if wl_modem then + local p_modem = LED{parent=system,label="MODEM B",colors=ind_grn} + system.line_break() + + p_modem.register(databus.ps, "has_modem_b", p_modem.update) + end ---@diagnostic disable-next-line: undefined-field local comp_id = util.sprintf("(%d)", os.getComputerID()) - TextBox{parent=system,x=9,y=4,width=6,text=comp_id,fg_bg=style.fp.disabled_fg} + TextBox{parent=system,x=11,y=4,width=6,text=comp_id,fg_bg=style.fp.disabled_fg} -- -- about footer diff --git a/supervisor/pcie.lua b/supervisor/pcie.lua new file mode 100644 index 0000000..6629e30 --- /dev/null +++ b/supervisor/pcie.lua @@ -0,0 +1,159 @@ +-- +-- PCIe - Borrowed the name of that protocol for fun (this manages physical peripherals) +-- + +local log = require("scada-common.log") +local network = require("scada-common.network") +local ppm = require("scada-common.ppm") + +local databus = require("supervisor.databus") + +local pcie_bus = {} + +local bus = { + c_wired = false, ---@type string|false wired comms modem + c_nic = nil, ---@type nic core nic + p_nic = nil ---@type nic|nil pocket nic +} + +-- network cards +---@class _svr_pcie_nic +---@field core nic the core comms NIC +---@field pocket nic the pocket NIC +pcie_bus.nic = { + -- close all channels then open a specified one on all nics + ---@param channel integer + reset_open = function (channel) + bus.c_nic.closeAll() + bus.c_nic.open(channel) + + if bus.p_nic then + bus.p_nic.closeAll() + bus.p_nic.open(channel) + end + end +} + +-- initialize peripherals +---@param config svr_config +---@param println function +function pcie_bus.init(config, println) + -- setup networking peripheral(s) + local core_modem, core_iface = ppm.get_wireless_modem() + if type(config.WiredModem) == "string" then + bus.c_wired = config.WiredModem + core_modem = ppm.get_wired_modem(config.WiredModem) + end + + if not (core_modem and core_iface) then + println("startup> core comms modem not found") + log.fatal("no core comms modem on startup") + return + end + + bus.c_nic = network.nic(core_iface, core_modem) + + if config.WirelessModem and config.WiredModem then + local pocket_modem, pocket_iface = ppm.get_wireless_modem() + + if not (pocket_modem and pocket_iface) then + println("startup> pocket wireless modem not found") + log.fatal("no pocket wireless modem on startup") + return + end + + bus.p_nic = network.nic(pocket_iface, pocket_modem) + end + + pcie_bus.nic.core = bus.c_nic + pcie_bus.nic.pocket = bus.p_nic or bus.c_nic + + databus.tx_hw_c_modem(true) + databus.tx_hw_p_modem(config.WirelessModem) +end + +-- handle the connecting of a device +---@param iface string +---@param type string +---@param device table +---@param println function +function pcie_bus.connect(iface, type, device, println) + if type == "modem" then + ---@cast device Modem + if device.isWireless() then + if not (bus.c_wired or bus.c_nic.is_connected()) then + -- reconnected comms modem + bus.c_nic.connect(device) + + println("core comms modem reconnected") + log.info("core comms modem reconnected") + + databus.tx_hw_c_modem(true) + elseif bus.p_nic and not bus.p_nic.is_connected() then + -- reconnected pocket modem + bus.p_nic.connect(device) + + println("pocket modem reconnected") + log.info("pocket modem reconnected") + + databus.tx_hw_p_modem(true) + else + log.info("unused wireless modem reconnected") + end + elseif iface == bus.c_wired then + -- reconnected wired comms modem + bus.c_nic.connect(device) + + println("core comms modem reconnected") + log.info("core comms modem reconnected") + + databus.tx_hw_c_modem(true) + else + log.info("wired modem reconnected") + end + end +end + +-- handle the removal of a device +---@param type string +---@param device table +---@param println function +function pcie_bus.remove(type, device, println) + if type == "modem" then + ---@cast device Modem + if bus.c_nic.is_modem(device) then + bus.c_nic.disconnect() + + println("core comms modem disconnected") + log.warning("core comms modem disconnected") + + local other_modem = ppm.get_wireless_modem() + if other_modem and not bus.c_wired then + log.info("found another wireless modem, using it for comms") + bus.c_nic.connect(other_modem) + else + databus.tx_hw_c_modem(false) + end + elseif bus.p_nic and bus.p_nic.is_modem(device) then + bus.p_nic.disconnect() + + println("pocket modem disconnected") + log.warning("pocket modem disconnected") + + local other_modem = ppm.get_wireless_modem() + if other_modem then + log.info("found another wireless modem, using it for pocket comms") + bus.p_nic.connect(other_modem) + else + databus.tx_hw_p_modem(false) + end + else + log.warning("non-comms modem disconnected") + end + end +end + +-- check if a dedicated pocket nic is in use +function pcie_bus.has_pocket_nic() return bus.p_nic ~= nil end + +return pcie_bus diff --git a/supervisor/renderer.lua b/supervisor/renderer.lua index 57c6b4b..1acde3a 100644 --- a/supervisor/renderer.lua +++ b/supervisor/renderer.lua @@ -19,10 +19,11 @@ local ui = { } -- try to start the UI +---@param wl_modem boolean if there is a separate wireless modem to display the status of ---@param theme FP_THEME front panel theme ---@param color_mode COLOR_MODE color mode ---@return boolean success, any error_msg -function renderer.try_start_ui(theme, color_mode) +function renderer.try_start_ui(wl_modem, theme, color_mode) local status, msg = true, nil if ui.display == nil then @@ -49,7 +50,7 @@ function renderer.try_start_ui(theme, color_mode) -- init front panel view status, msg = pcall(function () ui.display = DisplayBox{window=term.current(),fg_bg=style.fp.root} - panel_view(ui.display) + panel_view(ui.display, wl_modem) end) if status then diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index 087fe19..efdaec9 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -6,6 +6,7 @@ local log = require("scada-common.log") local mqueue = require("scada-common.mqueue") local types = require("scada-common.types") local util = require("scada-common.util") +local pcie = require("supervisor.pcie") local databus = require("supervisor.databus") @@ -41,20 +42,17 @@ svsessions.SESSION_TYPE = SESSION_TYPE local self = { -- references to supervisor state and other data - nic = nil, ---@type nic|nil fp_ok = false, - config = nil, ---@type svr_config + config = nil, ---@type svr_config|nil facility = nil, ---@type facility|nil plc_ini_reset = {}, -- lists of connected sessions ----@diagnostic disable: missing-fields sessions = { - rtu = {}, ---@type rtu_session_struct - plc = {}, ---@type plc_session_struct - crd = {}, ---@type crd_session_struct - pdg = {} ---@type pdg_session_struct + rtu = {}, ---@type rtu_session_struct[] + plc = {}, ---@type plc_session_struct[] + crd = {}, ---@type crd_session_struct[] + pdg = {} ---@type pdg_session_struct[] }, ----@diagnostic enable: missing-fields -- next session IDs next_ids = { rtu = 0, plc = 0, crd = 0, pdg = 0 }, -- rtu device tracking and invalid assignment detection @@ -83,7 +81,9 @@ local function _sv_handle_outq(session) if msg ~= nil then if msg.qtype == mqueue.TYPE.PACKET then -- handle a packet to be sent - self.nic.transmit(session.r_chan, self.config.SVR_Channel, msg.message) + if session.r_chan == self.config.PKT_Channel then + pcie.nic.pocket.transmit(session.r_chan, self.config.SVR_Channel, msg.message) + else pcie.nic.core.transmit(session.r_chan, self.config.SVR_Channel, msg.message) end elseif msg.qtype == mqueue.TYPE.COMMAND then -- handle instruction/notification elseif msg.qtype == mqueue.TYPE.DATA then @@ -139,12 +139,9 @@ end local function _iterate(sessions) for i = 1, #sessions do local session = sessions[i] - if session.open and session.instance.iterate() then _sv_handle_outq(session) - else - session.open = false - end + else session.open = false end end end @@ -158,7 +155,9 @@ local function _shutdown(session) while session.out_queue.ready() do local msg = session.out_queue.pop() if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then - self.nic.transmit(session.r_chan, self.config.SVR_Channel, msg.message) + if session.r_chan == self.config.PKT_Channel then + pcie.nic.pocket.transmit(session.r_chan, self.config.SVR_Channel, msg.message) + else pcie.nic.core.transmit(session.r_chan, self.config.SVR_Channel, msg.message) end end end @@ -358,12 +357,10 @@ function svsessions.check_rtu_id(unit, list, max) end -- initialize svsessions ----@param nic nic network interface device ---@param fp_ok boolean front panel active ---@param config svr_config supervisor configuration ---@param facility facility -function svsessions.init(nic, fp_ok, config, facility) - self.nic = nic +function svsessions.init(fp_ok, config, facility) self.fp_ok = fp_ok self.config = config self.facility = facility diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 4a88018..29c32b3 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -3,6 +3,7 @@ -- require("/initenv").init_env() +local pcie = require("supervisor.pcie") local crash = require("scada-common.crash") local comms = require("scada-common.comms") @@ -125,18 +126,11 @@ local function main() network.init_mac(config.AuthKey) end - -- get modem - local modem = ppm.get_wireless_modem() - if modem == nil then - println("startup> wireless modem not found") - log.fatal("no wireless modem on startup") - return - end - - databus.tx_hw_modem(true) + -- hardware bus initialization + pcie.init(config, println) -- start UI - local fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode) + local fp_ok, message = renderer.try_start_ui(pcie.has_pocket_nic(), config.FrontPanelTheme, config.ColorMode) if not fp_ok then println_ts(util.c("UI error: ", message)) @@ -150,8 +144,7 @@ local function main() local sv_facility = facility.new(config) -- create network interface then setup comms - local nic = network.nic(modem) - local superv_comms = supervisor.comms(SUPERVISOR_VERSION, nic, fp_ok, sv_facility) + local superv_comms = supervisor.comms(SUPERVISOR_VERSION, fp_ok, sv_facility) -- base loop clock (6.67Hz, 3 ticks) local MAIN_CLOCK = 0.15 @@ -173,49 +166,13 @@ local function main() -- handle event if event == "peripheral_detach" then local type, device = ppm.handle_unmount(param1) - if type ~= nil and device ~= nil then - if type == "modem" then - ---@cast device Modem - -- we only care if this is our wireless modem - if nic.is_modem(device) then - nic.disconnect() - - println_ts("wireless modem disconnected!") - log.warning("comms modem disconnected") - - local other_modem = ppm.get_wireless_modem() - if other_modem then - log.info("found another wireless modem, using it for comms") - nic.connect(other_modem) - else - databus.tx_hw_modem(false) - end - else - log.warning("non-comms modem disconnected") - end - end + pcie.remove(type, device, println_ts) end elseif event == "peripheral" then local type, device = ppm.mount(param1) - if type ~= nil and device ~= nil then - if type == "modem" then - ---@cast device Modem - if device.isWireless() and not nic.is_connected() then - -- reconnected modem - nic.connect(device) - - println_ts("wireless modem reconnected.") - log.info("comms modem reconnected") - - databus.tx_hw_modem(true) - elseif device.isWireless() then - log.info("unused wireless modem reconnected") - else - log.info("wired modem reconnected") - end - end + pcie.connect(param1, type, device, println_ts) end elseif event == "timer" and loop_clock.is_clock(param1) then -- main loop tick diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index b30e218..3da4700 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -1,6 +1,7 @@ local comms = require("scada-common.comms") local log = require("scada-common.log") local util = require("scada-common.util") +local pcie = require("supervisor.pcie") local themes = require("graphics.themes") @@ -123,11 +124,10 @@ end -- supervisory controller communications ---@nodiscard ---@param _version string supervisor version ----@param nic nic network interface device ---@param fp_ok boolean if the front panel UI is running ---@param facility facility facility instance ---@diagnostic disable-next-line: unused-local -function supervisor.comms(_version, nic, fp_ok, facility) +function supervisor.comms(_version, fp_ok, facility) -- print a log message to the terminal as long as the UI isn't running local function println(message) if not fp_ok then util.println_ts(message) end end @@ -137,20 +137,24 @@ function supervisor.comms(_version, nic, fp_ok, facility) comms.set_trusted_range(config.TrustedRange) - -- PRIVATE FUNCTIONS -- - -- configure modem channels - nic.closeAll() - nic.open(config.SVR_Channel) + pcie.nic.reset_open(config.SVR_Channel) -- pass system data and objects to svsessions - svsessions.init(nic, fp_ok, config, facility) + svsessions.init(fp_ok, config, facility) + + -- get nic references + local c_nic = pcie.nic.core + local p_nic = pcie.nic.pocket + + -- PRIVATE FUNCTIONS -- -- send an establish request response + ---@param nic nic ---@param packet scada_packet ---@param ack ESTABLISH_ACK ---@param data? any optional data - local function _send_establish(packet, ack, data) + local function _send_establish(nic, packet, ack, data) local s_pkt = comms.scada_packet() local m_pkt = comms.mgmt_packet() @@ -175,36 +179,33 @@ function supervisor.comms(_version, nic, fp_ok, facility) ---@param distance integer ---@return modbus_frame|rplc_frame|mgmt_frame|crdn_frame|nil packet function public.parse_packet(side, sender, reply_to, message, distance) - local s_pkt = nic.receive(side, sender, reply_to, message, distance) local pkt = nil + local s_pkt = c_nic.receive(side, sender, reply_to, message, distance) + + if p_nic and not s_pkt then + -- try for it being from the pocket modem + s_pkt = p_nic.receive(side, sender, reply_to, message, distance) + end if s_pkt then -- get as MODBUS TCP packet if s_pkt.protocol() == PROTOCOL.MODBUS_TCP then local m_pkt = comms.modbus_packet() - if m_pkt.decode(s_pkt) then - pkt = m_pkt.get() - end + if m_pkt.decode(s_pkt) then pkt = m_pkt.get() end -- get as RPLC packet elseif s_pkt.protocol() == PROTOCOL.RPLC then local rplc_pkt = comms.rplc_packet() - if rplc_pkt.decode(s_pkt) then - pkt = rplc_pkt.get() - end + if rplc_pkt.decode(s_pkt) then pkt = rplc_pkt.get() end -- get as SCADA management packet elseif s_pkt.protocol() == PROTOCOL.SCADA_MGMT then local mgmt_pkt = comms.mgmt_packet() - if mgmt_pkt.decode(s_pkt) then - pkt = mgmt_pkt.get() - end + if mgmt_pkt.decode(s_pkt) then pkt = mgmt_pkt.get() end -- get as coordinator packet elseif s_pkt.protocol() == PROTOCOL.SCADA_CRDN then local crdn_pkt = comms.crdn_packet() - if crdn_pkt.decode(s_pkt) then - pkt = crdn_pkt.get() - end + if crdn_pkt.decode(s_pkt) then pkt = crdn_pkt.get() end else - log.debug("attempted parse of illegal packet type " .. s_pkt.protocol(), true) + log.debug("receive[" .. side .. "] attempted parse of illegal packet type " .. s_pkt.protocol(), true) end end @@ -257,7 +258,7 @@ function supervisor.comms(_version, nic, fp_ok, facility) log.info(util.c("dropping PLC establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) end - _send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) elseif dev_type == DEVICE_TYPE.PLC then -- PLC linking request if packet.length == 4 and type(packet.data[4]) == "number" then @@ -270,7 +271,7 @@ function supervisor.comms(_version, nic, fp_ok, facility) log.warning(util.c("PLC_ESTABLISH: denied assignment ", reactor_id, " outside of configured unit count ", config.UnitCount)) end - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) else -- try to establish the session local plc_id = svsessions.establish_plc_session(src_addr, i_seq_num, reactor_id, firmware_v) @@ -281,25 +282,25 @@ function supervisor.comms(_version, nic, fp_ok, facility) log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id)) end - _send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.COLLISION) else -- got an ID; assigned to a reactor successfully println(util.c("PLC (", firmware_v, ") [@", src_addr, "] \xbb reactor ", reactor_id, " connected")) log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [@", src_addr, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id)) - _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.ALLOW) end end else log.debug("PLC_ESTABLISH: packet length mismatch/bad parameter type") - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug(util.c("illegal establish packet for device ", dev_type, " on PLC channel")) - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug("invalid establish packet (on PLC channel)") - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it @@ -343,7 +344,7 @@ function supervisor.comms(_version, nic, fp_ok, facility) log.info(util.c("dropping RTU establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) end - _send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) elseif dev_type == DEVICE_TYPE.RTU then if packet.length == 4 then -- this is an RTU advertisement for a new session @@ -352,18 +353,18 @@ function supervisor.comms(_version, nic, fp_ok, facility) println(util.c("RTU (", firmware_v, ") [@", src_addr, "] \xbb connected")) log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [@", src_addr, "] connected with session ID ", s_id)) - _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.ALLOW) else log.debug("RTU_ESTABLISH: packet length mismatch") - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug(util.c("illegal establish packet for device ", dev_type, " on RTU channel")) - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug("invalid establish packet (on RTU channel)") - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it @@ -397,7 +398,7 @@ function supervisor.comms(_version, nic, fp_ok, facility) log.info(util.c("dropping coordinator establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) end - _send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) elseif dev_type == DEVICE_TYPE.CRD then -- this is an attempt to establish a new coordinator session local s_id = svsessions.establish_crd_session(src_addr, i_seq_num, firmware_v) @@ -406,21 +407,21 @@ function supervisor.comms(_version, nic, fp_ok, facility) println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected")) log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id)) - _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, { config.UnitCount, facility.get_cooling_conf() }) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.ALLOW, { config.UnitCount, facility.get_cooling_conf() }) else if last_ack ~= ESTABLISH_ACK.COLLISION then log.info("CRD_ESTABLISH: denied new coordinator [@" .. src_addr .. "] due to already being connected to another coordinator") end - _send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.COLLISION) end else log.debug(util.c("illegal establish packet for device ", dev_type, " on coordinator channel")) - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug("CRD_ESTABLISH: establish packet length mismatch") - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it @@ -464,7 +465,7 @@ function supervisor.comms(_version, nic, fp_ok, facility) log.info(util.c("dropping PDG establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) end - _send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) + _send_establish(p_nic or c_nic, packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) elseif dev_type == DEVICE_TYPE.PKT then -- this is an attempt to establish a new pocket diagnostic session local s_id = svsessions.establish_pdg_session(src_addr, i_seq_num, firmware_v) @@ -472,14 +473,14 @@ function supervisor.comms(_version, nic, fp_ok, facility) println(util.c("PKT (", firmware_v, ") [@", src_addr, "] \xbb connected")) log.info(util.c("PDG_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id)) - _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) + _send_establish(p_nic or c_nic, packet.scada_frame, ESTABLISH_ACK.ALLOW) else log.debug(util.c("illegal establish packet for device ", dev_type, " on pocket channel")) - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(p_nic or c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug("PDG_ESTABLISH: establish packet length mismatch") - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + _send_establish(p_nic or c_nic, packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it