#580 work on supervisor wired modem configuration

This commit is contained in:
Mikayla Fischler 2025-06-15 15:43:04 -04:00
parent bee1cdf01c
commit 4a7fc6200e
9 changed files with 298 additions and 138 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

159
supervisor/pcie.lua Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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