From 20bffec79f94b1d1f97001be0b8e41f84f372c28 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 10 Dec 2024 04:31:53 +0000 Subject: [PATCH] reworked computed status logic and handle dynamic tank data --- coordinator/iocontrol.lua | 145 ++++++++++++++++----------------- coordinator/session/pocket.lua | 10 ++- pocket/iocontrol.lua | 2 - pocket/iorx.lua | 105 ++++++++++++------------ 4 files changed, 133 insertions(+), 129 deletions(-) diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index ad78add..f7dde3d 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -20,6 +20,13 @@ local ENERGY_UNITS = types.ENERGY_SCALE_UNITS local TEMP_SCALE = types.TEMP_SCALE local TEMP_UNITS = types.TEMP_SCALE_UNITS +local RCT_STATE = types.REACTOR_STATE +local BLR_STATE = types.BOILER_STATE +local TRB_STATE = types.TURBINE_STATE +local TNK_STATE = types.TANK_STATE +local MTX_STATE = types.IMATRIX_STATE +local SPS_STATE = types.SPS_STATE + -- nominal RTT is ping (0ms to 10ms usually) + 500ms for CRD main loop tick local WARN_RTT = 1000 -- 2x as long as expected w/ 0 ping local HIGH_RTT = 1500 -- 3.33x as long as expected w/ 0 ping @@ -119,7 +126,6 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) induction_ps_tbl = {}, ---@type psil[] induction_data_tbl = {}, ---@type imatrix_session_db[] - sps_status = 1, sps_ps_tbl = {}, ---@type psil[] sps_data_tbl = {}, ---@type sps_session_db[] @@ -151,10 +157,6 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) local entry = { unit_id = i, connected = false, - rtu_hw = { - boilers = {}, ---@type { connected: boolean, faulted: boolean }[] - turbines = {} ---@type { connected: boolean, faulted: boolean }[] - }, num_boilers = 0, num_turbines = 0, @@ -249,14 +251,12 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) for _ = 1, conf.cooling.r_cool[i].BoilerCount do table.insert(entry.boiler_ps_tbl, psil.create()) table.insert(entry.boiler_data_tbl, {}) - table.insert(entry.rtu_hw.boilers, { connected = false, faulted = false }) end -- create turbine tables for _ = 1, conf.cooling.r_cool[i].TurbineCount do table.insert(entry.turbine_ps_tbl, psil.create()) table.insert(entry.turbine_data_tbl, {}) - table.insert(entry.rtu_hw.turbines, { connected = false, faulted = false }) end -- create tank tables @@ -629,10 +629,12 @@ function iocontrol.update_facility_status(status) -- induction matricies statuses if type(rtu_statuses.induction) == "table" then + local matrix_status = MTX_STATE.OFFLINE + for id = 1, #fac.induction_ps_tbl do if rtu_statuses.induction[id] == nil then -- disconnected - fac.induction_ps_tbl[id].publish("computed_status", 1) + fac.induction_ps_tbl[id].publish("computed_status", matrix_status) end end @@ -644,18 +646,20 @@ function iocontrol.update_facility_status(status) local rtu_faulted = _record_multiblock_status(matrix, data, ps) if rtu_faulted then - ps.publish("computed_status", 3) -- faulted + matrix_status = MTX_STATE.FAULT elseif data.formed then if data.tanks.energy_fill >= 0.99 then - ps.publish("computed_status", 6) -- full + matrix_status = MTX_STATE.HIGH_CHARGE elseif data.tanks.energy_fill <= 0.01 then - ps.publish("computed_status", 5) -- empty + matrix_status = MTX_STATE.LOW_CHARGE else - ps.publish("computed_status", 4) -- on-line + matrix_status = MTX_STATE.ONLINE end else - ps.publish("computed_status", 2) -- not formed + matrix_status = MTX_STATE.UNFORMED end + + ps.publish("computed_status", matrix_status) else log.debug(util.c(log_header, "invalid induction matrix id ", id)) end @@ -667,12 +671,12 @@ function iocontrol.update_facility_status(status) -- SPS statuses if type(rtu_statuses.sps) == "table" then - local sps_status = 1 + local sps_status = SPS_STATE.OFFLINE for id = 1, #fac.sps_ps_tbl do if rtu_statuses.sps[id] == nil then -- disconnected - fac.sps_ps_tbl[id].publish("computed_status", 1) + fac.sps_ps_tbl[id].publish("computed_status", sps_status) end end @@ -684,11 +688,11 @@ function iocontrol.update_facility_status(status) local rtu_faulted = _record_multiblock_status(sps, data, ps) if rtu_faulted then - sps_status = 3 -- faulted + sps_status = SPS_STATE.FAULT elseif data.formed then -- active / idle - sps_status = util.trinary(data.state.process_rate > 0, 5, 4) - else sps_status = 2 end -- not formed + sps_status = util.trinary(data.state.process_rate > 0, SPS_STATE.ACTIVE, SPS_STATE.IDLE) + else sps_status = SPS_STATE.UNFORMED end ps.publish("computed_status", sps_status) @@ -697,8 +701,6 @@ function iocontrol.update_facility_status(status) log.debug(util.c(log_header, "invalid sps id ", id)) end end - - io.facility.sps_status = sps_status else log.debug(log_header .. "sps list not a table") valid = false @@ -706,10 +708,12 @@ function iocontrol.update_facility_status(status) -- dynamic tank statuses if type(rtu_statuses.tanks) == "table" then + local tank_status = TNK_STATE.OFFLINE + for id = 1, #fac.tank_ps_tbl do if rtu_statuses.tanks[id] == nil then -- disconnected - fac.tank_ps_tbl[id].publish("computed_status", 1) + fac.tank_ps_tbl[id].publish("computed_status", tank_status) end end @@ -721,18 +725,18 @@ function iocontrol.update_facility_status(status) local rtu_faulted = _record_multiblock_status(tank, data, ps) if rtu_faulted then - ps.publish("computed_status", 3) -- faulted + tank_status = TNK_STATE.FAULT elseif data.formed then if data.tanks.fill >= 0.99 then - ps.publish("computed_status", 6) -- full + tank_status = TNK_STATE.HIGH_FILL elseif data.tanks.fill < 0.20 then - ps.publish("computed_status", 5) -- low + tank_status = TNK_STATE.LOW_FILL else - ps.publish("computed_status", 4) -- on-line + tank_status = TNK_STATE.ONLINE end - else - ps.publish("computed_status", 2) -- not formed - end + else tank_status = TNK_STATE.UNFORMED end + + ps.publish("computed_status", tank_status) else log.debug(util.c(log_header, "invalid dynamic tank id ", id)) end @@ -832,9 +836,11 @@ function iocontrol.update_unit_statuses(statuses) log.debug(log_header .. "reactor status not a table") end + local computed_status = RCT_STATE.OFFLINE + if #reactor_status == 0 then unit.connected = false - unit.unit_ps.publish("computed_status", 1) -- disconnected + unit.unit_ps.publish("computed_status", computed_status) elseif #reactor_status == 3 then local mek_status = reactor_status[1] local rps_status = reactor_status[2] @@ -873,22 +879,23 @@ function iocontrol.update_unit_statuses(statuses) burn_rate_sum = burn_rate_sum + burn_rate if unit.reactor_data.mek_status.status then - unit.unit_ps.publish("computed_status", 5) -- running + computed_status = RCT_STATE.ACTIVE else if unit.reactor_data.no_reactor then - unit.unit_ps.publish("computed_status", 3) -- faulted + computed_status = RCT_STATE.FAULT elseif not unit.reactor_data.formed then - unit.unit_ps.publish("computed_status", 2) -- multiblock not formed + computed_status = RCT_STATE.UNFORMED elseif unit.reactor_data.rps_status.force_dis then - unit.unit_ps.publish("computed_status", 7) -- reactor force disabled + computed_status = RCT_STATE.FORCE_DISABLED elseif unit.reactor_data.rps_tripped and unit.reactor_data.rps_trip_cause ~= "manual" then - unit.unit_ps.publish("computed_status", 6) -- SCRAM + computed_status = RCT_STATE.SCRAMMED else - unit.unit_ps.publish("computed_status", 4) -- disabled + computed_status = RCT_STATE.DISABLED end end unit.connected = true + unit.unit_ps.publish("computed_status", computed_status) else log.debug(log_header .. "reactor status length mismatch") valid = false @@ -902,13 +909,11 @@ function iocontrol.update_unit_statuses(statuses) if type(rtu_statuses.boilers) == "table" then local boil_sum = 0 - for id = 1, #unit.boiler_ps_tbl do - local connected = rtu_statuses.boilers[id] ~= nil - unit.rtu_hw.boilers[id].connected = connected + computed_status = BLR_STATE.OFFLINE - if not connected then - -- disconnected - unit.boiler_ps_tbl[id].publish("computed_status", 1) + for id = 1, #unit.boiler_ps_tbl do + if rtu_statuses.boilers[id] == nil then + unit.boiler_ps_tbl[id].publish("computed_status", computed_status) end end @@ -918,21 +923,15 @@ function iocontrol.update_unit_statuses(statuses) local ps = unit.boiler_ps_tbl[id] local rtu_faulted = _record_multiblock_status(boiler, data, ps) - unit.rtu_hw.boilers[id].faulted = rtu_faulted if rtu_faulted then - ps.publish("computed_status", 3) -- faulted + computed_status = BLR_STATE.FAULT elseif data.formed then boil_sum = boil_sum + data.state.boil_rate + computed_status = util.trinary(data.state.boil_rate > 0, BLR_STATE.ACTIVE, BLR_STATE.IDLE) + else computed_status = BLR_STATE.UNFORMED end - if data.state.boil_rate > 0 then - ps.publish("computed_status", 5) -- active - else - ps.publish("computed_status", 4) -- idle - end - else - ps.publish("computed_status", 2) -- not formed - end + unit.boiler_ps_tbl[id].publish("computed_status", computed_status) else log.debug(util.c(log_header, "invalid boiler id ", id)) valid = false @@ -949,13 +948,11 @@ function iocontrol.update_unit_statuses(statuses) if type(rtu_statuses.turbines) == "table" then local flow_sum = 0 - for id = 1, #unit.turbine_ps_tbl do - local connected = rtu_statuses.turbines[id] ~= nil - unit.rtu_hw.turbines[id].connected = connected + computed_status = TRB_STATE.OFFLINE - if not connected then - -- disconnected - unit.turbine_ps_tbl[id].publish("computed_status", 1) + for id = 1, #unit.turbine_ps_tbl do + if rtu_statuses.turbines[id] == nil then + unit.turbine_ps_tbl[id].publish("computed_status", computed_status) end end @@ -965,23 +962,22 @@ function iocontrol.update_unit_statuses(statuses) local ps = unit.turbine_ps_tbl[id] local rtu_faulted = _record_multiblock_status(turbine, data, ps) - unit.rtu_hw.turbines[id].faulted = rtu_faulted if rtu_faulted then - ps.publish("computed_status", 3) -- faulted + computed_status = TRB_STATE.FAULT elseif data.formed then flow_sum = flow_sum + data.state.flow_rate if data.tanks.energy_fill >= 0.99 then - ps.publish("computed_status", 6) -- trip + computed_status = TRB_STATE.TRIPPED elseif data.state.flow_rate < 100 then - ps.publish("computed_status", 4) -- idle + computed_status = TRB_STATE.IDLE else - ps.publish("computed_status", 5) -- active + computed_status = TRB_STATE.ACTIVE end - else - ps.publish("computed_status", 2) -- not formed - end + else computed_status = TRB_STATE.UNFORMED end + + unit.turbine_ps_tbl[id].publish("computed_status", computed_status) else log.debug(util.c(log_header, "invalid turbine id ", id)) valid = false @@ -996,10 +992,11 @@ function iocontrol.update_unit_statuses(statuses) -- dynamic tank statuses if type(rtu_statuses.tanks) == "table" then + computed_status = TNK_STATE.OFFLINE + for id = 1, #unit.tank_ps_tbl do if rtu_statuses.tanks[id] == nil then - -- disconnected - unit.tank_ps_tbl[id].publish("computed_status", 1) + unit.tank_ps_tbl[id].publish("computed_status", computed_status) end end @@ -1011,18 +1008,18 @@ function iocontrol.update_unit_statuses(statuses) local rtu_faulted = _record_multiblock_status(tank, data, ps) if rtu_faulted then - ps.publish("computed_status", 3) -- faulted + computed_status = TNK_STATE.FAULT elseif data.formed then if data.tanks.fill >= 0.99 then - ps.publish("computed_status", 6) -- full + computed_status = TNK_STATE.HIGH_FILL elseif data.tanks.fill < 0.20 then - ps.publish("computed_status", 5) -- low + computed_status = TNK_STATE.LOW_FILL else - ps.publish("computed_status", 4) -- on-line + computed_status = TNK_STATE.ONLINE end - else - ps.publish("computed_status", 2) -- not formed - end + else computed_status = TNK_STATE.UNFORMED end + + unit.tank_ps_tbl[id].publish("computed_status", computed_status) else log.debug(util.c(log_header, "invalid dynamic tank id ", id)) valid = false diff --git a/coordinator/session/pocket.lua b/coordinator/session/pocket.lua index 6d60ab0..c8b77de 100644 --- a/coordinator/session/pocket.lua +++ b/coordinator/session/pocket.lua @@ -269,11 +269,17 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout) if pkt.length == 1 and type(pkt.data[1]) == "number" then local u = db.units[pkt.data[1]] + local statuses = { u.unit_ps.get("computed_status") } + + for i = 1, #u.boiler_ps_tbl do table.insert(statuses, u.boiler_ps_tbl[i].get("computed_status")) end + for i = 1, #u.turbine_ps_tbl do table.insert(statuses, u.turbine_ps_tbl[i].get("computed_status")) end + for i = 1, #u.tank_ps_tbl do table.insert(statuses, u.tank_ps_tbl[i].get("computed_status")) end + if u then local data = { u.unit_id, u.connected, - u.rtu_hw, + statuses, u.a_group, u.alarms, u.annunciator, @@ -375,7 +381,7 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout) proc.pu_fallback, proc.sps_low_power, fac.waste_stats, - fac.sps_status, + fac.sps_ps_tbl[1].get("computed_status") or types.SPS_STATE.OFFLINE, process_rate } diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index fee041d..b860aa4 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -196,8 +196,6 @@ function iocontrol.init_fac(conf) local entry = { unit_id = i, connected = false, - ---@type { boilers: { connected: boolean, faulted: boolean }[], turbines: { connected: boolean, faulted: boolean }[] } - rtu_hw = {}, num_boilers = 0, num_turbines = 0, diff --git a/pocket/iorx.lua b/pocket/iorx.lua index 522c14f..0aac073 100644 --- a/pocket/iorx.lua +++ b/pocket/iorx.lua @@ -9,6 +9,10 @@ local util = require("scada-common.util") local ALARM = types.ALARM local ALARM_STATE = types.ALARM_STATE +local BLR_STATE = types.BOILER_STATE +local TRB_STATE = types.TURBINE_STATE +local TNK_STATE = types.TANK_STATE + local io ---@type pocket_ioctl local iorx = {} ---@class iorx @@ -61,10 +65,12 @@ function iorx.record_unit_data(data) local unit = io.units[data[1]] unit.connected = data[2] - unit.rtu_hw = data[3] + local comp_statuses = data[3] unit.a_group = data[4] unit.alarms = data[5] + local next_c_stat = 1 + unit.unit_ps.publish("auto_group_id", unit.a_group) unit.unit_ps.publish("auto_group", types.AUTO_GROUP_NAMES[unit.a_group + 1]) @@ -152,43 +158,27 @@ function iorx.record_unit_data(data) local control_status = 1 local reactor_status = 1 - local reactor_state = 1 local rps_status = 1 if unit.connected then -- update RPS status if unit.reactor_data.rps_tripped then control_status = 2 - - if unit.reactor_data.rps_trip_cause == "manual" then - reactor_state = 4 -- disabled - rps_status = 3 - else - reactor_state = 6 -- SCRAM - rps_status = 2 - end + rps_status = util.trinary(unit.reactor_data.rps_trip_cause == "manual", 3, 2) else rps_status = 4 - reactor_state = 4 end + reactor_status = 4 -- ok, until proven otherwise + -- update reactor/control status if unit.reactor_data.mek_status.status then - reactor_status = 4 - reactor_state = 5 -- running control_status = util.trinary(unit.annunciator.AutoControl, 4, 3) else if unit.reactor_data.no_reactor then reactor_status = 2 - reactor_state = 3 -- faulted - elseif not unit.reactor_data.formed then + elseif (not unit.reactor_data.formed) or unit.reactor_data.rps_status.force_dis then reactor_status = 3 - reactor_state = 2 -- not formed - elseif unit.reactor_data.rps_status.force_dis then - reactor_status = 3 - reactor_state = 7 -- force disabled - else - reactor_status = 4 end end @@ -213,9 +203,11 @@ function iorx.record_unit_data(data) unit.unit_ps.publish("U_ControlStatus", control_status) unit.unit_ps.publish("U_ReactorStatus", reactor_status) - unit.unit_ps.publish("U_ReactorStateStatus", reactor_state) + unit.unit_ps.publish("U_ReactorStateStatus", comp_statuses[next_c_stat]) unit.unit_ps.publish("U_RPS", rps_status) + next_c_stat = next_c_stat + 1 + --#endregion --#region RTU Devices @@ -225,32 +217,26 @@ function iorx.record_unit_data(data) for id = 1, #unit.boiler_data_tbl do local boiler = unit.boiler_data_tbl[id] local ps = unit.boiler_ps_tbl[id] + local c_stat = comp_statuses[next_c_stat] local boiler_status = 1 - local computed_status = 1 - if unit.rtu_hw.boilers[id].connected then - if unit.rtu_hw.boilers[id].faulted then + if c_stat ~= BLR_STATE.OFFLINE then + if c_stat == BLR_STATE.FAULT then boiler_status = 3 - computed_status = 3 - elseif boiler.formed then + elseif c_stat ~= BLR_STATE.UNFORMED then boiler_status = 4 - - if boiler.state.boil_rate > 0 then - computed_status = 5 - else - computed_status = 4 - end else boiler_status = 2 - computed_status = 2 end - _record_multiblock_status(unit.rtu_hw.boilers[id].faulted, boiler, ps) + _record_multiblock_status(c_stat == BLR_STATE.FAULT, boiler, ps) end ps.publish("BoilerStatus", boiler_status) - ps.publish("BoilerStateStatus", computed_status) + ps.publish("BoilerStateStatus", c_stat) + + next_c_stat = next_c_stat + 1 end unit.turbine_data_tbl = data[9] @@ -258,38 +244,55 @@ function iorx.record_unit_data(data) for id = 1, #unit.turbine_data_tbl do local turbine = unit.turbine_data_tbl[id] local ps = unit.turbine_ps_tbl[id] + local c_stat = comp_statuses[next_c_stat] local turbine_status = 1 - local computed_status = 1 - if unit.rtu_hw.turbines[id].connected then - if unit.rtu_hw.turbines[id].faulted then + if c_stat ~= TRB_STATE.OFFLINE then + if c_stat == TRB_STATE.FAULT then turbine_status = 3 - computed_status = 3 elseif turbine.formed then turbine_status = 4 - - if turbine.tanks.energy_fill >= 0.99 then - computed_status = 6 - elseif turbine.state.flow_rate < 100 then - computed_status = 4 - else - computed_status = 5 - end else turbine_status = 2 - computed_status = 2 end - _record_multiblock_status(unit.rtu_hw.turbines[id].faulted, turbine, ps) + _record_multiblock_status(c_stat == TRB_STATE.FAULT, turbine, ps) end ps.publish("TurbineStatus", turbine_status) - ps.publish("TurbineStateStatus", computed_status) + ps.publish("TurbineStateStatus", c_stat) + + next_c_stat = next_c_stat + 1 end unit.tank_data_tbl = data[10] + for id = 1, #unit.tank_data_tbl do + local tank = unit.tank_data_tbl[id] + local ps = unit.tank_ps_tbl[id] + local c_stat = comp_statuses[next_c_stat] + + local tank_status = 1 + + if c_stat ~= TNK_STATE.OFFLINE then + if c_stat == TNK_STATE.FAULT then + tank_status = 3 + elseif tank.formed then + tank_status = 4 + else + tank_status = 2 + end + + _record_multiblock_status(c_stat == TNK_STATE.FAULT, tank, ps) + end + + ps.publish("DynamicTankStatus", tank_status) + ps.publish("DynamicTankStateStatus", c_stat) + + next_c_stat = next_c_stat + 1 + end + unit.last_rate_change_ms = data[11] unit.turbine_flow_stable = data[12]