diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua
index 71da387..36163eb 100644
--- a/coordinator/coordinator.lua
+++ b/coordinator/coordinator.lua
@@ -24,6 +24,7 @@ local LINK_TIMEOUT = 60.0
local coordinator = {}
---@type crd_config
+---@diagnostic disable-next-line: missing-fields
local config = {}
coordinator.config = config
diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua
index 7108c01..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,
@@ -224,6 +226,7 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale)
ALARM_STATE.INACTIVE -- turbine trip
},
+---@diagnostic disable-next-line: missing-fields
annunciator = {}, ---@type annunciator
unit_ps = psil.create(),
@@ -248,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
@@ -366,6 +367,7 @@ local function _record_multiblock_build(id, entry, data_tbl, ps_tbl, create)
if exists or create then
if not exists then
ps_tbl[id] = psil.create()
+---@diagnostic disable-next-line: missing-fields
data_tbl[id] = {}
end
@@ -627,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
@@ -642,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
@@ -665,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
@@ -682,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)
@@ -695,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
@@ -704,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
@@ -719,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
@@ -830,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]
@@ -871,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
@@ -900,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
@@ -916,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
@@ -947,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
@@ -963,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
@@ -994,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
@@ -1009,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
@@ -1085,6 +1084,7 @@ function iocontrol.update_unit_statuses(statuses)
unit.annunciator = status[3]
if type(unit.annunciator) ~= "table" then
+---@diagnostic disable-next-line: missing-fields
unit.annunciator = {}
log.debug(log_header .. "annunciator state not a table")
valid = false
diff --git a/coordinator/process.lua b/coordinator/process.lua
index 1f2b914..1866686 100644
--- a/coordinator/process.lua
+++ b/coordinator/process.lua
@@ -286,6 +286,7 @@ function process.create_handle()
handle.unit_ack = {}
for u = 1, pctl.io.facility.num_units do
+---@diagnostic disable-next-line: missing-fields
handle.unit_ack[u] = {}
---@class process_unit_ack
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/coordinator/startup.lua b/coordinator/startup.lua
index 52ce676..8f7f1a6 100644
--- a/coordinator/startup.lua
+++ b/coordinator/startup.lua
@@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer")
local sounder = require("coordinator.sounder")
local threads = require("coordinator.threads")
-local COORDINATOR_VERSION = "v1.5.16"
+local COORDINATOR_VERSION = "v1.5.17"
local CHUNK_LOAD_DELAY_S = 30.0
diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua
index 306fe2e..6f8f795 100644
--- a/coordinator/ui/style.lua
+++ b/coordinator/ui/style.lua
@@ -147,235 +147,102 @@ style.gray_white = cpair(colors.gray, colors.white)
-- UI COMPONENTS --
style.reactor = {
- -- reactor states
+ -- reactor states
+ ---@see REACTOR_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "PLC OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "PLC FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "DISABLED"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- },
- {
- color = cpair(colors.black, colors.red),
- text = "SCRAMMED"
- },
- {
- color = cpair(colors.black, colors.red),
- text = "FORCE DISABLED"
- }
+ { color = cpair(colors.black, colors.yellow), text = "PLC OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "PLC FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "DISABLED" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" },
+ { color = cpair(colors.black, colors.red), text = "SCRAMMED" },
+ { color = cpair(colors.black, colors.red), text = "FORCE DISABLED" }
}
}
style.boiler = {
- -- boiler states
+ -- boiler states
+ ---@see BOILER_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "IDLE"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- }
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "IDLE" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" }
}
}
style.turbine = {
- -- turbine states
+ -- turbine states
+ ---@see TURBINE_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "IDLE"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- },
- {
- color = cpair(colors.black, colors.red),
- text = "TRIP"
- }
- }
-}
-
-style.imatrix = {
- -- induction matrix states
- states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ONLINE"
- },
- {
- color = cpair(colors.black, colors.yellow),
- text = "LOW CHARGE"
- },
- {
- color = cpair(colors.black, colors.yellow),
- text = "HIGH CHARGE"
- }
- }
-}
-
-style.sps = {
- -- SPS states
- states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "IDLE"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- }
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "IDLE" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" },
+ { color = cpair(colors.black, colors.red), text = "TRIP" }
}
}
style.dtank = {
- -- dynamic tank states
+ -- dynamic tank states
+ ---@see TANK_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ONLINE"
- },
- {
- color = cpair(colors.black, colors.yellow),
- text = "LOW FILL"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "FILLED"
- },
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.black, colors.green), text = "ONLINE" },
+ { color = cpair(colors.black, colors.yellow), text = "LOW FILL" },
+ { color = cpair(colors.black, colors.green), text = "FILLED" }
+ }
+}
+
+style.imatrix = {
+ -- induction matrix states
+ ---@see IMATRIX_STATE
+ states = {
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.black, colors.green), text = "ONLINE" },
+ { color = cpair(colors.black, colors.yellow), text = "LOW CHARGE" },
+ { color = cpair(colors.black, colors.yellow), text = "HIGH CHARGE" }
+ }
+}
+
+style.sps = {
+ -- SPS states
+ ---@see SPS_STATE
+ states = {
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "IDLE" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" }
}
}
style.waste = {
-- auto waste processing states
states = {
- {
- color = cpair(colors.black, colors.green),
- text = "PLUTONIUM"
- },
- {
- color = cpair(colors.black, colors.cyan),
- text = "POLONIUM"
- },
- {
- color = cpair(colors.black, colors.purple),
- text = "ANTI MATTER"
- }
+ { color = cpair(colors.black, colors.green), text = "PLUTONIUM" },
+ { color = cpair(colors.black, colors.cyan), text = "POLONIUM" },
+ { color = cpair(colors.black, colors.purple), text = "ANTI MATTER" }
},
states_abbrv = {
- {
- color = cpair(colors.black, colors.green),
- text = "Pu"
- },
- {
- color = cpair(colors.black, colors.cyan),
- text = "Po"
- },
- {
- color = cpair(colors.black, colors.purple),
- text = "AM"
- }
+ { color = cpair(colors.black, colors.green), text = "Pu" },
+ { color = cpair(colors.black, colors.cyan), text = "Po" },
+ { color = cpair(colors.black, colors.purple), text = "AM" }
},
-- process radio button options
options = { "Plutonium", "Polonium", "Antimatter" },
-- unit waste selection
unit_opts = {
- {
- text = "Auto",
- fg_bg = cpair(colors.black, colors.lightGray),
- active_fg_bg = cpair(colors.white, colors.gray)
- },
- {
- text = "Pu",
- fg_bg = cpair(colors.black, colors.lightGray),
- active_fg_bg = cpair(colors.black, colors.green)
- },
- {
- text = "Po",
- fg_bg = cpair(colors.black, colors.lightGray),
- active_fg_bg = cpair(colors.black, colors.cyan)
- },
- {
- text = "AM",
- fg_bg = cpair(colors.black, colors.lightGray),
- active_fg_bg = cpair(colors.black, colors.purple)
- }
+ { text = "Auto", fg_bg = cpair(colors.black, colors.lightGray), active_fg_bg = cpair(colors.white, colors.gray) },
+ { text = "Pu", fg_bg = cpair(colors.black, colors.lightGray), active_fg_bg = cpair(colors.black, colors.green) },
+ { text = "Po", fg_bg = cpair(colors.black, colors.lightGray), active_fg_bg = cpair(colors.black, colors.cyan) },
+ { text = "AM", fg_bg = cpair(colors.black, colors.lightGray), active_fg_bg = cpair(colors.black, colors.purple) }
}
}
diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua
index 0a3af09..b860aa4 100644
--- a/pocket/iocontrol.lua
+++ b/pocket/iocontrol.lua
@@ -2,11 +2,11 @@
-- I/O Control for Pocket Integration with Supervisor & Coordinator
--
-local const = require("scada-common.constants")
local psil = require("scada-common.psil")
local types = require("scada-common.types")
local util = require("scada-common.util")
+local iorx = require("pocket.iorx")
local process = require("pocket.process")
local ALARM = types.ALARM
@@ -50,6 +50,8 @@ function iocontrol.init_core(pkt_comms, nav, cfg)
comms = pkt_comms
config = cfg
+ iocontrol.rx = iorx(io)
+
io.nav = nav
---@class pocket_ioctl_diag
@@ -194,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,
@@ -239,6 +239,7 @@ function iocontrol.init_fac(conf)
---@type { [ALARM]: ALARM_STATE }
alarms = { ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE },
+---@diagnostic disable-next-line: missing-fields
annunciator = {}, ---@type annunciator
unit_ps = psil.create(),
@@ -347,642 +348,6 @@ function iocontrol.report_crd_tt(trip_time)
io.ps.publish("crd_conn_quality", state)
end
--- populate facility data from API_GET_FAC
----@param data table
----@return boolean valid
-function iocontrol.record_facility_data(data)
- local valid = true
-
- local fac = io.facility
-
- fac.all_sys_ok = data[1]
- fac.rtu_count = data[2]
- fac.radiation = data[3]
-
- -- auto control
- if type(data[4]) == "table" and #data[4] == 4 then
- fac.auto_ready = data[4][1]
- fac.auto_active = data[4][2]
- fac.auto_ramping = data[4][3]
- fac.auto_saturated = data[4][4]
- end
-
- -- waste
- if type(data[5]) == "table" and #data[5] == 2 then
- fac.auto_current_waste_product = data[5][1]
- fac.auto_pu_fallback_active = data[5][2]
- end
-
- fac.num_tanks = data[6]
- fac.has_imatrix = data[7]
- fac.has_sps = data[8]
-
- return valid
-end
-
-local function tripped(state) return state == ALARM_STATE.TRIPPED or state == ALARM_STATE.ACKED end
-
-local function _record_multiblock_status(faulted, data, ps)
- ps.publish("formed", data.formed)
- ps.publish("faulted", faulted)
-
- for key, val in pairs(data.state) do ps.publish(key, val) end
- for key, val in pairs(data.tanks) do ps.publish(key, val) end
-end
-
--- update unit status data from API_GET_UNIT
----@param data table
-function iocontrol.record_unit_data(data)
- local unit = io.units[data[1]]
-
- unit.connected = data[2]
- unit.rtu_hw = data[3]
- unit.a_group = data[4]
- unit.alarms = data[5]
-
- unit.unit_ps.publish("auto_group_id", unit.a_group)
- unit.unit_ps.publish("auto_group", types.AUTO_GROUP_NAMES[unit.a_group + 1])
-
- --#region Annunciator
-
- unit.annunciator = data[6]
-
- local rcs_disconn, rcs_warn, rcs_hazard = false, false, false
-
- for key, val in pairs(unit.annunciator) do
- if key == "BoilerOnline" or key == "TurbineOnline" then
- local every = true
-
- -- split up online arrays
- for id = 1, #val do
- every = every and val[id]
-
- if key == "BoilerOnline" then
- unit.boiler_ps_tbl[id].publish(key, val[id])
- else
- unit.turbine_ps_tbl[id].publish(key, val[id])
- end
- end
-
- if not every then rcs_disconn = true end
-
- unit.unit_ps.publish("U_" .. key, every)
- elseif key == "HeatingRateLow" or key == "WaterLevelLow" then
- -- split up array for all boilers
- local any = false
- for id = 1, #val do
- any = any or val[id]
- unit.boiler_ps_tbl[id].publish(key, val[id])
- end
-
- if key == "HeatingRateLow" and any then
- rcs_warn = true
- elseif key == "WaterLevelLow" and any then
- rcs_hazard = true
- end
-
- unit.unit_ps.publish("U_" .. key, any)
- elseif key == "SteamDumpOpen" or key == "TurbineOverSpeed" or key == "GeneratorTrip" or key == "TurbineTrip" then
- -- split up array for all turbines
- local any = false
- for id = 1, #val do
- any = any or val[id]
- unit.turbine_ps_tbl[id].publish(key, val[id])
- end
-
- if key == "GeneratorTrip" and any then
- rcs_warn = true
- elseif (key == "TurbineOverSpeed" or key == "TurbineTrip") and any then
- rcs_hazard = true
- end
-
- unit.unit_ps.publish("U_" .. key, any)
- else
- -- non-table fields
- unit.unit_ps.publish(key, val)
- end
- end
-
- local anc = unit.annunciator
- rcs_hazard = rcs_hazard or anc.RCPTrip
- rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or
- anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch
-
- local rcs_status = 4
- if rcs_hazard then
- rcs_status = 2
- elseif rcs_warn then
- rcs_status = 3
- elseif rcs_disconn then
- rcs_status = 1
- end
-
- unit.unit_ps.publish("U_RCS", rcs_status)
-
- --#endregion
-
- --#region Reactor Data
-
- unit.reactor_data = data[7]
-
- 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
- else
- rps_status = 4
- reactor_state = 4
- end
-
- -- 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
- 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
-
- for key, val in pairs(unit.reactor_data) do
- if key ~= "rps_status" and key ~= "mek_struct" and key ~= "mek_status" then
- unit.unit_ps.publish(key, val)
- end
- end
-
- for key, val in pairs(unit.reactor_data.rps_status) do
- unit.unit_ps.publish(key, val)
- end
-
- for key, val in pairs(unit.reactor_data.mek_struct) do
- unit.unit_ps.publish(key, val)
- end
-
- for key, val in pairs(unit.reactor_data.mek_status) do
- unit.unit_ps.publish(key, val)
- end
- end
-
- 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_RPS", rps_status)
-
- --#endregion
-
- --#region RTU Devices
-
- unit.boiler_data_tbl = data[8]
-
- for id = 1, #unit.boiler_data_tbl do
- local boiler = unit.boiler_data_tbl[id]
- local ps = unit.boiler_ps_tbl[id]
-
- local boiler_status = 1
- local computed_status = 1
-
- if unit.rtu_hw.boilers[id].connected then
- if unit.rtu_hw.boilers[id].faulted then
- boiler_status = 3
- computed_status = 3
- elseif boiler.formed 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)
- end
-
- ps.publish("BoilerStatus", boiler_status)
- ps.publish("BoilerStateStatus", computed_status)
- end
-
- unit.turbine_data_tbl = data[9]
-
- for id = 1, #unit.turbine_data_tbl do
- local turbine = unit.turbine_data_tbl[id]
- local ps = unit.turbine_ps_tbl[id]
-
- local turbine_status = 1
- local computed_status = 1
-
- if unit.rtu_hw.turbines[id].connected then
- if unit.rtu_hw.turbines[id].faulted 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)
- end
-
- ps.publish("TurbineStatus", turbine_status)
- ps.publish("TurbineStateStatus", computed_status)
- end
-
- unit.tank_data_tbl = data[10]
-
- unit.last_rate_change_ms = data[11]
- unit.turbine_flow_stable = data[12]
-
- --#endregion
-
- --#region Status Information Display
-
- local ecam = {} -- aviation reference :)
-
- -- local function red(text) return { text = text, color = colors.red } end
- local function white(text) return { text = text, color = colors.white } end
- local function blue(text) return { text = text, color = colors.blue } end
-
- -- if unit.reactor_data.rps_status then
- -- for k, v in pairs(unit.alarms) do
- -- unit.alarms[k] = ALARM_STATE.TRIPPED
- -- end
- -- end
-
- if tripped(unit.alarms[ALARM.ContainmentBreach]) then
- local items = { white("REACTOR MELTDOWN"), blue("DON HAZMAT SUIT") }
- table.insert(ecam, { color = colors.red, text = "CONTAINMENT BREACH", help = "ContainmentBreach", items = items })
- end
-
- if tripped(unit.alarms[ALARM.ContainmentRadiation]) then
- local items = {
- white("RADIATION DETECTED"),
- blue("DON HAZMAT SUIT"),
- blue("RESOLVE LEAK"),
- blue("AWAIT SAFE LEVELS")
- }
-
- table.insert(ecam, { color = colors.red, text = "RADIATION LEAK", help = "ContainmentRadiation", items = items })
- end
-
- if tripped(unit.alarms[ALARM.CriticalDamage]) then
- local items = { white("MELTDOWN IMMINENT"), blue("EVACUATE") }
- table.insert(ecam, { color = colors.red, text = "RCT DAMAGE CRITICAL", help = "CriticalDamage", items = items })
- end
-
- if tripped(unit.alarms[ALARM.ReactorLost]) then
- local items = { white("REACTOR OFF-LINE"), blue("CHECK PLC") }
- table.insert(ecam, { color = colors.red, text = "REACTOR CONN LOST", help = "ReactorLost", items = items })
- end
-
- if tripped(unit.alarms[ALARM.ReactorDamage]) then
- local items = { white("REACTOR DAMAGED"), blue("CHECK RCS"), blue("AWAIT DMG REDUCED") }
- table.insert(ecam, { color = colors.red, text = "REACTOR DAMAGE", help = "ReactorDamage", items = items })
- end
-
- if tripped(unit.alarms[ALARM.ReactorOverTemp]) then
- local items = { white("DAMAGING TEMP"), blue("CHECK RCS"), blue("AWAIT COOLDOWN") }
- table.insert(ecam, { color = colors.red, text = "REACTOR OVER TEMP", help = "ReactorOverTemp", items = items })
- end
-
- if tripped(unit.alarms[ALARM.ReactorHighTemp]) then
- local items = { white("OVER EXPECTED TEMP"), blue("CHECK RCS") }
- table.insert(ecam, { color = colors.yellow, text = "REACTOR HIGH TEMP", help = "ReactorHighTemp", items = items})
- end
-
- if tripped(unit.alarms[ALARM.ReactorWasteLeak]) then
- local items = { white("AT WASTE CAPACITY"), blue("CHECK WASTE OUTPUT"), blue("KEEP RCT DISABLED") }
- table.insert(ecam, { color = colors.red, text = "REACTOR WASTE LEAK", help = "ReactorWasteLeak", items = items})
- end
-
- if tripped(unit.alarms[ALARM.ReactorHighWaste]) then
- local items = { blue("CHECK WASTE OUTPUT") }
- table.insert(ecam, { color = colors.yellow, text = "REACTOR WASTE HIGH", help = "ReactorHighWaste", items = items})
- end
-
- if tripped(unit.alarms[ALARM.RPSTransient]) then
- local items = {}
- local stat = unit.reactor_data.rps_status
-
- -- for k, _ in pairs(stat) do stat[k] = true end
-
- local function insert(cond, key, text, color) if cond[key] then table.insert(items, { text = text, help = key, color = color }) end end
-
- table.insert(items, white("REACTOR SCRAMMED"))
- insert(stat, "high_dmg", "HIGH DAMAGE", colors.red)
- insert(stat, "high_temp", "HIGH TEMPERATURE", colors.red)
- insert(stat, "low_cool", "CRIT LOW COOLANT")
- insert(stat, "ex_waste", "EXCESS WASTE")
- insert(stat, "ex_hcool", "EXCESS HEATED COOL")
- insert(stat, "no_fuel", "NO FUEL")
- insert(stat, "fault", "HARDWARE FAULT")
- insert(stat, "timeout", "SUPERVISOR DISCONN")
- insert(stat, "manual", "MANUAL SCRAM", colors.white)
- insert(stat, "automatic", "AUTOMATIC SCRAM")
- insert(stat, "sys_fail", "NOT FORMED", colors.red)
- insert(stat, "force_dis", "FORCE DISABLED", colors.red)
- table.insert(items, blue("RESOLVE PROBLEM"))
- table.insert(items, blue("RESET RPS"))
-
- table.insert(ecam, { color = colors.yellow, text = "RPS TRANSIENT", help = "RPSTransient", items = items})
- end
-
- if tripped(unit.alarms[ALARM.RCSTransient]) then
- local items = {}
- local annunc = unit.annunciator
-
- -- for k, v in pairs(annunc) do
- -- if type(v) == "boolean" then annunc[k] = true end
- -- if type(v) == "table" then
- -- for a, _ in pairs(v) do
- -- v[a] = true
- -- end
- -- end
- -- end
-
- local function insert(cond, key, text, color)
- if cond == true or (type(cond) == "table" and cond[key]) then table.insert(items, { text = text, help = key, color = color }) end
- end
-
- table.insert(items, white("COOLANT PROBLEM"))
-
- insert(annunc, "RCPTrip", "RCP TRIP", colors.red)
- insert(annunc, "CoolantLevelLow", "LOW COOLANT")
-
- if unit.num_boilers == 0 then
- if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then
- insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH")
- end
-
- if unit.turbine_flow_stable then
- insert(annunc, "RCSFlowLow", "RCS FLOW LOW")
- insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH")
- insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH")
- end
- else
- if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then
- insert(annunc, "RCSFlowLow", "RCS FLOW LOW")
- insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH")
- insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH")
- end
-
- if unit.turbine_flow_stable then
- insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH")
- end
- end
-
- insert(annunc, "MaxWaterReturnFeed", "MAX WTR RTRN FEED")
-
- for k, v in ipairs(annunc.WaterLevelLow) do insert(v, "WaterLevelLow", "BOILER " .. k .. " WTR LOW", colors.red) end
- for k, v in ipairs(annunc.HeatingRateLow) do insert(v, "HeatingRateLow", "BOILER " .. k .. " HEAT RATE") end
- for k, v in ipairs(annunc.TurbineOverSpeed) do insert(v, "TurbineOverSpeed", "TURBINE " .. k .. " OVERSPD", colors.red) end
- for k, v in ipairs(annunc.GeneratorTrip) do insert(v, "GeneratorTrip", "TURBINE " .. k .. " GEN TRIP") end
-
- table.insert(items, blue("CHECK COOLING SYS"))
-
- table.insert(ecam, { color = colors.yellow, text = "RCS TRANSIENT", help = "RCSTransient", items = items})
- end
-
- if tripped(unit.alarms[ALARM.TurbineTrip]) then
- local items = {}
-
- for k, v in ipairs(unit.annunciator.TurbineTrip) do
- if v then table.insert(items, { text = "TURBINE " .. k .. " TRIP", help = "TurbineTrip" }) end
- end
-
- table.insert(items, blue("CHECK ENERGY OUT"))
- table.insert(ecam, { color = colors.red, text = "TURBINE TRIP", help = "TurbineTripAlarm", items = items})
- end
-
- if not (tripped(unit.alarms[ALARM.ReactorLost]) or unit.connected) then
- local items = { blue("CHECK PLC") }
- table.insert(ecam, { color = colors.yellow, text = "REACTOR OFF-LINE", items = items })
- end
-
- for k, v in ipairs(unit.annunciator.BoilerOnline) do
- if not v then
- local items = { blue("CHECK RTU") }
- table.insert(ecam, { color = colors.yellow, text = "BOILER " .. k .. " OFF-LINE", items = items})
- end
- end
-
- for k, v in ipairs(unit.annunciator.TurbineOnline) do
- if not v then
- local items = { blue("CHECK RTU") }
- table.insert(ecam, { color = colors.yellow, text = "TURBINE " .. k .. " OFF-LINE", items = items})
- end
- end
-
- -- if no alarms, put some basic status messages in
- if #ecam == 0 then
- table.insert(ecam, { color = colors.green, text = "REACTOR " .. util.trinary(unit.reactor_data.mek_status.status, "NOMINAL", "IDLE"), items = {}})
-
- local plural = util.trinary(unit.num_turbines > 1, "S", "")
- table.insert(ecam, { color = colors.green, text = "TURBINE" .. plural .. util.trinary(unit.turbine_flow_stable, " STABLE", " STABILIZING"), items = {}})
- end
-
- unit.unit_ps.publish("U_ECAM", textutils.serialize(ecam))
-
- --#endregion
-end
-
--- update control app with unit data from API_GET_CTRL
----@param data table
-function iocontrol.record_control_data(data)
- for u_id = 1, #data do
- local unit = io.units[u_id]
- local u_data = data[u_id]
-
- 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.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])
-
- 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
- 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_fault", fac.ascram_status.matrix_fault)
- 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
-
--- update waste app with unit data from API_GET_WASTE
----@param data table
-function iocontrol.record_waste_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.waste_mode = u_data[1]
- unit.waste_product = u_data[2]
- unit.num_snas = u_data[3]
- unit.sna_peak_rate = u_data[4]
- unit.sna_max_rate = u_data[5]
- unit.sna_out_rate = u_data[6]
- unit.waste_stats = u_data[7]
-
- 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)
-
- unit.unit_ps.publish("sna_count", unit.num_snas)
- unit.unit_ps.publish("sna_peak_rate", unit.sna_peak_rate)
- unit.unit_ps.publish("sna_max_rate", unit.sna_max_rate)
- unit.unit_ps.publish("sna_out_rate", unit.sna_out_rate)
-
- unit.unit_ps.publish("pu_rate", unit.waste_stats[1])
- unit.unit_ps.publish("po_rate", unit.waste_stats[2])
- unit.unit_ps.publish("po_pl_rate", unit.waste_stats[3])
- end
-
- -- get facility data
- local fac = io.facility
- local f_data = data[#io.units + 1]
-
- fac.auto_current_waste_product = f_data[1]
- fac.auto_pu_fallback_active = f_data[2]
- fac.auto_sps_disabled = f_data[3]
-
- fac.ps.publish("current_waste_product", fac.auto_current_waste_product)
- fac.ps.publish("pu_fallback_active", fac.auto_pu_fallback_active)
- fac.ps.publish("sps_disabled_low_power", fac.auto_sps_disabled)
-
- fac.ps.publish("process_waste_product", f_data[4])
- fac.ps.publish("process_pu_fallback", f_data[5])
- fac.ps.publish("process_sps_low_power", f_data[6])
-
- fac.waste_stats = f_data[7]
-
- fac.ps.publish("burn_sum", fac.waste_stats[1])
- fac.ps.publish("pu_rate", fac.waste_stats[2])
- fac.ps.publish("po_rate", fac.waste_stats[3])
- fac.ps.publish("po_pl_rate", fac.waste_stats[4])
- fac.ps.publish("po_am_rate", fac.waste_stats[5])
- fac.ps.publish("spent_waste_rate", fac.waste_stats[6])
-
- fac.ps.publish("sps_computed_status", f_data[8])
- fac.ps.publish("sps_process_rate", f_data[9])
-end
-
-- get the IO controller database
function iocontrol.get_db() return io end
diff --git a/pocket/iorx.lua b/pocket/iorx.lua
new file mode 100644
index 0000000..0aac073
--- /dev/null
+++ b/pocket/iorx.lua
@@ -0,0 +1,657 @@
+--
+-- I/O Control's Data Receive (Rx) Handlers
+--
+
+local const = require("scada-common.constants")
+local types = require("scada-common.types")
+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
+
+-- populate facility data from API_GET_FAC
+---@param data table
+---@return boolean valid
+function iorx.record_facility_data(data)
+ local valid = true
+
+ local fac = io.facility
+
+ fac.all_sys_ok = data[1]
+ fac.rtu_count = data[2]
+ fac.radiation = data[3]
+
+ -- auto control
+ if type(data[4]) == "table" and #data[4] == 4 then
+ fac.auto_ready = data[4][1]
+ fac.auto_active = data[4][2]
+ fac.auto_ramping = data[4][3]
+ fac.auto_saturated = data[4][4]
+ end
+
+ -- waste
+ if type(data[5]) == "table" and #data[5] == 2 then
+ fac.auto_current_waste_product = data[5][1]
+ fac.auto_pu_fallback_active = data[5][2]
+ end
+
+ fac.num_tanks = data[6]
+ fac.has_imatrix = data[7]
+ fac.has_sps = data[8]
+
+ return valid
+end
+
+local function tripped(state) return state == ALARM_STATE.TRIPPED or state == ALARM_STATE.ACKED end
+
+local function _record_multiblock_status(faulted, data, ps)
+ ps.publish("formed", data.formed)
+ ps.publish("faulted", faulted)
+
+ for key, val in pairs(data.state) do ps.publish(key, val) end
+ for key, val in pairs(data.tanks) do ps.publish(key, val) end
+end
+
+-- update unit status data from API_GET_UNIT
+---@param data table
+function iorx.record_unit_data(data)
+ local unit = io.units[data[1]]
+
+ unit.connected = data[2]
+ 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])
+
+ --#region Annunciator
+
+ unit.annunciator = data[6]
+
+ local rcs_disconn, rcs_warn, rcs_hazard = false, false, false
+
+ for key, val in pairs(unit.annunciator) do
+ if key == "BoilerOnline" or key == "TurbineOnline" then
+ local every = true
+
+ -- split up online arrays
+ for id = 1, #val do
+ every = every and val[id]
+
+ if key == "BoilerOnline" then
+ unit.boiler_ps_tbl[id].publish(key, val[id])
+ else
+ unit.turbine_ps_tbl[id].publish(key, val[id])
+ end
+ end
+
+ if not every then rcs_disconn = true end
+
+ unit.unit_ps.publish("U_" .. key, every)
+ elseif key == "HeatingRateLow" or key == "WaterLevelLow" then
+ -- split up array for all boilers
+ local any = false
+ for id = 1, #val do
+ any = any or val[id]
+ unit.boiler_ps_tbl[id].publish(key, val[id])
+ end
+
+ if key == "HeatingRateLow" and any then
+ rcs_warn = true
+ elseif key == "WaterLevelLow" and any then
+ rcs_hazard = true
+ end
+
+ unit.unit_ps.publish("U_" .. key, any)
+ elseif key == "SteamDumpOpen" or key == "TurbineOverSpeed" or key == "GeneratorTrip" or key == "TurbineTrip" then
+ -- split up array for all turbines
+ local any = false
+ for id = 1, #val do
+ any = any or val[id]
+ unit.turbine_ps_tbl[id].publish(key, val[id])
+ end
+
+ if key == "GeneratorTrip" and any then
+ rcs_warn = true
+ elseif (key == "TurbineOverSpeed" or key == "TurbineTrip") and any then
+ rcs_hazard = true
+ end
+
+ unit.unit_ps.publish("U_" .. key, any)
+ else
+ -- non-table fields
+ unit.unit_ps.publish(key, val)
+ end
+ end
+
+ local anc = unit.annunciator
+ rcs_hazard = rcs_hazard or anc.RCPTrip
+ rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or
+ anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch
+
+ local rcs_status = 4
+ if rcs_hazard then
+ rcs_status = 2
+ elseif rcs_warn then
+ rcs_status = 3
+ elseif rcs_disconn then
+ rcs_status = 1
+ end
+
+ unit.unit_ps.publish("U_RCS", rcs_status)
+
+ --#endregion
+
+ --#region Reactor Data
+
+ unit.reactor_data = data[7]
+
+ local control_status = 1
+ local reactor_status = 1
+ local rps_status = 1
+
+ if unit.connected then
+ -- update RPS status
+ if unit.reactor_data.rps_tripped then
+ control_status = 2
+ rps_status = util.trinary(unit.reactor_data.rps_trip_cause == "manual", 3, 2)
+ else
+ rps_status = 4
+ end
+
+ reactor_status = 4 -- ok, until proven otherwise
+
+ -- update reactor/control status
+ if unit.reactor_data.mek_status.status then
+ control_status = util.trinary(unit.annunciator.AutoControl, 4, 3)
+ else
+ if unit.reactor_data.no_reactor then
+ reactor_status = 2
+ elseif (not unit.reactor_data.formed) or unit.reactor_data.rps_status.force_dis then
+ reactor_status = 3
+ end
+ end
+
+ for key, val in pairs(unit.reactor_data) do
+ if key ~= "rps_status" and key ~= "mek_struct" and key ~= "mek_status" then
+ unit.unit_ps.publish(key, val)
+ end
+ end
+
+ for key, val in pairs(unit.reactor_data.rps_status) do
+ unit.unit_ps.publish(key, val)
+ end
+
+ for key, val in pairs(unit.reactor_data.mek_struct) do
+ unit.unit_ps.publish(key, val)
+ end
+
+ for key, val in pairs(unit.reactor_data.mek_status) do
+ unit.unit_ps.publish(key, val)
+ end
+ end
+
+ unit.unit_ps.publish("U_ControlStatus", control_status)
+ unit.unit_ps.publish("U_ReactorStatus", reactor_status)
+ 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
+
+ unit.boiler_data_tbl = data[8]
+
+ 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
+
+ if c_stat ~= BLR_STATE.OFFLINE then
+ if c_stat == BLR_STATE.FAULT then
+ boiler_status = 3
+ elseif c_stat ~= BLR_STATE.UNFORMED then
+ boiler_status = 4
+ else
+ boiler_status = 2
+ end
+
+ _record_multiblock_status(c_stat == BLR_STATE.FAULT, boiler, ps)
+ end
+
+ ps.publish("BoilerStatus", boiler_status)
+ ps.publish("BoilerStateStatus", c_stat)
+
+ next_c_stat = next_c_stat + 1
+ end
+
+ unit.turbine_data_tbl = data[9]
+
+ 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
+
+ if c_stat ~= TRB_STATE.OFFLINE then
+ if c_stat == TRB_STATE.FAULT then
+ turbine_status = 3
+ elseif turbine.formed then
+ turbine_status = 4
+ else
+ turbine_status = 2
+ end
+
+ _record_multiblock_status(c_stat == TRB_STATE.FAULT, turbine, ps)
+ end
+
+ ps.publish("TurbineStatus", turbine_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]
+
+ --#endregion
+
+ --#region Status Information Display
+
+ local ecam = {} -- aviation reference :)
+
+ -- local function red(text) return { text = text, color = colors.red } end
+ local function white(text) return { text = text, color = colors.white } end
+ local function blue(text) return { text = text, color = colors.blue } end
+
+ -- if unit.reactor_data.rps_status then
+ -- for k, v in pairs(unit.alarms) do
+ -- unit.alarms[k] = ALARM_STATE.TRIPPED
+ -- end
+ -- end
+
+ if tripped(unit.alarms[ALARM.ContainmentBreach]) then
+ local items = { white("REACTOR MELTDOWN"), blue("DON HAZMAT SUIT") }
+ table.insert(ecam, { color = colors.red, text = "CONTAINMENT BREACH", help = "ContainmentBreach", items = items })
+ end
+
+ if tripped(unit.alarms[ALARM.ContainmentRadiation]) then
+ local items = {
+ white("RADIATION DETECTED"),
+ blue("DON HAZMAT SUIT"),
+ blue("RESOLVE LEAK"),
+ blue("AWAIT SAFE LEVELS")
+ }
+
+ table.insert(ecam, { color = colors.red, text = "RADIATION LEAK", help = "ContainmentRadiation", items = items })
+ end
+
+ if tripped(unit.alarms[ALARM.CriticalDamage]) then
+ local items = { white("MELTDOWN IMMINENT"), blue("EVACUATE") }
+ table.insert(ecam, { color = colors.red, text = "RCT DAMAGE CRITICAL", help = "CriticalDamage", items = items })
+ end
+
+ if tripped(unit.alarms[ALARM.ReactorLost]) then
+ local items = { white("REACTOR OFF-LINE"), blue("CHECK PLC") }
+ table.insert(ecam, { color = colors.red, text = "REACTOR CONN LOST", help = "ReactorLost", items = items })
+ end
+
+ if tripped(unit.alarms[ALARM.ReactorDamage]) then
+ local items = { white("REACTOR DAMAGED"), blue("CHECK RCS"), blue("AWAIT DMG REDUCED") }
+ table.insert(ecam, { color = colors.red, text = "REACTOR DAMAGE", help = "ReactorDamage", items = items })
+ end
+
+ if tripped(unit.alarms[ALARM.ReactorOverTemp]) then
+ local items = { white("DAMAGING TEMP"), blue("CHECK RCS"), blue("AWAIT COOLDOWN") }
+ table.insert(ecam, { color = colors.red, text = "REACTOR OVER TEMP", help = "ReactorOverTemp", items = items })
+ end
+
+ if tripped(unit.alarms[ALARM.ReactorHighTemp]) then
+ local items = { white("OVER EXPECTED TEMP"), blue("CHECK RCS") }
+ table.insert(ecam, { color = colors.yellow, text = "REACTOR HIGH TEMP", help = "ReactorHighTemp", items = items})
+ end
+
+ if tripped(unit.alarms[ALARM.ReactorWasteLeak]) then
+ local items = { white("AT WASTE CAPACITY"), blue("CHECK WASTE OUTPUT"), blue("KEEP RCT DISABLED") }
+ table.insert(ecam, { color = colors.red, text = "REACTOR WASTE LEAK", help = "ReactorWasteLeak", items = items})
+ end
+
+ if tripped(unit.alarms[ALARM.ReactorHighWaste]) then
+ local items = { blue("CHECK WASTE OUTPUT") }
+ table.insert(ecam, { color = colors.yellow, text = "REACTOR WASTE HIGH", help = "ReactorHighWaste", items = items})
+ end
+
+ if tripped(unit.alarms[ALARM.RPSTransient]) then
+ local items = {}
+ local stat = unit.reactor_data.rps_status
+
+ -- for k, _ in pairs(stat) do stat[k] = true end
+
+ local function insert(cond, key, text, color) if cond[key] then table.insert(items, { text = text, help = key, color = color }) end end
+
+ table.insert(items, white("REACTOR SCRAMMED"))
+ insert(stat, "high_dmg", "HIGH DAMAGE", colors.red)
+ insert(stat, "high_temp", "HIGH TEMPERATURE", colors.red)
+ insert(stat, "low_cool", "CRIT LOW COOLANT")
+ insert(stat, "ex_waste", "EXCESS WASTE")
+ insert(stat, "ex_hcool", "EXCESS HEATED COOL")
+ insert(stat, "no_fuel", "NO FUEL")
+ insert(stat, "fault", "HARDWARE FAULT")
+ insert(stat, "timeout", "SUPERVISOR DISCONN")
+ insert(stat, "manual", "MANUAL SCRAM", colors.white)
+ insert(stat, "automatic", "AUTOMATIC SCRAM")
+ insert(stat, "sys_fail", "NOT FORMED", colors.red)
+ insert(stat, "force_dis", "FORCE DISABLED", colors.red)
+ table.insert(items, blue("RESOLVE PROBLEM"))
+ table.insert(items, blue("RESET RPS"))
+
+ table.insert(ecam, { color = colors.yellow, text = "RPS TRANSIENT", help = "RPSTransient", items = items})
+ end
+
+ if tripped(unit.alarms[ALARM.RCSTransient]) then
+ local items = {}
+ local annunc = unit.annunciator
+
+ -- for k, v in pairs(annunc) do
+ -- if type(v) == "boolean" then annunc[k] = true end
+ -- if type(v) == "table" then
+ -- for a, _ in pairs(v) do
+ -- v[a] = true
+ -- end
+ -- end
+ -- end
+
+ local function insert(cond, key, text, color)
+ if cond == true or (type(cond) == "table" and cond[key]) then table.insert(items, { text = text, help = key, color = color }) end
+ end
+
+ table.insert(items, white("COOLANT PROBLEM"))
+
+ insert(annunc, "RCPTrip", "RCP TRIP", colors.red)
+ insert(annunc, "CoolantLevelLow", "LOW COOLANT")
+
+ if unit.num_boilers == 0 then
+ if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then
+ insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH")
+ end
+
+ if unit.turbine_flow_stable then
+ insert(annunc, "RCSFlowLow", "RCS FLOW LOW")
+ insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH")
+ insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH")
+ end
+ else
+ if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then
+ insert(annunc, "RCSFlowLow", "RCS FLOW LOW")
+ insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH")
+ insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH")
+ end
+
+ if unit.turbine_flow_stable then
+ insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH")
+ end
+ end
+
+ insert(annunc, "MaxWaterReturnFeed", "MAX WTR RTRN FEED")
+
+ for k, v in ipairs(annunc.WaterLevelLow) do insert(v, "WaterLevelLow", "BOILER " .. k .. " WTR LOW", colors.red) end
+ for k, v in ipairs(annunc.HeatingRateLow) do insert(v, "HeatingRateLow", "BOILER " .. k .. " HEAT RATE") end
+ for k, v in ipairs(annunc.TurbineOverSpeed) do insert(v, "TurbineOverSpeed", "TURBINE " .. k .. " OVERSPD", colors.red) end
+ for k, v in ipairs(annunc.GeneratorTrip) do insert(v, "GeneratorTrip", "TURBINE " .. k .. " GEN TRIP") end
+
+ table.insert(items, blue("CHECK COOLING SYS"))
+
+ table.insert(ecam, { color = colors.yellow, text = "RCS TRANSIENT", help = "RCSTransient", items = items})
+ end
+
+ if tripped(unit.alarms[ALARM.TurbineTrip]) then
+ local items = {}
+
+ for k, v in ipairs(unit.annunciator.TurbineTrip) do
+ if v then table.insert(items, { text = "TURBINE " .. k .. " TRIP", help = "TurbineTrip" }) end
+ end
+
+ table.insert(items, blue("CHECK ENERGY OUT"))
+ table.insert(ecam, { color = colors.red, text = "TURBINE TRIP", help = "TurbineTripAlarm", items = items})
+ end
+
+ if not (tripped(unit.alarms[ALARM.ReactorLost]) or unit.connected) then
+ local items = { blue("CHECK PLC") }
+ table.insert(ecam, { color = colors.yellow, text = "REACTOR OFF-LINE", items = items })
+ end
+
+ for k, v in ipairs(unit.annunciator.BoilerOnline) do
+ if not v then
+ local items = { blue("CHECK RTU") }
+ table.insert(ecam, { color = colors.yellow, text = "BOILER " .. k .. " OFF-LINE", items = items})
+ end
+ end
+
+ for k, v in ipairs(unit.annunciator.TurbineOnline) do
+ if not v then
+ local items = { blue("CHECK RTU") }
+ table.insert(ecam, { color = colors.yellow, text = "TURBINE " .. k .. " OFF-LINE", items = items})
+ end
+ end
+
+ -- if no alarms, put some basic status messages in
+ if #ecam == 0 then
+ table.insert(ecam, { color = colors.green, text = "REACTOR " .. util.trinary(unit.reactor_data.mek_status.status, "NOMINAL", "IDLE"), items = {}})
+
+ local plural = util.trinary(unit.num_turbines > 1, "S", "")
+ table.insert(ecam, { color = colors.green, text = "TURBINE" .. plural .. util.trinary(unit.turbine_flow_stable, " STABLE", " STABILIZING"), items = {}})
+ end
+
+ unit.unit_ps.publish("U_ECAM", textutils.serialize(ecam))
+
+ --#endregion
+end
+
+-- update control app with unit data from API_GET_CTRL
+---@param data table
+function iorx.record_control_data(data)
+ for u_id = 1, #data do
+ local unit = io.units[u_id]
+ local u_data = data[u_id]
+
+ 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.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])
+
+ 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
+ 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 iorx.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_fault", fac.ascram_status.matrix_fault)
+ 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
+
+-- update waste app with unit data from API_GET_WASTE
+---@param data table
+function iorx.record_waste_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.waste_mode = u_data[1]
+ unit.waste_product = u_data[2]
+ unit.num_snas = u_data[3]
+ unit.sna_peak_rate = u_data[4]
+ unit.sna_max_rate = u_data[5]
+ unit.sna_out_rate = u_data[6]
+ unit.waste_stats = u_data[7]
+
+ 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)
+
+ unit.unit_ps.publish("sna_count", unit.num_snas)
+ unit.unit_ps.publish("sna_peak_rate", unit.sna_peak_rate)
+ unit.unit_ps.publish("sna_max_rate", unit.sna_max_rate)
+ unit.unit_ps.publish("sna_out_rate", unit.sna_out_rate)
+
+ unit.unit_ps.publish("pu_rate", unit.waste_stats[1])
+ unit.unit_ps.publish("po_rate", unit.waste_stats[2])
+ unit.unit_ps.publish("po_pl_rate", unit.waste_stats[3])
+ end
+
+ -- get facility data
+ local fac = io.facility
+ local f_data = data[#io.units + 1]
+
+ fac.auto_current_waste_product = f_data[1]
+ fac.auto_pu_fallback_active = f_data[2]
+ fac.auto_sps_disabled = f_data[3]
+
+ fac.ps.publish("current_waste_product", fac.auto_current_waste_product)
+ fac.ps.publish("pu_fallback_active", fac.auto_pu_fallback_active)
+ fac.ps.publish("sps_disabled_low_power", fac.auto_sps_disabled)
+
+ fac.ps.publish("process_waste_product", f_data[4])
+ fac.ps.publish("process_pu_fallback", f_data[5])
+ fac.ps.publish("process_sps_low_power", f_data[6])
+
+ fac.waste_stats = f_data[7]
+
+ fac.ps.publish("burn_sum", fac.waste_stats[1])
+ fac.ps.publish("pu_rate", fac.waste_stats[2])
+ fac.ps.publish("po_rate", fac.waste_stats[3])
+ fac.ps.publish("po_pl_rate", fac.waste_stats[4])
+ fac.ps.publish("po_am_rate", fac.waste_stats[5])
+ fac.ps.publish("spent_waste_rate", fac.waste_stats[6])
+
+ fac.ps.publish("sps_computed_status", f_data[8])
+ fac.ps.publish("sps_process_rate", f_data[9])
+end
+
+return function (io_obj)
+ io = io_obj
+ return iorx
+end
diff --git a/pocket/pocket.lua b/pocket/pocket.lua
index 461f57a..8233c78 100644
--- a/pocket/pocket.lua
+++ b/pocket/pocket.lua
@@ -29,6 +29,7 @@ pocket.MQ__RENDER_CMD = MQ__RENDER_CMD
pocket.MQ__RENDER_DATA = MQ__RENDER_DATA
---@type pkt_config
+---@diagnostic disable-next-line: missing-fields
local config = {}
pocket.config = config
@@ -726,23 +727,23 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
end
elseif packet.type == CRDN_TYPE.API_GET_FAC then
if _check_length(packet, 11) then
- iocontrol.record_facility_data(packet.data)
+ iocontrol.rx.record_facility_data(packet.data)
end
elseif packet.type == CRDN_TYPE.API_GET_UNIT then
if _check_length(packet, 12) and type(packet.data[1]) == "number" and iocontrol.get_db().units[packet.data[1]] then
- iocontrol.record_unit_data(packet.data)
+ iocontrol.rx.record_unit_data(packet.data)
end
elseif packet.type == CRDN_TYPE.API_GET_CTRL then
if _check_length(packet, #iocontrol.get_db().units) then
- iocontrol.record_control_data(packet.data)
+ iocontrol.rx.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)
+ iocontrol.rx.record_process_data(packet.data)
end
elseif packet.type == CRDN_TYPE.API_GET_WASTE then
if _check_length(packet, #iocontrol.get_db().units + 1) then
- iocontrol.record_waste_data(packet.data)
+ iocontrol.rx.record_waste_data(packet.data)
end
else _fail_type(packet) end
else
diff --git a/pocket/startup.lua b/pocket/startup.lua
index 0eea7b0..0058904 100644
--- a/pocket/startup.lua
+++ b/pocket/startup.lua
@@ -20,7 +20,7 @@ local pocket = require("pocket.pocket")
local renderer = require("pocket.renderer")
local threads = require("pocket.threads")
-local POCKET_VERSION = "v0.12.10-alpha"
+local POCKET_VERSION = "v0.12.11-alpha"
local println = util.println
local println_ts = util.println_ts
diff --git a/pocket/ui/apps/unit.lua b/pocket/ui/apps/unit.lua
index bc34d5f..8335ff1 100644
--- a/pocket/ui/apps/unit.lua
+++ b/pocket/ui/apps/unit.lua
@@ -9,6 +9,7 @@ local pocket = require("pocket.pocket")
local style = require("pocket.ui.style")
+local dyn_tank = require("pocket.ui.pages.dynamic_tank")
local boiler = require("pocket.ui.pages.unit_boiler")
local reactor = require("pocket.ui.pages.unit_reactor")
local turbine = require("pocket.ui.pages.unit_turbine")
@@ -92,6 +93,10 @@ local function new_view(root)
table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].turbine[i] })
end
+ if #unit.tank_data_tbl > 0 then
+ table.insert(list, { label = "DYN", color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].d_tank })
+ end
+
app.set_sidebar(list)
end
@@ -363,6 +368,15 @@ local function new_view(root)
--#endregion
+ --#region Dynamic Tank Tab
+
+ if #unit.tank_data_tbl > 0 then
+ local tank_pane = Div{parent=page_div}
+ nav_links[i].d_tank = dyn_tank(app, u_page, panes, tank_pane, unit.tank_ps_tbl[1], update)
+ end
+
+ --#endregion
+
util.nop()
end
diff --git a/pocket/ui/pages/dynamic_tank.lua b/pocket/ui/pages/dynamic_tank.lua
new file mode 100644
index 0000000..bfc89de
--- /dev/null
+++ b/pocket/ui/pages/dynamic_tank.lua
@@ -0,0 +1,74 @@
+local types = require("scada-common.types")
+
+local style = require("pocket.ui.style")
+
+local core = require("graphics.core")
+
+local Div = require("graphics.elements.Div")
+local TextBox = require("graphics.elements.TextBox")
+
+local DataIndicator = require("graphics.elements.indicators.DataIndicator")
+local HorizontalBar = require("graphics.elements.indicators.HorizontalBar")
+local IconIndicator = require("graphics.elements.indicators.IconIndicator")
+local StateIndicator = require("graphics.elements.indicators.StateIndicator")
+
+local CONTAINER_MODE = types.CONTAINER_MODE
+
+local cpair = core.cpair
+
+local label = style.label
+local lu_col = style.label_unit_pair
+local text_fg = style.text_fg
+
+local mode_ind_s = {
+ { color = cpair(colors.black, colors.lightGray), symbol = "-" },
+ { color = cpair(colors.black, colors.white), symbol = "+" }
+}
+
+-- create a dynamic tank view for the unit or facility app
+---@param app pocket_app
+---@param page nav_tree_page
+---@param panes Div[]
+---@param tank_pane Div
+---@param ps psil
+---@param update function
+return function (app, page, panes, tank_pane, ps, update)
+ local tank_div = Div{parent=tank_pane,x=2,width=tank_pane.get_width()-2}
+ table.insert(panes, tank_div)
+
+ local tank_page = app.new_page(page, #panes)
+ tank_page.tasks = { update }
+
+ TextBox{parent=tank_div,y=1,text="Dyn Tank",width=9}
+ local status = StateIndicator{parent=tank_div,x=10,y=1,states=style.dtank.states,value=1,min_width=12}
+ status.register(ps, "DynamicTankStateStatus", status.update)
+
+ TextBox{parent=tank_div,y=3,text="Fill",width=10,fg_bg=label}
+ local tank_pcnt = DataIndicator{parent=tank_div,x=14,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_fg}
+ local tank_amnt = DataIndicator{parent=tank_div,label="",format="%18d",value=0,commas=true,unit="mB",lu_colors=lu_col,width=21,fg_bg=text_fg}
+
+ TextBox{parent=tank_div,y=6,text="Fluid Level",width=11,fg_bg=label}
+ local level = HorizontalBar{parent=tank_div,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=21}
+
+ TextBox{parent=tank_div,y=9,text="Tank Fill Mode",width=14,fg_bg=label}
+ local can_fill = IconIndicator{parent=tank_div,y=10,label="Fill",states=mode_ind_s}
+ local can_empty = IconIndicator{parent=tank_div,y=11,label="Empty",states=mode_ind_s}
+
+ local function _can_fill(mode)
+ can_fill.update((mode == CONTAINER_MODE.BOTH) or (mode == CONTAINER_MODE.FILL))
+ end
+
+ local function _can_empty(mode)
+ can_empty.update((mode == CONTAINER_MODE.BOTH) or (mode == CONTAINER_MODE.EMPTY))
+ end
+
+ tank_pcnt.register(ps, "fill", function (f) tank_pcnt.update(f * 100) end)
+ tank_amnt.register(ps, "stored", function (sto) tank_amnt.update(sto.amount) end)
+
+ level.register(ps, "fill", level.update)
+
+ can_fill.register(ps, "container_mode", _can_fill)
+ can_empty.register(ps, "container_mode", _can_empty)
+
+ return tank_page.nav_to
+end
diff --git a/pocket/ui/style.lua b/pocket/ui/style.lua
index aba5d19..7b35cc4 100644
--- a/pocket/ui/style.lua
+++ b/pocket/ui/style.lua
@@ -95,180 +95,93 @@ style.icon_states = states
-- MAIN LAYOUT --
style.reactor = {
- -- reactor states
+ -- reactor states
+ ---@see REACTOR_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "PLC FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "DISABLED"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- },
- {
- color = cpair(colors.black, colors.red),
- text = "SCRAMMED"
- },
- {
- color = cpair(colors.black, colors.red),
- text = "FORCE DSBL"
- }
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "PLC FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "DISABLED" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" },
+ { color = cpair(colors.black, colors.red), text = "SCRAMMED" },
+ { color = cpair(colors.black, colors.red), text = "FORCE DSBL" }
}
}
style.boiler = {
- -- boiler states
+ -- boiler states
+ ---@see BOILER_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "IDLE"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- }
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "IDLE" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" }
}
}
style.turbine = {
- -- turbine states
+ -- turbine states
+ ---@see TURBINE_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "IDLE"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- },
- {
- color = cpair(colors.black, colors.red),
- text = "TRIP"
- }
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "IDLE" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" },
+ { color = cpair(colors.black, colors.red), text = "TRIP" }
+ }
+}
+
+style.dtank = {
+ -- dynamic tank states
+ ---@see TANK_STATE
+ states = {
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.black, colors.green), text = "ONLINE" },
+ { color = cpair(colors.black, colors.yellow), text = "LOW FILL" },
+ { color = cpair(colors.black, colors.green), text = "FILLED" }
}
}
style.imatrix = {
- -- induction matrix states
+ -- induction matrix states
+ ---@see IMATRIX_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ONLINE"
- },
- {
- color = cpair(colors.black, colors.yellow),
- text = "LOW CHARGE"
- },
- {
- color = cpair(colors.black, colors.yellow),
- text = "HIGH CHARGE"
- },
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.black, colors.green), text = "ONLINE" },
+ { color = cpair(colors.black, colors.yellow), text = "LOW CHARGE" },
+ { color = cpair(colors.black, colors.yellow), text = "HIGH CHARGE" }
}
}
style.sps = {
- -- SPS states
+ -- SPS states
+ ---@see SPS_STATE
states = {
- {
- color = cpair(colors.black, colors.yellow),
- text = "OFF-LINE"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "NOT FORMED"
- },
- {
- color = cpair(colors.black, colors.orange),
- text = "RTU FAULT"
- },
- {
- color = cpair(colors.white, colors.gray),
- text = "IDLE"
- },
- {
- color = cpair(colors.black, colors.green),
- text = "ACTIVE"
- }
+ { color = cpair(colors.black, colors.yellow), text = "OFF-LINE" },
+ { color = cpair(colors.black, colors.orange), text = "NOT FORMED" },
+ { color = cpair(colors.black, colors.orange), text = "RTU FAULT" },
+ { color = cpair(colors.white, colors.gray), text = "IDLE" },
+ { color = cpair(colors.black, colors.green), text = "ACTIVE" }
}
}
style.waste = {
-- auto waste processing states
states = {
- {
- color = cpair(colors.black, colors.green),
- text = "PLUTONIUM"
- },
- {
- color = cpair(colors.black, colors.cyan),
- text = "POLONIUM"
- },
- {
- color = cpair(colors.black, colors.purple),
- text = "ANTI MATTER"
- }
+ { color = cpair(colors.black, colors.green), text = "PLUTONIUM" },
+ { color = cpair(colors.black, colors.cyan), text = "POLONIUM" },
+ { color = cpair(colors.black, colors.purple), text = "ANTI MATTER" }
},
states_abbrv = {
- {
- color = cpair(colors.black, colors.green),
- text = "Pu"
- },
- {
- color = cpair(colors.black, colors.cyan),
- text = "Po"
- },
- {
- color = cpair(colors.black, colors.purple),
- text = "AM"
- }
+ { color = cpair(colors.black, colors.green), text = "Pu" },
+ { color = cpair(colors.black, colors.cyan), text = "Po" },
+ { color = cpair(colors.black, colors.purple), text = "AM" }
},
-- process radio button options
options = { "Plutonium", "Polonium", "Antimatter" },
diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua
index 73a1126..a84e55a 100644
--- a/reactor-plc/plc.lua
+++ b/reactor-plc/plc.lua
@@ -29,6 +29,7 @@ local PCALL_SCRAM_MSG = "Scram requires the reactor to be active."
local PCALL_START_MSG = "Reactor is already active."
---@type plc_config
+---@diagnostic disable-next-line: missing-fields
local config = {}
plc.config = config
diff --git a/rtu/rtu.lua b/rtu/rtu.lua
index 0ea1779..06178ad 100644
--- a/rtu/rtu.lua
+++ b/rtu/rtu.lua
@@ -19,6 +19,7 @@ local MGMT_TYPE = comms.MGMT_TYPE
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
---@type rtu_config
+---@diagnostic disable-next-line: missing-fields
local config = {}
rtu.config = config
diff --git a/scada-common/psil.lua b/scada-common/psil.lua
index f3d810a..82e99f6 100644
--- a/scada-common/psil.lua
+++ b/scada-common/psil.lua
@@ -74,6 +74,13 @@ function psil.create()
end
end
+ -- get the currently stored value for a key, or nil if not set
+ ---@param key string data key
+ ---@return any
+ function public.get(key)
+ if ic[key] ~= nil then return ic[key].value else return nil end
+ end
+
-- clear the contents of the interconnect
function public.purge() ic = {} end
diff --git a/scada-common/types.lua b/scada-common/types.lua
index ca1b885..19392fe 100644
--- a/scada-common/types.lua
+++ b/scada-common/types.lua
@@ -253,6 +253,61 @@ types.ENERGY_SCALE_UNITS = {
"RF"
}
+local GENERIC_STATE = {
+ OFFLINE = 1,
+ UNFORMED = 2,
+ FAULT = 3,
+ IDLE = 4,
+ ACTIVE = 5
+}
+
+---@enum REACTOR_STATE
+types.REACTOR_STATE = {
+ OFFLINE = 1,
+ UNFORMED = 2,
+ FAULT = 3,
+ DISABLED = 4,
+ ACTIVE = 5,
+ SCRAMMED = 6,
+ FORCE_DISABLED = 7
+}
+
+---@enum BOILER_STATE
+types.BOILER_STATE = GENERIC_STATE
+
+---@enum TURBINE_STATE
+types.TURBINE_STATE = {
+ OFFLINE = 1,
+ UNFORMED = 2,
+ FAULT = 3,
+ IDLE = 4,
+ ACTIVE = 5,
+ TRIPPED = 6
+}
+
+---@enum TANK_STATE
+types.TANK_STATE = {
+ OFFLINE = 1,
+ UNFORMED = 2,
+ FAULT = 3,
+ ONLINE = 4,
+ LOW_FILL = 5,
+ HIGH_FILL = 6
+}
+
+---@enum IMATRIX_STATE
+types.IMATRIX_STATE = {
+ OFFLINE = 1,
+ UNFORMED = 2,
+ FAULT = 3,
+ ONLINE = 4,
+ LOW_CHARGE = 5,
+ HIGH_CHARGE = 6
+}
+
+---@enum SPS_STATE
+types.SPS_STATE = GENERIC_STATE
+
---@enum PANEL_LINK_STATE
types.PANEL_LINK_STATE = {
LINKED = 1,
diff --git a/supervisor/session/coordinator.lua b/supervisor/session/coordinator.lua
index e9c585d..b011d44 100644
--- a/supervisor/session/coordinator.lua
+++ b/supervisor/session/coordinator.lua
@@ -255,6 +255,7 @@ function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, tim
elseif cmd == FAC_COMMAND.START then
if pkt.length == 6 then
---@type sys_auto_config
+---@diagnostic disable-next-line: missing-fields
local config = {
mode = pkt.data[2],
burn_target = pkt.data[3],
diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua
index 6090eff..ac216d6 100644
--- a/supervisor/session/svsessions.lua
+++ b/supervisor/session/svsessions.lua
@@ -46,12 +46,14 @@ local self = {
config = nil, ---@type svr_config
facility = nil, ---@type facility|nil
-- lists of connected sessions
+---@diagnostic disable: missing-fields
sessions = {
rtu = {}, ---@type rtu_session_struct
plc = {}, ---@type plc_session_struct
crd = {}, ---@type crd_session_struct
pdg = {} ---@type pdg_session_struct
},
+---@diagnostic enable: missing-fields
-- next session IDs
next_ids = { rtu = 0, plc = 0, crd = 0, pdg = 0 },
-- rtu device tracking and invalid assignment detection
diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua
index 8b06d49..98cdc78 100644
--- a/supervisor/supervisor.lua
+++ b/supervisor/supervisor.lua
@@ -14,6 +14,7 @@ local ESTABLISH_ACK = comms.ESTABLISH_ACK
local MGMT_TYPE = comms.MGMT_TYPE
---@type svr_config
+---@diagnostic disable-next-line: missing-fields
local config = {}
supervisor.config = config
diff --git a/supervisor/unit.lua b/supervisor/unit.lua
index 794b3bf..dc59cff 100644
--- a/supervisor/unit.lua
+++ b/supervisor/unit.lua
@@ -90,6 +90,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
envd = {}, ---@type envd_session[]
-- redstone control
io_ctl = nil, ---@type rs_controller
+---@diagnostic disable-next-line: missing-fields
valves = {}, ---@type unit_valves
emcool_opened = false,
-- auto control