diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index d28c5c9..8d57bed 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -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, @@ -157,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(), @@ -539,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) @@ -553,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] @@ -1128,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) diff --git a/coordinator/process.lua b/coordinator/process.lua index 183a815..78be172 100644 --- a/coordinator/process.lua +++ b/coordinator/process.lua @@ -61,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 @@ -335,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 @@ -535,4 +545,6 @@ function process.sps_lp_ack_handle(response) pctl.io.facility.ps.publish("process_sps_low_power", response) end +--#endregion + return process diff --git a/coordinator/session/pocket.lua b/coordinator/session/pocket.lua index 3572d97..cb38a61 100644 --- a/coordinator/session/pocket.lua +++ b/coordinator/session/pocket.lua @@ -279,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 diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 36b5fd6..7b4cbd9 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -94,7 +94,8 @@ function iocontrol.init_core(pkt_comms, nav, cfg) ---@class pocket_ioctl_api io.api = { get_unit = function (unit) comms.api__get_unit(unit) end, - get_ctrl = function () comms.api__get_control() end + get_ctrl = function () comms.api__get_control() end, + get_proc = function () comms.api__get_process() end } end @@ -138,6 +139,8 @@ function iocontrol.init_fac(conf) all_sys_ok = false, rtu_count = 0, + status_lines = { "", "" }, + auto_ready = false, auto_active = false, auto_ramping = false, @@ -197,6 +200,11 @@ function iocontrol.init_fac(conf) 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(), @@ -817,48 +825,102 @@ function iocontrol.record_control_data(data) local unit = io.units[u_id] local u_data = data[u_id] - if type(u_data) ~= "table" then - log.debug(util.c("iocontrol.record_control_data: unit ", u_id, " data invalid")) - else - unit.connected = u_data[1] + unit.connected = u_data[1] - unit.reactor_data.rps_tripped = u_data[2] - unit.unit_ps.publish("rps_tripped", u_data[2]) - unit.reactor_data.mek_status.status = u_data[3] - unit.unit_ps.publish("status", u_data[3]) - unit.reactor_data.mek_status.temp = u_data[4] - unit.unit_ps.publish("temp", u_data[4]) - unit.reactor_data.mek_status.burn_rate = u_data[5] - unit.unit_ps.publish("burn_rate", u_data[5]) - unit.reactor_data.mek_status.act_burn_rate = u_data[6] - unit.unit_ps.publish("act_burn_rate", u_data[6]) - unit.reactor_data.mek_struct.max_burn = u_data[7] - unit.unit_ps.publish("max_burn", u_data[7]) + unit.reactor_data.rps_tripped = u_data[2] + unit.unit_ps.publish("rps_tripped", u_data[2]) + unit.reactor_data.mek_status.status = u_data[3] + unit.unit_ps.publish("status", u_data[3]) + unit.reactor_data.mek_status.temp = u_data[4] + unit.unit_ps.publish("temp", u_data[4]) + unit.reactor_data.mek_status.burn_rate = u_data[5] + unit.unit_ps.publish("burn_rate", u_data[5]) + unit.reactor_data.mek_status.act_burn_rate = u_data[6] + unit.unit_ps.publish("act_burn_rate", u_data[6]) + unit.reactor_data.mek_struct.max_burn = u_data[7] + unit.unit_ps.publish("max_burn", u_data[7]) - unit.annunciator.AutoControl = u_data[8] - unit.unit_ps.publish("AutoControl", u_data[8]) + unit.annunciator.AutoControl = u_data[8] + unit.unit_ps.publish("AutoControl", u_data[8]) - unit.a_group = u_data[9] - unit.unit_ps.publish("auto_group_id", unit.a_group) - unit.unit_ps.publish("auto_group", types.AUTO_GROUP_NAMES[unit.a_group + 1]) + unit.a_group = u_data[9] + unit.unit_ps.publish("auto_group_id", unit.a_group) + unit.unit_ps.publish("auto_group", types.AUTO_GROUP_NAMES[unit.a_group + 1]) - local control_status = 1 + local control_status = 1 - if unit.connected then - if unit.reactor_data.rps_tripped then - control_status = 2 - end - - if unit.reactor_data.mek_status.status then - control_status = util.trinary(unit.annunciator.AutoControl, 4, 3) - end + if unit.connected then + if unit.reactor_data.rps_tripped then + control_status = 2 end - unit.unit_ps.publish("U_ControlStatus", control_status) + if unit.reactor_data.mek_status.status then + control_status = util.trinary(unit.annunciator.AutoControl, 4, 3) + end end + + unit.unit_ps.publish("U_ControlStatus", control_status) end end +-- update process app with unit data from API_GET_PROC +---@param data table +function iocontrol.record_process_data(data) + -- get unit data + for u_id = 1, #io.units do + local unit = io.units[u_id] + local u_data = data[u_id] + + unit.reactor_data.mek_status.status = u_data[1] + unit.reactor_data.mek_struct.max_burn = u_data[2] + unit.annunciator.AutoControl = u_data[6] + unit.a_group = u_data[7] + + unit.unit_ps.publish("status", u_data[1]) + unit.unit_ps.publish("max_burn", u_data[2]) + unit.unit_ps.publish("burn_limit", u_data[3]) + unit.unit_ps.publish("U_AutoReady", u_data[4]) + unit.unit_ps.publish("U_AutoDegraded", u_data[5]) + unit.unit_ps.publish("AutoControl", u_data[6]) + unit.unit_ps.publish("auto_group_id", unit.a_group) + unit.unit_ps.publish("auto_group", types.AUTO_GROUP_NAMES[unit.a_group + 1]) + end + + -- get facility data + local fac = io.facility + local f_data = data[#io.units + 1] + + fac.status_lines = f_data[1] + + fac.auto_ready = f_data[2][1] + fac.auto_active = f_data[2][2] + fac.auto_ramping = f_data[2][3] + fac.auto_saturated = f_data[2][4] + + fac.auto_scram = f_data[3] + fac.ascram_status = f_data[4] + + fac.ps.publish("status_line_1", fac.status_lines[1]) + fac.ps.publish("status_line_2", fac.status_lines[2]) + + fac.ps.publish("auto_ready", fac.auto_ready) + fac.ps.publish("auto_active", fac.auto_active) + fac.ps.publish("auto_ramping", fac.auto_ramping) + fac.ps.publish("auto_saturated", fac.auto_saturated) + + fac.ps.publish("auto_scram", fac.auto_scram) + fac.ps.publish("as_matrix_dc", fac.ascram_status.matrix_dc) + fac.ps.publish("as_matrix_fill", fac.ascram_status.matrix_fill) + 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("process_mode", f_data[5][1]) + fac.ps.publish("process_burn_target", f_data[5][2]) + fac.ps.publish("process_charge_target", f_data[5][3]) + fac.ps.publish("process_gen_target", f_data[5][4]) +end + -- get the IO controller database function iocontrol.get_db() return io end diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 11b84a8..de6e6b2 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -560,6 +560,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) if self.api.linked then _send_api(CRDN_TYPE.API_GET_CTRL, {}) end end + -- coordinator get process app data + function public.api__get_process() + if self.api.linked then _send_api(CRDN_TYPE.API_GET_PROC, {}) end + end + -- send a facility command ---@param cmd FAC_COMMAND command ---@param option any? optional option options for the optional options (like waste mode) @@ -724,6 +729,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) if _check_length(packet, #iocontrol.get_db().units) then iocontrol.record_control_data(packet.data) end + elseif packet.type == CRDN_TYPE.API_GET_PROC then + if _check_length(packet, #iocontrol.get_db().units + 1) then + iocontrol.record_process_data(packet.data) + end else _fail_type(packet) end else log.debug("discarding coordinator SCADA_CRDN packet before linked") diff --git a/pocket/ui/apps/process.lua b/pocket/ui/apps/process.lua index ccc2423..5a8932b 100644 --- a/pocket/ui/apps/process.lua +++ b/pocket/ui/apps/process.lua @@ -90,7 +90,7 @@ local function new_view(root) -- refresh data callback, every 500ms it will re-send the query local function update() if util.time_ms() - last_update >= 500 then - -- db.api.get_ctrl() + db.api.get_proc() last_update = util.time_ms() end end @@ -134,7 +134,7 @@ local function new_view(root) else a_stb.update(false) end end) - local function _set_group(value) process.set_group(i, value) end + local function _set_group(value) process.set_group(i, value - 1) end local group = RadioButton{parent=u_div,y=10,options=types.AUTO_GROUP_NAMES,callback=_set_group,radio_colors=cpair(colors.lightGray,colors.gray),select_color=colors.purple,dis_fg_bg=style.btn_disable} @@ -161,6 +161,7 @@ local function new_view(root) local o_div = Div{parent=o_pane,x=2,width=main.get_width()-2} local opt_page = app.new_page(nil, db.facility.num_units + 2) + opt_page.tasks = { update } TextBox{parent=o_div,y=1,text="Process Options",alignment=ALIGN.CENTER} @@ -193,6 +194,7 @@ local function new_view(root) local c_div = Div{parent=c_pane,x=2,width=main.get_width()-2} local proc_ctrl = app.new_page(nil, db.facility.num_units + 1) + proc_ctrl.tasks = { update } TextBox{parent=c_div,y=1,text="Process Control",alignment=ALIGN.CENTER} @@ -264,6 +266,7 @@ local function new_view(root) local a_div = Div{parent=a_pane,x=2,width=main.get_width()-2} local annunc_page = app.new_page(nil, db.facility.num_units + 3) + annunc_page.tasks = { update } TextBox{parent=a_div,y=1,text="Automatic SCRAM",alignment=ALIGN.CENTER} diff --git a/scada-common/comms.lua b/scada-common/comms.lua index a1281ce..037ce5e 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -18,7 +18,7 @@ local comms = {} -- protocol/data versions (protocol/data independent changes tracked by util.lua version) comms.version = "3.0.0" -comms.api_version = "0.0.5" +comms.api_version = "0.0.6" ---@enum PROTOCOL local PROTOCOL = { @@ -68,7 +68,8 @@ local CRDN_TYPE = { UNIT_CMD = 6, -- command a reactor unit API_GET_FAC = 7, -- API: get all the facility data API_GET_UNIT = 8, -- API: get reactor unit data - API_GET_CTRL = 9 -- API: get data used for the control app + API_GET_CTRL = 9, -- API: get data used for the control app + API_GET_PROC = 10 -- API: get data used for the process app } ---@enum ESTABLISH_ACK