From dbd79cbc4f6d57c6d2d0b86c6fc433bce1799843 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 5 Sep 2024 21:49:47 -0400 Subject: [PATCH] #498 coordinator process handle system for manual controls --- coordinator/coordinator.lua | 10 +- coordinator/iocontrol.lua | 13 +-- coordinator/process.lua | 109 ++++++++++++---------- coordinator/threads.lua | 4 + coordinator/ui/components/process_ctl.lua | 4 +- coordinator/ui/components/unit_detail.lua | 8 +- 6 files changed, 81 insertions(+), 67 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 000ce5f..6e06fed 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -591,6 +591,8 @@ function coordinator.comms(version, nic, sv_watchdog) process.waste_ack_handle(packet.data[2]) elseif cmd == FAC_COMMAND.SET_PU_FB then process.pu_fb_ack_handle(packet.data[2]) + elseif cmd == FAC_COMMAND.SET_SPS_LP then + ---@todo else log.debug(util.c("received facility command ack with unknown command ", cmd)) end @@ -625,17 +627,17 @@ function coordinator.comms(version, nic, sv_watchdog) if unit ~= nil then if cmd == UNIT_COMMAND.SCRAM then - unit.scram_ack(ack) + process.unit_ack(unit_id, cmd, ack) elseif cmd == UNIT_COMMAND.START then - unit.start_ack(ack) + process.unit_ack(unit_id, cmd, ack) elseif cmd == UNIT_COMMAND.RESET_RPS then - unit.reset_rps_ack(ack) + process.unit_ack(unit_id, cmd, ack) elseif cmd == UNIT_COMMAND.SET_BURN then -- this also doesn't exist elseif cmd == UNIT_COMMAND.SET_WASTE then -- updated by unit updates elseif cmd == UNIT_COMMAND.ACK_ALL_ALARMS then - unit.ack_alarms_ack(ack) + process.unit_ack(unit_id, cmd, ack) elseif cmd == UNIT_COMMAND.SET_GROUP then -- updated by unit updates else diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index de2b8cc..956cf41 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -82,9 +82,6 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) io.energy_convert_to_fe = util.joules_to_fe_rf end - -- coordinator's process handle - io.process = process.create_handle() - -- facility data structure ---@class ioctl_facility io.facility = { @@ -121,8 +118,6 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) save_cfg_ack = __generic_ack, start_ack = __generic_ack, stop_ack = __generic_ack, - scram_ack = __generic_ack, - ack_alarms_ack = __generic_ack, alarm_tones = { false, false, false, false, false, false, false, false }, @@ -198,11 +193,6 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) set_group = function (grp) process.set_group(i, grp) end, ---@param grp integer|0 group ID or 0 for manual - start_ack = __generic_ack, - scram_ack = __generic_ack, - reset_rps_ack = __generic_ack, - ack_alarms_ack = __generic_ack, - alarm_callbacks = { c_breach = { ack = function () ack(1) end, reset = function () reset(1) end }, radiation = { ack = function () ack(2) end, reset = function () reset(2) end }, @@ -282,6 +272,9 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) -- pass IO control here since it can't be require'd due to a require loop process.init(io, comms) + + -- coordinator's process handle + io.process = process.create_handle() end --#region Front Panel PSIL diff --git a/coordinator/process.lua b/coordinator/process.lua index 06078e2..9df2884 100644 --- a/coordinator/process.lua +++ b/coordinator/process.lua @@ -19,8 +19,8 @@ local REQUEST_TIMEOUT_MS = 10000 local process = {} local pctl = { - io = nil, ---@type ioctl - comms = nil, ---@type coord_comms + io = nil, ---@type ioctl + comms = nil, ---@type coord_comms ---@class sys_control_states control_states = { ---@class sys_auto_config @@ -37,14 +37,16 @@ local pctl = { waste_modes = {}, priority_groups = {} }, - next_handle = 0, - commands = { unit = {}, fac = {} } + commands = { + unit = {}, ---@type process_command_state[][] + fac = {} ---@type process_command_state[] + } } ---@class process_command_state ----@field active boolean ----@field timeout integer ----@field requestors table +---@field active boolean if this command is live +---@field timeout integer expiration time of this command request +---@field requestors table list of callbacks from the requestors -- write auto process control to config file local function _write_auto_config() @@ -138,15 +140,12 @@ end -- create a handle to process control for usage of commands that get acknowledgements function process.create_handle() - local self = { - id = pctl.next_handle - } - - pctl.next_handle = pctl.next_handle + 1 - ---@class process_handle local handle = {} + -- add this handle to the requestors and activate the command if inactive + ---@param cmd process_command_state + ---@param ack function local function request(cmd, ack) local new = not cmd.active @@ -155,7 +154,7 @@ function process.create_handle() cmd.timeout = util.time_ms() + REQUEST_TIMEOUT_MS end - table.insert(cmd.requstors, ack) + table.insert(cmd.requestors, ack) return new end @@ -167,7 +166,7 @@ function process.create_handle() -- facility SCRAM command function handle.fac_scram() - if f_request(F_CMD.SCRAM_ALL, handle.on_fac_scram_ack) then + if f_request(F_CMD.SCRAM_ALL, handle.fac_ack.on_scram) then pctl.comms.send_fac_command(F_CMD.SCRAM_ALL) log.debug("PROCESS: FAC SCRAM ALL") end @@ -175,23 +174,25 @@ function process.create_handle() -- facility alarm acknowledge command function handle.fac_ack_alarms() - if f_request(F_CMD.ACK_ALL_ALARMS, handle.on_fac_ack_alarms_ack) then + if f_request(F_CMD.ACK_ALL_ALARMS, handle.fac_ack.on_ack_alarms) then pctl.comms.send_fac_command(F_CMD.ACK_ALL_ALARMS) log.debug("PROCESS: FAC ACK ALL ALARMS") end end + handle.fac_ack = {} + -- luacheck: no unused args -- facility SCRAM ack, override to implement ---@param success boolean ---@diagnostic disable-next-line: unused-local - function handle.on_fac_scram_ack(success) end + function handle.fac_ack.on_scram(success) end -- facility acknowledge all alarms ack, override to implement ---@param success boolean ---@diagnostic disable-next-line: unused-local - function handle.on_fac_ack_alarms_ack(success) end + function handle.fac_ack.on_ack_alarms(success) end -- luacheck: unused args @@ -202,7 +203,7 @@ function process.create_handle() -- start a reactor ---@param id integer unit ID function handle.start(id) - if u_request(id, U_CMD.START, handle.on_unit_start_ack) then + if u_request(id, U_CMD.START, handle.unit_ack[id].on_start) then pctl.io.units[id].control_state = true pctl.comms.send_unit_command(U_CMD.START, id) log.debug(util.c("PROCESS: UNIT[", id, "] START")) @@ -212,7 +213,7 @@ function process.create_handle() -- SCRAM reactor ---@param id integer unit ID function handle.scram(id) - if u_request(id, U_CMD.SCRAM, handle.on_unit_scram_ack) then + if u_request(id, U_CMD.SCRAM, handle.unit_ack[id].on_scram) then pctl.io.units[id].control_state = false pctl.comms.send_unit_command(U_CMD.SCRAM, id) log.debug(util.c("PROCESS: UNIT[", id, "] SCRAM")) @@ -222,7 +223,7 @@ function process.create_handle() -- reset reactor protection system ---@param id integer unit ID function handle.reset_rps(id) - if u_request(id, U_CMD.RESET_RPS, handle.on_unit_rps_reset_ack) then + if u_request(id, U_CMD.RESET_RPS, handle.unit_ack[id].on_rps_reset) then pctl.comms.send_unit_command(U_CMD.RESET_RPS, id) log.debug(util.c("PROCESS: UNIT[", id, "] RESET RPS")) end @@ -231,52 +232,64 @@ function process.create_handle() -- acknowledge all alarms ---@param id integer unit ID function handle.ack_all_alarms(id) - if u_request(id, U_CMD.ACK_ALL_ALARMS, handle.on_unit_ack_alarms_ack) then + if u_request(id, U_CMD.ACK_ALL_ALARMS, handle.unit_ack[id].on_ack_alarms) then pctl.comms.send_unit_command(U_CMD.ACK_ALL_ALARMS, id) log.debug(util.c("PROCESS: UNIT[", id, "] ACK ALL ALARMS")) end end - -- luacheck: no unused args + -- unit command acknowledgement callbacks, indexed by unit ID + ---@type process_unit_ack[] + handle.unit_ack = {} - -- unit start ack, override to implement - ---@param success boolean - ---@diagnostic disable-next-line: unused-local - function handle.on_unit_start_ack(success) end + for u = 1, pctl.io.facility.num_units do + handle.unit_ack[u] = {} - -- unit SCRAM ack, override to implement - ---@param success boolean - ---@diagnostic disable-next-line: unused-local - function handle.on_unit_scram_ack(success) end + ---@class process_unit_ack + local u_ack = handle.unit_ack[u] - -- unit RPS reset ack, override to implement - ---@param success boolean - ---@diagnostic disable-next-line: unused-local - function handle.on_unit_rps_reset_ack(success) end + -- luacheck: no unused args - -- unit acknowledge all alarms ack, override to implement - ---@param success boolean - ---@diagnostic disable-next-line: unused-local - function handle.on_unit_ack_alarms_ack(success) end + -- unit start ack, override to implement + ---@param success boolean + ---@diagnostic disable-next-line: unused-local + function u_ack.on_start(success) end - -- luacheck: unused args + -- unit SCRAM ack, override to implement + ---@param success boolean + ---@diagnostic disable-next-line: unused-local + function u_ack.on_scram(success) end + + -- unit RPS reset ack, override to implement + ---@param success boolean + ---@diagnostic disable-next-line: unused-local + function u_ack.on_rps_reset(success) end + + -- unit acknowledge all alarms ack, override to implement + ---@param success boolean + ---@diagnostic disable-next-line: unused-local + function u_ack.on_ack_alarms(success) end + + -- luacheck: unused args + end --#endregion return handle end +-- clear outstanding process commands that have timed out function process.clear_timed_out() local now = util.time_ms() local objs = { pctl.commands.fac, table.unpack(pctl.commands.unit) } for _, obj in pairs(objs) do - ---@cast obj process_command_state - -- cancel expired requests - if obj.active and now > obj.timeout then - obj.active = false - obj.requestors = {} + for _, cmd in pairs(obj) do + if cmd.active and now > cmd.timeout then + cmd.active = false + cmd.requestors = {} + end end end end @@ -303,11 +316,13 @@ function process.fac_ack(command, success) end -- handle a unit command acknowledgement ----@param unit integer unit ID +---@param unit any unit ID (invalid index protected) ---@param command UNIT_COMMAND command ---@param success boolean if the command was successful function process.unit_ack(unit, command, success) - cmd_ack(pctl.commands.unit[unit][command], success) + if pctl.commands.unit[unit] then + cmd_ack(pctl.commands.unit[unit][command], success) + end end --#region One-Way Commands (no acknowledgements) diff --git a/coordinator/threads.lua b/coordinator/threads.lua index 20ba7ae..cefe705 100644 --- a/coordinator/threads.lua +++ b/coordinator/threads.lua @@ -6,6 +6,7 @@ local util = require("scada-common.util") local coordinator = require("coordinator.coordinator") local iocontrol = require("coordinator.iocontrol") +local process = require("coordinator.process") local renderer = require("coordinator.renderer") local sounder = require("coordinator.sounder") @@ -147,6 +148,9 @@ function threads.thread__main(smem) apisessions.iterate_all() apisessions.free_all_closed() + -- clear timed out process commands + process.clear_timed_out() + if renderer.ui_ready() then -- update clock used on main and flow monitors iocontrol.get_db().facility.ps.publish("date_time", os.date(smem.date_format)) diff --git a/coordinator/ui/components/process_ctl.lua b/coordinator/ui/components/process_ctl.lua index cbfa420..85e0436 100644 --- a/coordinator/ui/components/process_ctl.lua +++ b/coordinator/ui/components/process_ctl.lua @@ -66,8 +66,8 @@ local function new_view(root, x, y) local scram = HazardButton{parent=main,x=1,y=1,text="FAC SCRAM",accent=colors.yellow,dis_colors=dis_colors,callback=db.process.fac_scram,fg_bg=hzd_fg_bg} local ack_a = HazardButton{parent=main,x=16,y=1,text="ACK \x13",accent=colors.orange,dis_colors=dis_colors,callback=db.process.fac_ack_alarms,fg_bg=hzd_fg_bg} - db.process.on_fac_scram_ack = scram.on_response - db.process.on_fac_ack_alarms_ack = ack_a.on_response + db.process.fac_ack.on_scram = scram.on_response + db.process.fac_ack.on_ack_alarms = ack_a.on_response local all_ok = IndicatorLight{parent=main,y=5,label="Unit Systems Online",colors=ind_grn} local rad_mon = TriIndicatorLight{parent=main,label="Radiation Monitor",c1=style.ind_bkg,c2=ind_yel.fgd,c3=ind_grn.fgd} diff --git a/coordinator/ui/components/unit_detail.lua b/coordinator/ui/components/unit_detail.lua index 479c099..d898d34 100644 --- a/coordinator/ui/components/unit_detail.lua +++ b/coordinator/ui/components/unit_detail.lua @@ -373,10 +373,10 @@ local function init(parent, id) local scram = HazardButton{parent=main,x=2,y=32,text="SCRAM",accent=colors.yellow,dis_colors=dis_colors,callback=unit.scram,fg_bg=hzd_fg_bg} local reset = HazardButton{parent=main,x=22,y=32,text="RESET",accent=colors.red,dis_colors=dis_colors,callback=unit.reset_rps,fg_bg=hzd_fg_bg} - unit.start_ack = start.on_response - unit.scram_ack = scram.on_response - unit.reset_rps_ack = reset.on_response - unit.ack_alarms_ack = ack_a.on_response + db.process.unit_ack[id].on_start = start.on_response + db.process.unit_ack[id].on_scram = scram.on_response + db.process.unit_ack[id].on_rps_reset = reset.on_response + db.process.unit_ack[id].on_ack_alarms = ack_a.on_response local function start_button_en_check() if (unit.reactor_data ~= nil) and (unit.reactor_data.mek_status ~= nil) then