#225 work in progress comms changes

This commit is contained in:
Mikayla 2023-06-05 05:13:22 +00:00
parent 50c0a4a3eb
commit 337fca7e7c
19 changed files with 373 additions and 293 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
-- 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,
config.SCADA_DEV_LISTEN, config.SCADA_SV_CTL_LISTEN, config.TRUSTED_RANGE)
channels, config.TRUSTED_RANGE)
-- base loop clock (6.67Hz, 3 ticks)
local MAIN_CLOCK = 0.15

View File

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

View File

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

View File

@ -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: ")