#47 supervisor luadoc, bugfixes

This commit is contained in:
Mikayla Fischler
2022-05-11 12:31:19 -04:00
parent 95c4d51e67
commit 0d090fe9e2
6 changed files with 176 additions and 111 deletions

View File

@@ -37,6 +37,10 @@ local PERIODICS = {
} }
-- PLC supervisor session -- PLC supervisor session
---@param id integer
---@param for_reactor integer
---@param in_queue mqueue
---@param out_queue mqueue
plc.new_session = function (id, for_reactor, in_queue, out_queue) plc.new_session = function (id, for_reactor, in_queue, out_queue)
local log_header = "plc_session(" .. id .. "): " local log_header = "plc_session(" .. id .. "): "
@@ -78,6 +82,7 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
rps_reset = true rps_reset = true
}, },
-- session database -- session database
---@class reactor_db
sDB = { sDB = {
last_status_update = 0, last_status_update = 0,
control_state = false, control_state = false,
@@ -85,6 +90,7 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
degraded = false, degraded = false,
rps_tripped = false, rps_tripped = false,
rps_trip_cause = "ok", rps_trip_cause = "ok",
---@class rps_status
rps_status = { rps_status = {
dmg_crit = false, dmg_crit = false,
ex_hcool = false, ex_hcool = false,
@@ -94,45 +100,52 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
no_cool = false, no_cool = false,
timed_out = false timed_out = false
}, },
---@class mek_status
mek_status = { mek_status = {
heating_rate = 0, heating_rate = 0.0,
status = false, status = false,
burn_rate = 0, burn_rate = 0.0,
act_burn_rate = 0, act_burn_rate = 0.0,
temp = 0, temp = 0.0,
damage = 0, damage = 0.0,
boil_eff = 0, boil_eff = 0.0,
env_loss = 0, env_loss = 0.0,
fuel = 0, fuel = 0.0,
fuel_need = 0, fuel_need = 0.0,
fuel_fill = 0, fuel_fill = 0.0,
waste = 0, waste = 0.0,
waste_need = 0, waste_need = 0.0,
waste_fill = 0, waste_fill = 0.0,
cool_type = "?", cool_type = "?",
cool_amnt = 0, cool_amnt = 0.0,
cool_need = 0, cool_need = 0.0,
cool_fill = 0, cool_fill = 0.0,
hcool_type = "?", hcool_type = "?",
hcool_amnt = 0, hcool_amnt = 0.0,
hcool_need = 0, hcool_need = 0.0,
hcool_fill = 0 hcool_fill = 0.0
}, },
---@class mek_struct
mek_struct = { mek_struct = {
heat_cap = 0, heat_cap = 0.0,
fuel_asm = 0, fuel_asm = 0.0,
fuel_sa = 0, fuel_sa = 0.0,
fuel_cap = 0, fuel_cap = 0.0,
waste_cap = 0, waste_cap = 0.0,
cool_cap = 0, cool_cap = 0.0,
hcool_cap = 0, hcool_cap = 0.0,
max_burn = 0 max_burn = 0.0
} }
} }
} }
---@class plc_session
local public = {}
-- copy in the RPS status
---@param rps_status table
local _copy_rps_status = function (rps_status) local _copy_rps_status = function (rps_status)
self.sDB.rps_status.dmg_crit = rps_status[1] self.sDB.rps_status.dmg_crit = rps_status[1]
self.sDB.rps_status.ex_hcool = rps_status[2] self.sDB.rps_status.ex_hcool = rps_status[2]
@@ -143,6 +156,8 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
self.sDB.rps_status.timed_out = rps_status[7] self.sDB.rps_status.timed_out = rps_status[7]
end end
-- copy in the reactor status
---@param mek_data table
local _copy_status = function (mek_data) local _copy_status = function (mek_data)
-- copy status information -- copy status information
self.sDB.mek_status.status = mek_data[1] self.sDB.mek_status.status = mek_data[1]
@@ -174,6 +189,8 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
end end
-- copy in the reactor structure
---@param mek_data table
local _copy_struct = function (mek_data) local _copy_struct = function (mek_data)
self.sDB.mek_struct.heat_cap = mek_data[1] self.sDB.mek_struct.heat_cap = mek_data[1]
self.sDB.mek_struct.fuel_asm = mek_data[2] self.sDB.mek_struct.fuel_asm = mek_data[2]
@@ -186,6 +203,8 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
-- send an RPLC packet -- send an RPLC packet
---@param msg_type RPLC_TYPES
---@param msg table
local _send = function (msg_type, msg) local _send = function (msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
local r_pkt = comms.rplc_packet() local r_pkt = comms.rplc_packet()
@@ -198,6 +217,8 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
-- send a SCADA management packet -- send a SCADA management packet
---@param msg_type SCADA_MGMT_TYPES
---@param msg table
local _send_mgmt = function (msg_type, msg) local _send_mgmt = function (msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
local m_pkt = comms.mgmt_packet() local m_pkt = comms.mgmt_packet()
@@ -210,6 +231,8 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
-- get an ACK status -- get an ACK status
---@param pkt rplc_frame
---@return boolean|nil ack
local _get_ack = function (pkt) local _get_ack = function (pkt)
if pkt.length == 1 then if pkt.length == 1 then
return pkt.data[1] return pkt.data[1]
@@ -220,6 +243,7 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
-- handle a packet -- handle a packet
---@param pkt rplc_frame
local _handle_packet = function (pkt) local _handle_packet = function (pkt)
-- check sequence number -- check sequence number
if self.r_seq_num == nil then if self.r_seq_num == nil then
@@ -378,13 +402,13 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
-- get the session ID -- get the session ID
local get_id = function () return self.id end public.get_id = function () return self.id end
-- get the session database -- get the session database
local get_db = function () return self.sDB end public.get_db = function () return self.sDB end
-- get the reactor structure -- get the reactor structure
local get_struct = function () public.get_struct = function ()
if self.received_struct then if self.received_struct then
return self.sDB.mek_struct return self.sDB.mek_struct
else else
@@ -393,7 +417,7 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
-- get the reactor structure -- get the reactor structure
local get_status = function () public.get_status = function ()
if self.received_status_cache then if self.received_status_cache then
return self.sDB.mek_status return self.sDB.mek_status
else else
@@ -402,12 +426,12 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
-- check if a timer matches this session's watchdog -- check if a timer matches this session's watchdog
local check_wd = function (timer) public.check_wd = function (timer)
return self.plc_conn_watchdog.is_timer(timer) return self.plc_conn_watchdog.is_timer(timer)
end end
-- close the connection -- close the connection
local close = function () public.close = function ()
self.plc_conn_watchdog.cancel() self.plc_conn_watchdog.cancel()
self.connected = false self.connected = false
_send_mgmt(SCADA_MGMT_TYPES.CLOSE, {}) _send_mgmt(SCADA_MGMT_TYPES.CLOSE, {})
@@ -416,7 +440,8 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
end end
-- iterate the session -- iterate the session
local iterate = function () ---@return boolean connected
public.iterate = function ()
if self.connected then if self.connected then
------------------ ------------------
-- handle queue -- -- handle queue --
@@ -428,6 +453,7 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
-- get a new message to process -- get a new message to process
local message = self.in_q.pop() local message = self.in_q.pop()
if message ~= nil then
if message.qtype == mqueue.TYPE.PACKET then if message.qtype == mqueue.TYPE.PACKET then
-- handle a packet -- handle a packet
_handle_packet(message.message) _handle_packet(message.message)
@@ -469,6 +495,7 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
_send(RPLC_TYPES.MEK_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate }) _send(RPLC_TYPES.MEK_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate })
end end
end end
end
-- max 100ms spent processing queue -- max 100ms spent processing queue
if util.time() - handle_start > 100 then if util.time() - handle_start > 100 then
@@ -567,15 +594,7 @@ plc.new_session = function (id, for_reactor, in_queue, out_queue)
return self.connected return self.connected
end end
return { return public
get_id = get_id,
get_db = get_db,
get_struct = get_struct,
get_status = get_status,
check_wd = check_wd,
close = close,
iterate = iterate
}
end end
return plc return plc

View File

@@ -100,7 +100,7 @@ rtu.new_session = function (id, in_queue, out_queue)
elseif pkt.type == SCADA_MGMT_TYPES.RTU_ADVERT then elseif pkt.type == SCADA_MGMT_TYPES.RTU_ADVERT then
-- RTU unit advertisement -- RTU unit advertisement
for i = 1, pkt.length do for i = 1, pkt.length do
local unit = pkt.data[i] local unit = pkt.data[i] ---@type rtu_advertisement
end end
else else
log.debug(log_header .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type) log.debug(log_header .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type)

View File

@@ -31,16 +31,17 @@ local self = {
-- PRIVATE FUNCTIONS -- -- PRIVATE FUNCTIONS --
-- iterate all the given sessions -- iterate all the given sessions
---@param sessions table
local function _iterate(sessions) local function _iterate(sessions)
for i = 1, #sessions do for i = 1, #sessions do
local session = sessions[i] local session = sessions[i] ---@type plc_session_struct
if session.open then if session.open then
local ok = session.instance.iterate() local ok = session.instance.iterate()
if ok then if ok then
-- send packets in out queue -- send packets in out queue
while session.out_queue.ready() do while session.out_queue.ready() do
local msg = session.out_queue.pop() local msg = session.out_queue.pop()
if msg.qtype == mqueue.TYPE.PACKET then 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_port, session.l_port, msg.message.raw_sendable())
end end
end end
@@ -52,6 +53,7 @@ local function _iterate(sessions)
end end
-- cleanly close a session -- cleanly close a session
---@param session plc_session_struct
local function _shutdown(session) local function _shutdown(session)
session.open = false session.open = false
session.instance.close() session.instance.close()
@@ -59,7 +61,7 @@ local function _shutdown(session)
-- send packets in out queue (namely the close packet) -- send packets in out queue (namely the close packet)
while session.out_queue.ready() do while session.out_queue.ready() do
local msg = session.out_queue.pop() local msg = session.out_queue.pop()
if msg.qtype == mqueue.TYPE.PACKET then 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_port, session.l_port, msg.message.raw_sendable())
end end
end end
@@ -68,9 +70,10 @@ local function _shutdown(session)
end end
-- close connections -- close connections
---@param sessions table
local function _close(sessions) local function _close(sessions)
for i = 1, #sessions do for i = 1, #sessions do
local session = sessions[i] local session = sessions[i] ---@type plc_session_struct
if session.open then if session.open then
_shutdown(session) _shutdown(session)
end end
@@ -78,9 +81,11 @@ local function _close(sessions)
end end
-- check if a watchdog timer event matches that of one of the provided sessions -- check if a watchdog timer event matches that of one of the provided sessions
---@param sessions table
---@param timer_event number
local function _check_watchdogs(sessions, timer_event) local function _check_watchdogs(sessions, timer_event)
for i = 1, #sessions do for i = 1, #sessions do
local session = sessions[i] local session = sessions[i] ---@type plc_session_struct
if session.open then if session.open then
local triggered = session.instance.check_wd(timer_event) local triggered = session.instance.check_wd(timer_event)
if triggered then if triggered then
@@ -92,10 +97,11 @@ local function _check_watchdogs(sessions, timer_event)
end end
-- delete any closed sessions -- delete any closed sessions
---@param sessions table
local function _free_closed(sessions) local function _free_closed(sessions)
local move_to = 1 local move_to = 1
for i = 1, #sessions do for i = 1, #sessions do
local session = sessions[i] local session = sessions[i] ---@type plc_session_struct
if session ~= nil then if session ~= nil then
if session.open then if session.open then
if sessions[move_to] == nil then if sessions[move_to] == nil then
@@ -113,11 +119,15 @@ end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
-- link the modem
---@param modem table
svsessions.link_modem = function (modem) svsessions.link_modem = function (modem)
self.modem = modem self.modem = modem
end end
-- find a session by the remote port -- find a session by the remote port
---@param remote_port integer
---@return plc_session_struct|nil
svsessions.find_session = function (remote_port) svsessions.find_session = function (remote_port)
-- check RTU sessions -- check RTU sessions
for i = 1, #self.rtu_sessions do for i = 1, #self.rtu_sessions do
@@ -144,6 +154,8 @@ svsessions.find_session = function (remote_port)
end end
-- get a session by reactor ID -- get a session by reactor ID
---@param reactor integer
---@return plc_session_struct session
svsessions.get_reactor_session = function (reactor) svsessions.get_reactor_session = function (reactor)
local session = nil local session = nil
@@ -157,8 +169,13 @@ svsessions.get_reactor_session = function (reactor)
end end
-- establish a new PLC session -- establish a new PLC session
---@param local_port integer
---@param remote_port integer
---@param for_reactor integer
---@return integer|false session_id
svsessions.establish_plc_session = function (local_port, remote_port, for_reactor) svsessions.establish_plc_session = function (local_port, remote_port, for_reactor)
if svsessions.get_reactor_session(for_reactor) == nil then if svsessions.get_reactor_session(for_reactor) == nil then
---@class plc_session_struct
local plc_s = { local plc_s = {
open = true, open = true,
reactor = for_reactor, reactor = for_reactor,
@@ -185,6 +202,7 @@ svsessions.establish_plc_session = function (local_port, remote_port, for_reacto
end end
-- attempt to identify which session's watchdog timer fired -- attempt to identify which session's watchdog timer fired
---@param timer_event number
svsessions.check_all_watchdogs = function (timer_event) svsessions.check_all_watchdogs = function (timer_event)
-- check RTU session watchdogs -- check RTU session watchdogs
_check_watchdogs(self.rtu_sessions, timer_event) _check_watchdogs(self.rtu_sessions, timer_event)

View File

@@ -6,15 +6,12 @@ local log = require("scada-common.log")
local ppm = require("scada-common.ppm") local ppm = require("scada-common.ppm")
local util = require("scada-common.util") local util = require("scada-common.util")
local coordinator = require("supervisor.session.coordinator")
local plc = require("supervisor.session.plc")
local rtu = require("supervisor.session.rtu")
local svsessions = require("supervisor.session.svsessions") local svsessions = require("supervisor.session.svsessions")
local config = require("supervisor.config") local config = require("supervisor.config")
local supervisor = require("supervisor.supervisor") local supervisor = require("supervisor.supervisor")
local SUPERVISOR_VERSION = "alpha-v0.3.4" local SUPERVISOR_VERSION = "alpha-v0.3.5"
local print = util.print local print = util.print
local println = util.println local println = util.println

View File

@@ -20,6 +20,10 @@ local print_ts = util.print_ts
local println_ts = util.println_ts local println_ts = util.println_ts
-- supervisory controller communications -- supervisory controller communications
---@param num_reactors integer
---@param modem table
---@param dev_listen integer
---@param coord_listen integer
supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen) supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
local self = { local self = {
ln_seq_num = 0, ln_seq_num = 0,
@@ -30,6 +34,9 @@ supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
reactor_struct_cache = nil reactor_struct_cache = nil
} }
---@class superv_comms
local public = {}
-- PRIVATE FUNCTIONS -- -- PRIVATE FUNCTIONS --
-- open all channels -- open all channels
@@ -50,6 +57,8 @@ supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
svsessions.link_modem(self.modem) svsessions.link_modem(self.modem)
-- send PLC link request responses -- send PLC link request responses
---@param dest integer
---@param msg table
local _send_plc_linking = function (dest, msg) local _send_plc_linking = function (dest, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
local r_pkt = comms.rplc_packet() local r_pkt = comms.rplc_packet()
@@ -64,14 +73,22 @@ supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
-- reconnect a newly connected modem -- reconnect a newly connected modem
local reconnect_modem = function (modem) ---@param modem table
---@diagnostic disable-next-line: redefined-local
public.reconnect_modem = function (modem)
self.modem = modem self.modem = modem
svsessions.link_modem(self.modem) svsessions.link_modem(self.modem)
_open_channels() _open_channels()
end end
-- parse a packet -- parse a packet
local parse_packet = function(side, sender, reply_to, message, distance) ---@param side string
---@param sender integer
---@param reply_to integer
---@param message any
---@param distance integer
---@return modbus_frame|rplc_frame|mgmt_frame|coord_frame|nil packet
public.parse_packet = function(side, sender, reply_to, message, distance)
local pkt = nil local pkt = nil
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@@ -111,7 +128,9 @@ supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
return pkt return pkt
end end
local handle_packet = function(packet) -- handle a packet
---@param packet modbus_frame|rplc_frame|mgmt_frame|coord_frame
public.handle_packet = function(packet)
if packet ~= nil then if packet ~= nil then
local l_port = packet.scada_frame.local_port() local l_port = packet.scada_frame.local_port()
local r_port = packet.scada_frame.remote_port() local r_port = packet.scada_frame.remote_port()
@@ -126,7 +145,7 @@ supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
-- MODBUS response -- MODBUS response
elseif protocol == PROTOCOLS.RPLC then elseif protocol == PROTOCOLS.RPLC then
-- reactor PLC packet -- reactor PLC packet
if session then if session ~= nil then
if packet.type == RPLC_TYPES.LINK_REQ then if packet.type == RPLC_TYPES.LINK_REQ then
-- new device on this port? that's a collision -- new device on this port? that's a collision
log.debug("PLC_LNK: request from existing connection received on " .. r_port .. ", responding with collision") log.debug("PLC_LNK: request from existing connection received on " .. r_port .. ", responding with collision")
@@ -162,7 +181,7 @@ supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
end end
elseif protocol == PROTOCOLS.SCADA_MGMT then elseif protocol == PROTOCOLS.SCADA_MGMT then
-- SCADA management packet -- SCADA management packet
if session then if session ~= nil then
-- pass the packet onto the session handler -- pass the packet onto the session handler
session.in_queue.push_packet(packet) session.in_queue.push_packet(packet)
end end
@@ -184,11 +203,7 @@ supervisor.comms = function (num_reactors, modem, dev_listen, coord_listen)
end end
end end
return { return public
reconnect_modem = reconnect_modem,
parse_packet = parse_packet,
handle_packet = handle_packet
}
end end
return supervisor return supervisor

View File

@@ -1,8 +1,8 @@
local unit = {} local unit = {}
-- create a new reactor unit
---@param for_reactor integer
unit.new = function (for_reactor) unit.new = function (for_reactor)
local public = {}
local self = { local self = {
r_id = for_reactor, r_id = for_reactor,
plc_s = nil, plc_s = nil,
@@ -11,6 +11,7 @@ unit.new = function (for_reactor)
energy_storage = {}, energy_storage = {},
redstone = {}, redstone = {},
db = { db = {
---@class annunciator
annunciator = { annunciator = {
-- RPS -- RPS
-- reactor -- reactor
@@ -37,18 +38,36 @@ unit.new = function (for_reactor)
} }
} }
---@class reactor_unit
local public = {}
-- PRIVATE FUNCTIONS --
-- update the annunciator
local _update_annunciator = function ()
self.db.annunciator.PLCOnline = (self.plc_s ~= nil) and (self.plc_s.open)
self.db.annunciator.ReactorTrip = false
end
-- PUBLIC FUNCTIONS --
-- link the PLC
---@param plc_session plc_session_struct
public.link_plc_session = function (plc_session) public.link_plc_session = function (plc_session)
self.plc_s = plc_session self.plc_s = plc_session
end end
-- link a turbine RTU
public.add_turbine = function (turbine) public.add_turbine = function (turbine)
table.insert(self.turbines, turbine) table.insert(self.turbines, turbine)
end end
-- link a boiler RTU
public.add_boiler = function (boiler) public.add_boiler = function (boiler)
table.insert(self.boilers, boiler) table.insert(self.boilers, boiler)
end end
-- link a redstone RTU capability
public.add_redstone = function (field, accessor) public.add_redstone = function (field, accessor)
-- ensure field exists -- ensure field exists
if self.redstone[field] == nil then if self.redstone[field] == nil then
@@ -59,11 +78,7 @@ unit.new = function (for_reactor)
table.insert(self.redstone[field], accessor) table.insert(self.redstone[field], accessor)
end end
local _update_annunciator = function () -- update (iterate) this session
self.db.annunciator.PLCOnline = (self.plc_s ~= nil) and (self.plc_s.open)
self.db.annunciator.ReactorTrip = false
end
public.update = function () public.update = function ()
-- unlink PLC if session was closed -- unlink PLC if session was closed
if not self.plc_s.open then if not self.plc_s.open then
@@ -74,6 +89,7 @@ unit.new = function (for_reactor)
_update_annunciator() _update_annunciator()
end end
-- get the annunciator status
public.get_annunciator = function () return self.db.annunciator end public.get_annunciator = function () return self.db.annunciator end
return public return public