From b7895080cb01c6906052cf944a5f42718f8a6287 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 24 Feb 2023 23:36:16 -0500 Subject: [PATCH] #118 supervisor cleanup --- supervisor/facility.lua | 28 +++--- supervisor/session/coordinator.lua | 23 ++--- supervisor/session/plc.lua | 59 ++++++------ supervisor/session/rsctl.lua | 1 + supervisor/session/rtu.lua | 18 ++-- supervisor/session/rtu/boilerv.lua | 2 + supervisor/session/rtu/envd.lua | 2 + supervisor/session/rtu/imatrix.lua | 2 + supervisor/session/rtu/redstone.lua | 6 ++ supervisor/session/rtu/sna.lua | 2 + supervisor/session/rtu/sps.lua | 4 +- supervisor/session/rtu/turbinev.lua | 2 + supervisor/session/rtu/txnctrl.lua | 7 +- supervisor/session/rtu/unit_session.lua | 26 ++++-- supervisor/session/svsessions.lua | 39 +++++--- supervisor/startup.lua | 10 +-- supervisor/supervisor.lua | 115 +++++++++++++----------- supervisor/unit.lua | 41 +++++---- supervisor/unitlogic.lua | 10 ++- 19 files changed, 241 insertions(+), 156 deletions(-) diff --git a/supervisor/facility.lua b/supervisor/facility.lua index 24f03c2..db56aec 100644 --- a/supervisor/facility.lua +++ b/supervisor/facility.lua @@ -53,6 +53,7 @@ local rate_Kd = -1.0 local facility = {} -- create a new facility management object +---@nodiscard ---@param num_reactors integer number of reactor units ---@param cooling_conf table cooling configurations of reactor units function facility.new(num_reactors, cooling_conf) @@ -124,6 +125,7 @@ function facility.new(num_reactors, cooling_conf) end -- check if all auto-controlled units completed ramping + ---@nodiscard local function _all_units_ramped() local all_ramped = true @@ -185,10 +187,7 @@ function facility.new(num_reactors, cooling_conf) unallocated = math.max(0, unallocated - ctl.br100) - if last ~= ctl.br100 then - log.debug("unit " .. u.get_id() .. ": set to " .. ctl.br100 .. " (was " .. last .. ")") - u.a_commit_br100(ramp) - end + if last ~= ctl.br100 then u.a_commit_br100(ramp) end end end end @@ -426,7 +425,7 @@ function facility.new(num_reactors, cooling_conf) self.accumulator = self.accumulator + (error * (now - self.last_time)) end - local runtime = now - self.time_start + -- local runtime = now - self.time_start local integral = self.accumulator local derivative = (error - self.last_error) / (now - self.last_time) @@ -441,8 +440,8 @@ function facility.new(num_reactors, cooling_conf) self.saturated = output ~= out_c - log.debug(util.sprintf("CHARGE[%f] { CHRG[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%d] }", - runtime, avg_charge, error, integral, output, out_c, P, I, D)) + -- log.debug(util.sprintf("CHARGE[%f] { CHRG[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%d] }", + -- runtime, avg_charge, error, integral, output, out_c, P, I, D)) _allocate_burn_rate(out_c, true) @@ -495,7 +494,7 @@ function facility.new(num_reactors, cooling_conf) self.accumulator = self.accumulator + (error * (now - self.last_time)) end - local runtime = now - self.time_start + -- local runtime = now - self.time_start local integral = self.accumulator local derivative = (error - self.last_error) / (now - self.last_time) @@ -513,8 +512,8 @@ function facility.new(num_reactors, cooling_conf) self.saturated = output ~= out_c - log.debug(util.sprintf("GEN_RATE[%f] { RATE[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%f] }", - runtime, avg_inflow, error, integral, output, out_c, P, I, D)) + -- log.debug(util.sprintf("GEN_RATE[%f] { RATE[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%f] }", + -- runtime, avg_inflow, error, integral, output, out_c, P, I, D)) _allocate_burn_rate(out_c, false) @@ -814,6 +813,7 @@ function facility.new(num_reactors, cooling_conf) -- READ STATES/PROPERTIES -- -- get build properties of all machines + ---@nodiscard ---@param inc_imatrix boolean? true/nil to include induction matrix build, false to exclude function public.get_build(inc_imatrix) local build = {} @@ -830,6 +830,7 @@ function facility.new(num_reactors, cooling_conf) end -- get automatic process control status + ---@nodiscard function public.get_control_status() local astat = self.ascram_status return { @@ -851,6 +852,7 @@ function facility.new(num_reactors, cooling_conf) end -- get RTU statuses + ---@nodiscard function public.get_rtu_statuses() local status = {} @@ -889,9 +891,9 @@ function facility.new(num_reactors, cooling_conf) return status end - function public.get_units() - return self.units - end + -- get the units in this facility + ---@nodiscard + function public.get_units() return self.units end return public end diff --git a/supervisor/session/coordinator.lua b/supervisor/session/coordinator.lua index 3b3f231..1402993 100644 --- a/supervisor/session/coordinator.lua +++ b/supervisor/session/coordinator.lua @@ -13,6 +13,7 @@ local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local SCADA_CRDN_TYPE = comms.SCADA_CRDN_TYPE local UNIT_COMMAND = comms.UNIT_COMMAND local FAC_COMMAND = comms.FAC_COMMAND + local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local SV_Q_CMDS = svqtypes.SV_Q_CMDS @@ -46,6 +47,7 @@ local PERIODICS = { } -- coordinator supervisor session +---@nodiscard ---@param id integer session ID ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue @@ -55,8 +57,6 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility) local log_header = "crdn_session(" .. id .. "): " local self = { - in_q = in_queue, - out_q = out_queue, units = facility.get_units(), -- connection properties seq_num = 0, @@ -100,7 +100,7 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility) c_pkt.make(msg_type, msg) s_pkt.make(self.seq_num, PROTOCOL.SCADA_CRDN, c_pkt.raw_sendable()) - self.out_q.push_packet(s_pkt) + out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 end @@ -114,7 +114,7 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility) m_pkt.make(msg_type, msg) s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - self.out_q.push_packet(s_pkt) + out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 end @@ -275,14 +275,14 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility) local unit = self.units[uid] ---@type reactor_unit if cmd == UNIT_COMMAND.START then - self.out_q.push_data(SV_Q_DATA.START, data) + out_queue.push_data(SV_Q_DATA.START, data) elseif cmd == UNIT_COMMAND.SCRAM then - self.out_q.push_data(SV_Q_DATA.SCRAM, data) + out_queue.push_data(SV_Q_DATA.SCRAM, data) elseif cmd == UNIT_COMMAND.RESET_RPS then - self.out_q.push_data(SV_Q_DATA.RESET_RPS, data) + out_queue.push_data(SV_Q_DATA.RESET_RPS, data) elseif cmd == UNIT_COMMAND.SET_BURN then if pkt.length == 3 then - self.out_q.push_data(SV_Q_DATA.SET_BURN, data) + out_queue.push_data(SV_Q_DATA.SET_BURN, data) else log.debug(log_header .. "CRDN unit command burn rate missing option") end @@ -333,9 +333,11 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility) local public = {} -- get the session ID + ---@nodiscard function public.get_id() return id end -- check if a timer matches this session's watchdog + ---@nodiscard function public.check_wd(timer) return self.conn_watchdog.is_timer(timer) and self.connected end @@ -349,6 +351,7 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility) end -- iterate the session + ---@nodiscard ---@return boolean connected function public.iterate() if self.connected then @@ -358,9 +361,9 @@ function coordinator.new_session(id, in_queue, out_queue, timeout, facility) local handle_start = util.time() - while self.in_q.ready() and self.connected do + while in_queue.ready() and self.connected do -- get a new message to process - local message = self.in_q.pop() + local message = in_queue.pop() if message ~= nil then if message.qtype == mqueue.TYPE.PACKET then diff --git a/supervisor/session/plc.lua b/supervisor/session/plc.lua index b311369..676263c 100644 --- a/supervisor/session/plc.lua +++ b/supervisor/session/plc.lua @@ -12,7 +12,6 @@ local PROTOCOL = comms.PROTOCOL local RPLC_TYPE = comms.RPLC_TYPE local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local PLC_AUTO_ACK = comms.PLC_AUTO_ACK - local UNIT_COMMAND = comms.UNIT_COMMAND local print = util.print @@ -47,18 +46,16 @@ local PERIODICS = { } -- PLC supervisor session +---@nodiscard ---@param id integer session ID ----@param for_reactor integer reactor ID +---@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 -function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) +function plc.new_session(id, reactor_id, in_queue, out_queue, timeout) local log_header = "plc_session(" .. id .. "): " local self = { - for_reactor = for_reactor, - in_q = in_queue, - out_q = out_queue, commanded_state = false, commanded_burn_rate = 0.0, auto_cmd_token = 0, @@ -250,10 +247,10 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) local s_pkt = comms.scada_packet() local r_pkt = comms.rplc_packet() - r_pkt.make(for_reactor, msg_type, msg) + r_pkt.make(reactor_id, msg_type, msg) s_pkt.make(self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable()) - self.out_q.push_packet(s_pkt) + out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 end @@ -267,11 +264,12 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) m_pkt.make(msg_type, msg) s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - self.out_q.push_packet(s_pkt) + out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 end -- get an ACK status + ---@nodiscard ---@param pkt rplc_frame ---@return boolean|nil ack local function _get_ack(pkt) @@ -299,8 +297,8 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) -- process packet if pkt.scada_frame.protocol() == PROTOCOL.RPLC then -- check reactor ID - if pkt.id ~= for_reactor then - log.warning(log_header .. "RPLC packet with ID not matching reactor ID: reactor " .. self.for_reactor .. " != " .. pkt.id) + if pkt.id ~= reactor_id then + log.warning(log_header .. "RPLC packet with ID not matching reactor ID: reactor " .. reactor_id .. " != " .. pkt.id) return end @@ -342,7 +340,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) if status then -- copied in structure data OK self.received_struct = true - self.out_q.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, for_reactor) + out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id) else -- error copying structure data log.error(log_header .. "failed to parse struct packet data") @@ -360,8 +358,8 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) end -- send acknowledgement to coordinator - self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { - unit = self.for_reactor, + out_queue.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { + unit = reactor_id, cmd = UNIT_COMMAND.SET_BURN, ack = ack }) @@ -375,8 +373,8 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) end -- send acknowledgement to coordinator - self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { - unit = self.for_reactor, + out_queue.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { + unit = reactor_id, cmd = UNIT_COMMAND.START, ack = ack }) @@ -391,8 +389,8 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) end -- send acknowledgement to coordinator - self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { - unit = self.for_reactor, + out_queue.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { + unit = reactor_id, cmd = UNIT_COMMAND.SCRAM, ack = ack }) @@ -443,8 +441,8 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) end -- send acknowledgement to coordinator - self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { - unit = self.for_reactor, + out_queue.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, { + unit = reactor_id, cmd = UNIT_COMMAND.RESET_RPS, ack = ack }) @@ -503,17 +501,22 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) -- PUBLIC FUNCTIONS -- -- get the session ID + ---@nodiscard function public.get_id() return id end -- get the session database + ---@nodiscard function public.get_db() return self.sDB end -- check if ramping is completed by first verifying auto command token ack + ---@nodiscard function public.is_ramp_complete() return (self.sDB.auto_ack_token == self.auto_cmd_token) and (self.commanded_burn_rate == self.sDB.mek_status.act_burn_rate) end -- get the reactor structure + ---@nodiscard + ---@return mek_struct|table struct struct or empty table function public.get_struct() if self.received_struct then return self.sDB.mek_struct @@ -523,6 +526,8 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) end -- get the reactor status + ---@nodiscard + ---@return mek_status|table struct status or empty table function public.get_status() if self.received_status_cache then return self.sDB.mek_status @@ -532,11 +537,13 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) end -- get the reactor RPS status + ---@nodiscard function public.get_rps() return self.sDB.rps_status end -- get the general status information + ---@nodiscard function public.get_general_status() return { self.sDB.last_status_update, @@ -564,10 +571,11 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) ---@param ramp boolean true to ramp, false to not function public.auto_set_burn(rate, ramp) self.ramping_rate = ramp - self.in_q.push_data(PLC_S_DATA.AUTO_BURN_RATE, rate) + in_queue.push_data(PLC_S_DATA.AUTO_BURN_RATE, rate) end -- check if a timer matches this session's watchdog + ---@nodiscard function public.check_wd(timer) return self.plc_conn_watchdog.is_timer(timer) and self.connected end @@ -576,11 +584,12 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) function public.close() _close() _send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) - println("connection to reactor " .. self.for_reactor .. " PLC closed by server") + println("connection to reactor " .. reactor_id .. " PLC closed by server") log.info(log_header .. "session closed by server") end -- iterate the session + ---@nodiscard ---@return boolean connected function public.iterate() if self.connected then @@ -590,9 +599,9 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) local handle_start = util.time() - while self.in_q.ready() and self.connected do + while in_queue.ready() and self.connected do -- get a new message to process - local message = self.in_q.pop() + local message = in_queue.pop() if message ~= nil then if message.qtype == mqueue.TYPE.PACKET then @@ -688,7 +697,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue, timeout) -- exit if connection was closed if not self.connected then - println("connection to reactor " .. self.for_reactor .. " PLC closed by remote host") + println("connection to reactor " .. reactor_id .. " PLC closed by remote host") log.info(log_header .. "session closed by remote host") return self.connected end diff --git a/supervisor/session/rsctl.lua b/supervisor/session/rsctl.lua index 1544cc3..fb17efe 100644 --- a/supervisor/session/rsctl.lua +++ b/supervisor/session/rsctl.lua @@ -5,6 +5,7 @@ local rsctl = {} -- create a new redstone RTU I/O controller +---@nodiscard ---@param redstone_rtus table redstone RTU sessions function rsctl.new(redstone_rtus) ---@class rs_controller diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index a0fe916..9d178fe 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -32,6 +32,7 @@ local PERIODICS = { } -- create a new RTU session +---@nodiscard ---@param id integer session ID ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue @@ -42,8 +43,6 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili local log_header = "rtu_session(" .. id .. "): " local self = { - in_q = in_queue, - out_q = out_queue, modbus_q = mqueue.new(), advert = advertisement, fac_units = facility.get_units(), @@ -196,7 +195,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili s_pkt.make(self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable()) - self.out_q.push_packet(s_pkt) + out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 end @@ -210,7 +209,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili m_pkt.make(msg_type, msg) s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - self.out_q.push_packet(s_pkt) + out_queue.push_packet(s_pkt) self.seq_num = self.seq_num + 1 end @@ -262,10 +261,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili elseif pkt.type == SCADA_MGMT_TYPE.RTU_ADVERT then -- RTU unit advertisement log.debug(log_header .. "received updated advertisement") - - -- copy advertisement and remove version tag self.advert = pkt.data - table.remove(self.advert, 1) -- handle advertisement; this will re-create all unit sub-sessions _handle_advertisement() @@ -291,6 +287,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili function public.get_id() return id end -- check if a timer matches this session's watchdog + ---@nodiscard ---@param timer number function public.check_wd(timer) return self.rtu_conn_watchdog.is_timer(timer) and self.connected @@ -305,6 +302,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili end -- iterate the session + ---@nodiscard ---@return boolean connected function public.iterate() if self.connected then @@ -314,9 +312,9 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili local handle_start = util.time() - while self.in_q.ready() and self.connected do + while in_queue.ready() and self.connected do -- get a new message to process - local msg = self.in_q.pop() + local msg = in_queue.pop() if msg ~= nil then if msg.qtype == mqueue.TYPE.PACKET then @@ -389,7 +387,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili -- instruction with body local cmd = msg.message ---@type queue_data if cmd.key == unit_session.RTU_US_DATA.BUILD_CHANGED then - self.out_q.push_data(svqtypes.SV_Q_DATA.RTU_BUILD_CHANGED, cmd.val) + out_queue.push_data(svqtypes.SV_Q_DATA.RTU_BUILD_CHANGED, cmd.val) end end end diff --git a/supervisor/session/rtu/boilerv.lua b/supervisor/session/rtu/boilerv.lua index 58043dd..2f3c231 100644 --- a/supervisor/session/rtu/boilerv.lua +++ b/supervisor/session/rtu/boilerv.lua @@ -31,6 +31,7 @@ local PERIODICS = { } -- create a new boilerv rtu session runner +---@nodiscard ---@param session_id integer RTU session ID ---@param unit_id integer RTU unit ID ---@param advert rtu_advertisement RTU advertisement table @@ -238,6 +239,7 @@ function boilerv.new(session_id, unit_id, advert, out_queue) end -- get the unit session database + ---@nodiscard function public.get_db() return self.db end return public diff --git a/supervisor/session/rtu/envd.lua b/supervisor/session/rtu/envd.lua index 626aecb..3b4b666 100644 --- a/supervisor/session/rtu/envd.lua +++ b/supervisor/session/rtu/envd.lua @@ -22,6 +22,7 @@ local PERIODICS = { } -- create a new environment detector rtu session runner +---@nodiscard ---@param session_id integer ---@param unit_id integer ---@param advert rtu_advertisement @@ -99,6 +100,7 @@ function envd.new(session_id, unit_id, advert, out_queue) end -- get the unit session database + ---@nodiscard function public.get_db() return self.db end return public diff --git a/supervisor/session/rtu/imatrix.lua b/supervisor/session/rtu/imatrix.lua index fcb616d..0b120b4 100644 --- a/supervisor/session/rtu/imatrix.lua +++ b/supervisor/session/rtu/imatrix.lua @@ -31,6 +31,7 @@ local PERIODICS = { } -- create a new imatrix rtu session runner +---@nodiscard ---@param session_id integer RTU session ID ---@param unit_id integer RTU unit ID ---@param advert rtu_advertisement RTU advertisement table @@ -212,6 +213,7 @@ function imatrix.new(session_id, unit_id, advert, out_queue) end -- get the unit session database + ---@nodiscard function public.get_db() return self.db end return public diff --git a/supervisor/session/rtu/redstone.lua b/supervisor/session/rtu/redstone.lua index 50764c4..7c813a2 100644 --- a/supervisor/session/rtu/redstone.lua +++ b/supervisor/session/rtu/redstone.lua @@ -45,6 +45,7 @@ local PERIODICS = { ---@field req IO_LVL -- create a new redstone rtu session runner +---@nodiscard ---@param session_id integer ---@param unit_id integer ---@param advert rtu_advertisement @@ -118,6 +119,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) ---@class rs_db_dig_io local io_f = { + ---@nodiscard read = function () return rsio.digital_is_active(port, self.phy_io.digital_in[port].phy) end, ---@param active boolean write = function (active) end @@ -132,6 +134,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) ---@class rs_db_dig_io local io_f = { + ---@nodiscard read = function () return rsio.digital_is_active(port, self.phy_io.digital_out[port].phy) end, ---@param active boolean write = function (active) @@ -149,6 +152,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) ---@class rs_db_ana_io local io_f = { + ---@nodiscard ---@return integer read = function () return self.phy_io.analog_in[port].phy end, ---@param value integer @@ -164,6 +168,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) ---@class rs_db_ana_io local io_f = { + ---@nodiscard ---@return integer read = function () return self.phy_io.analog_out[port].phy end, ---@param value integer @@ -378,6 +383,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) end -- get the unit session database + ---@nodiscard function public.get_db() return self.db end return public diff --git a/supervisor/session/rtu/sna.lua b/supervisor/session/rtu/sna.lua index 085db14..006222b 100644 --- a/supervisor/session/rtu/sna.lua +++ b/supervisor/session/rtu/sna.lua @@ -28,6 +28,7 @@ local PERIODICS = { } -- create a new sna rtu session runner +---@nodiscard ---@param session_id integer RTU session ID ---@param unit_id integer RTU unit ID ---@param advert rtu_advertisement RTU advertisement table @@ -175,6 +176,7 @@ function sna.new(session_id, unit_id, advert, out_queue) end -- get the unit session database + ---@nodiscard function public.get_db() return self.db end return public diff --git a/supervisor/session/rtu/sps.lua b/supervisor/session/rtu/sps.lua index 7d74acf..da036cd 100644 --- a/supervisor/session/rtu/sps.lua +++ b/supervisor/session/rtu/sps.lua @@ -31,6 +31,7 @@ local PERIODICS = { } -- create a new sps rtu session runner +---@nodiscard ---@param session_id integer RTU session ID ---@param unit_id integer RTU unit ID ---@param advert rtu_advertisement RTU advertisement table @@ -112,7 +113,7 @@ function sps.new(session_id, unit_id, advert, out_queue) -- query the tanks of the device local function _request_tanks() -- read input registers 11 through 19 (start = 11, count = 9) - self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 10, 12 }) + self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 11, 9 }) end -- PUBLIC FUNCTIONS -- @@ -222,6 +223,7 @@ function sps.new(session_id, unit_id, advert, out_queue) end -- get the unit session database + ---@nodiscard function public.get_db() return self.db end return public diff --git a/supervisor/session/rtu/turbinev.lua b/supervisor/session/rtu/turbinev.lua index b8e7228..4cf32c4 100644 --- a/supervisor/session/rtu/turbinev.lua +++ b/supervisor/session/rtu/turbinev.lua @@ -43,6 +43,7 @@ local PERIODICS = { } -- create a new turbinev rtu session runner +---@nodiscard ---@param session_id integer RTU session ID ---@param unit_id integer RTU unit ID ---@param advert rtu_advertisement RTU advertisement table @@ -309,6 +310,7 @@ function turbinev.new(session_id, unit_id, advert, out_queue) end -- get the unit session database + ---@nodiscard function public.get_db() return self.db end return public diff --git a/supervisor/session/rtu/txnctrl.lua b/supervisor/session/rtu/txnctrl.lua index a1f3b4b..9766bab 100644 --- a/supervisor/session/rtu/txnctrl.lua +++ b/supervisor/session/rtu/txnctrl.lua @@ -6,9 +6,10 @@ local util = require("scada-common.util") local txnctrl = {} -local TIMEOUT = 2000 -- 2000ms max wait +local TIMEOUT = 2000 -- 2000ms max wait -- create a new transaction controller +---@nodiscard function txnctrl.new() local self = { list = {}, @@ -22,16 +23,19 @@ function txnctrl.new() local remove = table.remove -- get the length of the transaction list + ---@nodiscard function public.length() return #self.list end -- check if there are no active transactions + ---@nodiscard function public.empty() return #self.list == 0 end -- create a new transaction of the given type + ---@nodiscard ---@param txn_type integer ---@return integer txn_id function public.create(txn_type) @@ -49,6 +53,7 @@ function txnctrl.new() end -- mark a transaction as resolved to get its transaction type + ---@nodiscard ---@param txn_id integer ---@return integer txn_type function public.resolve(txn_id) diff --git a/supervisor/session/rtu/unit_session.lua b/supervisor/session/rtu/unit_session.lua index 069de28..700f9b1 100644 --- a/supervisor/session/rtu/unit_session.lua +++ b/supervisor/session/rtu/unit_session.lua @@ -23,6 +23,7 @@ unit_session.RTU_US_CMDS = RTU_US_CMDS unit_session.RTU_US_DATA = RTU_US_DATA -- create a new unit session runner +---@nodiscard ---@param session_id integer RTU session ID ---@param unit_id integer MODBUS unit ID ---@param advert rtu_advertisement RTU advertisement for this unit @@ -31,12 +32,8 @@ unit_session.RTU_US_DATA = RTU_US_DATA ---@param txn_tags table transaction log tags function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_tags) local self = { - log_tag = log_tag, - txn_tags = txn_tags, - unit_id = unit_id, device_index = advert.index, reactor = advert.reactor, - out_q = out_queue, transaction_controller = txnctrl.new(), connected = true, device_fail = false @@ -61,21 +58,22 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t local m_pkt = comms.modbus_packet() local txn_id = self.transaction_controller.create(txn_type) - m_pkt.make(txn_id, self.unit_id, f_code, register_param) + m_pkt.make(txn_id, unit_id, f_code, register_param) - self.out_q.push_packet(m_pkt) + out_queue.push_packet(m_pkt) return txn_id end -- try to resolve a MODBUS transaction + ---@nodiscard ---@param m_pkt modbus_frame MODBUS packet ---@return integer|false txn_type, integer txn_id transaction type or false on error/busy, transaction ID function protected.try_resolve(m_pkt) if m_pkt.scada_frame.protocol() == PROTOCOL.MODBUS_TCP then - if m_pkt.unit_id == self.unit_id then + if m_pkt.unit_id == unit_id then local txn_type = self.transaction_controller.resolve(m_pkt.txn_id) - local txn_tag = " (" .. util.strval(self.txn_tags[txn_type]) .. ")" + local txn_tag = " (" .. util.strval(txn_tags[txn_type]) .. ")" if bit.band(m_pkt.func_code, MODBUS_FCODE.ERROR_FLAG) ~= 0 then -- transaction incomplete or failed @@ -135,26 +133,35 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t end -- get the public interface + ---@nodiscard function protected.get() return public end -- PUBLIC FUNCTIONS -- -- get the unit ID + ---@nodiscard function public.get_session_id() return session_id end -- get the unit ID - function public.get_unit_id() return self.unit_id end + ---@nodiscard + function public.get_unit_id() return unit_id end -- get the device index + ---@nodiscard function public.get_device_idx() return self.device_index end -- get the reactor ID + ---@nodiscard function public.get_reactor() return self.reactor end -- get the command queue + ---@nodiscard function public.get_cmd_queue() return protected.in_q end -- close this unit + ---@nodiscard function public.close() self.connected = false end -- check if this unit is connected + ---@nodiscard function public.is_connected() return self.connected end -- check if this unit is faulted + ---@nodiscard function public.is_faulted() return self.device_fail end -- PUBLIC TEMPLATE FUNCTIONS -- @@ -179,6 +186,7 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t end -- get the unit session database + ---@nodiscard function public.get_db() log.debug("template unit_session.get_db() called", true) return {} diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index b7c8ef5..76fb6d1 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -183,9 +183,10 @@ local function _free_closed(sessions) end -- find a session by remote port +---@nodiscard ---@param list table ---@param port integer ----@return plc_session_struct|rtu_session_struct|nil +---@return plc_session_struct|rtu_session_struct|coord_session_struct|nil local function _find_session(list, port) for i = 1, #list do if list[i].r_port == port then return list[i] end @@ -212,54 +213,63 @@ function svsessions.relink_modem(modem) end -- find an RTU session by the remote port +---@nodiscard ---@param remote_port integer ---@return rtu_session_struct|nil function svsessions.find_rtu_session(remote_port) -- check RTU sessions ----@diagnostic disable-next-line: return-type-mismatch - return _find_session(self.rtu_sessions, remote_port) + local session = _find_session(self.rtu_sessions, remote_port) + ---@cast session rtu_session_struct + return session end -- find a PLC session by the remote port +---@nodiscard ---@param remote_port integer ---@return plc_session_struct|nil function svsessions.find_plc_session(remote_port) -- check PLC sessions ----@diagnostic disable-next-line: return-type-mismatch - return _find_session(self.plc_sessions, remote_port) + local session = _find_session(self.plc_sessions, remote_port) + ---@cast session plc_session_struct + return session end -- find a PLC/RTU 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 s = _find_session(self.rtu_sessions, remote_port) + local session = _find_session(self.rtu_sessions, remote_port) -- check PLC sessions - if s == nil then s = _find_session(self.plc_sessions, remote_port) end + if session == nil then session = _find_session(self.plc_sessions, remote_port) end + ---@cast session plc_session_struct|rtu_session_struct|nil - return s + return session end --- find a coordinator session by the remote port --- +-- find a coordinator session by the remote port
-- only one coordinator is allowed, but this is kept to be consistent with all other session tables +---@nodiscard ---@param remote_port integer ----@return nil +---@return coord_session_struct|nil function svsessions.find_coord_session(remote_port) -- check coordinator sessions ----@diagnostic disable-next-line: return-type-mismatch - return _find_session(self.coord_sessions, remote_port) + local session = _find_session(self.coord_sessions, remote_port) + ---@cast session coord_session_struct + return session end -- get the a coordinator session if exists +---@nodiscard ---@return coord_session_struct|nil function svsessions.get_coord_session() return self.coord_sessions[1] end -- get a session by reactor ID +---@nodiscard ---@param reactor integer ---@return plc_session_struct|nil session function svsessions.get_reactor_session(reactor) @@ -275,6 +285,7 @@ function svsessions.get_reactor_session(reactor) end -- establish a new PLC session +---@nodiscard ---@param local_port integer ---@param remote_port integer ---@param for_reactor integer @@ -314,6 +325,7 @@ function svsessions.establish_plc_session(local_port, remote_port, for_reactor, end -- establish a new RTU session +---@nodiscard ---@param local_port integer ---@param remote_port integer ---@param advertisement table @@ -344,6 +356,7 @@ function svsessions.establish_rtu_session(local_port, remote_port, advertisement end -- establish a new coordinator session +---@nodiscard ---@param local_port integer ---@param remote_port integer ---@param version string diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 5cba7f7..e537572 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -14,7 +14,7 @@ local svsessions = require("supervisor.session.svsessions") local config = require("supervisor.config") local supervisor = require("supervisor.supervisor") -local SUPERVISOR_VERSION = "beta-v0.12.2" +local SUPERVISOR_VERSION = "v0.13.0" local print = util.print local println = util.println @@ -81,7 +81,7 @@ local function main() local modem = ppm.get_wireless_modem() if modem == nil then - println("boot> wireless modem not found") + println("startup> wireless modem not found") log.fatal("no wireless modem on startup") return end @@ -110,7 +110,7 @@ local function main() -- we only care if this is our wireless modem if device == modem then println_ts("wireless modem disconnected!") - log.error("comms modem disconnected!") + log.warning("comms modem disconnected") else log.warning("non-comms modem disconnected") end @@ -127,9 +127,9 @@ local function main() superv_comms.reconnect_modem(modem) println_ts("wireless modem reconnected.") - log.info("comms modem reconnected.") + log.info("comms modem reconnected") else - log.info("wired modem reconnected.") + log.info("wired modem reconnected") end end end diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index f28dd66..848dfc3 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -17,6 +17,7 @@ local print_ts = util.print_ts local println_ts = util.println_ts -- supervisory controller communications +---@nodiscard ---@param version string supervisor version ---@param num_reactors integer number of reactors ---@param cooling_conf table cooling configuration table @@ -26,32 +27,24 @@ local println_ts = util.println_ts ---@param range integer trusted device connection range function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen, coord_listen, range) local self = { - version = version, - num_reactors = num_reactors, - modem = modem, - dev_listen = dev_listen, - coord_listen = coord_listen, - reactor_struct_cache = nil + last_est_acks = {} } - ---@class superv_comms - local public = {} - comms.set_trusted_range(range) -- PRIVATE FUNCTIONS -- -- configure modem channels local function _conf_channels() - self.modem.closeAll() - self.modem.open(self.dev_listen) - self.modem.open(self.coord_listen) + modem.closeAll() + modem.open(dev_listen) + modem.open(coord_listen) end _conf_channels() -- link modem to svsessions - svsessions.init(self.modem, num_reactors, cooling_conf) + svsessions.init(modem, num_reactors, cooling_conf) -- send an establish request response to a PLC/RTU ---@param dest integer @@ -63,7 +56,7 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, msg) s_pkt.make(seq_id, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - self.modem.transmit(dest, self.dev_listen, s_pkt.raw_sendable()) + modem.transmit(dest, dev_listen, s_pkt.raw_sendable()) end -- send coordinator connection establish response @@ -77,21 +70,24 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen c_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, msg) s_pkt.make(seq_id, PROTOCOL.SCADA_MGMT, c_pkt.raw_sendable()) - self.modem.transmit(dest, self.coord_listen, s_pkt.raw_sendable()) + modem.transmit(dest, coord_listen, s_pkt.raw_sendable()) end -- PUBLIC FUNCTIONS -- + ---@class superv_comms + local public = {} + -- reconnect a newly connected modem - ---@param modem table ----@diagnostic disable-next-line: redefined-local - function public.reconnect_modem(modem) - self.modem = modem - svsessions.relink_modem(self.modem) + ---@param new_modem table + function public.reconnect_modem(new_modem) + modem = new_modem + svsessions.relink_modem(new_modem) _conf_channels() end -- parse a packet + ---@nodiscard ---@param side string ---@param sender integer ---@param reply_to integer @@ -147,8 +143,9 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen local protocol = packet.scada_frame.protocol() -- device (RTU/PLC) listening channel - if l_port == self.dev_listen then + if l_port == dev_listen then if protocol == PROTOCOL.MODBUS_TCP then + ---@cast packet modbus_frame -- look for an associated session local session = svsessions.find_rtu_session(r_port) @@ -161,6 +158,7 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen 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) @@ -174,6 +172,7 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen _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) @@ -192,13 +191,13 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen local dev_type = packet.data[3] if comms_v ~= comms.version then - log.debug(util.c("dropping establish packet with incorrect comms version v", comms_v, - " (expected v", comms.version, ")")) - _send_dev_establish(next_seq_id, r_port, { ESTABLISH_ACK.BAD_VERSION }) - return - end + 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 + end - if dev_type == DEVICE_TYPE.PLC then + _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] @@ -206,13 +205,19 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen if plc_id == false then -- reactor already has a PLC assigned - log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id)) + 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") @@ -226,6 +231,7 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen 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 }) else log.debug("RTU_ESTABLISH: packet length mismatch") @@ -247,11 +253,12 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen log.debug("illegal packet type " .. protocol .. " on device listening channel") end -- coordinator listening channel - elseif l_port == self.coord_listen then + elseif l_port == coord_listen then -- look for an associated session local session = svsessions.find_coord_session(r_port) if protocol == PROTOCOL.SCADA_MGMT then + ---@cast packet mgmt_frame -- SCADA management packet if session ~= nil then -- pass the packet onto the session handler @@ -267,32 +274,39 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen local dev_type = packet.data[3] if comms_v ~= comms.version then - log.debug(util.c("dropping establish packet with incorrect comms version v", comms_v, - " (expected v", comms.version, ")")) + if self.last_est_acks[r_port] ~= 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_crdn_establish(next_seq_id, r_port, { ESTABLISH_ACK.BAD_VERSION }) - return elseif dev_type ~= DEVICE_TYPE.CRDN then log.debug(util.c("illegal establish packet for device ", dev_type, " on CRDN listening channel")) _send_crdn_establish(next_seq_id, r_port, { ESTABLISH_ACK.DENY }) - return - end - - -- this is an attempt to establish a new session - local s_id = svsessions.establish_coord_session(l_port, r_port, firmware_v) - - if s_id ~= false then - local config = { self.num_reactors } - for i = 1, #cooling_conf do - table.insert(config, cooling_conf[i].BOILERS) - table.insert(config, cooling_conf[i].TURBINES) - end - - println(util.c("CRD (",firmware_v, ") [:", r_port, "] \xbb connected")) - log.info(util.c("CRDN_ESTABLISH: coordinator (",firmware_v, ") [:", r_port, "] connected with session ID ", s_id)) - _send_crdn_establish(next_seq_id, r_port, { ESTABLISH_ACK.ALLOW, config }) else - log.debug("CRDN_ESTABLISH: denied new coordinator due to already being connected to another coordinator") - _send_crdn_establish(next_seq_id, r_port, { ESTABLISH_ACK.COLLISION }) + -- this is an attempt to establish a new session + local s_id = svsessions.establish_coord_session(l_port, r_port, firmware_v) + + if s_id ~= false then + local config = { num_reactors } + for i = 1, #cooling_conf do + table.insert(config, cooling_conf[i].BOILERS) + table.insert(config, cooling_conf[i].TURBINES) + end + + println(util.c("CRD (",firmware_v, ") [:", r_port, "] \xbb connected")) + log.info(util.c("CRDN_ESTABLISH: coordinator (",firmware_v, ") [:", r_port, "] connected with session ID ", s_id)) + + _send_crdn_establish(next_seq_id, r_port, { ESTABLISH_ACK.ALLOW, config }) + self.last_est_acks[r_port] = ESTABLISH_ACK.ALLOW + else + if self.last_est_acks[r_port] ~= ESTABLISH_ACK.COLLISION then + log.info("CRDN_ESTABLISH: denied new coordinator due to already being connected to another coordinator") + self.last_est_acks[r_port] = ESTABLISH_ACK.COLLISION + end + + _send_crdn_establish(next_seq_id, r_port, { ESTABLISH_ACK.COLLISION }) + end end else log.debug("CRDN_ESTABLISH: establish packet length mismatch") @@ -303,6 +317,7 @@ function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen log.debug(r_port .. "->" .. l_port .. ": discarding SCADA_MGMT packet without a known session") end elseif protocol == PROTOCOL.SCADA_CRDN then + ---@cast packet crdn_frame -- coordinator packet if session ~= nil then -- pass the packet onto the session handler diff --git a/supervisor/unit.lua b/supervisor/unit.lua index b809cbb..b79036e 100644 --- a/supervisor/unit.lua +++ b/supervisor/unit.lua @@ -11,15 +11,14 @@ local rsctl = require("supervisor.session.rsctl") ---@class reactor_control_unit local unit = {} -local WASTE_MODE = types.WASTE_MODE - -local ALARM = types.ALARM -local PRIO = types.ALARM_PRIORITY -local ALARM_STATE = types.ALARM_STATE - -local TRI_FAIL = types.TRI_FAIL +local WASTE_MODE = types.WASTE_MODE local DUMPING_MODE = types.DUMPING_MODE +local ALARM = types.ALARM +local PRIO = types.ALARM_PRIORITY +local ALARM_STATE = types.ALARM_STATE +local TRI_FAIL = types.TRI_FAIL + local PLC_S_CMDS = plc.PLC_S_CMDS local IO = rsio.IO @@ -61,13 +60,14 @@ unit.FLOW_STABILITY_DELAY_MS = FLOW_STABILITY_DELAY_MS ---@field tier integer alarm urgency tier (0 = highest) -- create a new reactor unit ----@param for_reactor integer reactor unit number +---@nodiscard +---@param reactor_id integer reactor unit number ---@param num_boilers integer number of boilers expected ---@param num_turbines integer number of turbines expected -function unit.new(for_reactor, num_boilers, num_turbines) +function unit.new(reactor_id, num_boilers, num_turbines) ---@class _unit_self local self = { - r_id = for_reactor, + r_id = reactor_id, plc_s = nil, ---@class plc_session_struct plc_i = nil, ---@class plc_session num_boilers = num_boilers, @@ -278,6 +278,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) local function _reset_dt(key) self.deltas[key] = nil end -- get the delta t of a value + ---@nodiscard ---@param key string value key ---@return number value value or 0 if not known function self._get_dt(key) if self.deltas[key] then return self.deltas[key].dt else return 0.0 end end @@ -326,7 +327,6 @@ function unit.new(for_reactor, num_boilers, num_turbines) --#region redstone I/O local __rs_w = self.io_ctl.digital_write - local __rs_r = self.io_ctl.digital_read -- valves local waste_pu = { open = function () __rs_w(IO.WASTE_PU, true) end, close = function () __rs_w(IO.WASTE_PU, false) end } @@ -525,9 +525,9 @@ function unit.new(for_reactor, num_boilers, num_turbines) end end - -- get the actual limit of this unit - -- + -- get the actual limit of this unit
-- if it is degraded or not ready, the limit will be 0 + ---@nodiscard ---@return integer lim_br100 function public.a_get_effective_limit() if not self.db.control.ready or self.db.control.degraded or self.plc_cache.rps_trip then @@ -551,6 +551,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) end -- check if ramping is complete (burn rate is same as target) + ---@nodiscard ---@return boolean complete function public.a_ramp_complete() if self.plc_i ~= nil then @@ -610,7 +611,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) -- acknowledge an alarm (if possible) ---@param id ALARM alarm ID function public.ack_alarm(id) - if (type(id) == "number") and (self.db.alarm_states[id] == ALARM_STATE.TRIPPED) then + if type(id) == "number" and self.db.alarm_states[id] == ALARM_STATE.TRIPPED then self.db.alarm_states[id] = ALARM_STATE.ACKED end end @@ -618,7 +619,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) -- reset an alarm (if possible) ---@param id ALARM alarm ID function public.reset_alarm(id) - if (type(id) == "number") and (self.db.alarm_states[id] == ALARM_STATE.RING_BACK) then + if type(id) == "number" and self.db.alarm_states[id] == ALARM_STATE.RING_BACK then self.db.alarm_states[id] = ALARM_STATE.INACTIVE end end @@ -675,6 +676,8 @@ function unit.new(for_reactor, num_boilers, num_turbines) --#region -- check if a critical alarm is tripped + ---@nodiscard + ---@return boolean tripped function public.has_critical_alarm() for _, alarm in pairs(self.alarms) do if alarm.tier == PRIO.CRITICAL and (alarm.state == AISTATE.TRIPPED or alarm.state == AISTATE.ACKED) then @@ -686,6 +689,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) end -- get build properties of all machines + ---@nodiscard ---@param inc_plc boolean? true/nil to include PLC build, false to exclude ---@param inc_boilers boolean? true/nil to include boiler builds, false to exclude ---@param inc_turbines boolean? true/nil to include turbine builds, false to exclude @@ -718,6 +722,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) end -- get reactor status + ---@nodiscard function public.get_reactor_status() local status = {} if self.plc_i ~= nil then @@ -728,6 +733,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) end -- get RTU statuses + ---@nodiscard function public.get_rtu_statuses() local status = {} @@ -769,20 +775,25 @@ function unit.new(for_reactor, num_boilers, num_turbines) end -- get the annunciator status + ---@nodiscard function public.get_annunciator() return self.db.annunciator end -- get the alarm states + ---@nodiscard function public.get_alarms() return self.db.alarm_states end -- get information required for automatic reactor control + ---@nodiscard function public.get_control_inf() return self.db.control end -- get unit state + ---@nodiscard function public.get_state() return { self.status_text[1], self.status_text[2], self.waste_mode, self.db.control.ready, self.db.control.degraded } end -- get the reactor ID + ---@nodiscard function public.get_id() return self.r_id end --#endregion diff --git a/supervisor/unitlogic.lua b/supervisor/unitlogic.lua index 9249193..1e701a8 100644 --- a/supervisor/unitlogic.lua +++ b/supervisor/unitlogic.lua @@ -5,12 +5,12 @@ local util = require("scada-common.util") local plc = require("supervisor.session.plc") -local PRIO = types.ALARM_PRIORITY -local ALARM_STATE = types.ALARM_STATE - local TRI_FAIL = types.TRI_FAIL local DUMPING_MODE = types.DUMPING_MODE +local PRIO = types.ALARM_PRIORITY +local ALARM_STATE = types.ALARM_STATE + local IO = rsio.IO local PLC_S_CMDS = plc.PLC_S_CMDS @@ -555,6 +555,7 @@ function logic.update_status_text(self) local AISTATE = self.types.AISTATE -- check if an alarm is active (tripped or ack'd) + ---@nodiscard ---@param alarm table alarm entry ---@return boolean active local function is_active(alarm) @@ -670,7 +671,7 @@ function logic.update_status_text(self) end end else - self.status_text = { "Reactor Off-line", "awaiting connection..." } + self.status_text = { "REACTOR OFF-LINE", "awaiting connection..." } end end @@ -680,6 +681,7 @@ function logic.handle_redstone(self) local AISTATE = self.types.AISTATE -- check if an alarm is active (tripped or ack'd) + ---@nodiscard ---@param alarm table alarm entry ---@return boolean active local function is_active(alarm)