Merge pull request #560 from MikaylaFischler/pocket-alpha-dev

Pocket Process Control
This commit is contained in:
Mikayla
2024-10-14 13:24:45 -04:00
committed by GitHub
22 changed files with 827 additions and 174 deletions

View File

@@ -387,10 +387,14 @@ function coordinator.comms(version, nic, sv_watchdog)
end
-- send the auto process control configuration with a start command
---@param auto_cfg sys_auto_config configuration
function public.send_auto_start(auto_cfg)
---@param mode PROCESS process control mode
---@param burn_target number burn rate target
---@param charge_target number charge level target
---@param gen_target number generation rate target
---@param limits number[] unit burn rate limits
function public.send_auto_start(mode, burn_target, charge_target, gen_target, limits)
_send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.FAC_CMD, {
FAC_COMMAND.START, auto_cfg.mode, auto_cfg.burn_target, auto_cfg.charge_target, auto_cfg.gen_target, auto_cfg.limits
FAC_COMMAND.START, mode, burn_target, charge_target, gen_target, limits
})
end
@@ -578,7 +582,7 @@ function coordinator.comms(version, nic, sv_watchdog)
if cmd == FAC_COMMAND.SCRAM_ALL then
process.fac_ack(cmd, ack)
elseif cmd == FAC_COMMAND.STOP then
iocontrol.get_db().facility.stop_ack(ack)
process.fac_ack(cmd, ack)
elseif cmd == FAC_COMMAND.START then
if packet.length == 7 then
process.start_ack_handle({ table.unpack(packet.data, 2) })

View File

@@ -84,6 +84,8 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale)
all_sys_ok = false,
rtu_count = 0,
status_lines = { "", "" },
auto_ready = false,
auto_active = false,
auto_ramping = false,
@@ -107,8 +109,6 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale)
radiation = types.new_zero_radiation_reading(),
save_cfg_ack = nil, ---@type fun(success: boolean)
start_ack = nil, ---@type fun(success: boolean)
stop_ack = nil, ---@type fun(success: boolean)
---@type { [TONE]: boolean }
alarm_tones = { false, false, false, false, false, false, false, false },
@@ -159,6 +159,11 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale)
num_snas = 0,
has_tank = conf.cooling.r_cool[i].TankConnection,
status_lines = { "", "" },
auto_ready = false,
auto_degraded = false,
control_state = false,
burn_rate_cmd = 0.0,
radiation = types.new_zero_radiation_reading(),
@@ -541,8 +546,8 @@ function iocontrol.update_facility_status(status)
fac.ascram_status.radiation = ctl_status[10]
fac.ascram_status.gen_fault = ctl_status[11]
fac.status_line_1 = ctl_status[12]
fac.status_line_2 = ctl_status[13]
fac.status_lines[1] = ctl_status[12]
fac.status_lines[2] = ctl_status[13]
fac.ps.publish("all_sys_ok", fac.all_sys_ok)
fac.ps.publish("auto_ready", fac.auto_ready)
@@ -555,8 +560,8 @@ function iocontrol.update_facility_status(status)
fac.ps.publish("as_crit_alarm", fac.ascram_status.crit_alarm)
fac.ps.publish("as_radiation", fac.ascram_status.radiation)
fac.ps.publish("as_gen_fault", fac.ascram_status.gen_fault)
fac.ps.publish("status_line_1", fac.status_line_1)
fac.ps.publish("status_line_2", fac.status_line_2)
fac.ps.publish("status_line_1", fac.status_lines[1])
fac.ps.publish("status_line_2", fac.status_lines[2])
local group_map = ctl_status[14]
@@ -1130,15 +1135,19 @@ function iocontrol.update_unit_statuses(statuses)
if type(unit_state) == "table" then
if #unit_state == 8 then
unit.status_lines[1] = unit_state[1]
unit.status_lines[2] = unit_state[2]
unit.auto_ready = unit_state[3]
unit.auto_degraded = unit_state[4]
unit.waste_mode = unit_state[5]
unit.waste_product = unit_state[6]
unit.last_rate_change_ms = unit_state[7]
unit.turbine_flow_stable = unit_state[8]
unit.unit_ps.publish("U_StatusLine1", unit_state[1])
unit.unit_ps.publish("U_StatusLine2", unit_state[2])
unit.unit_ps.publish("U_AutoReady", unit_state[3])
unit.unit_ps.publish("U_AutoDegraded", unit_state[4])
unit.unit_ps.publish("U_StatusLine1", unit.status_lines[1])
unit.unit_ps.publish("U_StatusLine2", unit.status_lines[2])
unit.unit_ps.publish("U_AutoReady", unit.auto_ready)
unit.unit_ps.publish("U_AutoDegraded", unit.auto_degraded)
unit.unit_ps.publish("U_AutoWaste", unit.waste_mode == types.WASTE_MODE.AUTO)
unit.unit_ps.publish("U_WasteMode", unit.waste_mode)
unit.unit_ps.publish("U_WasteProduct", unit.waste_product)

View File

@@ -25,12 +25,12 @@ local pctl = {
control_states = {
---@class sys_auto_config
process = {
mode = PROCESS.INACTIVE,
mode = PROCESS.INACTIVE, ---@type PROCESS
burn_target = 0.0,
charge_target = 0.0,
gen_target = 0.0,
limits = {}, ---@type number[]
waste_product = PRODUCT.PLUTONIUM,
limits = {}, ---@type number[]
waste_product = PRODUCT.PLUTONIUM, ---@type WASTE_PRODUCT
pu_fallback = false,
sps_low_power = false
},
@@ -49,6 +49,7 @@ local pctl = {
---@field requestors function[] list of callbacks from the requestors
-- write auto process control to config file
---@return boolean saved
local function _write_auto_config()
-- save config
settings.set("ControlStates", pctl.control_states)
@@ -60,6 +61,8 @@ local function _write_auto_config()
return saved
end
--#region Core
-- initialize the process controller
---@param iocontrol ioctl iocontrl system
---@param coord_comms coord_comms coordinator communications
@@ -180,6 +183,36 @@ function process.create_handle()
end
end
-- start automatic process control with current settings
function handle.process_start()
if f_request(F_CMD.START, handle.fac_ack.on_start) then
local p = pctl.control_states.process
pctl.comms.send_auto_start(p.mode, p.burn_target, p.charge_target, p.gen_target, p.limits)
log.debug("PROCESS: START AUTO CTRL")
end
end
-- start automatic process control with remote settings that haven't been set on the coordinator
---@param mode PROCESS process control mode
---@param burn_target number burn rate target
---@param charge_target number charge level target
---@param gen_target number generation rate target
---@param limits number[] unit burn rate limits
function handle.process_start_remote(mode, burn_target, charge_target, gen_target, limits)
if f_request(F_CMD.START, handle.fac_ack.on_start) then
pctl.comms.send_auto_start(mode, burn_target, charge_target, gen_target, limits)
log.debug("PROCESS: START AUTO CTRL")
end
end
-- stop process control
function handle.process_stop()
if f_request(F_CMD.STOP, handle.fac_ack.on_stop) then
pctl.comms.send_fac_command(F_CMD.STOP)
log.debug("PROCESS: STOP AUTO CTRL")
end
end
handle.fac_ack = {}
-- luacheck: no unused args
@@ -194,6 +227,16 @@ function process.create_handle()
---@diagnostic disable-next-line: unused-local
function handle.fac_ack.on_ack_alarms(success) end
-- facility auto control start ack, override to implement
---@param success boolean
---@diagnostic disable-next-line: unused-local
function handle.fac_ack.on_start(success) end
-- facility auto control stop ack, override to implement
---@param success boolean
---@diagnostic disable-next-line: unused-local
function handle.fac_ack.on_stop(success) end
-- luacheck: unused args
--#endregion
@@ -294,6 +337,14 @@ function process.clear_timed_out()
end
end
-- get the control states table
---@nodiscard
function process.get_control_states() return pctl.control_states end
--#endregion
--#region Command Handling
-- handle a command acknowledgement
---@param cmd_state process_command_state
---@param success boolean if the command was successful
@@ -335,6 +386,21 @@ function process.set_rate(id, rate)
log.debug(util.c("PROCESS: UNIT[", id, "] SET BURN ", rate))
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)
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))
pctl.control_states.priority_groups[unit_id] = group_id
settings.set("ControlStates", pctl.control_states)
if not settings.save("/coordinator.settings") then
log.error("process.set_group(): failed to save coordinator settings file")
end
end
-- set waste mode
---@param id integer unit ID
---@param mode integer waste mode
@@ -369,39 +435,12 @@ function process.reset_alarm(id, alarm)
log.debug(util.c("PROCESS: UNIT[", id, "] RESET ALARM ", alarm))
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)
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))
pctl.control_states.priority_groups[unit_id] = group_id
settings.set("ControlStates", pctl.control_states)
if not settings.save("/coordinator.settings") then
log.error("process.set_group(): failed to save coordinator settings file")
end
end
--#endregion
--------------------------
-- AUTO PROCESS CONTROL --
--------------------------
-- start automatic process control
function process.start_auto()
pctl.comms.send_auto_start(pctl.control_states.process)
log.debug("PROCESS: START AUTO CTL")
end
-- stop automatic process control
function process.stop_auto()
pctl.comms.send_fac_command(F_CMD.STOP)
log.debug("PROCESS: STOP AUTO CTL")
end
-- set automatic process control waste mode
---@param product WASTE_PRODUCT waste product for auto control
function process.set_process_waste(product)
@@ -439,9 +478,9 @@ function process.set_sps_low_power(enabled)
end
-- save process control settings
---@param mode PROCESS control mode
---@param mode PROCESS process control mode
---@param burn_target number burn rate target
---@param charge_target number charge target
---@param charge_target number charge level target
---@param gen_target number generation rate target
---@param limits number[] unit burn rate limits
function process.save(mode, burn_target, charge_target, gen_target, limits)
@@ -472,9 +511,7 @@ function process.start_ack_handle(response)
for i = 1, math.min(#response[6], pctl.io.facility.num_units) do
ctl_proc.limits[i] = response[6][i]
local unit = pctl.io.units[i]
unit.unit_ps.publish("burn_limit", ctl_proc.limits[i])
pctl.io.units[i].unit_ps.publish("burn_limit", ctl_proc.limits[i])
end
pctl.io.facility.ps.publish("process_mode", ctl_proc.mode)
@@ -482,7 +519,9 @@ function process.start_ack_handle(response)
pctl.io.facility.ps.publish("process_charge_target", pctl.io.energy_convert_from_fe(ctl_proc.charge_target))
pctl.io.facility.ps.publish("process_gen_target", pctl.io.energy_convert_from_fe(ctl_proc.gen_target))
pctl.io.facility.start_ack(ack)
_write_auto_config()
process.fac_ack(F_CMD.START, ack)
end
-- record waste product settting after attempting to change it
@@ -506,4 +545,6 @@ function process.sps_lp_ack_handle(response)
pctl.io.facility.ps.publish("process_sps_low_power", response)
end
--#endregion
return process

View File

@@ -108,14 +108,20 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
-- link callback transmissions
self.proc_handle.fac_ack.on_scram = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.SCRAM_ALL, success }) end
self.proc_handle.fac_ack.on_ack_alarms = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.ACK_ALL_ALARMS, success }) end
local f_ack = self.proc_handle.fac_ack
f_ack.on_scram = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.SCRAM_ALL, success }) end
f_ack.on_ack_alarms = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.ACK_ALL_ALARMS, success }) end
f_ack.on_start = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.START, success }) end
f_ack.on_stop = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.STOP, success }) end
for u = 1, iocontrol.get_db().facility.num_units do
self.proc_handle.unit_ack[u].on_start = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.START, u, success }) end
self.proc_handle.unit_ack[u].on_scram = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.SCRAM, u, success }) end
self.proc_handle.unit_ack[u].on_rps_reset = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.RESET_RPS, u, success }) end
self.proc_handle.unit_ack[u].on_ack_alarms = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.ACK_ALL_ALARMS, u, success }) end
local u_ack = self.proc_handle.unit_ack[u]
u_ack.on_start = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.START, u, success }) end
u_ack.on_scram = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.SCRAM, u, success }) end
u_ack.on_rps_reset = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.RESET_RPS, u, success }) end
u_ack.on_ack_alarms = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.ACK_ALL_ALARMS, u, success }) end
end
-- handle a packet
@@ -147,7 +153,15 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
log.info(log_tag .. "FAC SCRAM ALL")
self.proc_handle.fac_scram()
elseif cmd == FAC_COMMAND.STOP then
log.info(log_tag .. "STOP PROCESS CTRL")
self.proc_handle.process_stop()
elseif cmd == FAC_COMMAND.START then
if pkt.length == 6 then
log.info(log_tag .. "START PROCESS CTRL")
self.proc_handle.process_start_remote(pkt.data[2], pkt.data[3], pkt.data[4], pkt.data[5], pkt.data[6])
else
log.debug(log_tag .. "CRDN auto start (with configuration) packet length mismatch")
end
elseif cmd == FAC_COMMAND.ACK_ALL_ALARMS then
log.info(log_tag .. "FAC ACK ALL ALARMS")
self.proc_handle.fac_ack_alarms()
@@ -191,6 +205,12 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
elseif cmd == UNIT_COMMAND.ACK_ALARM then
elseif cmd == UNIT_COMMAND.RESET_ALARM then
elseif cmd == UNIT_COMMAND.SET_GROUP then
if pkt.length == 3 then
log.info(util.c(log_tag, "UNIT[", uid, "] SET GROUP ", pkt.data[3]))
process.set_group(uid, pkt.data[3])
else
log.debug(log_tag .. "CRDN unit set group missing option")
end
else
log.debug(log_tag .. "CRDN unit command unknown")
end
@@ -259,6 +279,37 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
end
_send(CRDN_TYPE.API_GET_CTRL, data)
elseif pkt.type == CRDN_TYPE.API_GET_PROC then
local data = {}
local fac = db.facility
local proc = process.get_control_states().process
-- unit data
for i = 1, #db.units do
local u = db.units[i]
data[i] = {
u.reactor_data.mek_status.status,
u.reactor_data.mek_struct.max_burn,
proc.limits[i],
u.auto_ready,
u.auto_degraded,
u.annunciator.AutoControl,
u.a_group
}
end
-- facility data
data[#db.units + 1] = {
fac.status_lines,
{ fac.auto_ready, fac.auto_active, fac.auto_ramping, fac.auto_saturated },
fac.auto_scram,
fac.ascram_status,
{ proc.mode, proc.burn_target, proc.charge_target, proc.gen_target }
}
_send(CRDN_TYPE.API_GET_PROC, data)
else
log.debug(log_tag .. "handler received unsupported CRDN packet type " .. pkt.type)
end

View File

@@ -264,24 +264,22 @@ local function new_view(root, x, y)
local limits = {}
for i = 1, #rate_limits do limits[i] = rate_limits[i].get_value() end
process.save(mode.get_value(), b_target.get_value(),
db.energy_convert_to_fe(c_target.get_value()),
db.energy_convert_to_fe(g_target.get_value()),
limits)
process.save(mode.get_value(), b_target.get_value(), db.energy_convert_to_fe(c_target.get_value()),
db.energy_convert_to_fe(g_target.get_value()), limits)
end
-- start automatic control after saving process control settings
local function _start_auto()
_save_cfg()
process.start_auto()
db.process.process_start()
end
local save = HazardButton{parent=auto_controls,x=2,y=2,text="SAVE",accent=colors.purple,dis_colors=dis_colors,callback=_save_cfg,fg_bg=hzd_fg_bg}
local start = HazardButton{parent=auto_controls,x=13,y=2,text="START",accent=colors.lightBlue,dis_colors=dis_colors,callback=_start_auto,fg_bg=hzd_fg_bg}
local stop = HazardButton{parent=auto_controls,x=23,y=2,text="STOP",accent=colors.red,dis_colors=dis_colors,callback=process.stop_auto,fg_bg=hzd_fg_bg}
local stop = HazardButton{parent=auto_controls,x=23,y=2,text="STOP",accent=colors.red,dis_colors=dis_colors,callback=db.process.process_stop,fg_bg=hzd_fg_bg}
facility.start_ack = start.on_response
facility.stop_ack = stop.on_response
db.process.fac_ack.on_start = start.on_response
db.process.fac_ack.on_stop = stop.on_response
function facility.save_cfg_ack(ack)
tcd.dispatch(0.2, function () save.on_response(ack) end)