2023-02-23 04:09:47 +00:00
|
|
|
--
|
|
|
|
-- Process Control Management
|
|
|
|
--
|
2022-12-18 18:56:04 +00:00
|
|
|
|
|
|
|
local comms = require("scada-common.comms")
|
|
|
|
local log = require("scada-common.log")
|
2023-01-26 23:26:26 +00:00
|
|
|
local types = require("scada-common.types")
|
2022-12-18 18:56:04 +00:00
|
|
|
local util = require("scada-common.util")
|
|
|
|
|
2023-02-21 16:05:57 +00:00
|
|
|
local FAC_COMMAND = comms.FAC_COMMAND
|
|
|
|
local UNIT_COMMAND = comms.UNIT_COMMAND
|
2022-12-18 18:56:04 +00:00
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
local PROCESS = types.PROCESS
|
2023-07-08 20:57:13 +00:00
|
|
|
local PRODUCT = types.WASTE_PRODUCT
|
2023-01-26 23:26:26 +00:00
|
|
|
|
2022-12-18 18:56:04 +00:00
|
|
|
---@class process_controller
|
|
|
|
local process = {}
|
|
|
|
|
|
|
|
local self = {
|
|
|
|
io = nil, ---@type ioctl
|
2023-01-26 23:26:26 +00:00
|
|
|
comms = nil, ---@type coord_comms
|
2024-02-19 01:47:37 +00:00
|
|
|
---@class coord_control_states
|
|
|
|
control_states = {
|
|
|
|
---@class coord_auto_config
|
|
|
|
process = {
|
|
|
|
mode = PROCESS.INACTIVE,
|
|
|
|
burn_target = 0.0,
|
|
|
|
charge_target = 0.0,
|
|
|
|
gen_target = 0.0,
|
|
|
|
limits = {},
|
|
|
|
waste_product = PRODUCT.PLUTONIUM,
|
2024-04-28 06:01:21 +00:00
|
|
|
pu_fallback = false,
|
|
|
|
sps_low_power = false
|
2024-02-19 01:47:37 +00:00
|
|
|
},
|
|
|
|
waste_modes = {},
|
|
|
|
priority_groups = {}
|
2023-01-26 23:26:26 +00:00
|
|
|
}
|
2022-12-18 18:56:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
--------------------------
|
|
|
|
-- UNIT COMMAND CONTROL --
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
-- initialize the process controller
|
2023-02-23 04:09:47 +00:00
|
|
|
---@param iocontrol ioctl iocontrl system
|
|
|
|
---@param coord_comms coord_comms coordinator communications
|
|
|
|
function process.init(iocontrol, coord_comms)
|
2022-12-18 18:56:04 +00:00
|
|
|
self.io = iocontrol
|
2023-02-23 04:09:47 +00:00
|
|
|
self.comms = coord_comms
|
2023-01-24 01:47:45 +00:00
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
local ctl_proc = self.control_states.process
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
for i = 1, self.io.facility.num_units do
|
2024-02-19 01:47:37 +00:00
|
|
|
ctl_proc.limits[i] = 0.1
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
local ctrl_states = settings.get("ControlStates", {})
|
|
|
|
local config = ctrl_states.process ---@type coord_auto_config
|
2023-01-24 01:47:45 +00:00
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- facility auto control configuration
|
2023-01-26 23:26:26 +00:00
|
|
|
if type(config) == "table" then
|
2024-02-19 01:47:37 +00:00
|
|
|
ctl_proc.mode = config.mode
|
|
|
|
ctl_proc.burn_target = config.burn_target
|
|
|
|
ctl_proc.charge_target = config.charge_target
|
|
|
|
ctl_proc.gen_target = config.gen_target
|
|
|
|
ctl_proc.limits = config.limits
|
|
|
|
ctl_proc.waste_product = config.waste_product
|
|
|
|
ctl_proc.pu_fallback = config.pu_fallback
|
2024-04-28 06:01:21 +00:00
|
|
|
ctl_proc.sps_low_power = config.sps_low_power
|
2024-02-19 01:47:37 +00:00
|
|
|
|
|
|
|
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
|
|
|
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
2024-07-27 16:34:01 +00:00
|
|
|
self.io.facility.ps.publish("process_charge_target", self.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))
|
2024-02-19 01:47:37 +00:00
|
|
|
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
|
|
|
|
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
|
2024-04-28 06:01:21 +00:00
|
|
|
self.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power)
|
2024-02-19 01:47:37 +00:00
|
|
|
|
|
|
|
for id = 1, math.min(#ctl_proc.limits, self.io.facility.num_units) do
|
2023-01-26 23:26:26 +00:00
|
|
|
local unit = self.io.units[id] ---@type ioctl_unit
|
2024-02-19 01:47:37 +00:00
|
|
|
unit.unit_ps.publish("burn_limit", ctl_proc.limits[id])
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
log.info("PROCESS: loaded auto control settings")
|
2023-07-18 02:09:21 +00:00
|
|
|
|
|
|
|
-- notify supervisor of auto waste config
|
2024-02-19 01:47:37 +00:00
|
|
|
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, ctl_proc.waste_product)
|
|
|
|
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, ctl_proc.pu_fallback)
|
2024-04-28 06:01:21 +00:00
|
|
|
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, ctl_proc.sps_low_power)
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- unit waste states
|
2024-02-19 01:47:37 +00:00
|
|
|
local waste_modes = ctrl_states.waste_modes ---@type table|nil
|
2023-07-08 20:57:13 +00:00
|
|
|
if type(waste_modes) == "table" then
|
|
|
|
for id, mode in pairs(waste_modes) do
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.waste_modes[id] = mode
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.SET_WASTE, id, mode)
|
2023-01-24 01:47:45 +00:00
|
|
|
end
|
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
log.info("PROCESS: loaded unit waste mode settings")
|
2023-01-24 01:47:45 +00:00
|
|
|
end
|
2023-02-03 21:40:58 +00:00
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- unit priority groups
|
2024-02-19 01:47:37 +00:00
|
|
|
local prio_groups = ctrl_states.priority_groups ---@type table|nil
|
2023-02-03 21:40:58 +00:00
|
|
|
if type(prio_groups) == "table" then
|
|
|
|
for id, group in pairs(prio_groups) do
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.priority_groups[id] = group
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.SET_GROUP, id, group)
|
2023-02-03 21:40:58 +00:00
|
|
|
end
|
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
log.info("PROCESS: loaded priority groups settings")
|
2023-02-03 21:40:58 +00:00
|
|
|
end
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
2023-02-07 23:44:34 +00:00
|
|
|
-- facility SCRAM command
|
|
|
|
function process.fac_scram()
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_fac_command(FAC_COMMAND.SCRAM_ALL)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug("PROCESS: FAC SCRAM ALL")
|
2023-02-07 23:44:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- facility alarm acknowledge command
|
|
|
|
function process.fac_ack_alarms()
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_fac_command(FAC_COMMAND.ACK_ALL_ALARMS)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug("PROCESS: FAC ACK ALL ALARMS")
|
2023-02-07 23:44:34 +00:00
|
|
|
end
|
|
|
|
|
2022-12-18 18:56:04 +00:00
|
|
|
-- start reactor
|
|
|
|
---@param id integer unit ID
|
|
|
|
function process.start(id)
|
|
|
|
self.io.units[id].control_state = true
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.START, id)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] START"))
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- SCRAM reactor
|
|
|
|
---@param id integer unit ID
|
|
|
|
function process.scram(id)
|
|
|
|
self.io.units[id].control_state = false
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.SCRAM, id)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] SCRAM"))
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- reset reactor protection system
|
|
|
|
---@param id integer unit ID
|
|
|
|
function process.reset_rps(id)
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.RESET_RPS, id)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] RESET RPS"))
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
2023-01-13 19:03:47 +00:00
|
|
|
-- set burn rate
|
2022-12-18 18:56:04 +00:00
|
|
|
---@param id integer unit ID
|
|
|
|
---@param rate number burn rate
|
|
|
|
function process.set_rate(id, rate)
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.SET_BURN, id, rate)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] SET BURN ", rate))
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- set waste mode
|
|
|
|
---@param id integer unit ID
|
|
|
|
---@param mode integer waste mode
|
2023-07-08 20:57:13 +00:00
|
|
|
function process.set_unit_waste(id, mode)
|
2023-02-03 21:40:58 +00:00
|
|
|
-- publish so that if it fails then it gets reset
|
|
|
|
self.io.units[id].unit_ps.publish("U_WasteMode", mode)
|
|
|
|
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.SET_WASTE, id, mode)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] SET WASTE ", mode))
|
2023-01-24 01:47:45 +00:00
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.waste_modes[id] = mode
|
|
|
|
settings.set("ControlStates", self.control_states)
|
2023-01-24 01:47:45 +00:00
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
if not settings.save("/coordinator.settings") then
|
2023-07-08 20:57:13 +00:00
|
|
|
log.error("process.set_unit_waste(): failed to save coordinator settings file")
|
2023-01-24 01:47:45 +00:00
|
|
|
end
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- acknowledge all alarms
|
|
|
|
---@param id integer unit ID
|
|
|
|
function process.ack_all_alarms(id)
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.ACK_ALL_ALARMS, id)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] ACK ALL ALARMS"))
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- acknowledge an alarm
|
|
|
|
---@param id integer unit ID
|
|
|
|
---@param alarm integer alarm ID
|
|
|
|
function process.ack_alarm(id, alarm)
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.ACK_ALARM, id, alarm)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] ACK ALARM ", alarm))
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- reset an alarm
|
|
|
|
---@param id integer unit ID
|
|
|
|
---@param alarm integer alarm ID
|
|
|
|
function process.reset_alarm(id, alarm)
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.RESET_ALARM, id, alarm)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", id, "] RESET ALARM ", alarm))
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- assign a unit to a group
|
|
|
|
---@param unit_id integer unit ID
|
|
|
|
---@param group_id integer|0 group ID or 0 for independent
|
|
|
|
function process.set_group(unit_id, group_id)
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_unit_command(UNIT_COMMAND.SET_GROUP, unit_id, group_id)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug(util.c("PROCESS: UNIT[", unit_id, "] SET GROUP ", group_id))
|
2023-02-03 21:40:58 +00:00
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.priority_groups[unit_id] = group_id
|
|
|
|
settings.set("ControlStates", self.control_states)
|
2023-02-03 21:40:58 +00:00
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
if not settings.save("/coordinator.settings") then
|
2023-02-03 21:40:58 +00:00
|
|
|
log.error("process.set_group(): failed to save coordinator settings file")
|
|
|
|
end
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
--------------------------
|
|
|
|
-- AUTO PROCESS CONTROL --
|
|
|
|
--------------------------
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- write auto process control to config file
|
|
|
|
local function _write_auto_config()
|
|
|
|
-- save config
|
2024-02-19 01:47:37 +00:00
|
|
|
settings.set("ControlStates", self.control_states)
|
|
|
|
local saved = settings.save("/coordinator.settings")
|
2023-07-08 20:57:13 +00:00
|
|
|
if not saved then
|
2023-07-08 21:11:51 +00:00
|
|
|
log.warning("process._write_auto_config(): failed to save coordinator settings file")
|
2023-07-08 20:57:13 +00:00
|
|
|
end
|
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
return saved
|
2023-07-08 20:57:13 +00:00
|
|
|
end
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
-- stop automatic process control
|
|
|
|
function process.stop_auto()
|
2023-02-21 16:05:57 +00:00
|
|
|
self.comms.send_fac_command(FAC_COMMAND.STOP)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug("PROCESS: STOP AUTO CTL")
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- start automatic process control
|
|
|
|
function process.start_auto()
|
2024-02-19 01:47:37 +00:00
|
|
|
self.comms.send_auto_start(self.control_states.process)
|
2023-02-23 04:09:47 +00:00
|
|
|
log.debug("PROCESS: START AUTO CTL")
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- set automatic process control waste mode
|
|
|
|
---@param product WASTE_PRODUCT waste product for auto control
|
|
|
|
function process.set_process_waste(product)
|
|
|
|
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, product)
|
|
|
|
|
|
|
|
log.debug(util.c("PROCESS: SET WASTE ", product))
|
|
|
|
|
|
|
|
-- update config table and save
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.process.waste_product = product
|
2023-07-08 20:57:13 +00:00
|
|
|
_write_auto_config()
|
|
|
|
end
|
|
|
|
|
|
|
|
-- set automatic process control plutonium fallback
|
|
|
|
---@param enabled boolean whether to enable plutonium fallback
|
|
|
|
function process.set_pu_fallback(enabled)
|
|
|
|
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, enabled)
|
|
|
|
|
|
|
|
log.debug(util.c("PROCESS: SET PU FALLBACK ", enabled))
|
|
|
|
|
|
|
|
-- update config table and save
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.process.pu_fallback = enabled
|
2023-07-08 20:57:13 +00:00
|
|
|
_write_auto_config()
|
|
|
|
end
|
|
|
|
|
2024-04-28 06:01:21 +00:00
|
|
|
-- set automatic process control SPS usage at low power
|
|
|
|
---@param enabled boolean whether to enable SPS usage at low power
|
|
|
|
function process.set_sps_low_power(enabled)
|
|
|
|
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, enabled)
|
|
|
|
|
|
|
|
log.debug(util.c("PROCESS: SET SPS LOW POWER ", enabled))
|
|
|
|
|
|
|
|
-- update config table and save
|
|
|
|
self.control_states.process.sps_low_power = enabled
|
|
|
|
_write_auto_config()
|
|
|
|
end
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
-- save process control settings
|
|
|
|
---@param mode PROCESS control mode
|
|
|
|
---@param burn_target number burn rate target
|
|
|
|
---@param charge_target number charge target
|
|
|
|
---@param gen_target number generation rate target
|
|
|
|
---@param limits table unit burn rate limits
|
|
|
|
function process.save(mode, burn_target, charge_target, gen_target, limits)
|
2023-07-08 20:57:13 +00:00
|
|
|
log.debug("PROCESS: SAVE")
|
2023-01-26 23:26:26 +00:00
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- update config table
|
2024-02-19 01:47:37 +00:00
|
|
|
local ctl_proc = self.control_states.process
|
|
|
|
ctl_proc.mode = mode
|
|
|
|
ctl_proc.burn_target = burn_target
|
|
|
|
ctl_proc.charge_target = charge_target
|
|
|
|
ctl_proc.gen_target = gen_target
|
|
|
|
ctl_proc.limits = limits
|
2023-01-26 23:26:26 +00:00
|
|
|
|
|
|
|
-- save config
|
2023-07-08 20:57:13 +00:00
|
|
|
self.io.facility.save_cfg_ack(_write_auto_config())
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- handle a start command acknowledgement
|
|
|
|
---@param response table ack and configuration reply
|
|
|
|
function process.start_ack_handle(response)
|
|
|
|
local ack = response[1]
|
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
local ctl_proc = self.control_states.process
|
|
|
|
ctl_proc.mode = response[2]
|
|
|
|
ctl_proc.burn_target = response[3]
|
|
|
|
ctl_proc.charge_target = response[4]
|
|
|
|
ctl_proc.gen_target = response[5]
|
2023-01-26 23:26:26 +00:00
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
for i = 1, math.min(#response[6], self.io.facility.num_units) do
|
2024-02-19 01:47:37 +00:00
|
|
|
ctl_proc.limits[i] = response[6][i]
|
2023-07-08 20:57:13 +00:00
|
|
|
|
|
|
|
local unit = self.io.units[i] ---@type ioctl_unit
|
2024-02-19 01:47:37 +00:00
|
|
|
unit.unit_ps.publish("burn_limit", ctl_proc.limits[i])
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-02-19 01:47:37 +00:00
|
|
|
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
|
|
|
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
2024-07-27 16:34:01 +00:00
|
|
|
self.io.facility.ps.publish("process_charge_target", self.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))
|
2023-01-26 23:26:26 +00:00
|
|
|
|
|
|
|
self.io.facility.start_ack(ack)
|
2023-01-13 19:03:47 +00:00
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- record waste product state after attempting to change it
|
|
|
|
---@param response WASTE_PRODUCT supervisor waste product state
|
|
|
|
function process.waste_ack_handle(response)
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.process.waste_product = response
|
2023-07-08 20:57:13 +00:00
|
|
|
self.io.facility.ps.publish("process_waste_product", response)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- record plutonium fallback state after attempting to change it
|
|
|
|
---@param response boolean supervisor plutonium fallback state
|
|
|
|
function process.pu_fb_ack_handle(response)
|
2024-02-19 01:47:37 +00:00
|
|
|
self.control_states.process.pu_fallback = response
|
2023-07-08 20:57:13 +00:00
|
|
|
self.io.facility.ps.publish("process_pu_fallback", response)
|
|
|
|
end
|
|
|
|
|
2022-12-18 18:56:04 +00:00
|
|
|
return process
|