mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2025-07-26 23:57:07 +00:00
719 lines
25 KiB
Lua
719 lines
25 KiB
Lua
local log = require("scada-common.log")
|
|
local types = require("scada-common.types")
|
|
local util = require("scada-common.util")
|
|
|
|
local alarm_ctl = require("supervisor.alarm_ctl")
|
|
local unit = require("supervisor.unit")
|
|
local fac_update = require("supervisor.facility_update")
|
|
|
|
local rsctl = require("supervisor.session.rsctl")
|
|
local svsessions = require("supervisor.session.svsessions")
|
|
|
|
local AISTATE = alarm_ctl.AISTATE
|
|
|
|
local ALARM = types.ALARM
|
|
local ALARM_STATE = types.ALARM_STATE
|
|
local AUTO_GROUP = types.AUTO_GROUP
|
|
local PRIO = types.ALARM_PRIORITY
|
|
local PROCESS = types.PROCESS
|
|
local RTU_ID_FAIL = types.RTU_ID_FAIL
|
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
|
local WASTE = types.WASTE_PRODUCT
|
|
|
|
---@enum AUTO_SCRAM
|
|
local AUTO_SCRAM = {
|
|
NONE = 0,
|
|
MATRIX_FAULT = 1,
|
|
MATRIX_FILL = 2,
|
|
CRIT_ALARM = 3,
|
|
RADIATION = 4,
|
|
GEN_FAULT = 5
|
|
}
|
|
|
|
---@enum START_STATUS
|
|
local START_STATUS = {
|
|
OK = 0,
|
|
NO_UNITS = 1,
|
|
BLADE_MISMATCH = 2
|
|
}
|
|
|
|
---@enum RECOVERY_STATE
|
|
local RCV_STATE = {
|
|
INACTIVE = 0,
|
|
PRIMED = 1,
|
|
RUNNING = 2,
|
|
STOPPED = 3
|
|
}
|
|
|
|
local CHARGE_SCALER = 1000000 -- convert MFE to FE
|
|
local GEN_SCALER = 1000 -- convert kFE to FE
|
|
|
|
---@class facility_management
|
|
local facility = {}
|
|
|
|
-- create a new facility management object
|
|
---@nodiscard
|
|
---@param config svr_config supervisor configuration
|
|
function facility.new(config)
|
|
---@class _facility_self
|
|
local self = {
|
|
units = {}, ---@type reactor_unit[]
|
|
types = { AUTO_SCRAM = AUTO_SCRAM, START_STATUS = START_STATUS, RCV_STATE = RCV_STATE },
|
|
status_text = { "START UP", "initializing..." },
|
|
all_sys_ok = false,
|
|
allow_testing = false,
|
|
-- facility tanks
|
|
---@class sv_cooling_conf
|
|
cooling_conf = {
|
|
r_cool = config.CoolingConfig,
|
|
fac_tank_mode = config.FacilityTankMode,
|
|
fac_tank_defs = config.FacilityTankDefs,
|
|
fac_tank_list = config.FacilityTankList,
|
|
fac_tank_conns = config.FacilityTankConns,
|
|
tank_fluid_types = config.TankFluidTypes,
|
|
aux_coolant = config.AuxiliaryCoolant
|
|
},
|
|
-- rtus
|
|
rtu_gw_conn_count = 0,
|
|
rtu_list = {}, ---@type unit_session[][]
|
|
redstone = {}, ---@type redstone_session[]
|
|
induction = {}, ---@type imatrix_session[]
|
|
sps = {}, ---@type sps_session[]
|
|
tanks = {}, ---@type dynamicv_session[]
|
|
envd = {}, ---@type envd_session[]
|
|
-- redstone I/O control
|
|
io_ctl = nil, ---@type rs_controller
|
|
-- process control
|
|
recovery = RCV_STATE.INACTIVE, ---@type RECOVERY_STATE
|
|
recovery_boot_state = nil, ---@type sv_boot_state|nil
|
|
last_unit_states = {}, ---@type boolean[]
|
|
units_ready = false,
|
|
mode = PROCESS.INACTIVE, ---@type PROCESS
|
|
last_mode = PROCESS.INACTIVE, ---@type PROCESS
|
|
return_mode = PROCESS.INACTIVE, ---@type PROCESS
|
|
mode_set = PROCESS.MAX_BURN, ---@type PROCESS
|
|
start_fail = START_STATUS.OK, ---@type START_STATUS
|
|
max_burn_combined = 0.0, -- maximum burn rate to clamp at
|
|
burn_target = 0.1, -- burn rate target for aggregate burn mode
|
|
charge_setpoint = 0, -- FE charge target setpoint
|
|
gen_rate_setpoint = 0, -- FE/t charge rate target setpoint
|
|
group_map = {}, ---@type AUTO_GROUP[] units -> group IDs
|
|
prio_defs = { {}, {}, {}, {} }, ---@type reactor_unit[][] priority definitions (each level is a table of units)
|
|
at_max_burn = false,
|
|
ascram = false,
|
|
ascram_reason = AUTO_SCRAM.NONE,
|
|
---@class ascram_status
|
|
ascram_status = {
|
|
matrix_fault = false,
|
|
matrix_fill = false,
|
|
crit_alarm = false,
|
|
radiation = false,
|
|
gen_fault = false
|
|
},
|
|
-- closed loop control
|
|
charge_conversion = 1.0,
|
|
time_start = 0.0,
|
|
initial_ramp = true,
|
|
waiting_on_ramp = false, -- waiting on auto ramping
|
|
waiting_on_stable = false, -- waiting on gen rate stabilization
|
|
accumulator = 0.0,
|
|
saturated = false,
|
|
last_update = 0,
|
|
last_error = 0.0,
|
|
last_time = 0.0,
|
|
-- waste processing
|
|
waste_product = WASTE.PLUTONIUM, ---@type WASTE_PRODUCT
|
|
current_waste_product = WASTE.PLUTONIUM, ---@type WASTE_PRODUCT
|
|
pu_fallback = false,
|
|
sps_low_power = false,
|
|
disabled_sps = false,
|
|
-- alarm tones
|
|
tone_states = {}, ---@type { [TONE]: boolean }
|
|
test_tone_set = false,
|
|
test_tone_reset = false,
|
|
test_tone_states = {}, ---@type { [TONE]: boolean }
|
|
test_alarm_states = {}, ---@type { [ALARM]: boolean }
|
|
-- statistics
|
|
im_stat_init = false,
|
|
avg_charge = util.mov_avg(3), -- 3 seconds
|
|
avg_inflow = util.mov_avg(6), -- 3 seconds
|
|
avg_outflow = util.mov_avg(6), -- 3 seconds
|
|
-- induction matrix charge delta stats
|
|
avg_net = util.mov_avg(60), -- 60 seconds
|
|
imtx_last_capacity = 0,
|
|
imtx_last_charge = 0,
|
|
imtx_last_charge_t = 0,
|
|
-- track faulted induction matrix update times to reject
|
|
imtx_faulted_times = { 0, 0, 0 },
|
|
-- facility alarms
|
|
---@type { [string]: alarm_def }
|
|
alarms = {
|
|
-- radiation monitor alarm for the facility
|
|
FacilityRadiation = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.FacilityRadiation, tier = PRIO.CRITICAL },
|
|
},
|
|
---@type { [ALARM]: ALARM_STATE }
|
|
alarm_states = {
|
|
[ALARM.FacilityRadiation] = ALARM_STATE.INACTIVE
|
|
}
|
|
}
|
|
|
|
--#region SETUP
|
|
|
|
-- provide self to facility update functions
|
|
local f_update = fac_update(self)
|
|
|
|
-- create units
|
|
for i = 1, config.UnitCount do
|
|
table.insert(self.units, unit.new(i, self.cooling_conf.r_cool[i].BoilerCount, self.cooling_conf.r_cool[i].TurbineCount, config.ExtChargeIdling, self.cooling_conf.aux_coolant[i]))
|
|
table.insert(self.group_map, AUTO_GROUP.MANUAL)
|
|
table.insert(self.last_unit_states, false)
|
|
end
|
|
|
|
-- list for RTU session management
|
|
self.rtu_list = { self.redstone, self.induction, self.sps, self.tanks, self.envd }
|
|
|
|
-- init redstone RTU I/O controller
|
|
self.io_ctl = rsctl.new(self.redstone, 0)
|
|
|
|
-- fill blank alarm/tone states
|
|
for _ = 1, 12 do table.insert(self.test_alarm_states, false) end
|
|
for _ = 1, 8 do
|
|
table.insert(self.tone_states, false)
|
|
table.insert(self.test_tone_states, false)
|
|
end
|
|
|
|
-- init next boot state
|
|
settings.set("LastProcessState", PROCESS.INACTIVE)
|
|
settings.set("LastUnitStates", self.last_unit_states)
|
|
if not settings.save("/supervisor.settings") then
|
|
log.warning("FAC: failed to save initial control state into supervisor settings file")
|
|
end
|
|
|
|
--#endregion
|
|
|
|
-- PRIVATE FUNCTIONS --
|
|
|
|
-- check an auto process control configuration and save it if its valid (does not start the process)
|
|
---@param auto_cfg start_auto_config configuration
|
|
---@return boolean ready, number[] unit_limits
|
|
local function _auto_check_and_save(auto_cfg)
|
|
local ready = false
|
|
|
|
-- load up current limits
|
|
local limits = {}
|
|
for i = 1, config.UnitCount do
|
|
limits[i] = self.units[i].get_control_inf().lim_br100 * 100
|
|
end
|
|
|
|
-- only allow changes if not running
|
|
if self.mode == PROCESS.INACTIVE then
|
|
if (type(auto_cfg.mode) == "number") and (auto_cfg.mode > PROCESS.INACTIVE) and (auto_cfg.mode <= PROCESS.GEN_RATE) then
|
|
self.mode_set = auto_cfg.mode
|
|
end
|
|
|
|
if (type(auto_cfg.burn_target) == "number") and auto_cfg.burn_target >= 0.1 then
|
|
self.burn_target = auto_cfg.burn_target
|
|
end
|
|
|
|
if (type(auto_cfg.charge_target) == "number") and auto_cfg.charge_target >= 0 then
|
|
self.charge_setpoint = auto_cfg.charge_target * CHARGE_SCALER
|
|
end
|
|
|
|
if (type(auto_cfg.gen_target) == "number") and auto_cfg.gen_target >= 0 then
|
|
self.gen_rate_setpoint = auto_cfg.gen_target * GEN_SCALER
|
|
end
|
|
|
|
if (type(auto_cfg.limits) == "table") and (#auto_cfg.limits == config.UnitCount) then
|
|
for i = 1, config.UnitCount do
|
|
local limit = auto_cfg.limits[i]
|
|
|
|
if (type(limit) == "number") and (limit >= 0.1) then
|
|
limits[i] = limit
|
|
self.units[i].set_burn_limit(limit)
|
|
end
|
|
end
|
|
end
|
|
|
|
ready = self.mode_set > 0
|
|
|
|
if ((self.mode_set == PROCESS.CHARGE) and (self.charge_setpoint <= 0)) or
|
|
((self.mode_set == PROCESS.GEN_RATE) and (self.gen_rate_setpoint <= 0)) or
|
|
((self.mode_set == PROCESS.BURN_RATE) and (self.burn_target < 0.1)) then
|
|
ready = false
|
|
end
|
|
end
|
|
|
|
return ready, limits
|
|
end
|
|
|
|
-- PUBLIC FUNCTIONS --
|
|
|
|
---@class facility
|
|
local public = {}
|
|
|
|
--#region Add/Link Devices
|
|
|
|
-- link a redstone RTU session
|
|
---@param rs_unit unit_session
|
|
function public.add_redstone(rs_unit) table.insert(self.redstone, rs_unit) end
|
|
|
|
-- link an induction matrix RTU session
|
|
---@param imatrix unit_session
|
|
---@return boolean linked induction matrix accepted (max 1)
|
|
function public.add_imatrix(imatrix)
|
|
local fail_code, fail_str = svsessions.check_rtu_id(imatrix, self.induction, 1)
|
|
local ok = fail_code == RTU_ID_FAIL.OK
|
|
|
|
if ok then
|
|
table.insert(self.induction, imatrix)
|
|
log.debug(util.c("FAC: linked induction matrix [", imatrix.get_unit_id(), "@", imatrix.get_session_id(), "]"))
|
|
else
|
|
log.warning(util.c("FAC: rejected induction matrix linking due to failure code ", fail_code, " (", fail_str, ")"))
|
|
end
|
|
|
|
return ok
|
|
end
|
|
|
|
-- link an SPS RTU session
|
|
---@param sps unit_session
|
|
---@return boolean linked SPS accepted (max 1)
|
|
function public.add_sps(sps)
|
|
local fail_code, fail_str = svsessions.check_rtu_id(sps, self.sps, 1)
|
|
local ok = fail_code == RTU_ID_FAIL.OK
|
|
|
|
if ok then
|
|
table.insert(self.sps, sps)
|
|
log.debug(util.c("FAC: linked SPS [", sps.get_unit_id(), "@", sps.get_session_id(), "]"))
|
|
else
|
|
log.warning(util.c("FAC: rejected SPS linking due to failure code ", fail_code, " (", fail_str, ")"))
|
|
end
|
|
|
|
return ok
|
|
end
|
|
|
|
-- link a dynamic tank RTU session
|
|
---@param dynamic_tank unit_session
|
|
function public.add_tank(dynamic_tank)
|
|
local fail_code, fail_str = svsessions.check_rtu_id(dynamic_tank, self.tanks, #self.cooling_conf.fac_tank_list)
|
|
local ok = fail_code == RTU_ID_FAIL.OK
|
|
|
|
if ok then
|
|
table.insert(self.tanks, dynamic_tank)
|
|
log.debug(util.c("FAC: linked dynamic tank #", dynamic_tank.get_device_idx(), " [", dynamic_tank.get_unit_id(), "@", dynamic_tank.get_session_id(), "]"))
|
|
else
|
|
log.warning(util.c("FAC: rejected dynamic tank linking due to failure code ", fail_code, " (", fail_str, ")"))
|
|
end
|
|
|
|
return ok
|
|
end
|
|
|
|
-- link an environment detector RTU session
|
|
---@param envd unit_session
|
|
---@return boolean linked environment detector accepted
|
|
function public.add_envd(envd)
|
|
local fail_code, fail_str = svsessions.check_rtu_id(envd, self.envd, 99)
|
|
local ok = fail_code == RTU_ID_FAIL.OK
|
|
|
|
if ok then
|
|
table.insert(self.envd, envd)
|
|
log.debug(util.c("FAC: linked environment detector #", envd.get_device_idx(), " [", envd.get_unit_id(), "@", envd.get_session_id(), "]"))
|
|
else
|
|
log.warning(util.c("FAC: rejected environment detector linking due to failure code ", fail_code, " (", fail_str, ")"))
|
|
end
|
|
|
|
return ok
|
|
end
|
|
|
|
-- purge devices associated with the given RTU session ID
|
|
---@param session integer RTU session ID
|
|
function public.purge_rtu_devices(session)
|
|
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (s) return s.get_session_id() ~= session end) end
|
|
end
|
|
|
|
--#endregion
|
|
|
|
--#region Update
|
|
|
|
-- update (iterate) the facility management
|
|
function public.update()
|
|
-- run reboot recovery routine if needed
|
|
f_update.boot_recovery()
|
|
|
|
-- run process control and evaluate automatic SCRAM
|
|
f_update.pre_auto()
|
|
f_update.auto_control(config.ExtChargeIdling)
|
|
f_update.auto_safety()
|
|
f_update.post_auto()
|
|
|
|
-- handle redstone I/O
|
|
f_update.redstone(public.ack_all)
|
|
|
|
-- unit tasks
|
|
f_update.unit_mgmt()
|
|
|
|
-- update alarm states right before updating the audio
|
|
f_update.update_alarms()
|
|
|
|
-- update alarm tones
|
|
f_update.alarm_audio()
|
|
end
|
|
|
|
-- call the update function of all units in the facility<br>
|
|
-- additionally sets the requested auto waste mode if applicable
|
|
function public.update_units()
|
|
for i = 1, #self.units do
|
|
local u = self.units[i]
|
|
u.auto_set_waste(self.current_waste_product)
|
|
u.update()
|
|
end
|
|
end
|
|
|
|
--#endregion
|
|
|
|
--#region Startup Recovery
|
|
|
|
-- on exit, use this to clear the boot state so we don't resume when exiting cleanly
|
|
function public.clear_boot_state()
|
|
settings.unset("LastProcessState")
|
|
settings.unset("LastUnitStates")
|
|
|
|
if not settings.save("/supervisor.settings") then
|
|
log.warning("facility.clear_boot_state(): failed to save supervisor settings file")
|
|
else
|
|
log.debug("FAC: cleared boot state on exit")
|
|
end
|
|
end
|
|
|
|
-- initialize facility resume boot recovery
|
|
---@param state sv_boot_state|nil
|
|
function public.boot_recovery_init(state)
|
|
if self.recovery == RCV_STATE.INACTIVE and state then
|
|
self.recovery_boot_state = state
|
|
self.recovery = RCV_STATE.PRIMED
|
|
log.info("FAC: startup resume ready")
|
|
end
|
|
end
|
|
|
|
-- attempt facility resume boot recovery
|
|
---@param auto_cfg start_auto_config configuration
|
|
function public.boot_recovery_start(auto_cfg)
|
|
if self.recovery == RCV_STATE.PRIMED then
|
|
self.recovery = util.trinary(_auto_check_and_save(auto_cfg), RCV_STATE.RUNNING, RCV_STATE.STOPPED)
|
|
log.info(util.c("FAC: startup resume ", util.trinary(self.recovery == RCV_STATE.RUNNING, "started", "failed")))
|
|
else self.recovery = RCV_STATE.STOPPED end
|
|
end
|
|
|
|
-- used on certain coordinator commands to end reboot recovery (remain in current operational state)
|
|
function public.cancel_recovery()
|
|
if self.recovery == RCV_STATE.RUNNING then
|
|
self.recovery = RCV_STATE.STOPPED
|
|
self.recovery_boot_state = nil
|
|
log.info("FAC: process startup resume cancelled by user operation")
|
|
end
|
|
end
|
|
|
|
--#endregion
|
|
|
|
--#region Commands
|
|
|
|
-- SCRAM all reactor units
|
|
function public.scram_all()
|
|
for i = 1, #self.units do
|
|
self.units[i].scram()
|
|
end
|
|
end
|
|
|
|
-- ack all alarms on all reactor units and the facility
|
|
function public.ack_all()
|
|
-- unit alarms
|
|
for i = 1, #self.units do self.units[i].ack_all() end
|
|
|
|
-- facility alarms
|
|
for id, state in pairs(self.alarm_states) do
|
|
if state == ALARM_STATE.TRIPPED then self.alarm_states[id] = ALARM_STATE.ACKED end
|
|
end
|
|
end
|
|
|
|
-- check automatic control mode
|
|
function public.auto_is_active() return self.mode ~= PROCESS.INACTIVE end
|
|
|
|
-- stop auto control
|
|
function public.auto_stop() self.mode = PROCESS.INACTIVE end
|
|
|
|
-- set automatic control configuration and start the process
|
|
---@param auto_cfg start_auto_config configuration
|
|
---@return table response ready state (successfully started) and current configuration (after updating)
|
|
function public.auto_start(auto_cfg)
|
|
local ready, limits = _auto_check_and_save(auto_cfg)
|
|
|
|
if ready and self.units_ready then
|
|
self.mode = self.mode_set
|
|
end
|
|
|
|
log.debug(util.c("FAC: process start ", util.trinary(ready, "accepted", "rejected")))
|
|
|
|
return {
|
|
ready,
|
|
self.mode_set,
|
|
self.burn_target,
|
|
self.charge_setpoint / CHARGE_SCALER,
|
|
self.gen_rate_setpoint / GEN_SCALER,
|
|
limits
|
|
}
|
|
end
|
|
|
|
--#endregion
|
|
|
|
--#region Settings
|
|
|
|
-- set the automatic control group of a unit
|
|
---@param unit_id integer unit ID
|
|
---@param group AUTO_GROUP group ID or 0 for independent
|
|
function public.set_group(unit_id, group)
|
|
if (group >= AUTO_GROUP.MANUAL and group <= AUTO_GROUP.BACKUP) and (unit_id > 0 and unit_id <= config.UnitCount) and self.mode == PROCESS.INACTIVE then
|
|
-- remove from old group if previously assigned
|
|
local old_group = self.group_map[unit_id]
|
|
if old_group ~= AUTO_GROUP.MANUAL then
|
|
util.filter_table(self.prio_defs[old_group], function (u) return u.get_id() ~= unit_id end)
|
|
end
|
|
|
|
self.group_map[unit_id] = group
|
|
|
|
-- add to group if not independent
|
|
if group > AUTO_GROUP.MANUAL then
|
|
table.insert(self.prio_defs[group], self.units[unit_id])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- get the automatic control group of a unit
|
|
---@param unit_id integer unit ID
|
|
---@nodiscard
|
|
function public.get_group(unit_id) return self.group_map[unit_id] end
|
|
|
|
-- set waste production
|
|
---@param product WASTE_PRODUCT target product
|
|
---@return WASTE_PRODUCT product newly set value, if valid
|
|
function public.set_waste_product(product)
|
|
if product == WASTE.PLUTONIUM or product == WASTE.POLONIUM or product == WASTE.ANTI_MATTER then
|
|
self.waste_product = product
|
|
end
|
|
|
|
return self.waste_product
|
|
end
|
|
|
|
-- enable/disable plutonium fallback
|
|
---@param enabled boolean requested state
|
|
---@return boolean enabled newly set value
|
|
function public.set_pu_fallback(enabled)
|
|
self.pu_fallback = enabled == true
|
|
return self.pu_fallback
|
|
end
|
|
|
|
-- enable/disable SPS at low power
|
|
---@param enabled boolean requested state
|
|
---@return boolean enabled newly set value
|
|
function public.set_sps_low_power(enabled)
|
|
self.sps_low_power = enabled == true
|
|
return self.sps_low_power
|
|
end
|
|
|
|
--#endregion
|
|
|
|
--#region Diagnostic Testing
|
|
|
|
-- attempt to set a test tone state
|
|
---@param id TONE|0 tone ID or 0 to disable all
|
|
---@param state boolean state
|
|
---@return boolean allow_testing, { [TONE]: boolean } test_tone_states
|
|
function public.diag_set_test_tone(id, state)
|
|
if self.allow_testing then
|
|
self.test_tone_set = true
|
|
self.test_tone_reset = false
|
|
|
|
if id == 0 then
|
|
for i = 1, #self.test_tone_states do self.test_tone_states[i] = false end
|
|
else
|
|
self.test_tone_states[id] = state
|
|
end
|
|
end
|
|
|
|
return self.allow_testing, self.test_tone_states
|
|
end
|
|
|
|
-- attempt to set a test alarm state
|
|
---@param id ALARM|0 alarm ID or 0 to disable all
|
|
---@param state boolean state
|
|
---@return boolean allow_testing, { [ALARM]: boolean } test_alarm_states
|
|
function public.diag_set_test_alarm(id, state)
|
|
if self.allow_testing then
|
|
self.test_tone_set = true
|
|
self.test_tone_reset = false
|
|
|
|
if id == 0 then
|
|
for i = 1, #self.test_alarm_states do self.test_alarm_states[i] = false end
|
|
else
|
|
self.test_alarm_states[id] = state
|
|
end
|
|
end
|
|
|
|
return self.allow_testing, self.test_alarm_states
|
|
end
|
|
|
|
--#endregion
|
|
|
|
--#region Read States/Properties
|
|
|
|
-- get current alarm tone on/off states
|
|
---@nodiscard
|
|
function public.get_alarm_tones() return self.tone_states end
|
|
|
|
-- get build properties of all facility devices
|
|
---@nodiscard
|
|
---@param type RTU_UNIT_TYPE? type or nil to include only a particular unit type, or to include all if nil
|
|
function public.get_build(type)
|
|
local all = type == nil
|
|
local build = {}
|
|
|
|
if all or type == RTU_UNIT_TYPE.IMATRIX then
|
|
build.induction = {}
|
|
for i = 1, #self.induction do
|
|
local matrix = self.induction[i]
|
|
build.induction[i] = { matrix.get_db().formed, matrix.get_db().build }
|
|
end
|
|
end
|
|
|
|
if all or type == RTU_UNIT_TYPE.SPS then
|
|
build.sps = {}
|
|
for i = 1, #self.sps do
|
|
local sps = self.sps[i]
|
|
build.sps[i] = { sps.get_db().formed, sps.get_db().build }
|
|
end
|
|
end
|
|
|
|
if all or type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
|
|
build.tanks = {}
|
|
for i = 1, #self.tanks do
|
|
local tank = self.tanks[i]
|
|
build.tanks[tank.get_device_idx()] = { tank.get_db().formed, tank.get_db().build }
|
|
end
|
|
end
|
|
|
|
return build
|
|
end
|
|
|
|
-- get automatic process control status
|
|
---@nodiscard
|
|
function public.get_control_status()
|
|
local astat = self.ascram_status
|
|
return {
|
|
self.all_sys_ok,
|
|
self.units_ready,
|
|
self.mode,
|
|
self.waiting_on_ramp or self.waiting_on_stable,
|
|
self.at_max_burn or self.saturated,
|
|
self.ascram,
|
|
astat.matrix_fault,
|
|
astat.matrix_fill,
|
|
astat.crit_alarm,
|
|
astat.radiation,
|
|
astat.gen_fault or self.mode == PROCESS.GEN_RATE_FAULT_IDLE,
|
|
self.status_text[1],
|
|
self.status_text[2],
|
|
self.group_map,
|
|
self.current_waste_product,
|
|
self.pu_fallback and (self.current_waste_product == WASTE.PLUTONIUM) and (self.waste_product ~= WASTE.PLUTONIUM),
|
|
self.disabled_sps
|
|
}
|
|
end
|
|
|
|
-- check which RTUs are connected
|
|
---@nodiscard
|
|
function public.check_rtu_conns()
|
|
local conns = {}
|
|
|
|
conns.induction = #self.induction > 0
|
|
conns.sps = #self.sps > 0
|
|
|
|
conns.tanks = {}
|
|
for i = 1, #self.tanks do
|
|
conns.tanks[self.tanks[i].get_device_idx()] = true
|
|
end
|
|
|
|
return conns
|
|
end
|
|
|
|
-- get RTU statuses
|
|
---@nodiscard
|
|
function public.get_rtu_statuses()
|
|
local status = {}
|
|
|
|
-- total count of all connected RTUs in the facility
|
|
status.count = self.rtu_gw_conn_count
|
|
|
|
-- power averages from induction matricies
|
|
status.power = {
|
|
self.avg_charge.compute(),
|
|
self.avg_inflow.compute(),
|
|
self.avg_outflow.compute(),
|
|
0
|
|
}
|
|
|
|
-- status of induction matricies (including tanks)
|
|
status.induction = {}
|
|
for i = 1, #self.induction do
|
|
local matrix = self.induction[i]
|
|
local db = matrix.get_db()
|
|
|
|
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
|
|
|
local fe_per_ms = self.avg_net.compute()
|
|
local remaining = util.joules_to_fe_rf(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy))
|
|
status.power[4] = remaining / fe_per_ms
|
|
end
|
|
|
|
-- status of sps
|
|
status.sps = {}
|
|
for i = 1, #self.sps do
|
|
local sps = self.sps[i]
|
|
local db = sps.get_db()
|
|
status.sps[i] = { sps.is_faulted(), db.formed, db.state, db.tanks }
|
|
end
|
|
|
|
-- status of dynamic tanks
|
|
status.tanks = {}
|
|
for i = 1, #self.tanks do
|
|
local tank = self.tanks[i]
|
|
local db = tank.get_db()
|
|
status.tanks[tank.get_device_idx()] = { tank.is_faulted(), db.formed, db.state, db.tanks }
|
|
end
|
|
|
|
-- radiation monitors (environment detectors)
|
|
status.envds = {}
|
|
for i = 1, #self.envd do
|
|
local envd = self.envd[i]
|
|
local db = envd.get_db()
|
|
status.envds[envd.get_device_idx()] = { envd.is_faulted(), db.radiation, db.radiation_raw }
|
|
end
|
|
|
|
return status
|
|
end
|
|
|
|
--#endregion
|
|
|
|
-- supervisor sessions reporting the list of active RTU gateway sessions
|
|
---@param sessions rtu_session_struct[] session list of all connected RTU gateways
|
|
function public.report_rtu_gateways(sessions) self.rtu_gw_conn_count = #sessions end
|
|
|
|
-- get the facility cooling configuration
|
|
function public.get_cooling_conf() return self.cooling_conf end
|
|
|
|
-- get the units in this facility
|
|
---@nodiscard
|
|
function public.get_units() return self.units end
|
|
|
|
return public
|
|
end
|
|
|
|
return facility
|