From 337fca7e7ce60f4a32f064b900db91cbd3b1f97a Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 5 Jun 2023 05:13:22 +0000 Subject: [PATCH 01/18] #225 work in progress comms changes --- coordinator/coordinator.lua | 4 +- coordinator/startup.lua | 6 +- pocket/config.lua | 12 +- pocket/pocket.lua | 38 +++-- pocket/startup.lua | 10 +- reactor-plc/config.lua | 8 +- reactor-plc/plc.lua | 116 +++++++------ reactor-plc/startup.lua | 8 +- rtu/config.lua | 10 +- rtu/rtu.lua | 21 ++- rtu/startup.lua | 6 +- scada-common/comms.lua | 87 ++++++---- scada-common/util.lua | 2 +- supervisor/config.lua | 14 +- supervisor/session/svsessions.lua | 29 ++-- supervisor/startup.lua | 23 ++- supervisor/supervisor.lua | 268 ++++++++++++++++-------------- test/lockbox-benchmark.lua | 2 +- test/turbine_modbustest.lua | 2 +- 19 files changed, 373 insertions(+), 293 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index d7e58f7..486be82 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -435,8 +435,8 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range ---@param packet mgmt_frame|crdn_frame|capi_frame|nil function public.handle_packet(packet) if packet ~= nil then - local l_port = packet.scada_frame.local_port() - local r_port = packet.scada_frame.remote_port() + local l_port = packet.scada_frame.local_channel() + local r_port = packet.scada_frame.remote_channel() local protocol = packet.scada_frame.protocol() if l_port == api_listen then diff --git a/coordinator/startup.lua b/coordinator/startup.lua index e5863c8..c93b3ec 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -37,9 +37,9 @@ local log_comms_connecting = coordinator.log_comms_connecting local cfv = util.new_validator() -cfv.assert_port(config.SCADA_SV_PORT) -cfv.assert_port(config.SCADA_SV_CTL_LISTEN) -cfv.assert_port(config.SCADA_API_LISTEN) +cfv.assert_channel(config.SCADA_SV_PORT) +cfv.assert_channel(config.SCADA_SV_CTL_LISTEN) +cfv.assert_channel(config.SCADA_API_LISTEN) cfv.assert_type_int(config.TRUSTED_RANGE) cfv.assert_type_num(config.SV_TIMEOUT) cfv.assert_min(config.SV_TIMEOUT, 2) diff --git a/pocket/config.lua b/pocket/config.lua index 27e1489..64705ab 100644 --- a/pocket/config.lua +++ b/pocket/config.lua @@ -1,11 +1,11 @@ local config = {} --- port of the SCADA supervisor -config.SCADA_SV_PORT = 16100 --- port for SCADA coordinator API access -config.SCADA_API_PORT = 16200 --- port to listen to incoming packets FROM servers -config.LISTEN_PORT = 16201 +-- supervisor access channel +config.SVR_CHANNEL = 16240 +-- coordinator access channel +config.CRD_CHANNEL = 16243 +-- pocket communication channel +config.PKT_CHANNEL = 16244 -- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active diff --git a/pocket/pocket.lua b/pocket/pocket.lua index cc4d870..a7482fd 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -18,22 +18,24 @@ local pocket = {} ---@nodiscard ---@param version string pocket version ---@param modem table modem device ----@param local_port integer local pocket port ----@param sv_port integer port of supervisor ----@param api_port integer port of coordinator API +---@param pkt_channel integer pocket comms channel +---@param svr_channel integer supervisor access channel +---@param crd_channel integer coordinator access channel ---@param range integer trusted device connection range ---@param sv_watchdog watchdog ---@param api_watchdog watchdog -function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_watchdog, api_watchdog) +function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, range, sv_watchdog, api_watchdog) local self = { sv = { linked = false, + addr = comms.BROADCAST, seq_num = 0, r_seq_num = nil, ---@type nil|integer last_est_ack = ESTABLISH_ACK.ALLOW }, api = { linked = false, + addr = comms.BROADCAST, seq_num = 0, r_seq_num = nil, ---@type nil|integer last_est_ack = ESTABLISH_ACK.ALLOW @@ -48,7 +50,7 @@ function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_w -- configure modem channels local function _conf_channels() modem.closeAll() - modem.open(local_port) + modem.open(pkt_channel) end _conf_channels() @@ -61,9 +63,9 @@ function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_w local pkt = comms.mgmt_packet() pkt.make(msg_type, msg) - s_pkt.make(self.sv.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) + s_pkt.make(self.sv.addr, self.sv.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) - modem.transmit(sv_port, local_port, s_pkt.raw_sendable()) + modem.transmit(svr_channel, pkt_channel, s_pkt.raw_sendable()) self.sv.seq_num = self.sv.seq_num + 1 end @@ -75,9 +77,9 @@ function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_w local pkt = comms.mgmt_packet() pkt.make(msg_type, msg) - s_pkt.make(self.api.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) + s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) - modem.transmit(api_port, local_port, s_pkt.raw_sendable()) + modem.transmit(crd_channel, pkt_channel, s_pkt.raw_sendable()) self.api.seq_num = self.api.seq_num + 1 end @@ -89,9 +91,9 @@ function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_w -- local pkt = comms.capi_packet() -- pkt.make(msg_type, msg) - -- s_pkt.make(self.api.seq_num, PROTOCOL.COORD_API, pkt.raw_sendable()) + -- s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.COORD_API, pkt.raw_sendable()) - -- modem.transmit(api_port, local_port, s_pkt.raw_sendable()) + -- modem.transmit(crd_channel, pkt_channel, s_pkt.raw_sendable()) -- self.api.seq_num = self.api.seq_num + 1 -- end @@ -214,13 +216,13 @@ function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_w ---@param packet mgmt_frame|capi_frame|nil function public.handle_packet(packet) if packet ~= nil then - local l_port = packet.scada_frame.local_port() - local r_port = packet.scada_frame.remote_port() + local l_chan = packet.scada_frame.local_channel() + local r_chan = packet.scada_frame.remote_channel() local protocol = packet.scada_frame.protocol() - if l_port ~= local_port then - log.debug("received packet on unconfigured channel " .. l_port, true) - elseif r_port == api_port then + if l_chan ~= pkt_channel then + log.debug("received packet on unconfigured channel " .. l_chan, true) + elseif r_chan == crd_channel then -- check sequence number if self.api.r_seq_num == nil then self.api.r_seq_num = packet.scada_frame.seq_num() @@ -304,7 +306,7 @@ function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_w else log.debug("illegal packet type " .. protocol .. " from coordinator", true) end - elseif r_port == sv_port then + elseif r_chan == svr_channel then -- check sequence number if self.sv.r_seq_num == nil then self.sv.r_seq_num = packet.scada_frame.seq_num() @@ -388,7 +390,7 @@ function pocket.comms(version, modem, local_port, sv_port, api_port, range, sv_w log.debug("illegal packet type " .. protocol .. " from supervisor", true) end else - log.debug("received packet from unconfigured channel " .. r_port, true) + log.debug("received packet from unconfigured channel " .. r_chan, true) end end end diff --git a/pocket/startup.lua b/pocket/startup.lua index 459dd8e..c1e9ccc 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -28,9 +28,9 @@ local println_ts = util.println_ts local cfv = util.new_validator() -cfv.assert_port(config.SCADA_SV_PORT) -cfv.assert_port(config.SCADA_API_PORT) -cfv.assert_port(config.LISTEN_PORT) +cfv.assert_channel(config.SVR_CHANNEL) +cfv.assert_channel(config.CRD_CHANNEL) +cfv.assert_channel(config.PKT_CHANNEL) cfv.assert_type_int(config.TRUSTED_RANGE) cfv.assert_type_num(config.COMMS_TIMEOUT) cfv.assert_min(config.COMMS_TIMEOUT, 2) @@ -90,8 +90,8 @@ local function main() log.debug("startup> conn watchdogs created") -- start comms, open all channels - local pocket_comms = pocket.comms(POCKET_VERSION, modem, config.LISTEN_PORT, config.SCADA_SV_PORT, - config.SCADA_API_PORT, config.TRUSTED_RANGE, conn_wd.sv, conn_wd.api) + local pocket_comms = pocket.comms(POCKET_VERSION, modem, config.PKT_CHANNEL, config.SVR_CHANNEL, + config.CRD_CHANNEL, config.TRUSTED_RANGE, conn_wd.sv, conn_wd.api) log.debug("startup> comms init") -- base loop clock (2Hz, 10 ticks) diff --git a/reactor-plc/config.lua b/reactor-plc/config.lua index e402bbb..6845ec1 100644 --- a/reactor-plc/config.lua +++ b/reactor-plc/config.lua @@ -9,10 +9,10 @@ config.REACTOR_ID = 1 -- when emergency coolant is needed due to low coolant -- config.EMERGENCY_COOL = { side = "right", color = nil } --- port to send packets TO server -config.SERVER_PORT = 16000 --- port to listen to incoming packets FROM server -config.LISTEN_PORT = 14001 +-- supervisor access channel +config.SVR_CHANNEL = 16240 +-- PLC communication channel +config.PLC_CHANNEL = 16241 -- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 4f04138..a4f66dc 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -446,14 +446,15 @@ end ---@param id integer reactor ID ---@param version string PLC version ---@param modem table modem device ----@param local_port integer local listening port ----@param server_port integer remote server port +---@param plc_channel integer PLC comms channel +---@param svr_channel integer supervisor server channel ---@param range integer trusted device connection range ---@param reactor table reactor device ---@param rps rps RPS reference ---@param conn_watchdog watchdog watchdog reference -function plc.comms(id, version, modem, local_port, server_port, range, reactor, rps, conn_watchdog) +function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, rps, conn_watchdog) local self = { + sv_addr = comms.BROADCAST, seq_num = 0, r_seq_num = nil, scrammed = false, @@ -472,7 +473,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, -- configure modem channels local function _conf_channels() modem.closeAll() - modem.open(local_port) + modem.open(plc_channel) end _conf_channels() @@ -485,9 +486,9 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, local r_pkt = comms.rplc_packet() r_pkt.make(id, msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable()) + s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable()) - modem.transmit(server_port, local_port, s_pkt.raw_sendable()) + modem.transmit(svr_channel, plc_channel, s_pkt.raw_sendable()) self.seq_num = self.seq_num + 1 end @@ -499,9 +500,9 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, 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(self.sv_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(server_port, local_port, s_pkt.raw_sendable()) + modem.transmit(svr_channel, plc_channel, s_pkt.raw_sendable()) self.seq_num = self.seq_num + 1 end @@ -667,6 +668,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, -- unlink from the server function public.unlink() + self.sv_addr = comms.BROADCAST self.linked = false self.r_seq_num = nil self.status_cache = nil @@ -731,7 +733,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, end end - -- parse an RPLC packet + -- parse a packet ---@nodiscard ---@param side string ---@param sender integer @@ -760,14 +762,14 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, pkt = mgmt_pkt.get() end else - log.debug("illegal packet type " .. s_pkt.protocol(), true) + log.debug("unsupported packet type " .. s_pkt.protocol(), true) end end return pkt end - -- handle an RPLC packet + -- handle RPLC and MGMT packets ---@param packet rplc_frame|mgmt_frame packet frame ---@param plc_state plc_state PLC state ---@param setpoints setpoints setpoint control table @@ -775,10 +777,11 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, -- print a log message to the terminal as long as the UI isn't running local function println_ts(message) if not plc_state.fp_ok then util.println_ts(message) end end - local l_port = packet.scada_frame.local_port() + local l_chan = packet.scada_frame.local_channel() + local src_addr = packet.scada_frame.src_addr() -- handle packets now that we have prints setup - if l_port == local_port then + if l_chan == plc_channel then -- check sequence number if self.r_seq_num == nil then self.r_seq_num = packet.scada_frame.seq_num() @@ -797,7 +800,8 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, -- handle packet if protocol == PROTOCOL.RPLC then ---@cast packet rplc_frame - if self.linked then + -- if linked, only accept packets from configured supervisor + if self.linked and (self.sv_addr == src_addr) then if packet.type == RPLC_TYPE.STATUS then -- request of full status, clear cache first self.status_cache = nil @@ -928,15 +932,18 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, else log.debug("received unknown RPLC packet type " .. packet.type) end - else + elseif not self.linked then log.debug("discarding RPLC packet before linked") + else + log.debug("discarding RPLC from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. "sv_addr)") end elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame - if self.linked then + -- if linked, only accept packets from configured supervisor + if self.linked and (self.sv_addr == src_addr) then if packet.type == SCADA_MGMT_TYPE.ESTABLISH then -- link request confirmation - if packet.length == 1 then + if (packet.length == 1) and (self.sv_addr == src_addr) then log.debug("received unsolicited establish response") local est_ack = packet.data[1] @@ -945,22 +952,26 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, self.status_cache = nil _send_struct() public.send_status(plc_state.no_reactor, plc_state.reactor_formed) - log.debug("re-sent initial status data") - elseif est_ack == ESTABLISH_ACK.DENY then - println_ts("received unsolicited link denial, unlinking") - log.warning("unsolicited establish request denied") - elseif est_ack == ESTABLISH_ACK.COLLISION then - println_ts("received unsolicited link collision, unlinking") - log.warning("unsolicited establish request collision") - elseif est_ack == ESTABLISH_ACK.BAD_VERSION then - println_ts("received unsolicited link version mismatch, unlinking") - log.warning("unsolicited establish request version mismatch") + log.debug("re-sent initial status data due to re-establish") else - println_ts("invalid unsolicited link response") - log.debug("unsolicited unknown establish request response") - end + if est_ack == ESTABLISH_ACK.DENY then + println_ts("received unsolicited link denial, unlinking") + log.warning("unsolicited establish request denied") + elseif est_ack == ESTABLISH_ACK.COLLISION then + println_ts("received unsolicited link collision, unlinking") + log.warning("unsolicited establish request collision") + elseif est_ack == ESTABLISH_ACK.BAD_VERSION then + println_ts("received unsolicited link version mismatch, unlinking") + log.warning("unsolicited establish request version mismatch") + else + println_ts("invalid unsolicited link response") + log.debug("unsolicited unknown establish request response") + end - self.linked = est_ack == ESTABLISH_ACK.ALLOW + -- unlink + self.sv_addr = comms.BROADCAST + self.linked = false + end -- clear this since this is for something that was unsolicited self.last_est_ack = ESTABLISH_ACK.ALLOW @@ -980,7 +991,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, log.warning("PLC KEEP_ALIVE trip time > 750ms (" .. trip_time .. "ms)") end - -- log.debug("RPLC RTT = " .. trip_time .. "ms") + -- log.debug("PLC RTT = " .. trip_time .. "ms") _send_keep_alive_ack(timestamp) else @@ -1002,9 +1013,11 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, if est_ack == ESTABLISH_ACK.ALLOW then println_ts("linked!") - log.info("supervisor establish request approved, PLC is linked") + log.info("supervisor establish request approved, linked to SV (CID#" .. src_addr .. ")") - -- reset remote sequence number and cache + -- link + reset remote sequence number and cache + self.sv_addr = src_addr + self.linked = true self.r_seq_num = nil self.status_cache = nil @@ -1012,23 +1025,28 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, public.send_status(plc_state.no_reactor, plc_state.reactor_formed) log.debug("sent initial status data") - elseif self.last_est_ack ~= est_ack then - if est_ack == ESTABLISH_ACK.DENY then - println_ts("link request denied, retrying...") - log.info("supervisor establish request denied, retrying") - elseif est_ack == ESTABLISH_ACK.COLLISION then - println_ts("reactor PLC ID collision (check config), retrying...") - log.warning("establish request collision, retrying") - elseif est_ack == ESTABLISH_ACK.BAD_VERSION then - println_ts("supervisor version mismatch (try updating), retrying...") - log.warning("establish request version mismatch, retrying") - else - println_ts("invalid link response, bad channel? retrying...") - log.error("unknown establish request response, retrying") + else + if self.last_est_ack ~= est_ack then + if est_ack == ESTABLISH_ACK.DENY then + println_ts("link request denied, retrying...") + log.info("supervisor establish request denied, retrying") + elseif est_ack == ESTABLISH_ACK.COLLISION then + println_ts("reactor PLC ID collision (check config), retrying...") + log.warning("establish request collision, retrying") + elseif est_ack == ESTABLISH_ACK.BAD_VERSION then + println_ts("supervisor version mismatch (try updating), retrying...") + log.warning("establish request version mismatch, retrying") + else + println_ts("invalid link response, bad channel? retrying...") + log.error("unknown establish request response, retrying") + end end + + -- unlink + self.sv_addr = comms.BROADCAST + self.linked = false end - self.linked = est_ack == ESTABLISH_ACK.ALLOW self.last_est_ack = est_ack -- report link state @@ -1044,7 +1062,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor, log.error("illegal packet type " .. protocol, true) end else - log.debug("received packet on unconfigured channel " .. l_port, true) + log.debug("received packet on unconfigured channel " .. l_chan, true) end end diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 680fdee..cf303c8 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.1.17" +local R_PLC_VERSION = "v1.3.0" local println = util.println local println_ts = util.println_ts @@ -31,8 +31,8 @@ local cfv = util.new_validator() cfv.assert_type_bool(config.NETWORKED) cfv.assert_type_int(config.REACTOR_ID) -cfv.assert_port(config.SERVER_PORT) -cfv.assert_port(config.LISTEN_PORT) +cfv.assert_channel(config.SVR_CHANNEL) +cfv.assert_channel(config.PLC_CHANNEL) cfv.assert_type_int(config.TRUSTED_RANGE) cfv.assert_type_num(config.COMMS_TIMEOUT) cfv.assert_min(config.COMMS_TIMEOUT, 2) @@ -198,7 +198,7 @@ local function main() log.debug("init> conn watchdog started") -- start comms - smem_sys.plc_comms = plc.comms(config.REACTOR_ID, R_PLC_VERSION, smem_dev.modem, config.LISTEN_PORT, config.SERVER_PORT, + smem_sys.plc_comms = plc.comms(config.REACTOR_ID, R_PLC_VERSION, smem_dev.modem, config.PLC_CHANNEL, config.SVR_CHANNEL, config.TRUSTED_RANGE, smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog) log.debug("init> comms init") else diff --git a/rtu/config.lua b/rtu/config.lua index 1b96bec..c4eae91 100644 --- a/rtu/config.lua +++ b/rtu/config.lua @@ -2,11 +2,11 @@ local rsio = require("scada-common.rsio") local config = {} --- port to send packets TO server -config.SERVER_PORT = 16000 --- port to listen to incoming packets FROM server -config.LISTEN_PORT = 15001 --- max trusted modem message distance (< 1 to disable check) +-- supervisor access channel +config.SVR_CHANNEL = 16240 +-- RTU/MODBUS communication channel +config.RTU_CHANNEL = 16242 +-- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active config.COMMS_TIMEOUT = 5 diff --git a/rtu/rtu.lua b/rtu/rtu.lua index bb43706..c89dabb 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -159,12 +159,13 @@ end ---@nodiscard ---@param version string RTU version ---@param modem table modem device ----@param local_port integer local listening port ----@param server_port integer remote server port +---@param rtu_channel integer PLC comms channel +---@param svr_channel integer supervisor server channel ---@param range integer trusted device connection range ---@param conn_watchdog watchdog watchdog reference -function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog) +function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdog) local self = { + sv_addr = comms.BROADCAST, seq_num = 0, r_seq_num = nil, txn_id = 0, @@ -180,7 +181,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog -- configure modem channels local function _conf_channels() modem.closeAll() - modem.open(local_port) + modem.open(rtu_channel) end _conf_channels() @@ -193,9 +194,9 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog 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(self.sv_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(server_port, local_port, s_pkt.raw_sendable()) + modem.transmit(svr_channel, rtu_channel, s_pkt.raw_sendable()) self.seq_num = self.seq_num + 1 end @@ -238,8 +239,8 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog ---@param m_pkt modbus_packet function public.send_modbus(m_pkt) local s_pkt = comms.scada_packet() - s_pkt.make(self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable()) - modem.transmit(server_port, local_port, s_pkt.raw_sendable()) + s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable()) + modem.transmit(svr_channel, rtu_channel, s_pkt.raw_sendable()) self.seq_num = self.seq_num + 1 end @@ -254,6 +255,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog ---@param rtu_state rtu_state function public.unlink(rtu_state) rtu_state.linked = false + self.sv_addr = comms.BROADCAST self.r_seq_num = nil end @@ -327,7 +329,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog -- print a log message to the terminal as long as the UI isn't running local function println_ts(message) if not rtu_state.fp_ok then util.println_ts(message) end end - if packet.scada_frame.local_port() == local_port then + if packet.scada_frame.local_channel() == rtu_channel then -- check sequence number if self.r_seq_num == nil then self.r_seq_num = packet.scada_frame.seq_num() @@ -398,6 +400,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog if est_ack == ESTABLISH_ACK.ALLOW then -- establish allowed rtu_state.linked = true + self.sv_addr = packet.scada_frame.src_addr() self.r_seq_num = nil println_ts("supervisor connection established") log.info("supervisor connection established") diff --git a/rtu/startup.lua b/rtu/startup.lua index 3038310..366af28 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -42,8 +42,8 @@ local println_ts = util.println_ts local cfv = util.new_validator() -cfv.assert_port(config.SERVER_PORT) -cfv.assert_port(config.LISTEN_PORT) +cfv.assert_channel(config.SVR_CHANNEL) +cfv.assert_channel(config.RTU_CHANNEL) cfv.assert_type_int(config.TRUSTED_RANGE) cfv.assert_type_num(config.COMMS_TIMEOUT) cfv.assert_min(config.COMMS_TIMEOUT, 2) @@ -468,7 +468,7 @@ local function main() log.debug("startup> conn watchdog started") -- setup comms - smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_dev.modem, config.LISTEN_PORT, config.SERVER_PORT, + smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_dev.modem, config.RTU_CHANNEL, config.SVR_CHANNEL, config.TRUSTED_RANGE, smem_sys.conn_watchdog) log.debug("startup> comms init") diff --git a/scada-common/comms.lua b/scada-common/comms.lua index fb54101..41528dc 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -4,14 +4,17 @@ local log = require("scada-common.log") +local insert = table.insert + +---@diagnostic disable-next-line: undefined-field +local C_ID = os.getComputerID() ---@type integer computer ID + +local max_distance = nil ---@type integer|nil maximum acceptable transmission distance + ---@class comms local comms = {} -local insert = table.insert - -local max_distance = nil - -comms.version = "1.4.1" +comms.version = "2.0.0" ---@enum PROTOCOL local PROTOCOL = { @@ -122,6 +125,9 @@ comms.PLC_AUTO_ACK = PLC_AUTO_ACK comms.UNIT_COMMAND = UNIT_COMMAND comms.FAC_COMMAND = FAC_COMMAND +-- destination broadcast address (to all devices) +comms.BROADCAST = -1 + ---@alias packet scada_packet|modbus_packet|rplc_packet|mgmt_packet|crdn_packet|capi_packet ---@alias frame modbus_frame|rplc_frame|mgmt_frame|crdn_frame|capi_frame @@ -129,20 +135,18 @@ comms.FAC_COMMAND = FAC_COMMAND -- packets received with distances greater than this will be silently discarded ---@param distance integer max modem message distance (less than 1 disables the limit) function comms.set_trusted_range(distance) - if distance < 1 then - max_distance = nil - else - max_distance = distance - end + if distance < 1 then max_distance = nil else max_distance = distance end end -- generic SCADA packet object ---@nodiscard function comms.scada_packet() local self = { - modem_msg_in = nil, + modem_msg_in = nil, ---@type modem_message|nil valid = false, - raw = { -1, PROTOCOL.SCADA_MGMT, {} }, + raw = {}, + src_addr = comms.BROADCAST, + dest_addr = comms.BROADCAST, seq_num = -1, protocol = PROTOCOL.SCADA_MGMT, length = 0, @@ -153,34 +157,40 @@ function comms.scada_packet() local public = {} -- make a SCADA packet - ---@param seq_num integer + ---@param dest_addr integer destination computer address (ID) + ---@param seq_num integer sequence number ---@param protocol PROTOCOL ---@param payload table - function public.make(seq_num, protocol, payload) + function public.make(dest_addr, seq_num, protocol, payload) self.valid = true +---@diagnostic disable-next-line: undefined-field + self.src_addr = C_ID + self.dest_addr = dest_addr self.seq_num = seq_num self.protocol = protocol self.length = #payload self.payload = payload - self.raw = { self.seq_num, self.protocol, self.payload } + self.raw = { self.src_addr, self.dest_addr, self.seq_num, self.protocol, self.payload } end -- parse in a modem message as a SCADA packet ---@param side string modem side - ---@param sender integer sender port - ---@param reply_to integer reply port + ---@param sender integer sender channel + ---@param reply_to integer reply channel ---@param message any message body ---@param distance integer transmission distance ---@return boolean valid valid message received function public.receive(side, sender, reply_to, message, distance) + ---@class modem_message self.modem_msg_in = { iface = side, - s_port = sender, - r_port = reply_to, + s_channel = sender, + r_channel = reply_to, msg = message, dist = distance } + self.valid = false self.raw = self.modem_msg_in.msg if (type(max_distance) == "number") and (distance > max_distance) then @@ -188,20 +198,31 @@ function comms.scada_packet() -- log.debug("comms.scada_packet.receive(): discarding packet with distance " .. distance .. " outside of trusted range") else if type(self.raw) == "table" then - if #self.raw >= 3 then - self.seq_num = self.raw[1] - self.protocol = self.raw[2] + if #self.raw == 5 then + self.src_addr = self.raw[1] + self.dest_addr = self.raw[2] + self.seq_num = self.raw[3] + self.protocol = self.raw[4] - -- element 3 must be a table - if type(self.raw[3]) == "table" then - self.length = #self.raw[3] - self.payload = self.raw[3] + -- element 5 must be a table + if type(self.raw[5]) == "table" then + self.length = #self.raw[5] + self.payload = self.raw[5] end + else + self.src_addr = nil + self.dest_addr = nil + self.seq_num = nil + self.protocol = nil + self.length = 0 + self.payload = {} end - self.valid = type(self.seq_num) == "number" and - type(self.protocol) == "number" and - type(self.payload) == "table" + -- check if this packet is destined for this device + local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == C_ID) + + self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and + type(self.seq_num) == "number" and type(self.protocol) == "number" and type(self.payload) == "table" end end @@ -216,13 +237,17 @@ function comms.scada_packet() function public.raw_sendable() return self.raw end ---@nodiscard - function public.local_port() return self.modem_msg_in.s_port end + function public.local_channel() return self.modem_msg_in.s_channel end ---@nodiscard - function public.remote_port() return self.modem_msg_in.r_port end + function public.remote_channel() return self.modem_msg_in.r_channel end ---@nodiscard function public.is_valid() return self.valid end + ---@nodiscard + function public.src_addr() return self.src_addr end + ---@nodiscard + function public.dest_addr() return self.dest_addr end ---@nodiscard function public.seq_num() return self.seq_num end ---@nodiscard diff --git a/scada-common/util.lua b/scada-common/util.lua index 063143c..e13d6bb 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -539,7 +539,7 @@ function util.new_validator() function public.assert_range(check, min, max) valid = valid and check >= min and check <= max end function public.assert_range_ex(check, min, max) valid = valid and check > min and check < max end - function public.assert_port(port) valid = valid and type(port) == "number" and port >= 0 and port <= 65535 end + function public.assert_channel(channel) valid = valid and type(channel) == "number" and channel >= 0 and channel <= 65535 end -- check if all assertions passed successfully ---@nodiscard diff --git a/supervisor/config.lua b/supervisor/config.lua index b716d38..a4a595b 100644 --- a/supervisor/config.lua +++ b/supervisor/config.lua @@ -1,9 +1,15 @@ local config = {} --- scada network listen for PLC's and RTU's -config.SCADA_DEV_LISTEN = 16000 --- listen port for SCADA supervisor access -config.SCADA_SV_CTL_LISTEN = 16100 +-- supervisor comms channel +config.SVR_CHANNEL = 16240 +-- PLC comms channel +config.PLC_CHANNEL = 16241 +-- RTU/MODBUS comms channel +config.RTU_CHANNEL = 16242 +-- coordinator comms channel +config.CRD_CHANNEL = 16243 +-- pocket comms channel +config.PKT_CHANNEL = 16244 -- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index 8ad3366..60db434 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -231,32 +231,25 @@ function svsessions.find_plc_session(remote_port) return session end --- find a PLC/RTU session by the remote port +-- find a coordinator session by the remote port ---@nodiscard ---@param remote_port integer ----@return plc_session_struct|rtu_session_struct|nil -function svsessions.find_device_session(remote_port) - -- check RTU sessions - local session = _find_session(self.sessions.rtu, remote_port) - - -- check PLC sessions - if session == nil then session = _find_session(self.sessions.plc, remote_port) end - ---@cast session plc_session_struct|rtu_session_struct|nil - +---@return coord_session_struct|nil +function svsessions.find_coord_session(remote_port) + -- check coordinator sessions + local session = _find_session(self.sessions.coord, remote_port) + ---@cast session coord_session_struct|nil return session end --- find a coordinator or diagnostic access session by the remote port +-- find a pocket diagnostics session by the remote port ---@nodiscard ---@param remote_port integer ----@return coord_session_struct|diag_session_struct|nil -function svsessions.find_svctl_session(remote_port) - -- check coordinator sessions - local session = _find_session(self.sessions.coord, remote_port) - +---@return diag_session_struct|nil +function svsessions.find_pdg_session(remote_port) -- check diagnostic sessions - if session == nil then session = _find_session(self.sessions.diag, remote_port) end - ---@cast session coord_session_struct|diag_session_struct|nil + local session = _find_session(self.sessions.diag, remote_port) + ---@cast session diag_session_struct|nil return session end diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 2e16777..156a8ec 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -14,7 +14,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.15.7" +local SUPERVISOR_VERSION = "v0.16.0" local println = util.println local println_ts = util.println_ts @@ -25,8 +25,11 @@ local println_ts = util.println_ts local cfv = util.new_validator() -cfv.assert_port(config.SCADA_DEV_LISTEN) -cfv.assert_port(config.SCADA_SV_CTL_LISTEN) +cfv.assert_channel(config.SVR_CHANNEL) +cfv.assert_channel(config.PLC_CHANNEL) +cfv.assert_channel(config.RTU_CHANNEL) +cfv.assert_channel(config.CRD_CHANNEL) +cfv.assert_channel(config.PKT_CHANNEL) cfv.assert_type_int(config.TRUSTED_RANGE) cfv.assert_type_num(config.PLC_TIMEOUT) cfv.assert_min(config.PLC_TIMEOUT, 2) @@ -90,9 +93,17 @@ local function main() return end - -- start comms, open all channels - local superv_comms = supervisor.comms(SUPERVISOR_VERSION, config.NUM_REACTORS, config.REACTOR_COOLING, modem, - config.SCADA_DEV_LISTEN, config.SCADA_SV_CTL_LISTEN, config.TRUSTED_RANGE) + -- start comms + ---@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 + } + local superv_comms = supervisor.comms(SUPERVISOR_VERSION, config.NUM_REACTORS, config.REACTOR_COOLING, modem, + channels, config.TRUSTED_RANGE) -- base loop clock (6.67Hz, 3 ticks) local MAIN_CLOCK = 0.15 diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 998c438..d467b97 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -19,11 +19,16 @@ local println = util.println ---@param num_reactors integer number of reactors ---@param cooling_conf table cooling configuration table ---@param modem table modem device ----@param dev_listen integer listening port for PLC/RTU devices ----@param svctl_listen integer listening port for supervisor access +---@param channels sv_channel_list network channels ---@param range integer trusted device connection range ---@diagnostic disable-next-line: unused-local -function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_listen, svctl_listen, range) +function supervisor.comms(_version, num_reactors, cooling_conf, modem, channels, range) + 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 + local self = { last_est_acks = {} } @@ -35,8 +40,7 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste -- configure modem channels local function _conf_channels() modem.closeAll() - modem.open(dev_listen) - modem.open(svctl_listen) + modem.open(svr_channel) end _conf_channels() @@ -44,31 +48,19 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste -- link modem to svsessions svsessions.init(modem, num_reactors, cooling_conf) - -- send an establish request response to a PLC/RTU - ---@param dest integer - ---@param msg table - local function _send_dev_establish(seq_id, dest, msg) + -- send an establish request response + ---@param packet scada_packet + ---@param ack ESTABLISH_ACK + ---@param data? any optional data + local function _send_establish(packet, ack, data) local s_pkt = comms.scada_packet() local m_pkt = comms.mgmt_packet() - m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, msg) - s_pkt.make(seq_id, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) + m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack, data }) + s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(dest, dev_listen, s_pkt.raw_sendable()) - end - - -- send supervisor control access connection establish response - ---@param seq_id integer - ---@param dest integer - ---@param msg table - local function _send_svctl_establish(seq_id, dest, msg) - local s_pkt = comms.scada_packet() - local c_pkt = comms.mgmt_packet() - - c_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, msg) - s_pkt.make(seq_id, PROTOCOL.SCADA_MGMT, c_pkt.raw_sendable()) - - modem.transmit(dest, svctl_listen, s_pkt.raw_sendable()) + modem.transmit(packet.remote_channel(), svr_channel, s_pkt.raw_sendable()) + self.last_est_acks[packet.src_addr()] = ack end -- PUBLIC FUNCTIONS -- @@ -136,17 +128,95 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste ---@param packet modbus_frame|rplc_frame|mgmt_frame|crdn_frame|nil function public.handle_packet(packet) if packet ~= nil then - local l_port = packet.scada_frame.local_port() - local r_port = packet.scada_frame.remote_port() + local l_chan = packet.scada_frame.local_channel() + local r_chan = packet.scada_frame.remote_channel() + local s_addr = packet.scada_frame.src_addr() local protocol = packet.scada_frame.protocol() - -- device (RTU/PLC) listening channel - if l_port == dev_listen then + if l_chan ~= svr_channel then + log.debug("received packet on unconfigured channel " .. l_chan, true) + return + end + + if r_chan == plc_channel then + -- look for an associated session + local session = svsessions.find_plc_session(s_addr) + + if protocol == PROTOCOL.RPLC then + ---@cast packet rplc_frame + -- reactor PLC packet + if session ~= nil then + -- pass the packet onto the session handler + session.in_queue.push_packet(packet) + else + -- unknown session, force a re-link + log.debug("PLC_ESTABLISH: no session but not an establish, forcing relink") + _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + end + elseif 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 PLC 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.PLC then + -- 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) + + if plc_id == false then + -- reactor already has a PLC assigned + if last_ack ~= ESTABLISH_ACK.COLLISION then + log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id)) + end + + _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)) + _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) + end + else + log.debug("PLC_ESTABLISH: packet length mismatch/bad parameter type") + _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")) + _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + end + else + log.debug("invalid establish packet (on PLC listening 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")) + end + end + elseif r_chan == rtu_channel then + -- look for an associated session + local session = svsessions.find_rtu_session(s_addr) + if protocol == PROTOCOL.MODBUS_TCP then ---@cast packet modbus_frame - -- look for an associated session - local session = svsessions.find_rtu_session(r_port) - -- MODBUS response if session ~= nil then -- pass the packet onto the session handler @@ -155,105 +225,59 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste -- any other packet should be session related, discard it log.debug("discarding MODBUS_TCP packet without a known session") end - elseif protocol == PROTOCOL.RPLC then - ---@cast packet rplc_frame - -- look for an associated session - local session = svsessions.find_plc_session(r_port) - - -- reactor PLC packet - if session ~= nil then - -- pass the packet onto the session handler - session.in_queue.push_packet(packet) - else - -- unknown session, force a re-link - log.debug("PLC_ESTABLISH: no session but not an establish, forcing relink") - _send_dev_establish(packet.scada_frame.seq_num() + 1, r_port, { ESTABLISH_ACK.DENY }) - end elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame - -- look for an associated session - local session = svsessions.find_device_session(r_port) - -- 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 next_seq_id = packet.scada_frame.seq_num() + 1 + 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 comms_v = packet.data[1] local firmware_v = packet.data[2] - local dev_type = packet.data[3] + local dev_type = packet.data[3] if comms_v ~= comms.version then - if self.last_est_acks[r_port] ~= ESTABLISH_ACK.BAD_VERSION then - log.info(util.c("dropping device establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) - self.last_est_acks[r_port] = ESTABLISH_ACK.BAD_VERSION + if last_ack ~= ESTABLISH_ACK.BAD_VERSION then + log.info(util.c("dropping RTU establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) end - _send_dev_establish(next_seq_id, r_port, { 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 - local reactor_id = packet.data[4] - local plc_id = svsessions.establish_plc_session(l_port, r_port, reactor_id, firmware_v) - - if plc_id == false then - -- reactor already has a PLC assigned - if self.last_est_acks[r_port] ~= ESTABLISH_ACK.COLLISION then - log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id)) - self.last_est_acks[r_port] = ESTABLISH_ACK.COLLISION - end - - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.COLLISION }) - else - -- got an ID; assigned to a reactor successfully - println(util.c("PLC (", firmware_v, ") [:", r_port, "] \xbb reactor ", reactor_id, " connected")) - log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [:", r_port, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id)) - - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.ALLOW }) - self.last_est_acks[r_port] = ESTABLISH_ACK.ALLOW - end - else - log.debug("PLC_ESTABLISH: packet length mismatch/bad parameter type") - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) - end + _send_establish(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 local rtu_advert = packet.data[4] - local s_id = svsessions.establish_rtu_session(l_port, r_port, rtu_advert, firmware_v) + local s_id = svsessions.establish_rtu_session(l_chan, r_chan, rtu_advert, firmware_v) - println(util.c("RTU (", firmware_v, ") [:", r_port, "] \xbb connected")) - log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [:", r_port, "] connected with session ID ", s_id)) - - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.ALLOW }) + 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)) + _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) else log.debug("RTU_ESTABLISH: packet length mismatch") - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) + _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else - log.debug(util.c("illegal establish packet for device ", dev_type, " on PLC/RTU listening channel")) - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) + log.debug(util.c("illegal establish packet for device ", dev_type, " on RTU listening channel")) + _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else - log.debug("invalid establish packet (on PLC/RTU listening channel)") - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) + log.debug("invalid establish packet (on RTU listening 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_port, "->", l_port, ": discarding SCADA_MGMT packet without a known session")) + log.debug(util.c(r_chan, " -> ", l_chan, ": discarding SCADA_MGMT packet without a known session")) end else - log.debug("illegal packet type " .. protocol .. " on device listening channel") + log.debug("illegal packet type " .. protocol .. " on RTU listening channel") end - -- coordinator listening channel - elseif l_port == svctl_listen then + elseif r_chan == crd_channel then -- look for an associated session - local session = svsessions.find_svctl_session(r_port) + local session = svsessions.find_svctl_session(s_addr) if protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame @@ -263,24 +287,23 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste session.in_queue.push_packet(packet) elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then -- establish a new session - local next_seq_id = packet.scada_frame.seq_num() + 1 + 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 comms_v = packet.data[1] local firmware_v = packet.data[2] - local dev_type = packet.data[3] + local dev_type = packet.data[3] if comms_v ~= comms.version then - if self.last_est_acks[r_port] ~= ESTABLISH_ACK.BAD_VERSION then + if last_ack ~= ESTABLISH_ACK.BAD_VERSION then log.info(util.c("dropping coordinator establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) - self.last_est_acks[r_port] = ESTABLISH_ACK.BAD_VERSION end - _send_svctl_establish(next_seq_id, r_port, { ESTABLISH_ACK.BAD_VERSION }) + _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_port, r_port, firmware_v) + local s_id = svsessions.establish_coord_session(l_chan, r_chan, firmware_v) if s_id ~= false then local config = { num_reactors } @@ -289,39 +312,36 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste table.insert(config, cooling_conf[i].TURBINES) end - println(util.c("CRD (", firmware_v, ") [:", r_port, "] \xbb connected")) - log.info(util.c("SVCTL_ESTABLISH: coordinator (", firmware_v, ") [:", r_port, "] connected with session ID ", s_id)) + 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)) - _send_svctl_establish(next_seq_id, r_port, { ESTABLISH_ACK.ALLOW, config }) - self.last_est_acks[r_port] = ESTABLISH_ACK.ALLOW + _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, config) else - if self.last_est_acks[r_port] ~= ESTABLISH_ACK.COLLISION then - log.info("SVCTL_ESTABLISH: denied new coordinator due to already being connected to another coordinator") - self.last_est_acks[r_port] = ESTABLISH_ACK.COLLISION + if last_ack ~= ESTABLISH_ACK.COLLISION then + log.info("ESTABLISH: denied new coordinator [@" .. s_addr .. "] due to already being connected to another coordinator") end - _send_svctl_establish(next_seq_id, r_port, { ESTABLISH_ACK.COLLISION }) + _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_diag_session(l_port, r_port, firmware_v) + local s_id = svsessions.establish_diag_session(l_chan, r_chan, firmware_v) - println(util.c("PKT (", firmware_v, ") [:", r_port, "] \xbb connected")) - log.info(util.c("SVCTL_ESTABLISH: pocket (", firmware_v, ") [:", r_port, "] connected with session ID ", s_id)) + 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_svctl_establish(next_seq_id, r_port, { ESTABLISH_ACK.ALLOW }) - self.last_est_acks[r_port] = ESTABLISH_ACK.ALLOW + _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW) else log.debug(util.c("illegal establish packet for device ", dev_type, " on SVCTL listening channel")) - _send_svctl_establish(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) + _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug("SVCTL_ESTABLISH: establish packet length mismatch") - _send_svctl_establish(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) + _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it - log.debug(r_port .. "->" .. l_port .. ": discarding SCADA_MGMT packet without a known session") + log.debug(r_chan .. " -> " .. l_chan .. ": discarding SCADA_MGMT packet without a known session") end elseif protocol == PROTOCOL.SCADA_CRDN then ---@cast packet crdn_frame @@ -331,13 +351,15 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste session.in_queue.push_packet(packet) else -- any other packet should be session related, discard it - log.debug(r_port .. "->" .. l_port .. ": discarding SCADA_CRDN packet without a known session") + log.debug(r_chan .. "->" .. l_chan .. ": discarding SCADA_CRDN packet without a known session") end else log.debug("illegal packet type " .. protocol .. " on coordinator listening channel") end + elseif r_chan == pkt_channel then + else - log.debug("received packet on unconfigured channel " .. l_port, true) + log.debug("received packet for unknown channel " .. r_chan, true) end end end diff --git a/test/lockbox-benchmark.lua b/test/lockbox-benchmark.lua index 198f41b..7c6ae55 100644 --- a/test/lockbox-benchmark.lua +++ b/test/lockbox-benchmark.lua @@ -38,7 +38,7 @@ local pkt = comms.modbus_packet() ---@diagnostic disable-next-line: param-type-mismatch pkt.make(1, 2, 7, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) local spkt = comms.scada_packet() -spkt.make(1, 1, pkt.raw_sendable()) +spkt.make(0, 1, 1, pkt.raw_sendable()) start = util.time() local data = textutils.serialize(spkt.raw_sendable(), { allow_repetitions = true, compact = true }) diff --git a/test/turbine_modbustest.lua b/test/turbine_modbustest.lua index fe167d7..c8f7801 100644 --- a/test/turbine_modbustest.lua +++ b/test/turbine_modbustest.lua @@ -22,7 +22,7 @@ println("") -- RTU init -- -log.init("/log.txt", log.MODE.NEW) +log.init("/log.txt", log.MODE.NEW, true) print(">>> init turbine RTU: ") From 360609df1f20dcd87c3650d39162e4c077a3c209 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 5 Jun 2023 17:24:00 -0400 Subject: [PATCH 02/18] #225 network changes for supervisor sessions --- supervisor/session/coordinator.lua | 9 +- supervisor/session/plc.lua | 7 +- supervisor/session/pocket.lua | 5 +- supervisor/session/rtu.lua | 7 +- supervisor/session/svsessions.lua | 206 ++++++++++++++++------------- supervisor/startup.lua | 12 +- supervisor/supervisor.lua | 152 ++++++++++++++------- 7 files changed, 233 insertions(+), 165 deletions(-) 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 From 63147bfab53ec1d877669aa81262b2674f8b7429 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 5 Jun 2023 17:47:43 -0400 Subject: [PATCH 03/18] #248 fixed network light not going out on PLC/RTU when disconnected --- reactor-plc/panel/front_panel.lua | 3 ++- reactor-plc/plc.lua | 1 + reactor-plc/startup.lua | 2 +- rtu/panel/front_panel.lua | 3 ++- rtu/rtu.lua | 1 + rtu/startup.lua | 2 +- scada-common/types.lua | 9 +++++++++ 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/reactor-plc/panel/front_panel.lua b/reactor-plc/panel/front_panel.lua index 8e28a75..a8675a0 100644 --- a/reactor-plc/panel/front_panel.lua +++ b/reactor-plc/panel/front_panel.lua @@ -2,6 +2,7 @@ -- Main SCADA Coordinator GUI -- +local types = require("scada-common.types") local util = require("scada-common.util") local config = require("reactor-plc.config") @@ -49,7 +50,7 @@ local function init(panel) local reactor = LEDPair{parent=system,label="REACTOR",off=colors.red,c1=colors.yellow,c2=colors.green} local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)} local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}} - network.update(5) + network.update(types.PANEL_LINK_STATE.DISCONNECTED) system.line_break() reactor.register(databus.ps, "reactor_dev_state", reactor.update) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index a4f66dc..0d13091 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -672,6 +672,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, self.linked = false self.r_seq_num = nil self.status_cache = nil + databus.tx_link_state(types.PANEL_LINK_STATE.DISCONNECTED) end -- close the connection to the server diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 682e618..b5a2e3b 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.4.0" +local R_PLC_VERSION = "v1.4.1" local println = util.println local println_ts = util.println_ts diff --git a/rtu/panel/front_panel.lua b/rtu/panel/front_panel.lua index 606abbb..09270e9 100644 --- a/rtu/panel/front_panel.lua +++ b/rtu/panel/front_panel.lua @@ -2,6 +2,7 @@ -- Main SCADA Coordinator GUI -- +local types = require("scada-common.types") local util = require("scada-common.util") local databus = require("rtu.databus") @@ -53,7 +54,7 @@ local function init(panel, units) local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)} local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}} - network.update(5) + network.update(types.PANEL_LINK_STATE.DISCONNECTED) system.line_break() modem.register(databus.ps, "has_modem", modem.update) diff --git a/rtu/rtu.lua b/rtu/rtu.lua index c89dabb..5d2e2c8 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -257,6 +257,7 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo rtu_state.linked = false self.sv_addr = comms.BROADCAST self.r_seq_num = nil + databus.tx_link_state(types.PANEL_LINK_STATE.DISCONNECTED) end -- close the connection to the server diff --git a/rtu/startup.lua b/rtu/startup.lua index 020c2aa..d37be78 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "v1.2.8" +local RTU_VERSION = "v1.3.1" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE diff --git a/scada-common/types.lua b/scada-common/types.lua index 8df01c1..21429b5 100644 --- a/scada-common/types.lua +++ b/scada-common/types.lua @@ -74,6 +74,15 @@ function types.new_zero_coordinate() return { x = 0, y = 0, z = 0 } end -- ENUMERATION TYPES -- --#region +---@enum PANEL_LINK_STATE +types.PANEL_LINK_STATE = { + LINKED = 1, + DENIED = 2, + COLLISION = 3, + BAD_VERSION = 4, + DISCONNECTED = 5 +} + ---@enum RTU_UNIT_TYPE types.RTU_UNIT_TYPE = { VIRTUAL = 0, -- virtual device From b20d42ff38e4b50f245137016c0f34d7d7afde02 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 5 Jun 2023 18:10:53 -0400 Subject: [PATCH 04/18] #225 added computer IDs to PLC/RTU front panels, updated supervisor front panel to use computer ID terminology --- pocket/startup.lua | 2 +- reactor-plc/panel/front_panel.lua | 4 ++++ reactor-plc/startup.lua | 2 +- rtu/panel/front_panel.lua | 4 ++++ rtu/startup.lua | 2 +- supervisor/databus.lua | 28 +++++++++++------------ supervisor/panel/components/pdg_entry.lua | 4 ++-- supervisor/panel/components/rtu_entry.lua | 4 ++-- supervisor/panel/front_panel.lua | 16 ++++++++----- supervisor/session/svsessions.lua | 2 +- supervisor/startup.lua | 2 +- 11 files changed, 41 insertions(+), 29 deletions(-) diff --git a/pocket/startup.lua b/pocket/startup.lua index 1ac6a5a..9e55501 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -17,7 +17,7 @@ local coreio = require("pocket.coreio") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "alpha-v0.3.7" +local POCKET_VERSION = "alpha-v0.4.0" local println = util.println local println_ts = util.println_ts diff --git a/reactor-plc/panel/front_panel.lua b/reactor-plc/panel/front_panel.lua index a8675a0..12c8266 100644 --- a/reactor-plc/panel/front_panel.lua +++ b/reactor-plc/panel/front_panel.lua @@ -70,6 +70,10 @@ local function init(panel) rt_cmrx.register(databus.ps, "routine__comms_rx", rt_cmrx.update) rt_sctl.register(databus.ps, "routine__spctl", rt_sctl.update) +---@diagnostic disable-next-line: undefined-field + local comp_id = util.sprintf("(%d)", os.getComputerID()) + TextBox{parent=system,x=9,y=5,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)} + -- -- status & controls -- diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index b5a2e3b..71ebcb2 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.4.1" +local R_PLC_VERSION = "v1.4.2" local println = util.println local println_ts = util.println_ts diff --git a/rtu/panel/front_panel.lua b/rtu/panel/front_panel.lua index 09270e9..467386e 100644 --- a/rtu/panel/front_panel.lua +++ b/rtu/panel/front_panel.lua @@ -67,6 +67,10 @@ local function init(panel, units) rt_main.register(databus.ps, "routine__main", rt_main.update) rt_comm.register(databus.ps, "routine__comms", rt_comm.update) +---@diagnostic disable-next-line: undefined-field + local comp_id = util.sprintf("(%d)", os.getComputerID()) + TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)} + -- -- about label -- diff --git a/rtu/startup.lua b/rtu/startup.lua index d37be78..113b513 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "v1.3.1" +local RTU_VERSION = "v1.3.2" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE diff --git a/supervisor/databus.lua b/supervisor/databus.lua index a1e947e..664b3fe 100644 --- a/supervisor/databus.lua +++ b/supervisor/databus.lua @@ -31,11 +31,11 @@ end -- transmit PLC firmware version and session connection state ---@param reactor_id integer reactor unit ID ---@param fw string firmware version ----@param channel integer PLC remote port -function databus.tx_plc_connected(reactor_id, fw, channel) +---@param s_addr integer PLC computer ID +function databus.tx_plc_connected(reactor_id, fw, s_addr) databus.ps.publish("plc_" .. reactor_id .. "_fw", fw) databus.ps.publish("plc_" .. reactor_id .. "_conn", true) - databus.ps.publish("plc_" .. reactor_id .. "_chan", tostring(channel)) + databus.ps.publish("plc_" .. reactor_id .. "_addr", tostring(s_addr)) end -- transmit PLC disconnected @@ -43,7 +43,7 @@ end function databus.tx_plc_disconnected(reactor_id) databus.ps.publish("plc_" .. reactor_id .. "_fw", " ------- ") databus.ps.publish("plc_" .. reactor_id .. "_conn", false) - databus.ps.publish("plc_" .. reactor_id .. "_chan", " --- ") + databus.ps.publish("plc_" .. reactor_id .. "_addr", " --- ") databus.ps.publish("plc_" .. reactor_id .. "_rtt", 0) databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.lightGray) end @@ -66,10 +66,10 @@ end -- transmit RTU firmware version and session connection state ---@param session_id integer RTU session ---@param fw string firmware version ----@param channel integer RTU remote port -function databus.tx_rtu_connected(session_id, fw, channel) +---@param s_addr integer RTU computer ID +function databus.tx_rtu_connected(session_id, fw, s_addr) databus.ps.publish("rtu_" .. session_id .. "_fw", fw) - databus.ps.publish("rtu_" .. session_id .. "_chan", tostring(channel)) + databus.ps.publish("rtu_" .. session_id .. "_addr", tostring(s_addr)) pgi.create_rtu_entry(session_id) end @@ -103,18 +103,18 @@ end -- transmit coordinator firmware version and session connection state ---@param fw string firmware version ----@param channel integer coordinator remote port -function databus.tx_crd_connected(fw, channel) +---@param s_addr integer coordinator computer ID +function databus.tx_crd_connected(fw, s_addr) databus.ps.publish("crd_fw", fw) databus.ps.publish("crd_conn", true) - databus.ps.publish("crd_chan", tostring(channel)) + databus.ps.publish("crd_addr", tostring(s_addr)) end -- transmit coordinator disconnected function databus.tx_crd_disconnected() databus.ps.publish("crd_fw", " ------- ") databus.ps.publish("crd_conn", false) - databus.ps.publish("crd_chan", "---") + databus.ps.publish("crd_addr", "---") databus.ps.publish("crd_rtt", 0) databus.ps.publish("crd_rtt_color", colors.lightGray) end @@ -136,10 +136,10 @@ end -- transmit PKT firmware version and PDG session connection state ---@param session_id integer PDG session ---@param fw string firmware version ----@param channel integer PDG remote port -function databus.tx_pdg_connected(session_id, fw, channel) +---@param s_addr integer PDG computer ID +function databus.tx_pdg_connected(session_id, fw, s_addr) databus.ps.publish("pdg_" .. session_id .. "_fw", fw) - databus.ps.publish("pdg_" .. session_id .. "_chan", tostring(channel)) + databus.ps.publish("pdg_" .. session_id .. "_addr", tostring(s_addr)) pgi.create_pdg_entry(session_id) end diff --git a/supervisor/panel/components/pdg_entry.lua b/supervisor/panel/components/pdg_entry.lua index 94cd385..ea17cfd 100644 --- a/supervisor/panel/components/pdg_entry.lua +++ b/supervisor/panel/components/pdg_entry.lua @@ -28,9 +28,9 @@ local function init(parent, id) local ps_prefix = "pdg_" .. id .. "_" TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - local pdg_chan = TextBox{parent=entry,x=1,y=2,text=" :00000",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} + local pdg_addr = TextBox{parent=entry,x=1,y=2,text="C #?",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - pdg_chan.register(databus.ps, ps_prefix .. "chan", function (channel) pdg_chan.set_value(util.sprintf(" :%05d", channel)) end) + pdg_addr.register(databus.ps, ps_prefix .. "addr", function (addr) pdg_addr.set_value(util.sprintf("C #%d", addr)) end) TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1} local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=cpair(colors.lightGray,colors.white)} diff --git a/supervisor/panel/components/rtu_entry.lua b/supervisor/panel/components/rtu_entry.lua index a4a5a4e..e51b0bf 100644 --- a/supervisor/panel/components/rtu_entry.lua +++ b/supervisor/panel/components/rtu_entry.lua @@ -28,9 +28,9 @@ local function init(parent, id) local ps_prefix = "rtu_" .. id .. "_" TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - local rtu_chan = TextBox{parent=entry,x=1,y=2,text=" :00000",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} + local rtu_addr = TextBox{parent=entry,x=1,y=2,text="C #?",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - rtu_chan.register(databus.ps, ps_prefix .. "chan", function (channel) rtu_chan.set_value(util.sprintf(" :%05d", channel)) end) + rtu_addr.register(databus.ps, ps_prefix .. "addr", function (addr) rtu_addr.set_value(util.sprintf("C #%d", addr)) end) TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7,height=1} local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=cpair(colors.gray,colors.white)} diff --git a/supervisor/panel/front_panel.lua b/supervisor/panel/front_panel.lua index 8cdcf76..b6ee692 100644 --- a/supervisor/panel/front_panel.lua +++ b/supervisor/panel/front_panel.lua @@ -56,6 +56,10 @@ local function init(panel) modem.register(databus.ps, "has_modem", modem.update) +---@diagnostic disable-next-line: undefined-field + local comp_id = util.sprintf("(%d)", os.getComputerID()) + TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)} + -- -- about footer -- @@ -84,11 +88,11 @@ local function init(panel) TextBox{parent=plc_entry,x=1,y=2,text="UNIT "..i,alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=plc_entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - local conn = LED{parent=plc_entry,x=10,y=2,label="CONN",colors=cpair(colors.green,colors.green_off)} + local conn = LED{parent=plc_entry,x=10,y=2,label="LINK",colors=cpair(colors.green,colors.green_off)} conn.register(databus.ps, ps_prefix .. "conn", conn.update) - local plc_chan = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,height=1,fg_bg=cpair(colors.gray,colors.white)} - plc_chan.register(databus.ps, ps_prefix .. "chan", plc_chan.set_value) + local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,height=1,fg_bg=cpair(colors.gray,colors.white)} + plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value) TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3,height=1} local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,height=1,fg_bg=cpair(colors.lightGray,colors.white)} @@ -117,9 +121,9 @@ local function init(panel) local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green,colors.green_off)} crd_conn.register(databus.ps, "crd_conn", crd_conn.update) - TextBox{parent=crd_box,x=4,y=3,text="CHANNEL ",width=8,height=1,fg_bg=cpair(colors.gray,colors.white)} - local crd_chan = TextBox{parent=crd_box,x=12,y=3,text="---",width=5,height=1,fg_bg=cpair(colors.gray,colors.white)} - crd_chan.register(databus.ps, "crd_chan", crd_chan.set_value) + TextBox{parent=crd_box,x=4,y=3,text="COMPUTER",width=8,height=1,fg_bg=cpair(colors.gray,colors.white)} + local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,height=1,fg_bg=cpair(colors.gray,colors.white)} + crd_addr.register(databus.ps, "crd_addr", crd_addr.set_value) TextBox{parent=crd_box,x=22,y=2,text="FW:",width=3,height=1} local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,height=1,fg_bg=cpair(colors.lightGray,colors.white)} diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index 4d5b0f4..73a4e4f 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -251,7 +251,7 @@ end ---@return pdg_session_struct|nil function svsessions.find_pdg_session(source_addr) -- check diagnostic sessions - local session = _find_session(self.sessions.diag, source_addr) + local session = _find_session(self.sessions.pdg, source_addr) ---@cast session pdg_session_struct|nil return session end diff --git a/supervisor/startup.lua b/supervisor/startup.lua index ba943fe..9fdb517 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -20,7 +20,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.17.0" +local SUPERVISOR_VERSION = "v0.17.1" local println = util.println local println_ts = util.println_ts From c536b823e7b8e58b64043dc29a1c4d4985e0a70c Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 5 Jun 2023 19:12:43 -0400 Subject: [PATCH 05/18] #225 PLC/RTUs drop incoming packets from devices other than the configured supervisor while linked --- reactor-plc/plc.lua | 6 ++++-- reactor-plc/startup.lua | 2 +- rtu/rtu.lua | 14 +++++++++++--- rtu/startup.lua | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 0d13091..efe6aaa 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -778,6 +778,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, -- print a log message to the terminal as long as the UI isn't running local function println_ts(message) if not plc_state.fp_ok then util.println_ts(message) end end + local protocol = packet.scada_frame.protocol() local l_chan = packet.scada_frame.local_channel() local src_addr = packet.scada_frame.src_addr() @@ -789,6 +790,9 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, elseif self.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return + elseif self.linked and src_addr ~= self.sv_addr then + log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") + return else self.r_seq_num = packet.scada_frame.seq_num() end @@ -796,8 +800,6 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, -- feed the watchdog first so it doesn't uhh...eat our packets :) conn_watchdog.feed() - local protocol = packet.scada_frame.protocol() - -- handle packet if protocol == PROTOCOL.RPLC then ---@cast packet rplc_frame diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 71ebcb2..5d0234f 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.4.2" +local R_PLC_VERSION = "v1.4.3" local println = util.println local println_ts = util.println_ts diff --git a/rtu/rtu.lua b/rtu/rtu.lua index 5d2e2c8..a1cc9dc 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -330,13 +330,20 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- print a log message to the terminal as long as the UI isn't running local function println_ts(message) if not rtu_state.fp_ok then util.println_ts(message) end end - if packet.scada_frame.local_channel() == rtu_channel then + local protocol = packet.scada_frame.protocol() + local l_chan = packet.scada_frame.local_channel() + local src_addr = packet.scada_frame.src_addr() + + if l_chan == rtu_channel then -- check sequence number if self.r_seq_num == nil then self.r_seq_num = packet.scada_frame.seq_num() elseif rtu_state.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return + elseif rtu_state.linked and src_addr ~= self.sv_addr then + log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") + return else self.r_seq_num = packet.scada_frame.seq_num() end @@ -344,8 +351,7 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- feed watchdog on valid sequence number conn_watchdog.feed() - local protocol = packet.scada_frame.protocol() - + -- handle packet if protocol == PROTOCOL.MODBUS_TCP then ---@cast packet modbus_frame if rtu_state.linked then @@ -465,6 +471,8 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- should be unreachable assuming packet is from parse_packet() log.error("illegal packet type " .. protocol, true) end + else + log.debug("received packet on unconfigured channel " .. l_chan, true) end end diff --git a/rtu/startup.lua b/rtu/startup.lua index 113b513..58970f7 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "v1.3.2" +local RTU_VERSION = "v1.3.3" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE From cdff7af43153dd5ed5f88210902444932fd9924d Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 5 Jun 2023 20:59:28 -0400 Subject: [PATCH 06/18] #225 pocket properly handle disconnects and address validation --- pocket/pocket.lua | 25 +++++++++++++++++++++---- pocket/startup.lua | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index a7482fd..7f770a1 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -135,6 +135,8 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran function public.close_sv() sv_watchdog.cancel() self.sv.linked = false + self.sv.r_seq_num = nil + self.sv.addr = comms.BROADCAST _send_sv(SCADA_MGMT_TYPE.CLOSE, {}) end @@ -142,6 +144,8 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran function public.close_api() api_watchdog.cancel() self.api.linked = false + self.api.r_seq_num = nil + self.api.addr = comms.BROADCAST _send_crd(SCADA_MGMT_TYPE.CLOSE, {}) end @@ -216,9 +220,10 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran ---@param packet mgmt_frame|capi_frame|nil function public.handle_packet(packet) if packet ~= nil then - local l_chan = packet.scada_frame.local_channel() - local r_chan = packet.scada_frame.remote_channel() + local l_chan = packet.scada_frame.local_channel() + local r_chan = packet.scada_frame.remote_channel() local protocol = packet.scada_frame.protocol() + local src_addr = packet.scada_frame.src_addr() if l_chan ~= pkt_channel then log.debug("received packet on unconfigured channel " .. l_chan, true) @@ -227,7 +232,10 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran if self.api.r_seq_num == nil then self.api.r_seq_num = packet.scada_frame.seq_num() elseif self.connected and ((self.api.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then - log.warning("sequence out-of-order: last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) + log.warning("sequence out-of-order (API): last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) + return + elseif self.api.linked and self.api.addr ~= src_addr then + log.debug("received API packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") return else self.api.r_seq_num = packet.scada_frame.seq_num() @@ -249,6 +257,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran log.info("coordinator connection established") self.establish_delay_counter = 0 self.api.linked = true + self.api.addr = src_addr if self.sv.linked then coreio.report_link_state(LINK_STATE.LINKED) @@ -296,6 +305,8 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran -- handle session close api_watchdog.cancel() self.api.linked = false + self.api.r_seq_num = nil + self.api.addr = comms.BROADCAST log.info("coordinator server connection closed by remote host") else log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from coordinator") @@ -311,7 +322,10 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran if self.sv.r_seq_num == nil then self.sv.r_seq_num = packet.scada_frame.seq_num() elseif self.connected and ((self.sv.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then - log.warning("sequence out-of-order: last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) + log.warning("sequence out-of-order (SVR): last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) + return + elseif self.sv.linked and self.sv.addr ~= src_addr then + log.debug("received SVR packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") return else self.sv.r_seq_num = packet.scada_frame.seq_num() @@ -332,6 +346,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran log.info("supervisor connection established") self.establish_delay_counter = 0 self.sv.linked = true + self.sv.addr = src_addr if self.api.linked then coreio.report_link_state(LINK_STATE.LINKED) @@ -379,6 +394,8 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran -- handle session close sv_watchdog.cancel() self.sv.linked = false + self.sv.r_seq_num = nil + self.sv.addr = comms.BROADCAST log.info("supervisor server connection closed by remote host") else log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from supervisor") diff --git a/pocket/startup.lua b/pocket/startup.lua index 9e55501..4455105 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -17,7 +17,7 @@ local coreio = require("pocket.coreio") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "alpha-v0.4.0" +local POCKET_VERSION = "alpha-v0.4.1" local println = util.println local println_ts = util.println_ts From 0f5ae9a7564e415ab174079a5df45f128f41b9b0 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 6 Jun 2023 19:41:09 -0400 Subject: [PATCH 07/18] #225 coordinator changes for new comms --- coordinator/config.lua | 12 +-- coordinator/coordinator.lua | 84 +++++++++++---------- coordinator/session/apisessions.lua | 71 +++++++++-------- coordinator/session/{api.lua => pocket.lua} | 29 +++---- coordinator/startup.lua | 14 ++-- 5 files changed, 112 insertions(+), 98 deletions(-) rename coordinator/session/{api.lua => pocket.lua} (89%) diff --git a/coordinator/config.lua b/coordinator/config.lua index 3196e80..ecb7599 100644 --- a/coordinator/config.lua +++ b/coordinator/config.lua @@ -1,11 +1,11 @@ local config = {} --- port of the SCADA supervisor -config.SCADA_SV_PORT = 16100 --- port to listen to incoming packets from supervisor -config.SCADA_SV_CTL_LISTEN = 16101 --- listen port for SCADA coordinator API access -config.SCADA_API_LISTEN = 16200 +-- supervisor comms channel +config.SVR_CHANNEL = 16240 +-- coordinator comms channel +config.CRD_CHANNEL = 16243 +-- pocket comms channel +config.PKT_CHANNEL = 16244 -- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 6f586ef..f6e8038 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -213,14 +213,15 @@ end ---@nodiscard ---@param version string coordinator version ---@param modem table modem device ----@param sv_port integer port of configured supervisor ----@param sv_listen integer listening port for supervisor replys ----@param api_listen integer listening port for pocket API +---@param crd_channel integer port of configured supervisor +---@param svr_channel integer listening port for supervisor replys +---@param pkt_channel integer listening port for pocket API ---@param range integer trusted device connection range ---@param sv_watchdog watchdog -function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range, sv_watchdog) +function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel, range, sv_watchdog) local self = { sv_linked = false, + sv_addr = comms.BROADCAST, sv_seq_num = 0, sv_r_seq_num = nil, sv_config_err = false, @@ -236,8 +237,7 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range -- configure modem channels local function _conf_channels() modem.closeAll() - modem.open(sv_listen) - modem.open(api_listen) + modem.open(crd_channel) end _conf_channels() @@ -261,23 +261,24 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range end pkt.make(msg_type, msg) - s_pkt.make(self.sv_seq_num, protocol, pkt.raw_sendable()) + s_pkt.make(self.sv_addr, self.sv_seq_num, protocol, pkt.raw_sendable()) - modem.transmit(sv_port, sv_listen, s_pkt.raw_sendable()) + modem.transmit(svr_channel, crd_channel, s_pkt.raw_sendable()) self.sv_seq_num = self.sv_seq_num + 1 end -- send an API establish request response - ---@param dest integer - ---@param msg table - local function _send_api_establish_ack(seq_id, dest, msg) + ---@param packet scada_packet + ---@param ack ESTABLISH_ACK + local function _send_api_establish_ack(packet, ack) local s_pkt = comms.scada_packet() local m_pkt = comms.mgmt_packet() - m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, msg) - s_pkt.make(seq_id, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) + m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack }) + s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(dest, api_listen, s_pkt.raw_sendable()) + modem.transmit(pkt_channel, crd_channel, s_pkt.raw_sendable()) + self.last_api_est_acks[packet.src_addr()] = ack end -- attempt connection establishment @@ -307,7 +308,9 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range -- close the connection to the server function public.close() sv_watchdog.cancel() + self.sv_addr = comms.BROADCAST self.sv_linked = false + self.sv_r_seq_num = nil _send_sv(PROTOCOL.SCADA_MGMT, SCADA_MGMT_TYPE.CLOSE, {}) end @@ -436,15 +439,18 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range ---@param packet mgmt_frame|crdn_frame|capi_frame|nil function public.handle_packet(packet) if packet ~= nil then - local l_port = packet.scada_frame.local_channel() - local r_port = packet.scada_frame.remote_channel() + local l_chan = packet.scada_frame.local_channel() + local r_chan = packet.scada_frame.remote_channel() + local src_addr = packet.scada_frame.src_addr() local protocol = packet.scada_frame.protocol() - if l_port == api_listen then + if l_chan ~= crd_channel then + log.debug("received packet on unconfigured channel " .. l_chan, true) + elseif r_chan == pkt_channel then if protocol == PROTOCOL.COORD_API then ---@cast packet capi_frame -- look for an associated session - local session = apisessions.find_session(r_port) + local session = apisessions.find_session(src_addr) -- API packet if session ~= nil then @@ -457,7 +463,7 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame -- look for an associated session - local session = apisessions.find_session(r_port) + local session = apisessions.find_session(src_addr) -- SCADA management packet if session ~= nil then @@ -465,8 +471,6 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range session.in_queue.push_packet(packet) elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then -- establish a new session - local next_seq_id = packet.scada_frame.seq_num() + 1 - -- 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] @@ -474,42 +478,43 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range local dev_type = packet.data[3] if comms_v ~= comms.version then - if self.last_api_est_acks[r_port] ~= ESTABLISH_ACK.BAD_VERSION then + if self.last_api_est_acks[src_addr] ~= ESTABLISH_ACK.BAD_VERSION then log.info(util.c("dropping API establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")")) - self.last_api_est_acks[r_port] = ESTABLISH_ACK.BAD_VERSION end - _send_api_establish_ack(next_seq_id, r_port, { ESTABLISH_ACK.BAD_VERSION }) + _send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) elseif dev_type == DEVICE_TYPE.PKT then -- pocket linking request - local id = apisessions.establish_session(l_port, r_port, firmware_v) - println(util.c("API: pocket (", firmware_v, ") [:", r_port, "] connected with session ID ", id)) - coordinator.log_comms(util.c("API: pocket (", firmware_v, ") [:", r_port, "] connected with session ID ", id)) + local id = apisessions.establish_session(src_addr, firmware_v) + println(util.c("[API] pocket (", firmware_v, ") [@", src_addr, "] \xbb connected")) + coordinator.log_comms(util.c("API_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", id)) - _send_api_establish_ack(next_seq_id, r_port, { ESTABLISH_ACK.ALLOW }) - self.last_api_est_acks[r_port] = ESTABLISH_ACK.ALLOW + _send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.ALLOW) else - log.debug(util.c("illegal establish packet for device ", dev_type, " on API listening channel")) - _send_api_establish_ack(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) + log.debug(util.c("API_ESTABLISH: illegal establish packet for device ", dev_type, " on pocket channel")) + _send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.DENY) end else log.debug("invalid establish packet (on API listening channel)") - _send_api_establish_ack(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) + _send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.DENY) end else -- any other packet should be session related, discard it - log.debug(util.c(r_port, "->", l_port, ": discarding SCADA_MGMT packet without a known session")) + log.debug(util.c("discarding pocket SCADA_MGMT packet without a known session from computer ", src_addr)) end else - log.debug("illegal packet type " .. protocol .. " on api listening channel", true) + log.debug("illegal packet type " .. protocol .. " on pocket channel", true) end - elseif l_port == sv_listen then + elseif r_chan == svr_channel then -- check sequence number if self.sv_r_seq_num == nil then self.sv_r_seq_num = packet.scada_frame.seq_num() elseif self.connected and ((self.sv_r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order: last = " .. self.sv_r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return + elseif self.sv_linked and src_addr ~= self.sv_addr then + log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") + return else self.sv_r_seq_num = packet.scada_frame.seq_num() end @@ -660,6 +665,7 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range -- init io controller iocontrol.init(conf, public) + self.sv_addr = src_addr self.sv_linked = true self.sv_config_err = false else @@ -705,10 +711,10 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range local trip_time = util.time() - timestamp if trip_time > 750 then - log.warning("coord KEEP_ALIVE trip time > 750ms (" .. trip_time .. "ms)") + log.warning("coordinator KEEP_ALIVE trip time > 750ms (" .. trip_time .. "ms)") end - -- log.debug("coord RTT = " .. trip_time .. "ms") + -- log.debug("coordinator RTT = " .. trip_time .. "ms") iocontrol.get_db().facility.ps.publish("sv_ping", trip_time) @@ -719,7 +725,9 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range elseif packet.type == SCADA_MGMT_TYPE.CLOSE then -- handle session close sv_watchdog.cancel() + self.sv_addr = comms.BROADCAST self.sv_linked = false + self.sv_r_seq_num = nil println_ts("server connection closed by remote host") log.info("server connection closed by remote host") else @@ -732,7 +740,7 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, range log.debug("illegal packet type " .. protocol .. " on supervisor listening channel", true) end else - log.debug("received packet on unconfigured channel " .. l_port, true) + log.debug("received packet for unknown channel " .. r_chan, true) end end end diff --git a/coordinator/session/apisessions.lua b/coordinator/session/apisessions.lua index c6d5a20..17988f5 100644 --- a/coordinator/session/apisessions.lua +++ b/coordinator/session/apisessions.lua @@ -5,7 +5,7 @@ local util = require("scada-common.util") local config = require("coordinator.config") -local api = require("coordinator.session.api") +local pocket = require("coordinator.session.pocket") local apisessions = {} @@ -18,7 +18,7 @@ local self = { -- PRIVATE FUNCTIONS -- -- handle a session output queue ----@param session api_session_struct +---@param session pkt_session_struct local function _api_handle_outq(session) -- record handler start time local handle_start = util.time() @@ -31,7 +31,7 @@ local function _api_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(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message.raw_sendable()) elseif msg.qtype == mqueue.TYPE.COMMAND then -- handle instruction/notification elseif msg.qtype == mqueue.TYPE.DATA then @@ -41,15 +41,15 @@ local function _api_handle_outq(session) -- max 100ms spent processing queue if util.time() - handle_start > 100 then - log.warning("API out queue handler exceeded 100ms queue process limit") - log.warning(util.c("offending session: port ", session.r_port)) + log.warning("[API] out queue handler exceeded 100ms queue process limit") + log.warning(util.c("[API] offending session: ", session)) break end end end -- cleanly close a session ----@param session api_session_struct +---@param session pkt_session_struct local function _shutdown(session) session.open = false session.instance.close() @@ -58,11 +58,11 @@ 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.modem.transmit(session.r_port, session.l_port, msg.message.raw_sendable()) + self.modem.transmit(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message.raw_sendable()) end end - log.debug(util.c("closed API session ", session.instance.get_id(), " on remote port ", session.r_port)) + log.debug(util.c("[API] closed session ", session)) end -- PUBLIC FUNCTIONS -- @@ -81,54 +81,60 @@ end -- find a session by remote port ---@nodiscard ----@param port integer ----@return api_session_struct|nil -function apisessions.find_session(port) +---@param source_addr integer +---@return pkt_session_struct|nil +function apisessions.find_session(source_addr) for i = 1, #self.sessions do - if self.sessions[i].r_port == port then return self.sessions[i] end + if self.sessions[i].s_addr == source_addr then return self.sessions[i] end end return nil end -- establish a new API session ---@nodiscard ----@param local_port integer ----@param remote_port integer +---@param source_addr integer ---@param version string ---@return integer session_id -function apisessions.establish_session(local_port, remote_port, version) - ---@class api_session_struct - local api_s = { +function apisessions.establish_session(source_addr, version) + ---@class pkt_session_struct + local pkt_s = { open = true, version = version, - l_port = local_port, - r_port = remote_port, + s_addr = source_addr, in_queue = mqueue.new(), out_queue = mqueue.new(), - instance = nil ---@type api_session + instance = nil ---@type pkt_session } - api_s.instance = api.new_session(self.next_id, api_s.in_queue, api_s.out_queue, config.API_TIMEOUT) - table.insert(self.sessions, api_s) + local id = self.next_id - log.debug(util.c("established new API session to ", remote_port, " with ID ", self.next_id)) + pkt_s.instance = pocket.new_session(id, source_addr, pkt_s.in_queue, pkt_s.out_queue, config.API_TIMEOUT) + table.insert(self.sessions, pkt_s) - self.next_id = self.next_id + 1 + local mt = { + ---@param s pkt_session_struct + __tostring = function (s) return util.c("PKT [", id, "] (@", s.s_addr, ")") end + } + + setmetatable(pkt_s, mt) + + log.debug(util.c("[API] established new session: ", pkt_s)) + + self.next_id = id + 1 -- success - return api_s.instance.get_id() + return pkt_s.instance.get_id() end -- attempt to identify which session's watchdog timer fired ---@param timer_event number function apisessions.check_all_watchdogs(timer_event) for i = 1, #self.sessions do - local session = self.sessions[i] ---@type api_session_struct + local session = self.sessions[i] ---@type pkt_session_struct if session.open then local triggered = session.instance.check_wd(timer_event) if triggered then - log.debug(util.c("watchdog closing API session ", session.instance.get_id(), - " on remote port ", session.r_port, "...")) + log.debug(util.c("[API] watchdog closing session ", session, "...")) _shutdown(session) end end @@ -138,7 +144,7 @@ end -- iterate all the API sessions function apisessions.iterate_all() for i = 1, #self.sessions do - local session = self.sessions[i] ---@type api_session_struct + local session = self.sessions[i] ---@type pkt_session_struct if session.open and session.instance.iterate() then _api_handle_outq(session) @@ -152,10 +158,9 @@ end function apisessions.free_all_closed() local f = function (session) return session.open end - ---@param session api_session_struct + ---@param session pkt_session_struct local on_delete = function (session) - log.debug(util.c("free'ing closed API session ", session.instance.get_id(), - " on remote port ", session.r_port)) + log.debug(util.c("[API] free'ing closed session ", session)) end util.filter_table(self.sessions, f, on_delete) @@ -164,7 +169,7 @@ end -- close all open connections function apisessions.close_all() for i = 1, #self.sessions do - local session = self.sessions[i] ---@type api_session_struct + local session = self.sessions[i] ---@type pkt_session_struct if session.open then _shutdown(session) end end diff --git a/coordinator/session/api.lua b/coordinator/session/pocket.lua similarity index 89% rename from coordinator/session/api.lua rename to coordinator/session/pocket.lua index 4ba7383..ddabdda 100644 --- a/coordinator/session/api.lua +++ b/coordinator/session/pocket.lua @@ -3,7 +3,7 @@ local log = require("scada-common.log") local mqueue = require("scada-common.mqueue") local util = require("scada-common.util") -local api = {} +local pocket = {} local PROTOCOL = comms.PROTOCOL -- local CAPI_TYPE = comms.CAPI_TYPE @@ -21,8 +21,8 @@ local API_S_CMDS = { local API_S_DATA = { } -api.API_S_CMDS = API_S_CMDS -api.API_S_DATA = API_S_DATA +pocket.API_S_CMDS = API_S_CMDS +pocket.API_S_DATA = API_S_DATA local PERIODICS = { KEEP_ALIVE = 2000 @@ -31,11 +31,12 @@ local PERIODICS = { -- pocket API 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 -function api.new_session(id, in_queue, out_queue, timeout) - local log_header = "api_session(" .. id .. "): " +function pocket.new_session(id, s_addr, in_queue, out_queue, timeout) + local log_header = "pkt_session(" .. id .. "): " local self = { -- connection properties @@ -61,10 +62,10 @@ function api.new_session(id, in_queue, out_queue, timeout) } } - ---@class api_session + ---@class pkt_session local public = {} - -- mark this API session as closed, stop watchdog + -- mark this pocket session as closed, stop watchdog local function _close() self.conn_watchdog.cancel() self.connected = false @@ -92,7 +93,7 @@ function api.new_session(id, in_queue, out_queue, timeout) 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 @@ -134,11 +135,11 @@ function api.new_session(id, in_queue, out_queue, timeout) self.last_rtt = srv_now - srv_start if self.last_rtt > 750 then - log.warning(log_header .. "API KEEP_ALIVE round trip time > 750ms (" .. self.last_rtt .. "ms)") + log.warning(log_header .. "PKT KEEP_ALIVE round trip time > 750ms (" .. self.last_rtt .. "ms)") end - -- log.debug(log_header .. "API RTT = " .. self.last_rtt .. "ms") - -- log.debug(log_header .. "API TT = " .. (srv_now - api_send) .. "ms") + -- log.debug(log_header .. "PKT RTT = " .. self.last_rtt .. "ms") + -- log.debug(log_header .. "PKT TT = " .. (srv_now - api_send) .. "ms") else log.debug(log_header .. "SCADA keep alive packet length mismatch") end @@ -171,7 +172,7 @@ function api.new_session(id, in_queue, out_queue, timeout) function public.close() _close() _send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) - println("connection to API session " .. id .. " closed by server") + println("connection to pocket session " .. id .. " closed by server") log.info(log_header .. "session closed by server") end @@ -210,7 +211,7 @@ function api.new_session(id, in_queue, out_queue, timeout) -- exit if connection was closed if not self.connected then - println("connection to API session " .. id .. " closed by remote host") + println("connection to pocket session " .. id .. " closed by remote host") log.info(log_header .. "session closed by remote host") return self.connected end @@ -246,4 +247,4 @@ function api.new_session(id, in_queue, out_queue, timeout) return public end -return api +return pocket diff --git a/coordinator/startup.lua b/coordinator/startup.lua index aacfe1b..5d4fec8 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -20,7 +20,7 @@ local sounder = require("coordinator.sounder") local apisessions = require("coordinator.session.apisessions") -local COORDINATOR_VERSION = "v0.15.8" +local COORDINATOR_VERSION = "v0.16.0" local println = util.println local println_ts = util.println_ts @@ -37,9 +37,9 @@ local log_comms_connecting = coordinator.log_comms_connecting local cfv = util.new_validator() -cfv.assert_channel(config.SCADA_SV_PORT) -cfv.assert_channel(config.SCADA_SV_CTL_LISTEN) -cfv.assert_channel(config.SCADA_API_LISTEN) +cfv.assert_channel(config.SVR_CHANNEL) +cfv.assert_channel(config.CRD_CHANNEL) +cfv.assert_channel(config.PKT_CHANNEL) cfv.assert_type_int(config.TRUSTED_RANGE) cfv.assert_type_num(config.SV_TIMEOUT) cfv.assert_min(config.SV_TIMEOUT, 2) @@ -148,8 +148,8 @@ local function main() log.debug("startup> conn watchdog created") -- start comms, open all channels - local coord_comms = coordinator.comms(COORDINATOR_VERSION, modem, config.SCADA_SV_PORT, config.SCADA_SV_CTL_LISTEN, - config.SCADA_API_LISTEN, config.TRUSTED_RANGE, conn_watchdog) + local coord_comms = coordinator.comms(COORDINATOR_VERSION, modem, config.CRD_CHANNEL, config.SVR_CHANNEL, + config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog) log.debug("startup> comms init") log_comms("comms initialized") @@ -163,7 +163,7 @@ local function main() -- attempt to connect to the supervisor or exit local function init_connect_sv() - local tick_waiting, task_done = log_comms_connecting("attempting to connect to configured supervisor on channel " .. config.SCADA_SV_PORT) + local tick_waiting, task_done = log_comms_connecting("attempting to connect to configured supervisor on channel " .. config.SVR_CHANNEL) -- attempt to establish a connection with the supervisory computer if not coord_comms.sv_connect(60, tick_waiting, task_done) then From 55dab6d675188c6228f14f222eb638c8a50636bf Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 6 Jun 2023 19:41:55 -0400 Subject: [PATCH 08/18] don't print brackets in util.strval if metatable __tostring is present --- scada-common/util.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scada-common/util.lua b/scada-common/util.lua index e13d6bb..99f62a6 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -65,7 +65,8 @@ end ---@return string function util.strval(val) local t = type(val) - if t == "table" or t == "function" then + -- this depends on Lua short-circuiting the or check for metatables (note: metatables won't have metatables) + if (t == "table" and (getmetatable(val) == nil or getmetatable(val).__tostring == nil)) or t == "function" then return "[" .. tostring(val) .. "]" else return tostring(val) From e16b0d237eae929c5283a1e5eeb30531cce9bdb3 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 6 Jun 2023 19:45:04 -0400 Subject: [PATCH 09/18] #225 fixed svsessions __tostring for sessions, refactored s_addr to src_addr --- supervisor/session/svsessions.lua | 8 ++--- supervisor/startup.lua | 2 +- supervisor/supervisor.lua | 52 +++++++++++++++---------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index 73a4e4f..a311ab9 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -311,7 +311,7 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version) local mt = { ---@param s plc_session_struct - __to_string = function (s) return util.c("PLC [", s.instance.get_id(), "] for reactor #", s.reactor, + __tostring = function (s) return util.c("PLC [", s.instance.get_id(), "] for reactor #", s.reactor, " (@", s.s_addr, ")") end } @@ -357,7 +357,7 @@ function svsessions.establish_rtu_session(source_addr, advertisement, version) local mt = { ---@param s rtu_session_struct - __to_string = function (s) return util.c("RTU [", s.instance.get_id(), "] (@", s.s_addr, ")") end + __tostring = function (s) return util.c("RTU [", s.instance.get_id(), "] (@", s.s_addr, ")") end } setmetatable(rtu_s, mt) @@ -398,7 +398,7 @@ function svsessions.establish_crd_session(source_addr, version) local mt = { ---@param s crd_session_struct - __to_string = function (s) return util.c("CRD [", s.instance.get_id(), "] (@", s.s_addr, ")") end + __tostring = function (s) return util.c("CRD [", s.instance.get_id(), "] (@", s.s_addr, ")") end } setmetatable(crd_s, mt) @@ -441,7 +441,7 @@ function svsessions.establish_pdg_session(source_addr, version) local mt = { ---@param s pdg_session_struct - __to_string = function (s) return util.c("PDG [", s.instance.get_id(), "] (@", s.s_addr, ")") end + __tostring = function (s) return util.c("PDG [", s.instance.get_id(), "] (@", s.s_addr, ")") end } setmetatable(pdg_s, mt) diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 9fdb517..4f35394 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -20,7 +20,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.17.1" +local SUPERVISOR_VERSION = "v0.17.2" local println = util.println local println_ts = util.println_ts diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 42be454..e51fdd6 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -135,14 +135,14 @@ function supervisor.comms(_version, modem, fp_ok) if packet ~= nil then local l_chan = packet.scada_frame.local_channel() local r_chan = packet.scada_frame.remote_channel() - local s_addr = packet.scada_frame.src_addr() + local src_addr = packet.scada_frame.src_addr() local protocol = packet.scada_frame.protocol() if l_chan ~= svr_channel then log.debug("received packet on unconfigured channel " .. l_chan, true) elseif r_chan == plc_channel then -- look for an associated session - local session = svsessions.find_plc_session(s_addr) + local session = svsessions.find_plc_session(src_addr) if protocol == PROTOCOL.RPLC then ---@cast packet rplc_frame @@ -163,7 +163,7 @@ function supervisor.comms(_version, modem, fp_ok) 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] + local last_ack = self.last_est_acks[src_addr] -- validate packet and continue if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then @@ -181,7 +181,7 @@ function supervisor.comms(_version, modem, fp_ok) -- 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(s_addr, reactor_id, firmware_v) + local plc_id = svsessions.establish_plc_session(src_addr, reactor_id, firmware_v) if plc_id == false then -- reactor already has a PLC assigned @@ -192,8 +192,8 @@ function supervisor.comms(_version, modem, fp_ok) _send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION) else -- got an ID; assigned to a reactor successfully - 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)) + 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) end else @@ -210,14 +210,14 @@ function supervisor.comms(_version, modem, fp_ok) end else -- any other packet should be session related, discard it - log.debug(util.c("discarding PLC SCADA_MGMT packet without a known session from computer ", s_addr)) + log.debug(util.c("discarding PLC SCADA_MGMT packet without a known session from computer ", src_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 - local session = svsessions.find_rtu_session(s_addr) + local session = svsessions.find_rtu_session(src_addr) if protocol == PROTOCOL.MODBUS_TCP then ---@cast packet modbus_frame @@ -237,7 +237,7 @@ function supervisor.comms(_version, modem, fp_ok) 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] + local last_ack = self.last_est_acks[src_addr] -- validate packet and continue if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then @@ -255,10 +255,10 @@ function supervisor.comms(_version, modem, fp_ok) 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(s_addr, rtu_advert, firmware_v) + local s_id = svsessions.establish_rtu_session(src_addr, rtu_advert, firmware_v) - 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)) + 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) else log.debug("RTU_ESTABLISH: packet length mismatch") @@ -274,14 +274,14 @@ function supervisor.comms(_version, modem, fp_ok) end else -- any other packet should be session related, discard it - log.debug(util.c("discarding RTU SCADA_MGMT packet without a known session from computer ", s_addr)) + log.debug(util.c("discarding RTU SCADA_MGMT packet without a known session from computer ", src_addr)) end else 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_crd_session(s_addr) + local session = svsessions.find_crd_session(src_addr) if protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame @@ -291,7 +291,7 @@ function supervisor.comms(_version, modem, fp_ok) 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] + local last_ack = self.last_est_acks[src_addr] -- validate packet and continue if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then @@ -307,7 +307,7 @@ function supervisor.comms(_version, modem, fp_ok) _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_crd_session(s_addr, firmware_v) + local s_id = svsessions.establish_crd_session(src_addr, firmware_v) if s_id ~= false then local cfg = { num_reactors } @@ -316,13 +316,13 @@ function supervisor.comms(_version, modem, fp_ok) table.insert(cfg, cooling_conf[i].TURBINES) end - println(util.c("CRD (", firmware_v, ") [@", s_addr, "] \xbb connected")) - log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", s_addr, "] connected with session ID ", s_id)) + 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, cfg) else if last_ack ~= ESTABLISH_ACK.COLLISION then - log.info("CRD_ESTABLISH: denied new coordinator [@" .. s_addr .. "] due to already being connected to another coordinator") + 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) @@ -337,7 +337,7 @@ function supervisor.comms(_version, modem, fp_ok) end else -- any other packet should be session related, discard it - log.debug(util.c("discarding coordinator SCADA_MGMT packet without a known session from computer ", s_addr)) + log.debug(util.c("discarding coordinator SCADA_MGMT packet without a known session from computer ", src_addr)) end elseif protocol == PROTOCOL.SCADA_CRDN then ---@cast packet crdn_frame @@ -347,14 +347,14 @@ function supervisor.comms(_version, modem, fp_ok) session.in_queue.push_packet(packet) else -- any other packet should be session related, discard it - log.debug(util.c("discarding coordinator SCADA_CRDN packet without a known session from computer ", s_addr)) + log.debug(util.c("discarding coordinator SCADA_CRDN packet without a known session from computer ", src_addr)) end else 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) + local session = svsessions.find_pdg_session(src_addr) if protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame @@ -364,7 +364,7 @@ function supervisor.comms(_version, modem, fp_ok) 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] + local last_ack = self.last_est_acks[src_addr] -- validate packet and continue if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then @@ -380,7 +380,7 @@ function supervisor.comms(_version, modem, fp_ok) _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) + local s_id = svsessions.establish_pdg_session(src_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)) @@ -396,7 +396,7 @@ function supervisor.comms(_version, modem, fp_ok) 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)) + log.debug(util.c("discarding pocket SCADA_MGMT packet without a known session from computer ", src_addr)) end elseif protocol == PROTOCOL.SCADA_CRDN then ---@cast packet crdn_frame @@ -406,7 +406,7 @@ function supervisor.comms(_version, modem, fp_ok) 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)) + log.debug(util.c("discarding pocket SCADA_CRDN packet without a known session from computer ", src_addr)) end else log.debug(util.c("illegal packet type ", protocol, " on pocket channel")) From 671f8b55bcc8127efc34cc422e422b4110c8758d Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 6 Jun 2023 19:49:28 -0400 Subject: [PATCH 10/18] updated supervisor front panel RTT coloring limits --- supervisor/databus.lua | 20 ++++++++++++-------- supervisor/startup.lua | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/supervisor/databus.lua b/supervisor/databus.lua index 664b3fe..96e0e80 100644 --- a/supervisor/databus.lua +++ b/supervisor/databus.lua @@ -6,6 +6,10 @@ local psil = require("scada-common.psil") local pgi = require("supervisor.panel.pgi") +-- nominal RTT is ping (0ms to 10ms usually) + 150ms for SV main loop tick +local WARN_RTT = 300 -- 2x as long as expected w/ 0 ping +local HIGH_RTT = 500 -- 3.33x as long as expected w/ 0 ping + local databus = {} -- databus PSIL @@ -54,9 +58,9 @@ end function databus.tx_plc_rtt(reactor_id, rtt) databus.ps.publish("plc_" .. reactor_id .. "_rtt", rtt) - if rtt > 700 then + if rtt > HIGH_RTT then databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.red) - elseif rtt > 300 then + elseif rtt > WARN_RTT then databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.yellow_hc) else databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.green) @@ -85,9 +89,9 @@ end function databus.tx_rtu_rtt(session_id, rtt) databus.ps.publish("rtu_" .. session_id .. "_rtt", rtt) - if rtt > 700 then + if rtt > HIGH_RTT then databus.ps.publish("rtu_" .. session_id .. "_rtt_color", colors.red) - elseif rtt > 300 then + elseif rtt > WARN_RTT then databus.ps.publish("rtu_" .. session_id .. "_rtt_color", colors.yellow_hc) else databus.ps.publish("rtu_" .. session_id .. "_rtt_color", colors.green) @@ -124,9 +128,9 @@ end function databus.tx_crd_rtt(rtt) databus.ps.publish("crd_rtt", rtt) - if rtt > 700 then + if rtt > HIGH_RTT then databus.ps.publish("crd_rtt_color", colors.red) - elseif rtt > 300 then + elseif rtt > WARN_RTT then databus.ps.publish("crd_rtt_color", colors.yellow_hc) else databus.ps.publish("crd_rtt_color", colors.green) @@ -155,9 +159,9 @@ end function databus.tx_pdg_rtt(session_id, rtt) databus.ps.publish("pdg_" .. session_id .. "_rtt", rtt) - if rtt > 700 then + if rtt > HIGH_RTT then databus.ps.publish("pdg_" .. session_id .. "_rtt_color", colors.red) - elseif rtt > 300 then + elseif rtt > WARN_RTT then databus.ps.publish("pdg_" .. session_id .. "_rtt_color", colors.yellow_hc) else databus.ps.publish("pdg_" .. session_id .. "_rtt_color", colors.green) diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 4f35394..bb48cc7 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -20,7 +20,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.17.2" +local SUPERVISOR_VERSION = "v0.17.3" local println = util.println local println_ts = util.println_ts From 0a6fd35f936de04314f15e887727831d243a2c9e Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 6 Jun 2023 21:56:17 -0400 Subject: [PATCH 11/18] supervisor front panel computer IDs cleanup --- supervisor/databus.lua | 7 ++++--- supervisor/panel/components/pdg_entry.lua | 4 +--- supervisor/panel/components/rtu_entry.lua | 4 +--- supervisor/startup.lua | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/supervisor/databus.lua b/supervisor/databus.lua index 96e0e80..00185c7 100644 --- a/supervisor/databus.lua +++ b/supervisor/databus.lua @@ -3,6 +3,7 @@ -- local psil = require("scada-common.psil") +local util = require("scada-common.util") local pgi = require("supervisor.panel.pgi") @@ -39,7 +40,7 @@ end function databus.tx_plc_connected(reactor_id, fw, s_addr) databus.ps.publish("plc_" .. reactor_id .. "_fw", fw) databus.ps.publish("plc_" .. reactor_id .. "_conn", true) - databus.ps.publish("plc_" .. reactor_id .. "_addr", tostring(s_addr)) + databus.ps.publish("plc_" .. reactor_id .. "_addr", util.sprintf("@% 4d", s_addr)) end -- transmit PLC disconnected @@ -73,7 +74,7 @@ end ---@param s_addr integer RTU computer ID function databus.tx_rtu_connected(session_id, fw, s_addr) databus.ps.publish("rtu_" .. session_id .. "_fw", fw) - databus.ps.publish("rtu_" .. session_id .. "_addr", tostring(s_addr)) + databus.ps.publish("rtu_" .. session_id .. "_addr", util.sprintf("@ C% 3d", s_addr)) pgi.create_rtu_entry(session_id) end @@ -143,7 +144,7 @@ end ---@param s_addr integer PDG computer ID function databus.tx_pdg_connected(session_id, fw, s_addr) databus.ps.publish("pdg_" .. session_id .. "_fw", fw) - databus.ps.publish("pdg_" .. session_id .. "_addr", tostring(s_addr)) + databus.ps.publish("pdg_" .. session_id .. "_addr", util.sprintf("@ C% 3d", s_addr)) pgi.create_pdg_entry(session_id) end diff --git a/supervisor/panel/components/pdg_entry.lua b/supervisor/panel/components/pdg_entry.lua index ea17cfd..2c85d59 100644 --- a/supervisor/panel/components/pdg_entry.lua +++ b/supervisor/panel/components/pdg_entry.lua @@ -2,8 +2,6 @@ -- Pocket Diagnostics Connection Entry -- -local util = require("scada-common.util") - local databus = require("supervisor.databus") local core = require("graphics.core") @@ -30,7 +28,7 @@ local function init(parent, id) TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} local pdg_addr = TextBox{parent=entry,x=1,y=2,text="C #?",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - pdg_addr.register(databus.ps, ps_prefix .. "addr", function (addr) pdg_addr.set_value(util.sprintf("C #%d", addr)) end) + pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value) TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1} local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=cpair(colors.lightGray,colors.white)} diff --git a/supervisor/panel/components/rtu_entry.lua b/supervisor/panel/components/rtu_entry.lua index e51b0bf..94900ab 100644 --- a/supervisor/panel/components/rtu_entry.lua +++ b/supervisor/panel/components/rtu_entry.lua @@ -2,8 +2,6 @@ -- RTU Connection Entry -- -local util = require("scada-common.util") - local databus = require("supervisor.databus") local core = require("graphics.core") @@ -30,7 +28,7 @@ local function init(parent, id) TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} local rtu_addr = TextBox{parent=entry,x=1,y=2,text="C #?",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - rtu_addr.register(databus.ps, ps_prefix .. "addr", function (addr) rtu_addr.set_value(util.sprintf("C #%d", addr)) end) + rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value) TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7,height=1} local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=cpair(colors.gray,colors.white)} diff --git a/supervisor/startup.lua b/supervisor/startup.lua index bb48cc7..2919f0f 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -20,7 +20,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.17.3" +local SUPERVISOR_VERSION = "v0.17.4" local println = util.println local println_ts = util.println_ts From cf881548d7994fa4732c5e71d381fa46e6600faa Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 7 Jun 2023 12:25:50 -0400 Subject: [PATCH 12/18] config file comments --- pocket/config.lua | 6 +++--- reactor-plc/config.lua | 4 ++-- rtu/config.lua | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pocket/config.lua b/pocket/config.lua index 64705ab..0c35b59 100644 --- a/pocket/config.lua +++ b/pocket/config.lua @@ -1,10 +1,10 @@ local config = {} --- supervisor access channel +-- supervisor comms channel config.SVR_CHANNEL = 16240 --- coordinator access channel +-- coordinator comms channel config.CRD_CHANNEL = 16243 --- pocket communication channel +-- pocket comms channel config.PKT_CHANNEL = 16244 -- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 diff --git a/reactor-plc/config.lua b/reactor-plc/config.lua index 6845ec1..3462b2c 100644 --- a/reactor-plc/config.lua +++ b/reactor-plc/config.lua @@ -9,9 +9,9 @@ config.REACTOR_ID = 1 -- when emergency coolant is needed due to low coolant -- config.EMERGENCY_COOL = { side = "right", color = nil } --- supervisor access channel +-- supervisor comms channel config.SVR_CHANNEL = 16240 --- PLC communication channel +-- PLC comms channel config.PLC_CHANNEL = 16241 -- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 diff --git a/rtu/config.lua b/rtu/config.lua index c4eae91..2279759 100644 --- a/rtu/config.lua +++ b/rtu/config.lua @@ -2,9 +2,9 @@ local rsio = require("scada-common.rsio") local config = {} --- supervisor access channel +-- supervisor comms channel config.SVR_CHANNEL = 16240 --- RTU/MODBUS communication channel +-- RTU/MODBUS comms channel config.RTU_CHANNEL = 16242 -- max trusted modem message distance (0 to disable check) config.TRUSTED_RANGE = 0 From f4e7137eb36a901d2195631b086f1592bfce59a9 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 7 Jun 2023 12:27:13 -0400 Subject: [PATCH 13/18] #225 pocket verify packets are from linked computer --- pocket/pocket.lua | 12 ++++++++---- pocket/startup.lua | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 7f770a1..007f01d 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -284,7 +284,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("coordinator SCADA_MGMT establish packet length mismatch") end - elseif self.api.linked then + elseif self.api.linked and src_addr == self.api.addr then if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then -- keep alive request received, echo back if packet.length == 1 then @@ -311,8 +311,10 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from coordinator") end - else + elseif not self.api.linked then log.debug("discarding coordinator non-link SCADA_MGMT packet before linked") + else + log.debug("discarding SCADA_MGMT from different coordinator (src_addr " .. src_addr .. " ≠ " .. self.api.addr .. ")") end else log.debug("illegal packet type " .. protocol .. " from coordinator", true) @@ -373,7 +375,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("supervisor SCADA_MGMT establish packet length mismatch") end - elseif self.sv.linked then + elseif self.sv.linked and (src_addr == self.sv.addr) then if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then -- keep alive request received, echo back if packet.length == 1 then @@ -400,8 +402,10 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from supervisor") end - else + elseif not self.sv.linked then log.debug("discarding supervisor non-link SCADA_MGMT packet before linked") + else + log.debug("discarding SCADA_MGMT from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv.addr .. ")") end else log.debug("illegal packet type " .. protocol .. " from supervisor", true) diff --git a/pocket/startup.lua b/pocket/startup.lua index 4455105..c1dc37b 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -17,7 +17,7 @@ local coreio = require("pocket.coreio") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "alpha-v0.4.1" +local POCKET_VERSION = "alpha-v0.4.2" local println = util.println local println_ts = util.println_ts From 5ba06dcdaf5235d4619c46c399ee9520b3e82a14 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 7 Jun 2023 12:35:17 -0400 Subject: [PATCH 14/18] #225 log message fixes and sv addr checks for RTU --- pocket/pocket.lua | 4 ++-- pocket/startup.lua | 2 +- reactor-plc/plc.lua | 6 ++++-- reactor-plc/startup.lua | 2 +- rtu/rtu.lua | 12 ++++++++---- rtu/startup.lua | 2 +- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 007f01d..6f876ea 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -314,7 +314,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran elseif not self.api.linked then log.debug("discarding coordinator non-link SCADA_MGMT packet before linked") else - log.debug("discarding SCADA_MGMT from different coordinator (src_addr " .. src_addr .. " ≠ " .. self.api.addr .. ")") + log.debug("discarding non-link SCADA_MGMT packet from different coordinator (src_addr " .. src_addr .. " ≠ " .. self.api.addr .. ")") end else log.debug("illegal packet type " .. protocol .. " from coordinator", true) @@ -405,7 +405,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran elseif not self.sv.linked then log.debug("discarding supervisor non-link SCADA_MGMT packet before linked") else - log.debug("discarding SCADA_MGMT from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv.addr .. ")") + log.debug("discarding non-link SCADA_MGMT from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv.addr .. ")") end else log.debug("illegal packet type " .. protocol .. " from supervisor", true) diff --git a/pocket/startup.lua b/pocket/startup.lua index c1dc37b..14317fd 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -17,7 +17,7 @@ local coreio = require("pocket.coreio") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "alpha-v0.4.2" +local POCKET_VERSION = "alpha-v0.4.3" local println = util.println local println_ts = util.println_ts diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index efe6aaa..4cd34ad 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -938,7 +938,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, elseif not self.linked then log.debug("discarding RPLC packet before linked") else - log.debug("discarding RPLC from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. "sv_addr)") + log.debug("discarding RPLC packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") end elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame @@ -1057,8 +1057,10 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, else log.debug("SCADA_MGMT establish packet length mismatch") end - else + elseif not self.linked then log.debug("discarding non-link SCADA_MGMT packet before linked") + else + log.debug("discarding non-link SCADA_MGMT packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") end else -- should be unreachable assuming packet is from parse_packet() diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 5d0234f..e04238d 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.4.3" +local R_PLC_VERSION = "v1.4.4" local println = util.println local println_ts = util.println_ts diff --git a/rtu/rtu.lua b/rtu/rtu.lua index a1cc9dc..c97d918 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -354,7 +354,7 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- handle packet if protocol == PROTOCOL.MODBUS_TCP then ---@cast packet modbus_frame - if rtu_state.linked then + if rtu_state.linked and (src_addr == self.sv_addr) then local return_code ---@type boolean local reply ---@type modbus_packet @@ -394,8 +394,10 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo end public.send_modbus(reply) - else + elseif not rtu_state.linked then log.debug("discarding MODBUS packet before linked") + else + log.debug("discarding MODBUS packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") end elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame @@ -434,7 +436,7 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo else log.debug("SCADA_MGMT establish packet length mismatch") end - elseif rtu_state.linked then + elseif rtu_state.linked and (src_addr == self.sv_addr) then if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then -- keep alive request received, echo back if packet.length == 1 and type(packet.data[1]) == "number" then @@ -464,8 +466,10 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- not supported log.debug("received unsupported SCADA_MGMT message type " .. packet.type) end - else + elseif not rtu_state.linked then log.debug("discarding non-link SCADA_MGMT packet before linked") + else + log.debug("discarding non-link SCADA_MGMT packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") end else -- should be unreachable assuming packet is from parse_packet() diff --git a/rtu/startup.lua b/rtu/startup.lua index 58970f7..0bd3c35 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "v1.3.3" +local RTU_VERSION = "v1.3.4" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE From 15b071378ce89629d1f56edfeb93e035ab8cba6b Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 7 Jun 2023 12:48:43 -0400 Subject: [PATCH 15/18] #225 removed redundant checks on remote address, added clarity to log messages --- pocket/pocket.lua | 22 ++++++++++------------ pocket/startup.lua | 2 +- reactor-plc/plc.lua | 17 +++++++---------- reactor-plc/startup.lua | 2 +- rtu/rtu.lua | 17 +++++++---------- rtu/startup.lua | 2 +- 6 files changed, 27 insertions(+), 35 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 6f876ea..65ade67 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -234,8 +234,9 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran elseif self.connected and ((self.api.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order (API): last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return - elseif self.api.linked and self.api.addr ~= src_addr then - log.debug("received API packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") + elseif self.api.linked and (self.api.addr ~= src_addr) then + log.debug("received packet from unknown computer " .. src_addr .. " while linked (API expected " .. self.api.addr .. + "); channel in use by another system?") return else self.api.r_seq_num = packet.scada_frame.seq_num() @@ -284,7 +285,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("coordinator SCADA_MGMT establish packet length mismatch") end - elseif self.api.linked and src_addr == self.api.addr then + elseif self.api.linked then if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then -- keep alive request received, echo back if packet.length == 1 then @@ -311,10 +312,8 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from coordinator") end - elseif not self.api.linked then - log.debug("discarding coordinator non-link SCADA_MGMT packet before linked") else - log.debug("discarding non-link SCADA_MGMT packet from different coordinator (src_addr " .. src_addr .. " ≠ " .. self.api.addr .. ")") + log.debug("discarding coordinator non-link SCADA_MGMT packet before linked") end else log.debug("illegal packet type " .. protocol .. " from coordinator", true) @@ -326,8 +325,9 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran elseif self.connected and ((self.sv.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order (SVR): last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return - elseif self.sv.linked and self.sv.addr ~= src_addr then - log.debug("received SVR packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") + elseif self.sv.linked and (self.sv.addr ~= src_addr) then + log.debug("received packet from unknown computer " .. src_addr .. " while linked (SVR expected " .. self.sv.addr .. + "); channel in use by another system?") return else self.sv.r_seq_num = packet.scada_frame.seq_num() @@ -375,7 +375,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("supervisor SCADA_MGMT establish packet length mismatch") end - elseif self.sv.linked and (src_addr == self.sv.addr) then + elseif self.sv.linked then if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then -- keep alive request received, echo back if packet.length == 1 then @@ -402,10 +402,8 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran else log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from supervisor") end - elseif not self.sv.linked then - log.debug("discarding supervisor non-link SCADA_MGMT packet before linked") else - log.debug("discarding non-link SCADA_MGMT from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv.addr .. ")") + log.debug("discarding supervisor non-link SCADA_MGMT packet before linked") end else log.debug("illegal packet type " .. protocol .. " from supervisor", true) diff --git a/pocket/startup.lua b/pocket/startup.lua index 14317fd..0683af6 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -17,7 +17,7 @@ local coreio = require("pocket.coreio") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "alpha-v0.4.3" +local POCKET_VERSION = "alpha-v0.4.4" local println = util.println local println_ts = util.println_ts diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 4cd34ad..4000b00 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -790,8 +790,9 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, elseif self.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return - elseif self.linked and src_addr ~= self.sv_addr then - log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") + elseif self.linked and (src_addr ~= self.sv_addr) then + log.debug("received packet from unknown computer " .. src_addr .. " while linked (expected " .. self.sv_addr .. + "); channel in use by another system?") return else self.r_seq_num = packet.scada_frame.seq_num() @@ -804,7 +805,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, if protocol == PROTOCOL.RPLC then ---@cast packet rplc_frame -- if linked, only accept packets from configured supervisor - if self.linked and (self.sv_addr == src_addr) then + if self.linked then if packet.type == RPLC_TYPE.STATUS then -- request of full status, clear cache first self.status_cache = nil @@ -935,15 +936,13 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, else log.debug("received unknown RPLC packet type " .. packet.type) end - elseif not self.linked then - log.debug("discarding RPLC packet before linked") else - log.debug("discarding RPLC packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") + log.debug("discarding RPLC packet before linked") end elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame -- if linked, only accept packets from configured supervisor - if self.linked and (self.sv_addr == src_addr) then + if self.linked then if packet.type == SCADA_MGMT_TYPE.ESTABLISH then -- link request confirmation if (packet.length == 1) and (self.sv_addr == src_addr) then @@ -1057,10 +1056,8 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, else log.debug("SCADA_MGMT establish packet length mismatch") end - elseif not self.linked then - log.debug("discarding non-link SCADA_MGMT packet before linked") else - log.debug("discarding non-link SCADA_MGMT packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") + log.debug("discarding non-link SCADA_MGMT packet before linked") end else -- should be unreachable assuming packet is from parse_packet() diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index e04238d..227bd27 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.4.4" +local R_PLC_VERSION = "v1.4.5" local println = util.println local println_ts = util.println_ts diff --git a/rtu/rtu.lua b/rtu/rtu.lua index c97d918..831e231 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -341,8 +341,9 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo elseif rtu_state.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return - elseif rtu_state.linked and src_addr ~= self.sv_addr then - log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?") + elseif rtu_state.linked and (src_addr ~= self.sv_addr) then + log.debug("received packet from unknown computer " .. src_addr .. " while linked (expected " .. self.sv_addr .. + "); channel in use by another system?") return else self.r_seq_num = packet.scada_frame.seq_num() @@ -354,7 +355,7 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- handle packet if protocol == PROTOCOL.MODBUS_TCP then ---@cast packet modbus_frame - if rtu_state.linked and (src_addr == self.sv_addr) then + if rtu_state.linked then local return_code ---@type boolean local reply ---@type modbus_packet @@ -394,10 +395,8 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo end public.send_modbus(reply) - elseif not rtu_state.linked then - log.debug("discarding MODBUS packet before linked") else - log.debug("discarding MODBUS packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") + log.debug("discarding MODBUS packet before linked") end elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame @@ -436,7 +435,7 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo else log.debug("SCADA_MGMT establish packet length mismatch") end - elseif rtu_state.linked and (src_addr == self.sv_addr) then + elseif rtu_state.linked then if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then -- keep alive request received, echo back if packet.length == 1 and type(packet.data[1]) == "number" then @@ -466,10 +465,8 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- not supported log.debug("received unsupported SCADA_MGMT message type " .. packet.type) end - elseif not rtu_state.linked then - log.debug("discarding non-link SCADA_MGMT packet before linked") else - log.debug("discarding non-link SCADA_MGMT packet from different supervisor (src_addr " .. src_addr .. " ≠ " .. self.sv_addr .. ")") + log.debug("discarding non-link SCADA_MGMT packet before linked") end else -- should be unreachable assuming packet is from parse_packet() diff --git a/rtu/startup.lua b/rtu/startup.lua index 0bd3c35..05b131b 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "v1.3.4" +local RTU_VERSION = "v1.3.5" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE From f37f2f009fb3f833c604c11fb0cf043c97eb00c1 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 7 Jun 2023 14:17:10 -0400 Subject: [PATCH 16/18] comms only set nil max distance if requested max is exactly 0 --- scada-common/comms.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scada-common/comms.lua b/scada-common/comms.lua index 41528dc..c5e9fd2 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -9,7 +9,7 @@ local insert = table.insert ---@diagnostic disable-next-line: undefined-field local C_ID = os.getComputerID() ---@type integer computer ID -local max_distance = nil ---@type integer|nil maximum acceptable transmission distance +local max_distance = nil ---@type number|nil maximum acceptable transmission distance ---@class comms local comms = {} @@ -133,9 +133,9 @@ comms.BROADCAST = -1 -- configure the maximum allowable message receive distance
-- packets received with distances greater than this will be silently discarded ----@param distance integer max modem message distance (less than 1 disables the limit) +---@param distance integer max modem message distance (0 disables the limit) function comms.set_trusted_range(distance) - if distance < 1 then max_distance = nil else max_distance = distance end + if distance == 0 then max_distance = nil else max_distance = distance end end -- generic SCADA packet object From f1b1f0b75afeb1ba0f02118aa8935b88aeea3bc1 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 7 Jun 2023 14:18:13 -0400 Subject: [PATCH 17/18] updated supervisor front panel default computer ID place holders and fixed PDG establish using channel in messages --- supervisor/panel/components/pdg_entry.lua | 2 +- supervisor/panel/components/rtu_entry.lua | 2 +- supervisor/startup.lua | 2 +- supervisor/supervisor.lua | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/supervisor/panel/components/pdg_entry.lua b/supervisor/panel/components/pdg_entry.lua index 2c85d59..1e49fca 100644 --- a/supervisor/panel/components/pdg_entry.lua +++ b/supervisor/panel/components/pdg_entry.lua @@ -26,7 +26,7 @@ local function init(parent, id) local ps_prefix = "pdg_" .. id .. "_" TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - local pdg_addr = TextBox{parent=entry,x=1,y=2,text="C #?",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} + local pdg_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value) diff --git a/supervisor/panel/components/rtu_entry.lua b/supervisor/panel/components/rtu_entry.lua index 94900ab..d9634e9 100644 --- a/supervisor/panel/components/rtu_entry.lua +++ b/supervisor/panel/components/rtu_entry.lua @@ -26,7 +26,7 @@ local function init(parent, id) local ps_prefix = "rtu_" .. id .. "_" TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} - local rtu_addr = TextBox{parent=entry,x=1,y=2,text="C #?",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} + local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value) diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 2919f0f..687ede1 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -20,7 +20,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.17.4" +local SUPERVISOR_VERSION = "v0.17.5" local println = util.println local println_ts = util.println_ts diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index e51fdd6..cfc52d8 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -382,8 +382,8 @@ function supervisor.comms(_version, modem, fp_ok) -- this is an attempt to establish a new pocket diagnostic session local s_id = svsessions.establish_pdg_session(src_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)) + 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) else From 1decd884151062b9645150af1a217c375277acd6 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 7 Jun 2023 14:22:35 -0400 Subject: [PATCH 18/18] last few cleanups --- pocket/pocket.lua | 4 ++-- reactor-plc/plc.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 65ade67..0281e92 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -234,7 +234,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran elseif self.connected and ((self.api.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order (API): last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return - elseif self.api.linked and (self.api.addr ~= src_addr) then + elseif self.api.linked and (src_addr ~= self.api.addr) then log.debug("received packet from unknown computer " .. src_addr .. " while linked (API expected " .. self.api.addr .. "); channel in use by another system?") return @@ -325,7 +325,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran elseif self.connected and ((self.sv.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then log.warning("sequence out-of-order (SVR): last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) return - elseif self.sv.linked and (self.sv.addr ~= src_addr) then + elseif self.sv.linked and (src_addr ~= self.sv.addr) then log.debug("received packet from unknown computer " .. src_addr .. " while linked (SVR expected " .. self.sv.addr .. "); channel in use by another system?") return diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 4000b00..02e592a 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -945,7 +945,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, if self.linked then if packet.type == SCADA_MGMT_TYPE.ESTABLISH then -- link request confirmation - if (packet.length == 1) and (self.sv_addr == src_addr) then + if packet.length == 1 then log.debug("received unsolicited establish response") local est_ack = packet.data[1]