WIP rearchitecting process command orchestration

This commit is contained in:
Mikayla Fischler 2024-08-27 23:05:46 -04:00
parent 0f40c1d7f2
commit fbbd7e1ccd
3 changed files with 181 additions and 115 deletions

View File

@ -82,6 +82,9 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale)
io.energy_convert_to_fe = util.joules_to_fe_rf io.energy_convert_to_fe = util.joules_to_fe_rf
end end
-- coordinator's process handle
io.process = process.create_handle()
-- facility data structure -- facility data structure
---@class ioctl_facility ---@class ioctl_facility
io.facility = { io.facility = {
@ -186,10 +189,10 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale)
-- auto control group -- auto control group
a_group = 0, a_group = 0,
start = function () process.start(i) end, start = function () io.process.start(i) end,
scram = function () process.scram(i) end, scram = function () io.process.scram(i) end,
reset_rps = function () process.reset_rps(i) end, reset_rps = function () io.process.reset_rps(i) end,
ack_alarms = function () process.ack_all_alarms(i) end, ack_alarms = function () io.process.ack_all_alarms(i) end,
set_burn = function (rate) process.set_rate(i, rate) end, ---@param rate number burn rate set_burn = function (rate) process.set_rate(i, rate) end, ---@param rate number burn rate
set_waste = function (mode) process.set_unit_waste(i, mode) end, ---@param mode WASTE_MODE waste processing mode set_waste = function (mode) process.set_unit_waste(i, mode) end, ---@param mode WASTE_MODE waste processing mode

View File

@ -7,16 +7,18 @@ local log = require("scada-common.log")
local types = require("scada-common.types") local types = require("scada-common.types")
local util = require("scada-common.util") local util = require("scada-common.util")
local FAC_COMMAND = comms.FAC_COMMAND local F_CMD = comms.FAC_COMMAND
local UNIT_COMMAND = comms.UNIT_COMMAND local U_CMD = comms.UNIT_COMMAND
local PROCESS = types.PROCESS local PROCESS = types.PROCESS
local PRODUCT = types.WASTE_PRODUCT local PRODUCT = types.WASTE_PRODUCT
local REQUEST_TIMEOUT_MS = 5000
---@class process_controller ---@class process_controller
local process = {} local process = {}
local self = { local pctl = {
io = nil, ---@type ioctl io = nil, ---@type ioctl
comms = nil, ---@type coord_comms comms = nil, ---@type coord_comms
---@class sys_control_states ---@class sys_control_states
@ -34,23 +36,36 @@ local self = {
}, },
waste_modes = {}, waste_modes = {},
priority_groups = {} priority_groups = {}
} },
next_handle = 0,
commands = { unit = {}, fac = {} }
} }
-------------------------- for _, v in pairs(U_CMD) do pctl.commands.unit[v] = { active = false, timeout = 0, requestors = {} } end
-- UNIT COMMAND CONTROL -- for _, v in pairs(F_CMD) do pctl.commands.fac[v] = { active = false, timeout = 0, requestors = {} } end
--------------------------
-- write auto process control to config file
local function _write_auto_config()
-- save config
settings.set("ControlStates", pctl.control_states)
local saved = settings.save("/coordinator.settings")
if not saved then
log.warning("process._write_auto_config(): failed to save coordinator settings file")
end
return saved
end
-- initialize the process controller -- initialize the process controller
---@param iocontrol ioctl iocontrl system ---@param iocontrol ioctl iocontrl system
---@param coord_comms coord_comms coordinator communications ---@param coord_comms coord_comms coordinator communications
function process.init(iocontrol, coord_comms) function process.init(iocontrol, coord_comms)
self.io = iocontrol pctl.io = iocontrol
self.comms = coord_comms pctl.comms = coord_comms
local ctl_proc = self.control_states.process local ctl_proc = pctl.control_states.process
for i = 1, self.io.facility.num_units do for i = 1, pctl.io.facility.num_units do
ctl_proc.limits[i] = 0.1 ctl_proc.limits[i] = 0.1
end end
@ -68,33 +83,33 @@ function process.init(iocontrol, coord_comms)
ctl_proc.pu_fallback = config.pu_fallback ctl_proc.pu_fallback = config.pu_fallback
ctl_proc.sps_low_power = config.sps_low_power ctl_proc.sps_low_power = config.sps_low_power
self.io.facility.ps.publish("process_mode", ctl_proc.mode) pctl.io.facility.ps.publish("process_mode", ctl_proc.mode)
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target) pctl.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
self.io.facility.ps.publish("process_charge_target", self.io.energy_convert_from_fe(ctl_proc.charge_target)) pctl.io.facility.ps.publish("process_charge_target", pctl.io.energy_convert_from_fe(ctl_proc.charge_target))
self.io.facility.ps.publish("process_gen_target", self.io.energy_convert_from_fe(ctl_proc.gen_target)) pctl.io.facility.ps.publish("process_gen_target", pctl.io.energy_convert_from_fe(ctl_proc.gen_target))
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product) pctl.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback) pctl.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
self.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power) pctl.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power)
for id = 1, math.min(#ctl_proc.limits, self.io.facility.num_units) do for id = 1, math.min(#ctl_proc.limits, pctl.io.facility.num_units) do
local unit = self.io.units[id] ---@type ioctl_unit local unit = pctl.io.units[id] ---@type ioctl_unit
unit.unit_ps.publish("burn_limit", ctl_proc.limits[id]) unit.unit_ps.publish("burn_limit", ctl_proc.limits[id])
end end
log.info("PROCESS: loaded auto control settings") log.info("PROCESS: loaded auto control settings")
-- notify supervisor of auto waste config -- notify supervisor of auto waste config
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, ctl_proc.waste_product) pctl.comms.send_fac_command(F_CMD.SET_WASTE_MODE, ctl_proc.waste_product)
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, ctl_proc.pu_fallback) pctl.comms.send_fac_command(F_CMD.SET_PU_FB, ctl_proc.pu_fallback)
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, ctl_proc.sps_low_power) pctl.comms.send_fac_command(F_CMD.SET_SPS_LP, ctl_proc.sps_low_power)
end end
-- unit waste states -- unit waste states
local waste_modes = ctrl_states.waste_modes ---@type table|nil local waste_modes = ctrl_states.waste_modes ---@type table|nil
if type(waste_modes) == "table" then if type(waste_modes) == "table" then
for id, mode in pairs(waste_modes) do for id, mode in pairs(waste_modes) do
self.control_states.waste_modes[id] = mode pctl.control_states.waste_modes[id] = mode
self.comms.send_unit_command(UNIT_COMMAND.SET_WASTE, id, mode) pctl.comms.send_unit_command(U_CMD.SET_WASTE, id, mode)
end end
log.info("PROCESS: loaded unit waste mode settings") log.info("PROCESS: loaded unit waste mode settings")
@ -104,54 +119,119 @@ function process.init(iocontrol, coord_comms)
local prio_groups = ctrl_states.priority_groups ---@type table|nil local prio_groups = ctrl_states.priority_groups ---@type table|nil
if type(prio_groups) == "table" then if type(prio_groups) == "table" then
for id, group in pairs(prio_groups) do for id, group in pairs(prio_groups) do
self.control_states.priority_groups[id] = group pctl.control_states.priority_groups[id] = group
self.comms.send_unit_command(UNIT_COMMAND.SET_GROUP, id, group) pctl.comms.send_unit_command(U_CMD.SET_GROUP, id, group)
end end
log.info("PROCESS: loaded priority groups settings") log.info("PROCESS: loaded priority groups settings")
end end
end end
-- facility SCRAM command -- create a handle to process control for usage of commands that get acknowledgements
function process.fac_scram() function process.create_handle()
self.comms.send_fac_command(FAC_COMMAND.SCRAM_ALL) local self = {
log.debug("PROCESS: FAC SCRAM ALL") id = pctl.next_handle
}
pctl.next_handle = pctl.next_handle + 1
---@class process_handle
local handle = {}
local function request(cmd)
local new = not cmd.active
if new then
cmd.active = true
cmd.timeout = util.time_ms() + REQUEST_TIMEOUT_MS
end
cmd.requstors[self.id] = true
return new
end
local function u_request(cmd_id) return request(pctl.commands.unit[cmd_id]) end
local function f_request(cmd_id) return request(pctl.commands.fac[cmd_id]) end
--#region Facility Commands
-- facility SCRAM command
function handle.fac_scram()
if f_request(F_CMD.SCRAM_ALL) then
pctl.comms.send_fac_command(F_CMD.SCRAM_ALL)
log.debug("PROCESS: FAC SCRAM ALL")
end
end
-- facility alarm acknowledge command
function handle.fac_ack_alarms()
if f_request(F_CMD.ACK_ALL_ALARMS) then
pctl.comms.send_fac_command(F_CMD.ACK_ALL_ALARMS)
log.debug("PROCESS: FAC ACK ALL ALARMS")
end
end
--#endregion
--#region Unit Commands
-- start a reactor
---@param id integer unit ID
function handle.start(id)
if u_request(U_CMD.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"))
end
end
-- SCRAM reactor
---@param id integer unit ID
function handle.scram(id)
if u_request(U_CMD.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"))
end
end
-- reset reactor protection system
---@param id integer unit ID
function handle.reset_rps(id)
if u_request(U_CMD.RESET_RPS) then
pctl.comms.send_unit_command(U_CMD.RESET_RPS, id)
log.debug(util.c("PROCESS: UNIT[", id, "] RESET RPS"))
end
end
-- acknowledge all alarms
---@param id integer unit ID
function handle.ack_all_alarms(id)
if u_request(U_CMD.ACK_ALL_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
--#endregion
return handle
end end
-- facility alarm acknowledge command function process.clear_timed_out()
function process.fac_ack_alarms()
self.comms.send_fac_command(FAC_COMMAND.ACK_ALL_ALARMS)
log.debug("PROCESS: FAC ACK ALL ALARMS")
end end
-- start reactor function process.handle_ack()
---@param id integer unit ID
function process.start(id)
self.io.units[id].control_state = true
self.comms.send_unit_command(UNIT_COMMAND.START, id)
log.debug(util.c("PROCESS: UNIT[", id, "] START"))
end end
-- SCRAM reactor --#region One-Way Commands (no acknowledgements)
---@param id integer unit ID
function process.scram(id)
self.io.units[id].control_state = false
self.comms.send_unit_command(UNIT_COMMAND.SCRAM, id)
log.debug(util.c("PROCESS: UNIT[", id, "] SCRAM"))
end
-- reset reactor protection system
---@param id integer unit ID
function process.reset_rps(id)
self.comms.send_unit_command(UNIT_COMMAND.RESET_RPS, id)
log.debug(util.c("PROCESS: UNIT[", id, "] RESET RPS"))
end
-- set burn rate -- set burn rate
---@param id integer unit ID ---@param id integer unit ID
---@param rate number burn rate ---@param rate number burn rate
function process.set_rate(id, rate) function process.set_rate(id, rate)
self.comms.send_unit_command(UNIT_COMMAND.SET_BURN, id, rate) pctl.comms.send_unit_command(U_CMD.SET_BURN, id, rate)
log.debug(util.c("PROCESS: UNIT[", id, "] SET BURN ", rate)) log.debug(util.c("PROCESS: UNIT[", id, "] SET BURN ", rate))
end end
@ -160,31 +240,24 @@ end
---@param mode integer waste mode ---@param mode integer waste mode
function process.set_unit_waste(id, mode) function process.set_unit_waste(id, mode)
-- publish so that if it fails then it gets reset -- publish so that if it fails then it gets reset
self.io.units[id].unit_ps.publish("U_WasteMode", mode) pctl.io.units[id].unit_ps.publish("U_WasteMode", mode)
self.comms.send_unit_command(UNIT_COMMAND.SET_WASTE, id, mode) pctl.comms.send_unit_command(U_CMD.SET_WASTE, id, mode)
log.debug(util.c("PROCESS: UNIT[", id, "] SET WASTE ", mode)) log.debug(util.c("PROCESS: UNIT[", id, "] SET WASTE ", mode))
self.control_states.waste_modes[id] = mode pctl.control_states.waste_modes[id] = mode
settings.set("ControlStates", self.control_states) settings.set("ControlStates", pctl.control_states)
if not settings.save("/coordinator.settings") then if not settings.save("/coordinator.settings") then
log.error("process.set_unit_waste(): failed to save coordinator settings file") log.error("process.set_unit_waste(): failed to save coordinator settings file")
end end
end end
-- acknowledge all alarms
---@param id integer unit ID
function process.ack_all_alarms(id)
self.comms.send_unit_command(UNIT_COMMAND.ACK_ALL_ALARMS, id)
log.debug(util.c("PROCESS: UNIT[", id, "] ACK ALL ALARMS"))
end
-- acknowledge an alarm -- acknowledge an alarm
---@param id integer unit ID ---@param id integer unit ID
---@param alarm integer alarm ID ---@param alarm integer alarm ID
function process.ack_alarm(id, alarm) function process.ack_alarm(id, alarm)
self.comms.send_unit_command(UNIT_COMMAND.ACK_ALARM, id, alarm) pctl.comms.send_unit_command(U_CMD.ACK_ALARM, id, alarm)
log.debug(util.c("PROCESS: UNIT[", id, "] ACK ALARM ", alarm)) log.debug(util.c("PROCESS: UNIT[", id, "] ACK ALARM ", alarm))
end end
@ -192,7 +265,7 @@ end
---@param id integer unit ID ---@param id integer unit ID
---@param alarm integer alarm ID ---@param alarm integer alarm ID
function process.reset_alarm(id, alarm) function process.reset_alarm(id, alarm)
self.comms.send_unit_command(UNIT_COMMAND.RESET_ALARM, id, alarm) pctl.comms.send_unit_command(U_CMD.RESET_ALARM, id, alarm)
log.debug(util.c("PROCESS: UNIT[", id, "] RESET ALARM ", alarm)) log.debug(util.c("PROCESS: UNIT[", id, "] RESET ALARM ", alarm))
end end
@ -200,78 +273,68 @@ end
---@param unit_id integer unit ID ---@param unit_id integer unit ID
---@param group_id integer|0 group ID or 0 for independent ---@param group_id integer|0 group ID or 0 for independent
function process.set_group(unit_id, group_id) function process.set_group(unit_id, group_id)
self.comms.send_unit_command(UNIT_COMMAND.SET_GROUP, unit_id, group_id) pctl.comms.send_unit_command(U_CMD.SET_GROUP, unit_id, group_id)
log.debug(util.c("PROCESS: UNIT[", unit_id, "] SET GROUP ", group_id)) log.debug(util.c("PROCESS: UNIT[", unit_id, "] SET GROUP ", group_id))
self.control_states.priority_groups[unit_id] = group_id pctl.control_states.priority_groups[unit_id] = group_id
settings.set("ControlStates", self.control_states) settings.set("ControlStates", pctl.control_states)
if not settings.save("/coordinator.settings") then if not settings.save("/coordinator.settings") then
log.error("process.set_group(): failed to save coordinator settings file") log.error("process.set_group(): failed to save coordinator settings file")
end end
end end
--#endregion
-------------------------- --------------------------
-- AUTO PROCESS CONTROL -- -- AUTO PROCESS CONTROL --
-------------------------- --------------------------
-- write auto process control to config file -- start automatic process control
local function _write_auto_config() function process.start_auto()
-- save config pctl.comms.send_auto_start(pctl.control_states.process)
settings.set("ControlStates", self.control_states) log.debug("PROCESS: START AUTO CTL")
local saved = settings.save("/coordinator.settings")
if not saved then
log.warning("process._write_auto_config(): failed to save coordinator settings file")
end
return saved
end end
-- stop automatic process control -- stop automatic process control
function process.stop_auto() function process.stop_auto()
self.comms.send_fac_command(FAC_COMMAND.STOP) pctl.comms.send_fac_command(F_CMD.STOP)
log.debug("PROCESS: STOP AUTO CTL") log.debug("PROCESS: STOP AUTO CTL")
end end
-- start automatic process control
function process.start_auto()
self.comms.send_auto_start(self.control_states.process)
log.debug("PROCESS: START AUTO CTL")
end
-- set automatic process control waste mode -- set automatic process control waste mode
---@param product WASTE_PRODUCT waste product for auto control ---@param product WASTE_PRODUCT waste product for auto control
function process.set_process_waste(product) function process.set_process_waste(product)
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, product) pctl.comms.send_fac_command(F_CMD.SET_WASTE_MODE, product)
log.debug(util.c("PROCESS: SET WASTE ", product)) log.debug(util.c("PROCESS: SET WASTE ", product))
-- update config table and save -- update config table and save
self.control_states.process.waste_product = product pctl.control_states.process.waste_product = product
_write_auto_config() _write_auto_config()
end end
-- set automatic process control plutonium fallback -- set automatic process control plutonium fallback
---@param enabled boolean whether to enable plutonium fallback ---@param enabled boolean whether to enable plutonium fallback
function process.set_pu_fallback(enabled) function process.set_pu_fallback(enabled)
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, enabled) pctl.comms.send_fac_command(F_CMD.SET_PU_FB, enabled)
log.debug(util.c("PROCESS: SET PU FALLBACK ", enabled)) log.debug(util.c("PROCESS: SET PU FALLBACK ", enabled))
-- update config table and save -- update config table and save
self.control_states.process.pu_fallback = enabled pctl.control_states.process.pu_fallback = enabled
_write_auto_config() _write_auto_config()
end end
-- set automatic process control SPS usage at low power -- set automatic process control SPS usage at low power
---@param enabled boolean whether to enable SPS usage at low power ---@param enabled boolean whether to enable SPS usage at low power
function process.set_sps_low_power(enabled) function process.set_sps_low_power(enabled)
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, enabled) pctl.comms.send_fac_command(F_CMD.SET_SPS_LP, enabled)
log.debug(util.c("PROCESS: SET SPS LOW POWER ", enabled)) log.debug(util.c("PROCESS: SET SPS LOW POWER ", enabled))
-- update config table and save -- update config table and save
self.control_states.process.sps_low_power = enabled pctl.control_states.process.sps_low_power = enabled
_write_auto_config() _write_auto_config()
end end
@ -285,7 +348,7 @@ function process.save(mode, burn_target, charge_target, gen_target, limits)
log.debug("PROCESS: SAVE") log.debug("PROCESS: SAVE")
-- update config table -- update config table
local ctl_proc = self.control_states.process local ctl_proc = pctl.control_states.process
ctl_proc.mode = mode ctl_proc.mode = mode
ctl_proc.burn_target = burn_target ctl_proc.burn_target = burn_target
ctl_proc.charge_target = charge_target ctl_proc.charge_target = charge_target
@ -293,7 +356,7 @@ function process.save(mode, burn_target, charge_target, gen_target, limits)
ctl_proc.limits = limits ctl_proc.limits = limits
-- save config -- save config
self.io.facility.save_cfg_ack(_write_auto_config()) pctl.io.facility.save_cfg_ack(_write_auto_config())
end end
-- handle a start command acknowledgement -- handle a start command acknowledgement
@ -301,39 +364,39 @@ end
function process.start_ack_handle(response) function process.start_ack_handle(response)
local ack = response[1] local ack = response[1]
local ctl_proc = self.control_states.process local ctl_proc = pctl.control_states.process
ctl_proc.mode = response[2] ctl_proc.mode = response[2]
ctl_proc.burn_target = response[3] ctl_proc.burn_target = response[3]
ctl_proc.charge_target = response[4] ctl_proc.charge_target = response[4]
ctl_proc.gen_target = response[5] ctl_proc.gen_target = response[5]
for i = 1, math.min(#response[6], self.io.facility.num_units) do for i = 1, math.min(#response[6], pctl.io.facility.num_units) do
ctl_proc.limits[i] = response[6][i] ctl_proc.limits[i] = response[6][i]
local unit = self.io.units[i] ---@type ioctl_unit local unit = pctl.io.units[i] ---@type ioctl_unit
unit.unit_ps.publish("burn_limit", ctl_proc.limits[i]) unit.unit_ps.publish("burn_limit", ctl_proc.limits[i])
end end
self.io.facility.ps.publish("process_mode", ctl_proc.mode) pctl.io.facility.ps.publish("process_mode", ctl_proc.mode)
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target) pctl.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
self.io.facility.ps.publish("process_charge_target", self.io.energy_convert_from_fe(ctl_proc.charge_target)) pctl.io.facility.ps.publish("process_charge_target", pctl.io.energy_convert_from_fe(ctl_proc.charge_target))
self.io.facility.ps.publish("process_gen_target", self.io.energy_convert_from_fe(ctl_proc.gen_target)) pctl.io.facility.ps.publish("process_gen_target", pctl.io.energy_convert_from_fe(ctl_proc.gen_target))
self.io.facility.start_ack(ack) pctl.io.facility.start_ack(ack)
end end
-- record waste product state after attempting to change it -- record waste product state after attempting to change it
---@param response WASTE_PRODUCT supervisor waste product state ---@param response WASTE_PRODUCT supervisor waste product state
function process.waste_ack_handle(response) function process.waste_ack_handle(response)
self.control_states.process.waste_product = response pctl.control_states.process.waste_product = response
self.io.facility.ps.publish("process_waste_product", response) pctl.io.facility.ps.publish("process_waste_product", response)
end end
-- record plutonium fallback state after attempting to change it -- record plutonium fallback state after attempting to change it
---@param response boolean supervisor plutonium fallback state ---@param response boolean supervisor plutonium fallback state
function process.pu_fb_ack_handle(response) function process.pu_fb_ack_handle(response)
self.control_states.process.pu_fallback = response pctl.control_states.process.pu_fallback = response
self.io.facility.ps.publish("process_pu_fallback", response) pctl.io.facility.ps.publish("process_pu_fallback", response)
end end
return process return process

View File

@ -63,8 +63,8 @@ local function new_view(root, x, y)
local main = Div{parent=root,width=128,height=24,x=x,y=y} local main = Div{parent=root,width=128,height=24,x=x,y=y}
local scram = HazardButton{parent=main,x=1,y=1,text="FAC SCRAM",accent=colors.yellow,dis_colors=dis_colors,callback=process.fac_scram,fg_bg=hzd_fg_bg} 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=process.fac_ack_alarms,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}
facility.scram_ack = scram.on_response facility.scram_ack = scram.on_response
facility.ack_alarms_ack = ack_a.on_response facility.ack_alarms_ack = ack_a.on_response