diff --git a/supervisor/session/coordinator.lua b/supervisor/session/coordinator.lua index 36a5241..1b75078 100644 --- a/supervisor/session/coordinator.lua +++ b/supervisor/session/coordinator.lua @@ -45,12 +45,13 @@ local PERIODICS = { -- coordinator supervisor session ---@nodiscard ---@param id integer session ID +---@param s_addr integer device source address ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue ---@param timeout number communications timeout ---@param facility facility facility data table ---@param fp_ok boolean if the front panel UI is running -function coordinator.new_session(id, in_queue, out_queue, timeout, facility, fp_ok) +function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facility, fp_ok) -- 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 @@ -99,7 +100,7 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility, fp_ local c_pkt = comms.crdn_packet() c_pkt.make(msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOL.SCADA_CRDN, c_pkt.raw_sendable()) + s_pkt.make(s_addr, self.seq_num, PROTOCOL.SCADA_CRDN, c_pkt.raw_sendable()) out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 @@ -113,7 +114,7 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility, fp_ local m_pkt = comms.mgmt_packet() m_pkt.make(msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) + s_pkt.make(s_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 @@ -334,7 +335,7 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility, fp_ end end - ---@class coord_session + ---@class crd_session local public = {} -- get the session ID diff --git a/supervisor/session/plc.lua b/supervisor/session/plc.lua index 1436534..ac80ea6 100644 --- a/supervisor/session/plc.lua +++ b/supervisor/session/plc.lua @@ -45,12 +45,13 @@ local PERIODICS = { -- PLC supervisor session ---@nodiscard ---@param id integer session ID +---@param s_addr integer device source address ---@param reactor_id integer reactor ID ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue ---@param timeout number communications timeout ---@param fp_ok boolean if the front panel UI is running -function plc.new_session(id, reactor_id, in_queue, out_queue, timeout, fp_ok) +function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, fp_ok) -- 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 @@ -250,7 +251,7 @@ function plc.new_session(id, reactor_id, in_queue, out_queue, timeout, fp_ok) local r_pkt = comms.rplc_packet() r_pkt.make(reactor_id, msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable()) + s_pkt.make(s_addr, self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable()) out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 @@ -264,7 +265,7 @@ function plc.new_session(id, reactor_id, in_queue, out_queue, timeout, fp_ok) local m_pkt = comms.mgmt_packet() m_pkt.make(msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) + s_pkt.make(s_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 diff --git a/supervisor/session/pocket.lua b/supervisor/session/pocket.lua index ba6d179..9de55ab 100644 --- a/supervisor/session/pocket.lua +++ b/supervisor/session/pocket.lua @@ -29,11 +29,12 @@ local PERIODICS = { -- pocket diagnostics session ---@nodiscard ---@param id integer session ID +---@param s_addr integer device source address ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue ---@param timeout number communications timeout ---@param fp_ok boolean if the front panel UI is running -function pocket.new_session(id, in_queue, out_queue, timeout, fp_ok) +function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, fp_ok) -- 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 @@ -81,7 +82,7 @@ function pocket.new_session(id, in_queue, out_queue, timeout, fp_ok) local m_pkt = comms.mgmt_packet() m_pkt.make(msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) + s_pkt.make(s_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index 2f7091c..d1fbaec 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -31,13 +31,14 @@ local PERIODICS = { -- create a new RTU session ---@nodiscard ---@param id integer session ID +---@param s_addr integer device source address ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue ---@param timeout number communications timeout ---@param advertisement table RTU device advertisement ---@param facility facility facility data table ---@param fp_ok boolean if the front panel UI is running -function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facility, fp_ok) +function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement, facility, fp_ok) -- 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 @@ -204,7 +205,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili local function _send_modbus(m_pkt) local s_pkt = comms.scada_packet() - s_pkt.make(self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable()) + s_pkt.make(s_addr, self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable()) out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 @@ -218,7 +219,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili local m_pkt = comms.mgmt_packet() m_pkt.make(msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) + s_pkt.make(s_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index a534142..4d5b0f4 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -27,7 +27,7 @@ local svsessions = {} local SESSION_TYPE = { RTU_SESSION = 0, -- RTU gateway PLC_SESSION = 1, -- reactor PLC - COORD_SESSION = 2, -- coordinator + CRD_SESSION = 2, -- coordinator PDG_SESSION = 3 -- pocket diagnostics } @@ -38,11 +38,11 @@ local self = { fp_ok = false, num_reactors = 0, facility = nil, ---@type facility|nil - sessions = { rtu = {}, plc = {}, coord = {}, pdg = {} }, - next_ids = { rtu = 0, plc = 0, coord = 0, pdg = 0 } + sessions = { rtu = {}, plc = {}, crd = {}, pdg = {} }, + next_ids = { rtu = 0, plc = 0, crd = 0, pdg = 0 } } ----@alias sv_session_structs plc_session_struct|rtu_session_struct|coord_session_struct|pdg_session_struct +---@alias sv_session_structs plc_session_struct|rtu_session_struct|crd_session_struct|pdg_session_struct -- PRIVATE FUNCTIONS -- @@ -60,7 +60,7 @@ local function _sv_handle_outq(session) if msg ~= nil then if msg.qtype == mqueue.TYPE.PACKET then -- handle a packet to be sent - self.modem.transmit(session.r_port, session.l_port, msg.message.raw_sendable()) + self.modem.transmit(session.r_chan, config.SVR_CHANNEL, msg.message.raw_sendable()) elseif msg.qtype == mqueue.TYPE.COMMAND then -- handle instruction/notification elseif msg.qtype == mqueue.TYPE.DATA then @@ -81,11 +81,11 @@ local function _sv_handle_outq(session) elseif cmd.key == SV_Q_DATA.SET_BURN and type(cmd.val) == "table" and #cmd.val == 2 then plc_s.in_queue.push_data(PLC_S_DATA.BURN_RATE, cmd.val[2]) else - log.debug(util.c("unknown PLC SV queue command ", cmd.key)) + log.debug(util.c("[SVS] unknown PLC SV queue command ", cmd.key)) end end else - local crd_s = svsessions.get_coord_session() + local crd_s = svsessions.get_crd_session() if crd_s ~= nil then if cmd.key == SV_Q_DATA.CRDN_ACK then -- ack to be sent to coordinator @@ -104,8 +104,8 @@ local function _sv_handle_outq(session) -- max 100ms spent processing queue if util.time() - handle_start > 100 then - log.warning("supervisor out queue handler exceeded 100ms queue process limit") - log.warning(util.c("offending session: port ", session.r_port, " type '", session.s_type, "'")) + log.warning("[SVS] supervisor out queue handler exceeded 100ms queue process limit") + log.warning(util.c("[SVS] offending session: ", session)) break end end @@ -131,15 +131,15 @@ local function _shutdown(session) session.open = false session.instance.close() - -- send packets in out queue (namely the close packet) + -- send packets in out queue (for the close packet) while session.out_queue.ready() do local msg = session.out_queue.pop() if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then - self.modem.transmit(session.r_port, session.l_port, msg.message.raw_sendable()) + self.modem.transmit(session.r_chan, config.SVR_CHANNEL, msg.message.raw_sendable()) end end - log.debug(util.c("closed ", session.s_type, " session ", session.instance.get_id(), " on remote port ", session.r_port)) + log.debug(util.c("[SVS] closed session ", session)) end -- close connections @@ -160,8 +160,7 @@ local function _check_watchdogs(sessions, timer_event) if session.open then local triggered = session.instance.check_wd(timer_event) if triggered then - log.debug(util.c("watchdog closing ", session.s_type, " session ", session.instance.get_id(), - " on remote port ", session.r_port, "...")) + log.debug(util.c("[SVS] watchdog closing session ", session, "...")) _shutdown(session) end end @@ -175,21 +174,20 @@ local function _free_closed(sessions) ---@param session sv_session_structs local on_delete = function (session) - log.debug(util.c("free'ing closed ", session.s_type, " session ", session.instance.get_id(), - " on remote port ", session.r_port)) + log.debug(util.c("[SVS] free'ing closed session ", session)) end util.filter_table(sessions, f, on_delete) end --- find a session by remote port +-- find a session by computer ID ---@nodiscard ---@param list table ----@param port integer +---@param s_addr integer ---@return sv_session_structs|nil -local function _find_session(list, port) +local function _find_session(list, s_addr) for i = 1, #list do - if list[i].r_port == port then return list[i] end + if list[i].s_addr == s_addr then return list[i] end end return nil end @@ -214,55 +212,55 @@ function svsessions.relink_modem(modem) self.modem = modem end --- find an RTU session by the remote port +-- find an RTU session by the computer ID ---@nodiscard ----@param remote_port integer +---@param source_addr integer ---@return rtu_session_struct|nil -function svsessions.find_rtu_session(remote_port) +function svsessions.find_rtu_session(source_addr) -- check RTU sessions - local session = _find_session(self.sessions.rtu, remote_port) + local session = _find_session(self.sessions.rtu, source_addr) ---@cast session rtu_session_struct|nil return session end --- find a PLC session by the remote port +-- find a PLC session by the computer ID ---@nodiscard ----@param remote_port integer +---@param source_addr integer ---@return plc_session_struct|nil -function svsessions.find_plc_session(remote_port) +function svsessions.find_plc_session(source_addr) -- check PLC sessions - local session = _find_session(self.sessions.plc, remote_port) + local session = _find_session(self.sessions.plc, source_addr) ---@cast session plc_session_struct|nil return session end --- find a coordinator session by the remote port +-- find a coordinator session by the computer ID ---@nodiscard ----@param remote_port integer ----@return coord_session_struct|nil -function svsessions.find_coord_session(remote_port) +---@param source_addr integer +---@return crd_session_struct|nil +function svsessions.find_crd_session(source_addr) -- check coordinator sessions - local session = _find_session(self.sessions.coord, remote_port) - ---@cast session coord_session_struct|nil + local session = _find_session(self.sessions.crd, source_addr) + ---@cast session crd_session_struct|nil return session end --- find a pocket diagnostics session by the remote port +-- find a pocket diagnostics session by the computer ID ---@nodiscard ----@param remote_port integer +---@param source_addr integer ---@return pdg_session_struct|nil -function svsessions.find_pdg_session(remote_port) +function svsessions.find_pdg_session(source_addr) -- check diagnostic sessions - local session = _find_session(self.sessions.diag, remote_port) + local session = _find_session(self.sessions.diag, source_addr) ---@cast session pdg_session_struct|nil return session end -- get the a coordinator session if exists ---@nodiscard ----@return coord_session_struct|nil -function svsessions.get_coord_session() - return self.sessions.coord[1] +---@return crd_session_struct|nil +function svsessions.get_crd_session() + return self.sessions.crd[1] end -- get a session by reactor ID @@ -283,12 +281,11 @@ end -- establish a new PLC session ---@nodiscard ----@param local_port integer ----@param remote_port integer +---@param source_addr integer ---@param for_reactor integer ---@param version string ---@return integer|false session_id -function svsessions.establish_plc_session(local_port, remote_port, for_reactor, version) +function svsessions.establish_plc_session(source_addr, for_reactor, version) if svsessions.get_reactor_session(for_reactor) == nil and for_reactor >= 1 and for_reactor <= self.num_reactors then ---@class plc_session_struct local plc_s = { @@ -296,26 +293,34 @@ function svsessions.establish_plc_session(local_port, remote_port, for_reactor, open = true, reactor = for_reactor, version = version, - l_port = local_port, - r_port = remote_port, + r_chan = config.PLC_CHANNEL, + s_addr = source_addr, in_queue = mqueue.new(), out_queue = mqueue.new(), instance = nil ---@type plc_session } - plc_s.instance = plc.new_session(self.next_ids.plc, for_reactor, plc_s.in_queue, plc_s.out_queue, - config.PLC_TIMEOUT, self.fp_ok) + local id = self.next_ids.plc + + plc_s.instance = plc.new_session(id, source_addr, for_reactor, plc_s.in_queue, plc_s.out_queue, + config.PLC_TIMEOUT, self.fp_ok) table.insert(self.sessions.plc, plc_s) local units = self.facility.get_units() units[for_reactor].link_plc_session(plc_s) - log.debug(util.c("established new PLC session to ", remote_port, " with ID ", self.next_ids.plc, - " for reactor ", for_reactor)) + local mt = { + ---@param s plc_session_struct + __to_string = function (s) return util.c("PLC [", s.instance.get_id(), "] for reactor #", s.reactor, + " (@", s.s_addr, ")") end + } - databus.tx_plc_connected(for_reactor, version, remote_port) + setmetatable(plc_s, mt) - self.next_ids.plc = self.next_ids.plc + 1 + databus.tx_plc_connected(for_reactor, version, source_addr) + log.debug(util.c("[SVS] established new session: ", plc_s)) + + self.next_ids.plc = id + 1 -- success return plc_s.instance.get_id() @@ -327,70 +332,84 @@ end -- establish a new RTU session ---@nodiscard ----@param local_port integer ----@param remote_port integer +---@param source_addr integer ---@param advertisement table ---@param version string ---@return integer session_id -function svsessions.establish_rtu_session(local_port, remote_port, advertisement, version) +function svsessions.establish_rtu_session(source_addr, advertisement, version) ---@class rtu_session_struct local rtu_s = { s_type = "rtu", open = true, version = version, - l_port = local_port, - r_port = remote_port, + r_chan = config.RTU_CHANNEL, + s_addr = source_addr, in_queue = mqueue.new(), out_queue = mqueue.new(), instance = nil ---@type rtu_session } - rtu_s.instance = rtu.new_session(self.next_ids.rtu, rtu_s.in_queue, rtu_s.out_queue, config.RTU_TIMEOUT, advertisement, - self.facility, self.fp_ok) + local id = self.next_ids.rtu + + rtu_s.instance = rtu.new_session(id, source_addr, rtu_s.in_queue, rtu_s.out_queue, config.RTU_TIMEOUT, + advertisement, self.facility, self.fp_ok) table.insert(self.sessions.rtu, rtu_s) - log.debug("established new RTU session to " .. remote_port .. " with ID " .. self.next_ids.rtu) + local mt = { + ---@param s rtu_session_struct + __to_string = function (s) return util.c("RTU [", s.instance.get_id(), "] (@", s.s_addr, ")") end + } - databus.tx_rtu_connected(self.next_ids.rtu, version, remote_port) + setmetatable(rtu_s, mt) - self.next_ids.rtu = self.next_ids.rtu + 1 + databus.tx_rtu_connected(id, version, source_addr) + log.debug(util.c("[SVS] established new session: ", rtu_s)) + + self.next_ids.rtu = id + 1 -- success - return rtu_s.instance.get_id() + return id end -- establish a new coordinator session ---@nodiscard ----@param local_port integer ----@param remote_port integer +---@param source_addr integer ---@param version string ---@return integer|false session_id -function svsessions.establish_coord_session(local_port, remote_port, version) - if svsessions.get_coord_session() == nil then - ---@class coord_session_struct - local coord_s = { +function svsessions.establish_crd_session(source_addr, version) + if svsessions.get_crd_session() == nil then + ---@class crd_session_struct + local crd_s = { s_type = "crd", open = true, version = version, - l_port = local_port, - r_port = remote_port, + r_chan = config.CRD_CHANNEL, + s_addr = source_addr, in_queue = mqueue.new(), out_queue = mqueue.new(), - instance = nil ---@type coord_session + instance = nil ---@type crd_session } - coord_s.instance = coordinator.new_session(self.next_ids.coord, coord_s.in_queue, coord_s.out_queue, config.CRD_TIMEOUT, + local id = self.next_ids.crd + + crd_s.instance = coordinator.new_session(id, source_addr, crd_s.in_queue, crd_s.out_queue, config.CRD_TIMEOUT, self.facility, self.fp_ok) - table.insert(self.sessions.coord, coord_s) + table.insert(self.sessions.crd, crd_s) - log.debug("established new coordinator session to " .. remote_port .. " with ID " .. self.next_ids.coord) + local mt = { + ---@param s crd_session_struct + __to_string = function (s) return util.c("CRD [", s.instance.get_id(), "] (@", s.s_addr, ")") end + } - databus.tx_crd_connected(version, remote_port) + setmetatable(crd_s, mt) - self.next_ids.coord = self.next_ids.coord + 1 + databus.tx_crd_connected(version, source_addr) + log.debug(util.c("[SVS] established new session: ", crd_s)) + + self.next_ids.crd = id + 1 -- success - return coord_s.instance.get_id() + return id else -- we already have a coordinator linked return false @@ -399,34 +418,41 @@ end -- establish a new pocket diagnostics session ---@nodiscard ----@param local_port integer ----@param remote_port integer +---@param source_addr integer ---@param version string ---@return integer|false session_id -function svsessions.establish_pdg_session(local_port, remote_port, version) +function svsessions.establish_pdg_session(source_addr, version) ---@class pdg_session_struct local pdg_s = { s_type = "pkt", open = true, version = version, - l_port = local_port, - r_port = remote_port, + r_chan = config.PKT_CHANNEL, + s_addr = source_addr, in_queue = mqueue.new(), out_queue = mqueue.new(), instance = nil ---@type pdg_session } - pdg_s.instance = pocket.new_session(self.next_ids.pdg, pdg_s.in_queue, pdg_s.out_queue, config.PKT_TIMEOUT, self.fp_ok) + local id = self.next_ids.pdg + + pdg_s.instance = pocket.new_session(id, source_addr, pdg_s.in_queue, pdg_s.out_queue, config.PKT_TIMEOUT, self.fp_ok) table.insert(self.sessions.pdg, pdg_s) - log.debug("established new pocket diagnostics session to " .. remote_port .. " with ID " .. self.next_ids.pdg) + local mt = { + ---@param s pdg_session_struct + __to_string = function (s) return util.c("PDG [", s.instance.get_id(), "] (@", s.s_addr, ")") end + } - databus.tx_pdg_connected(self.next_ids.pdg, version, remote_port) + setmetatable(pdg_s, mt) - self.next_ids.pdg = self.next_ids.pdg + 1 + databus.tx_pdg_connected(id, version, source_addr) + log.debug(util.c("[SVS] established new session: ", pdg_s)) + + self.next_ids.pdg = id + 1 -- success - return pdg_s.instance.get_id() + return id end -- attempt to identify which session's watchdog timer fired @@ -458,9 +484,7 @@ end -- close all open connections function svsessions.close_all() -- close sessions - for _, list in pairs(self.sessions) do - _close(list) - end + for _, list in pairs(self.sessions) do _close(list) end -- free sessions svsessions.free_all_closed() diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 7d56895..ba943fe 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -115,18 +115,8 @@ local function main() println_ts = function (_) end end - ---@class sv_channel_list - local channels = { - SVR = config.SVR_CHANNEL, - PLC = config.PLC_CHANNEL, - RTU = config.RTU_CHANNEL, - CRD = config.CRD_CHANNEL, - PKT = config.PKT_CHANNEL - } - -- start comms - local superv_comms = supervisor.comms(SUPERVISOR_VERSION, config.NUM_REACTORS, config.REACTOR_COOLING, modem, - channels, config.TRUSTED_RANGE, fp_ok) + local superv_comms = supervisor.comms(SUPERVISOR_VERSION, modem, fp_ok) -- base loop clock (6.67Hz, 3 ticks) local MAIN_CLOCK = 0.15 diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 5a3ec8c..42be454 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -2,6 +2,8 @@ local comms = require("scada-common.comms") local log = require("scada-common.log") local util = require("scada-common.util") +local config = require("supervisor.config") + local svsessions = require("supervisor.session.svsessions") local supervisor = {} @@ -14,29 +16,29 @@ local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE -- supervisory controller communications ---@nodiscard ---@param _version string supervisor version ----@param num_reactors integer number of reactors ----@param cooling_conf table cooling configuration table ---@param modem table modem device ----@param channels sv_channel_list network channels ----@param range integer trusted device connection range ---@param fp_ok boolean if the front panel UI is running ---@diagnostic disable-next-line: unused-local -function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, range, fp_ok) +function supervisor.comms(_version, modem, fp_ok) -- 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 - -- channel list - local svr_channel = channels.SVR - local plc_channel = channels.PLC - local rtu_channel = channels.RTU - local crd_channel = channels.CRD - local pkt_channel = channels.PKT + -- channel list from config + local svr_channel = config.SVR_CHANNEL + local plc_channel = config.PLC_CHANNEL + local rtu_channel = config.RTU_CHANNEL + local crd_channel = config.CRD_CHANNEL + local pkt_channel = config.PKT_CHANNEL + + -- configuration data + local num_reactors = config.NUM_REACTORS + local cooling_conf = config.REACTOR_COOLING local self = { last_est_acks = {} } - comms.set_trusted_range(range) + comms.set_trusted_range(config.TRUSTED_RANGE) -- PRIVATE FUNCTIONS -- @@ -138,10 +140,7 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, if l_chan ~= svr_channel then log.debug("received packet on unconfigured channel " .. l_chan, true) - return - end - - if r_chan == plc_channel then + elseif r_chan == plc_channel then -- look for an associated session local session = svsessions.find_plc_session(s_addr) @@ -182,7 +181,7 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, -- PLC linking request if packet.length == 4 and type(packet.data[4]) == "number" then local reactor_id = packet.data[4] - local plc_id = svsessions.establish_plc_session(l_chan, r_chan, reactor_id, firmware_v) + local plc_id = svsessions.establish_plc_session(s_addr, reactor_id, firmware_v) if plc_id == false then -- reactor already has a PLC assigned @@ -193,8 +192,8 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, _send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION) else -- got an ID; assigned to a reactor successfully - println(util.c("PLC (", firmware_v, ") [:", r_chan, "] \xbb reactor ", reactor_id, " connected")) - log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [:", r_chan, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id)) + println(util.c("PLC (", firmware_v, ") [@", s_addr, "] \xbb reactor ", reactor_id, " connected")) + log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [@", s_addr, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id)) _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) end else @@ -202,17 +201,19 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else - log.debug(util.c("illegal establish packet for device ", dev_type, " on PLC listening channel")) + log.debug(util.c("illegal establish packet for device ", dev_type, " on PLC channel")) _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else - log.debug("invalid establish packet (on PLC listening channel)") + log.debug("invalid establish packet (on PLC channel)") _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it - log.debug(util.c(r_chan, " -> ", l_chan, ": discarding SCADA_MGMT packet without a known session")) + log.debug(util.c("discarding PLC SCADA_MGMT packet without a known session from computer ", s_addr)) end + else + log.debug(util.c("illegal packet type ", protocol, " on PLC channel")) end elseif r_chan == rtu_channel then -- look for an associated session @@ -254,33 +255,33 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, if packet.length == 4 then -- this is an RTU advertisement for a new session local rtu_advert = packet.data[4] - local s_id = svsessions.establish_rtu_session(l_chan, r_chan, rtu_advert, firmware_v) + local s_id = svsessions.establish_rtu_session(s_addr, rtu_advert, firmware_v) - println(util.c("RTU (", firmware_v, ") [:", r_chan, "] \xbb connected")) - log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [:", r_chan, "] connected with session ID ", s_id)) + println(util.c("RTU (", firmware_v, ") [@", s_addr, "] \xbb connected")) + log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [@", s_addr, "] connected with session ID ", s_id)) _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) else log.debug("RTU_ESTABLISH: packet length mismatch") _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else - log.debug(util.c("illegal establish packet for device ", dev_type, " on RTU listening channel")) + log.debug(util.c("illegal establish packet for device ", dev_type, " on RTU channel")) _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else - log.debug("invalid establish packet (on RTU listening channel)") + log.debug("invalid establish packet (on RTU channel)") _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it - log.debug(util.c(r_chan, " -> ", l_chan, ": discarding SCADA_MGMT packet without a known session")) + log.debug(util.c("discarding RTU SCADA_MGMT packet without a known session from computer ", s_addr)) end else - log.debug("illegal packet type " .. protocol .. " on RTU listening channel") + log.debug(util.c("illegal packet type ", protocol, " on RTU channel")) end elseif r_chan == crd_channel then -- look for an associated session - local session = svsessions.find_svctl_session(s_addr) + local session = svsessions.find_crd_session(s_addr) if protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame @@ -306,45 +307,37 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, _send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) elseif dev_type == DEVICE_TYPE.CRDN then -- this is an attempt to establish a new coordinator session - local s_id = svsessions.establish_coord_session(l_chan, r_chan, firmware_v) + local s_id = svsessions.establish_crd_session(s_addr, firmware_v) if s_id ~= false then - local config = { num_reactors } + local cfg = { num_reactors } for i = 1, #cooling_conf do - table.insert(config, cooling_conf[i].BOILERS) - table.insert(config, cooling_conf[i].TURBINES) + table.insert(cfg, cooling_conf[i].BOILERS) + table.insert(cfg, cooling_conf[i].TURBINES) end println(util.c("CRD (", firmware_v, ") [@", s_addr, "] \xbb connected")) - log.info(util.c("ESTABLISH: coordinator (", firmware_v, ") [@", s_addr, "] connected with session ID ", s_id)) + log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", s_addr, "] connected with session ID ", s_id)) - _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, config) + _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, cfg) else if last_ack ~= ESTABLISH_ACK.COLLISION then - log.info("ESTABLISH: denied new coordinator [@" .. s_addr .. "] due to already being connected to another coordinator") + log.info("CRD_ESTABLISH: denied new coordinator [@" .. s_addr .. "] due to already being connected to another coordinator") end _send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION) end - 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(l_chan, r_chan, firmware_v) - - println(util.c("PKT (", firmware_v, ") [:", r_chan, "] \xbb connected")) - log.info(util.c("SVCTL_ESTABLISH: pocket (", firmware_v, ") [:", r_chan, "] connected with session ID ", s_id)) - - _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) else - log.debug(util.c("illegal establish packet for device ", dev_type, " on SVCTL listening channel")) + log.debug(util.c("illegal establish packet for device ", dev_type, " on coordinator channel")) _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else - log.debug("SVCTL_ESTABLISH: establish packet length mismatch") + log.debug("CRD_ESTABLISH: establish packet length mismatch") _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it - log.debug(r_chan .. " -> " .. l_chan .. ": discarding SCADA_MGMT packet without a known session") + log.debug(util.c("discarding coordinator SCADA_MGMT packet without a known session from computer ", s_addr)) end elseif protocol == PROTOCOL.SCADA_CRDN then ---@cast packet crdn_frame @@ -354,13 +347,70 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, session.in_queue.push_packet(packet) else -- any other packet should be session related, discard it - log.debug(r_chan .. "->" .. l_chan .. ": discarding SCADA_CRDN packet without a known session") + log.debug(util.c("discarding coordinator SCADA_CRDN packet without a known session from computer ", s_addr)) end else - log.debug("illegal packet type " .. protocol .. " on coordinator listening channel") + log.debug(util.c("illegal packet type ", protocol, " on coordinator channel")) end elseif r_chan == pkt_channel then - + -- look for an associated session + local session = svsessions.find_pdg_session(s_addr) + + if protocol == PROTOCOL.SCADA_MGMT then + ---@cast packet mgmt_frame + -- SCADA management packet + if session ~= nil then + -- pass the packet onto the session handler + session.in_queue.push_packet(packet) + elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then + -- establish a new session + local last_ack = self.last_est_acks[s_addr] + + -- validate packet and continue + if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then + local comms_v = packet.data[1] + local firmware_v = packet.data[2] + local dev_type = packet.data[3] + + if comms_v ~= comms.version then + if last_ack ~= ESTABLISH_ACK.BAD_VERSION then + 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) + 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(s_addr, firmware_v) + + println(util.c("PKT (", firmware_v, ") [:", r_chan, "] \xbb connected")) + log.info(util.c("PDG_ESTABLISH: pocket (", firmware_v, ") [:", r_chan, "] connected with session ID ", s_id)) + + _send_establish(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) + end + else + log.debug("PDG_ESTABLISH: establish packet length mismatch") + _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + end + else + -- any other packet should be session related, discard it + log.debug(util.c("discarding pocket SCADA_MGMT packet without a known session from computer ", s_addr)) + end + elseif protocol == PROTOCOL.SCADA_CRDN then + ---@cast packet crdn_frame + -- coordinator packet + if session ~= nil then + -- pass the packet onto the session handler + session.in_queue.push_packet(packet) + else + -- any other packet should be session related, discard it + log.debug(util.c("discarding pocket SCADA_CRDN packet without a known session from computer ", s_addr)) + end + else + log.debug(util.c("illegal packet type ", protocol, " on pocket channel")) + end else log.debug("received packet for unknown channel " .. r_chan, true) end