2024-08-18 23:10:43 +00:00
|
|
|
local log = require("scada-common.log")
|
|
|
|
local types = require("scada-common.types")
|
|
|
|
local util = require("scada-common.util")
|
2022-12-10 18:58:17 +00:00
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
local unit = require("supervisor.unit")
|
2024-08-21 01:32:54 +00:00
|
|
|
local fac_update = require("supervisor.facility_update")
|
2022-12-18 18:56:04 +00:00
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
local rsctl = require("supervisor.session.rsctl")
|
2024-08-21 01:32:54 +00:00
|
|
|
local svsessions = require("supervisor.session.svsessions")
|
2023-08-21 02:56:51 +00:00
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
local PROCESS = types.PROCESS
|
2024-08-22 16:42:57 +00:00
|
|
|
local RTU_ID_FAIL = types.RTU_ID_FAIL
|
2024-08-18 23:10:43 +00:00
|
|
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
|
|
|
local WASTE = types.WASTE_PRODUCT
|
2022-12-18 18:56:04 +00:00
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
---@enum AUTO_SCRAM
|
2023-01-03 21:50:31 +00:00
|
|
|
local AUTO_SCRAM = {
|
|
|
|
NONE = 0,
|
|
|
|
MATRIX_DC = 1,
|
2023-02-07 05:32:50 +00:00
|
|
|
MATRIX_FILL = 2,
|
2023-02-11 19:27:29 +00:00
|
|
|
CRIT_ALARM = 3,
|
2023-02-15 03:55:40 +00:00
|
|
|
RADIATION = 4,
|
|
|
|
GEN_FAULT = 5
|
2023-01-03 21:50:31 +00:00
|
|
|
}
|
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
---@enum START_STATUS
|
2023-02-10 03:52:10 +00:00
|
|
|
local START_STATUS = {
|
|
|
|
OK = 0,
|
|
|
|
NO_UNITS = 1,
|
|
|
|
BLADE_MISMATCH = 2
|
|
|
|
}
|
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
---@class facility_management
|
|
|
|
local facility = {}
|
|
|
|
|
|
|
|
-- create a new facility management object
|
2023-02-25 04:36:16 +00:00
|
|
|
---@nodiscard
|
2024-04-12 04:13:05 +00:00
|
|
|
---@param config svr_config supervisor configuration
|
2024-08-21 01:32:54 +00:00
|
|
|
function facility.new(config)
|
2024-08-18 23:10:43 +00:00
|
|
|
---@class _facility_self
|
2022-12-10 18:58:17 +00:00
|
|
|
local self = {
|
2022-12-18 18:56:04 +00:00
|
|
|
units = {},
|
2024-08-18 23:10:43 +00:00
|
|
|
types = { AUTO_SCRAM = AUTO_SCRAM, START_STATUS = START_STATUS },
|
2023-02-03 03:58:51 +00:00
|
|
|
status_text = { "START UP", "initializing..." },
|
2023-02-04 18:47:00 +00:00
|
|
|
all_sys_ok = false,
|
2023-07-29 22:16:59 +00:00
|
|
|
allow_testing = false,
|
2024-08-19 03:04:07 +00:00
|
|
|
-- facility tanks
|
2024-08-21 01:32:54 +00:00
|
|
|
---@class sv_cooling_conf
|
|
|
|
cooling_conf = {
|
|
|
|
r_cool = config.CoolingConfig,
|
|
|
|
fac_tank_mode = config.FacilityTankMode,
|
|
|
|
fac_tank_defs = config.FacilityTankDefs,
|
|
|
|
fac_tank_list = {}
|
|
|
|
},
|
2023-02-19 05:14:27 +00:00
|
|
|
-- rtus
|
2023-02-16 00:52:28 +00:00
|
|
|
rtu_conn_count = 0,
|
2023-07-15 17:16:36 +00:00
|
|
|
rtu_list = {},
|
2023-02-19 05:14:27 +00:00
|
|
|
redstone = {},
|
|
|
|
induction = {},
|
2023-07-08 20:57:13 +00:00
|
|
|
sps = {},
|
2023-07-15 17:16:36 +00:00
|
|
|
tanks = {},
|
2023-02-19 05:14:27 +00:00
|
|
|
envd = {},
|
|
|
|
-- redstone I/O control
|
|
|
|
io_ctl = nil, ---@type rs_controller
|
2022-12-18 18:56:04 +00:00
|
|
|
-- process control
|
2023-02-03 03:58:51 +00:00
|
|
|
units_ready = false,
|
2022-12-18 18:56:04 +00:00
|
|
|
mode = PROCESS.INACTIVE,
|
2023-01-03 21:50:31 +00:00
|
|
|
last_mode = PROCESS.INACTIVE,
|
2023-02-07 05:32:50 +00:00
|
|
|
return_mode = PROCESS.INACTIVE,
|
2023-02-10 03:52:10 +00:00
|
|
|
mode_set = PROCESS.MAX_BURN,
|
|
|
|
start_fail = START_STATUS.OK,
|
2023-01-26 23:26:26 +00:00
|
|
|
max_burn_combined = 0.0, -- maximum burn rate to clamp at
|
|
|
|
burn_target = 0.1, -- burn rate target for aggregate burn mode
|
2023-02-08 04:47:58 +00:00
|
|
|
charge_setpoint = 0, -- FE charge target setpoint
|
|
|
|
gen_rate_setpoint = 0, -- FE/t charge rate target setpoint
|
2023-06-18 19:19:01 +00:00
|
|
|
group_map = {}, -- units -> group IDs
|
2023-01-03 21:50:31 +00:00
|
|
|
prio_defs = { {}, {}, {}, {} }, -- priority definitions (each level is a table of units)
|
2023-02-07 05:32:50 +00:00
|
|
|
at_max_burn = false,
|
2023-01-03 21:50:31 +00:00
|
|
|
ascram = false,
|
|
|
|
ascram_reason = AUTO_SCRAM.NONE,
|
2023-02-15 03:55:40 +00:00
|
|
|
---@class ascram_status
|
2023-02-13 23:08:32 +00:00
|
|
|
ascram_status = {
|
|
|
|
matrix_dc = false,
|
|
|
|
matrix_fill = false,
|
|
|
|
crit_alarm = false,
|
2023-02-15 03:55:40 +00:00
|
|
|
radiation = false,
|
2023-02-13 23:08:32 +00:00
|
|
|
gen_fault = false
|
|
|
|
},
|
2023-01-03 21:50:31 +00:00
|
|
|
-- closed loop control
|
|
|
|
charge_conversion = 1.0,
|
|
|
|
time_start = 0.0,
|
|
|
|
initial_ramp = true,
|
|
|
|
waiting_on_ramp = false,
|
2023-02-08 04:47:58 +00:00
|
|
|
waiting_on_stable = false,
|
2023-01-03 21:50:31 +00:00
|
|
|
accumulator = 0.0,
|
2023-01-26 23:26:26 +00:00
|
|
|
saturated = false,
|
2023-02-08 04:47:58 +00:00
|
|
|
last_update = 0,
|
2023-01-03 21:50:31 +00:00
|
|
|
last_error = 0.0,
|
|
|
|
last_time = 0.0,
|
2023-07-08 20:57:13 +00:00
|
|
|
-- waste processing
|
|
|
|
waste_product = WASTE.PLUTONIUM,
|
|
|
|
current_waste_product = WASTE.PLUTONIUM,
|
|
|
|
pu_fallback = false,
|
2024-04-28 06:01:21 +00:00
|
|
|
sps_low_power = false,
|
|
|
|
disabled_sps = false,
|
2023-07-27 00:48:11 +00:00
|
|
|
-- alarm tones
|
2023-07-29 22:16:59 +00:00
|
|
|
tone_states = {},
|
|
|
|
test_tone_set = false,
|
|
|
|
test_tone_reset = false,
|
|
|
|
test_tone_states = {},
|
|
|
|
test_alarm_states = {},
|
2022-12-18 18:56:04 +00:00
|
|
|
-- statistics
|
|
|
|
im_stat_init = false,
|
2024-04-27 20:27:01 +00:00
|
|
|
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
|
2024-04-28 17:08:16 +00:00
|
|
|
imtx_last_capacity = 0,
|
|
|
|
imtx_last_charge = 0,
|
2024-05-01 00:28:07 +00:00
|
|
|
imtx_last_charge_t = 0,
|
|
|
|
-- track faulted induction matrix update times to reject
|
|
|
|
imtx_faulted_times = { 0, 0, 0 }
|
2022-12-10 18:58:17 +00:00
|
|
|
}
|
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
-- provide self to facility update functions
|
|
|
|
local f_update = fac_update(self)
|
|
|
|
|
2022-12-18 18:56:04 +00:00
|
|
|
-- create units
|
2024-04-12 04:13:05 +00:00
|
|
|
for i = 1, config.UnitCount do
|
2024-08-21 01:32:54 +00:00
|
|
|
table.insert(self.units,
|
|
|
|
unit.new(i, self.cooling_conf.r_cool[i].BoilerCount, self.cooling_conf.r_cool[i].TurbineCount, config.ExtChargeIdling))
|
2023-06-18 19:19:01 +00:00
|
|
|
table.insert(self.group_map, 0)
|
2022-12-18 18:56:04 +00:00
|
|
|
end
|
|
|
|
|
2023-07-15 17:16:36 +00:00
|
|
|
-- list for RTU session management
|
|
|
|
self.rtu_list = { self.redstone, self.induction, self.sps, self.tanks, self.envd }
|
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
-- init redstone RTU I/O controller
|
2023-02-19 05:14:27 +00:00
|
|
|
self.io_ctl = rsctl.new(self.redstone)
|
2022-12-10 18:58:17 +00:00
|
|
|
|
2023-07-29 22:16:59 +00:00
|
|
|
-- 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
|
|
|
|
|
2024-08-19 03:04:07 +00:00
|
|
|
--#region decode tank configuration
|
|
|
|
|
2024-08-21 01:32:54 +00:00
|
|
|
local cool_conf = self.cooling_conf
|
|
|
|
|
2024-08-19 03:04:07 +00:00
|
|
|
-- determine tank information
|
2024-08-21 01:32:54 +00:00
|
|
|
if cool_conf.fac_tank_mode == 0 then
|
2024-08-21 02:28:41 +00:00
|
|
|
cool_conf.fac_tank_defs = {}
|
2024-08-19 03:04:07 +00:00
|
|
|
|
|
|
|
-- on facility tank mode 0, setup tank defs to match unit tank option
|
|
|
|
for i = 1, config.UnitCount do
|
2024-08-21 02:28:41 +00:00
|
|
|
cool_conf.fac_tank_defs[i] = util.trinary(cool_conf.r_cool[i].TankConnection, 1, 0)
|
2024-08-19 03:04:07 +00:00
|
|
|
end
|
|
|
|
|
2024-08-21 02:28:41 +00:00
|
|
|
cool_conf.fac_tank_list = { table.unpack(cool_conf.fac_tank_defs) }
|
2024-08-19 03:04:07 +00:00
|
|
|
else
|
|
|
|
-- decode the layout of tanks from the connections definitions
|
2024-08-21 01:32:54 +00:00
|
|
|
local tank_mode = cool_conf.fac_tank_mode
|
|
|
|
local tank_defs = cool_conf.fac_tank_defs
|
2024-08-19 03:04:07 +00:00
|
|
|
local tank_list = { table.unpack(tank_defs) }
|
|
|
|
|
|
|
|
local function calc_fdef(start_idx, end_idx)
|
|
|
|
local first = 4
|
|
|
|
for i = start_idx, end_idx do
|
2024-08-21 01:32:54 +00:00
|
|
|
if tank_defs[i] == 2 then
|
2024-08-19 03:04:07 +00:00
|
|
|
if i < first then first = i end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return first
|
|
|
|
end
|
|
|
|
|
|
|
|
if tank_mode == 1 then
|
|
|
|
-- (1) 1 total facility tank (A A A A)
|
|
|
|
local first_fdef = calc_fdef(1, #tank_defs)
|
|
|
|
for i = 1, #tank_defs do
|
|
|
|
if i > first_fdef and tank_defs[i] == 2 then
|
|
|
|
tank_list[i] = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif tank_mode == 2 then
|
|
|
|
-- (2) 2 total facility tanks (A A A B)
|
|
|
|
local first_fdef = calc_fdef(1, math.min(3, #tank_defs))
|
|
|
|
for i = 1, #tank_defs do
|
|
|
|
if (i ~= 4) and (i > first_fdef) and (tank_defs[i] == 2) then
|
|
|
|
tank_list[i] = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif tank_mode == 3 then
|
|
|
|
-- (3) 2 total facility tanks (A A B B)
|
|
|
|
for _, a in pairs({ 1, 3 }) do
|
|
|
|
local b = a + 1
|
|
|
|
if (tank_defs[a] == 2) and (tank_defs[b] == 2) then
|
|
|
|
tank_list[b] = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif tank_mode == 4 then
|
|
|
|
-- (4) 2 total facility tanks (A B B B)
|
|
|
|
local first_fdef = calc_fdef(2, #tank_defs)
|
|
|
|
for i = 1, #tank_defs do
|
|
|
|
if (i ~= 1) and (i > first_fdef) and (tank_defs[i] == 2) then
|
|
|
|
tank_list[i] = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif tank_mode == 5 then
|
|
|
|
-- (5) 3 total facility tanks (A A B C)
|
|
|
|
local first_fdef = calc_fdef(1, math.min(2, #tank_defs))
|
|
|
|
for i = 1, #tank_defs do
|
|
|
|
if (not (i == 3 or i == 4)) and (i > first_fdef) and (tank_defs[i] == 2) then
|
|
|
|
tank_list[i] = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif tank_mode == 6 then
|
|
|
|
-- (6) 3 total facility tanks (A B B C)
|
|
|
|
local first_fdef = calc_fdef(2, math.min(3, #tank_defs))
|
|
|
|
for i = 1, #tank_defs do
|
|
|
|
if (not (i == 1 or i == 4)) and (i > first_fdef) and (tank_defs[i] == 2) then
|
|
|
|
tank_list[i] = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif tank_mode == 7 then
|
|
|
|
-- (7) 3 total facility tanks (A B C C)
|
|
|
|
local first_fdef = calc_fdef(3, #tank_defs)
|
|
|
|
for i = 1, #tank_defs do
|
|
|
|
if (not (i == 1 or i == 2)) and (i > first_fdef) and (tank_defs[i] == 2) then
|
|
|
|
tank_list[i] = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-08-21 01:32:54 +00:00
|
|
|
cool_conf.fac_tank_list = tank_list
|
2024-08-19 03:04:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
--#endregion
|
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
-- PUBLIC FUNCTIONS --
|
|
|
|
|
|
|
|
---@class facility
|
|
|
|
local public = {}
|
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#region Add/Link Devices
|
2022-12-10 18:58:17 +00:00
|
|
|
|
|
|
|
-- link a redstone RTU session
|
|
|
|
---@param rs_unit unit_session
|
2023-11-12 16:54:47 +00:00
|
|
|
function public.add_redstone(rs_unit) table.insert(self.redstone, rs_unit) end
|
2022-12-10 18:58:17 +00:00
|
|
|
|
2023-07-06 05:36:06 +00:00
|
|
|
-- link an induction matrix RTU session
|
2022-12-10 18:58:17 +00:00
|
|
|
---@param imatrix unit_session
|
2023-07-15 17:16:36 +00:00
|
|
|
---@return boolean linked induction matrix accepted (max 1)
|
2022-12-10 18:58:17 +00:00
|
|
|
function public.add_imatrix(imatrix)
|
2024-08-21 01:32:54 +00:00
|
|
|
local fail_code, fail_str = svsessions.check_rtu_id(imatrix, self.induction, 1)
|
2024-08-22 16:42:57 +00:00
|
|
|
local ok = fail_code == RTU_ID_FAIL.OK
|
2024-08-16 18:08:53 +00:00
|
|
|
|
2024-08-22 16:42:57 +00:00
|
|
|
if ok then
|
2023-07-15 17:16:36 +00:00
|
|
|
table.insert(self.induction, imatrix)
|
2024-08-22 16:42:57 +00:00
|
|
|
log.debug(util.c("FAC: linked induction matrix [", imatrix.get_unit_id(), "@", imatrix.get_session_id(), "]"))
|
2024-08-16 18:08:53 +00:00
|
|
|
else
|
|
|
|
log.warning(util.c("FAC: rejected induction matrix linking due to failure code ", fail_code, " (", fail_str, ")"))
|
|
|
|
end
|
|
|
|
|
2024-08-22 16:42:57 +00:00
|
|
|
return ok
|
2022-12-10 18:58:17 +00:00
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- link an SPS RTU session
|
|
|
|
---@param sps unit_session
|
2023-07-15 17:16:36 +00:00
|
|
|
---@return boolean linked SPS accepted (max 1)
|
2023-07-08 20:57:13 +00:00
|
|
|
function public.add_sps(sps)
|
2024-08-21 01:32:54 +00:00
|
|
|
local fail_code, fail_str = svsessions.check_rtu_id(sps, self.sps, 1)
|
2024-08-22 16:42:57 +00:00
|
|
|
local ok = fail_code == RTU_ID_FAIL.OK
|
2024-08-16 18:08:53 +00:00
|
|
|
|
2024-08-22 16:42:57 +00:00
|
|
|
if ok then
|
2023-07-15 17:16:36 +00:00
|
|
|
table.insert(self.sps, sps)
|
2024-08-22 16:42:57 +00:00
|
|
|
log.debug(util.c("FAC: linked SPS [", sps.get_unit_id(), "@", sps.get_session_id(), "]"))
|
2024-08-16 18:08:53 +00:00
|
|
|
else
|
|
|
|
log.warning(util.c("FAC: rejected SPS linking due to failure code ", fail_code, " (", fail_str, ")"))
|
|
|
|
end
|
|
|
|
|
2024-08-22 16:42:57 +00:00
|
|
|
return ok
|
2023-07-15 17:16:36 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- link a dynamic tank RTU session
|
|
|
|
---@param dynamic_tank unit_session
|
2024-08-22 16:42:57 +00:00
|
|
|
function public.add_tank(dynamic_tank)
|
2024-08-22 16:45:36 +00:00
|
|
|
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
|
2024-08-22 16:42:57 +00:00
|
|
|
end
|
2023-07-08 20:57:13 +00:00
|
|
|
|
2023-02-14 03:11:31 +00:00
|
|
|
-- link an environment detector RTU session
|
|
|
|
---@param envd unit_session
|
2024-08-21 21:23:16 +00:00
|
|
|
---@return boolean linked environment detector accepted
|
|
|
|
function public.add_envd(envd)
|
|
|
|
local fail_code, fail_str = svsessions.check_rtu_id(envd, self.envd, 99)
|
2024-08-22 16:42:57 +00:00
|
|
|
local ok = fail_code == RTU_ID_FAIL.OK
|
2024-08-21 21:23:16 +00:00
|
|
|
|
2024-08-22 16:42:57 +00:00
|
|
|
if ok then
|
2024-08-21 21:23:16 +00:00
|
|
|
table.insert(self.envd, envd)
|
2024-08-22 16:42:57 +00:00
|
|
|
log.debug(util.c("FAC: linked environment detector #", envd.get_device_idx(), " [", envd.get_unit_id(), "@", envd.get_session_id(), "]"))
|
2024-08-21 21:23:16 +00:00
|
|
|
else
|
|
|
|
log.warning(util.c("FAC: rejected environment detector linking due to failure code ", fail_code, " (", fail_str, ")"))
|
|
|
|
end
|
|
|
|
|
2024-08-22 16:42:57 +00:00
|
|
|
return ok
|
2024-08-21 21:23:16 +00:00
|
|
|
end
|
2023-02-14 03:11:31 +00:00
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
-- purge devices associated with the given RTU session ID
|
|
|
|
---@param session integer RTU session ID
|
|
|
|
function public.purge_rtu_devices(session)
|
2023-07-15 17:16:36 +00:00
|
|
|
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (s) return s.get_session_id() ~= session end) end
|
2022-12-10 18:58:17 +00:00
|
|
|
end
|
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#endregion
|
2022-12-10 18:58:17 +00:00
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#region Update
|
2023-02-16 00:52:28 +00:00
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
-- update (iterate) the facility management
|
|
|
|
function public.update()
|
2024-08-18 23:10:43 +00:00
|
|
|
-- 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()
|
2023-02-15 03:55:40 +00:00
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
-- handle redstone I/O
|
|
|
|
f_update.redstone(public.ack_all)
|
2023-11-12 16:54:47 +00:00
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
-- unit tasks
|
2024-08-21 01:32:54 +00:00
|
|
|
f_update.unit_mgmt()
|
2024-03-06 02:47:14 +00:00
|
|
|
|
2024-08-18 23:10:43 +00:00
|
|
|
-- update alarm tones
|
2024-08-18 23:12:13 +00:00
|
|
|
f_update.alarm_audio()
|
2022-12-10 18:58:17 +00:00
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- call the update function of all units in the facility<br>
|
|
|
|
-- additionally sets the requested auto waste mode if applicable
|
2022-12-18 18:56:04 +00:00
|
|
|
function public.update_units()
|
|
|
|
for i = 1, #self.units do
|
|
|
|
local u = self.units[i] ---@type reactor_unit
|
2023-07-08 20:57:13 +00:00
|
|
|
u.auto_set_waste(self.current_waste_product)
|
2022-12-18 18:56:04 +00:00
|
|
|
u.update()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#endregion
|
|
|
|
|
|
|
|
--#region Commands
|
2023-01-26 23:26:26 +00:00
|
|
|
|
|
|
|
-- SCRAM all reactor units
|
|
|
|
function public.scram_all()
|
|
|
|
for i = 1, #self.units do
|
|
|
|
local u = self.units[i] ---@type reactor_unit
|
|
|
|
u.scram()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-02-07 23:44:34 +00:00
|
|
|
-- ack all alarms on all reactor units
|
|
|
|
function public.ack_all()
|
|
|
|
for i = 1, #self.units do
|
|
|
|
local u = self.units[i] ---@type reactor_unit
|
|
|
|
u.ack_all()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
-- stop auto control
|
2023-07-08 20:57:13 +00:00
|
|
|
function public.auto_stop() self.mode = PROCESS.INACTIVE end
|
2023-01-26 23:26:26 +00:00
|
|
|
|
|
|
|
-- set automatic control configuration and start the process
|
2024-04-12 04:13:05 +00:00
|
|
|
---@param auto_cfg coord_auto_config configuration
|
2023-01-26 23:26:26 +00:00
|
|
|
---@return table response ready state (successfully started) and current configuration (after updating)
|
2024-04-12 04:13:05 +00:00
|
|
|
function public.auto_start(auto_cfg)
|
2023-07-08 20:57:13 +00:00
|
|
|
local charge_scaler = 1000000 -- convert MFE to FE
|
|
|
|
local gen_scaler = 1000 -- convert kFE to FE
|
|
|
|
local ready = false
|
2023-01-26 23:26:26 +00:00
|
|
|
|
|
|
|
-- load up current limits
|
|
|
|
local limits = {}
|
2024-04-12 04:13:05 +00:00
|
|
|
for i = 1, config.UnitCount do
|
2023-01-26 23:26:26 +00:00
|
|
|
local u = self.units[i] ---@type reactor_unit
|
2023-02-09 01:26:13 +00:00
|
|
|
limits[i] = u.get_control_inf().lim_br100 * 100
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- only allow changes if not running
|
|
|
|
if self.mode == PROCESS.INACTIVE then
|
2024-04-12 04:13:05 +00:00
|
|
|
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
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-04-12 04:13:05 +00:00
|
|
|
if (type(auto_cfg.burn_target) == "number") and auto_cfg.burn_target >= 0.1 then
|
|
|
|
self.burn_target = auto_cfg.burn_target
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-04-12 04:13:05 +00:00
|
|
|
if (type(auto_cfg.charge_target) == "number") and auto_cfg.charge_target >= 0 then
|
|
|
|
self.charge_setpoint = auto_cfg.charge_target * charge_scaler
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-04-12 04:13:05 +00:00
|
|
|
if (type(auto_cfg.gen_target) == "number") and auto_cfg.gen_target >= 0 then
|
|
|
|
self.gen_rate_setpoint = auto_cfg.gen_target * gen_scaler
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-04-12 04:13:05 +00:00
|
|
|
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]
|
2023-01-26 23:26:26 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2023-07-15 17:16:36 +00:00
|
|
|
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
|
2023-01-26 23:26:26 +00:00
|
|
|
ready = false
|
|
|
|
end
|
|
|
|
|
2023-02-03 03:58:51 +00:00
|
|
|
ready = ready and self.units_ready
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
if ready then self.mode = self.mode_set end
|
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
return {
|
|
|
|
ready,
|
|
|
|
self.mode_set,
|
|
|
|
self.burn_target,
|
|
|
|
self.charge_setpoint / charge_scaler,
|
|
|
|
self.gen_rate_setpoint / gen_scaler,
|
|
|
|
limits
|
|
|
|
}
|
2023-01-26 23:26:26 +00:00
|
|
|
end
|
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#endregion
|
|
|
|
|
|
|
|
--#region Settings
|
2023-01-03 21:50:31 +00:00
|
|
|
|
|
|
|
-- set the automatic control group of a unit
|
|
|
|
---@param unit_id integer unit ID
|
|
|
|
---@param group integer group ID or 0 for independent
|
|
|
|
function public.set_group(unit_id, group)
|
2024-04-12 04:13:05 +00:00
|
|
|
if (group >= 0 and group <= 4) and (unit_id > 0 and unit_id <= config.UnitCount) and self.mode == PROCESS.INACTIVE then
|
2023-01-03 21:50:31 +00:00
|
|
|
-- remove from old group if previously assigned
|
|
|
|
local old_group = self.group_map[unit_id]
|
|
|
|
if old_group ~= 0 then
|
|
|
|
util.filter_table(self.prio_defs[old_group], function (u) return u.get_id() ~= unit_id end)
|
|
|
|
end
|
|
|
|
|
2023-02-02 02:55:02 +00:00
|
|
|
self.group_map[unit_id] = group
|
2023-01-03 21:50:31 +00:00
|
|
|
|
|
|
|
-- add to group if not independent
|
|
|
|
if group > 0 then
|
|
|
|
table.insert(self.prio_defs[group], self.units[unit_id])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- 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
|
|
|
|
|
2024-04-28 06:01:21 +00:00
|
|
|
-- 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
|
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#endregion
|
|
|
|
|
|
|
|
--#region Diagnostic Testing
|
2023-07-29 22:16:59 +00:00
|
|
|
|
|
|
|
-- attempt to set a test tone state
|
2023-07-30 04:13:26 +00:00
|
|
|
---@param id TONE|0 tone ID or 0 to disable all
|
2023-07-29 22:16:59 +00:00
|
|
|
---@param state boolean state
|
|
|
|
---@return boolean allow_testing, table 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, table 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
|
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#endregion
|
|
|
|
|
|
|
|
--#region Read States/Properties
|
2022-12-10 18:58:17 +00:00
|
|
|
|
2023-07-27 00:48:11 +00:00
|
|
|
-- get current alarm tone on/off states
|
|
|
|
---@nodiscard
|
|
|
|
function public.get_alarm_tones() return self.tone_states end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- get build properties of all facility devices
|
2023-02-25 04:36:16 +00:00
|
|
|
---@nodiscard
|
2023-07-08 20:57:13 +00:00
|
|
|
---@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
|
2022-12-10 18:58:17 +00:00
|
|
|
local build = {}
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
if all or type == RTU_UNIT_TYPE.IMATRIX then
|
2023-02-20 05:49:37 +00:00
|
|
|
build.induction = {}
|
|
|
|
for i = 1, #self.induction do
|
|
|
|
local matrix = self.induction[i] ---@type unit_session
|
2023-11-06 15:21:42 +00:00
|
|
|
build.induction[i] = { matrix.get_db().formed, matrix.get_db().build }
|
2023-02-20 05:49:37 +00:00
|
|
|
end
|
2022-12-10 18:58:17 +00:00
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
if all or type == RTU_UNIT_TYPE.SPS then
|
|
|
|
build.sps = {}
|
|
|
|
for i = 1, #self.sps do
|
2023-11-06 15:21:42 +00:00
|
|
|
local sps = self.sps[i] ---@type unit_session
|
|
|
|
build.sps[i] = { sps.get_db().formed, sps.get_db().build }
|
2023-07-08 20:57:13 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-15 17:16:36 +00:00
|
|
|
if all or type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
|
|
|
|
build.tanks = {}
|
|
|
|
for i = 1, #self.tanks do
|
2023-11-06 15:21:42 +00:00
|
|
|
local tank = self.tanks[i] ---@type unit_session
|
2023-07-15 17:16:36 +00:00
|
|
|
build.tanks[tank.get_device_idx()] = { tank.get_db().formed, tank.get_db().build }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
return build
|
|
|
|
end
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
-- get automatic process control status
|
2023-02-25 04:36:16 +00:00
|
|
|
---@nodiscard
|
2023-01-26 23:26:26 +00:00
|
|
|
function public.get_control_status()
|
2023-02-15 03:55:40 +00:00
|
|
|
local astat = self.ascram_status
|
2023-01-26 23:26:26 +00:00
|
|
|
return {
|
2023-02-04 18:47:00 +00:00
|
|
|
self.all_sys_ok,
|
2023-02-03 03:58:51 +00:00
|
|
|
self.units_ready,
|
2023-01-26 23:26:26 +00:00
|
|
|
self.mode,
|
2023-02-08 04:47:58 +00:00
|
|
|
self.waiting_on_ramp or self.waiting_on_stable,
|
2023-02-07 05:32:50 +00:00
|
|
|
self.at_max_burn or self.saturated,
|
2023-01-26 23:26:26 +00:00
|
|
|
self.ascram,
|
2023-02-15 03:55:40 +00:00
|
|
|
astat.matrix_dc,
|
|
|
|
astat.matrix_fill,
|
|
|
|
astat.crit_alarm,
|
|
|
|
astat.radiation,
|
|
|
|
astat.gen_fault or self.mode == PROCESS.GEN_RATE_FAULT_IDLE,
|
2023-02-02 02:55:02 +00:00
|
|
|
self.status_text[1],
|
|
|
|
self.status_text[2],
|
2023-07-08 20:57:13 +00:00
|
|
|
self.group_map,
|
|
|
|
self.current_waste_product,
|
2024-04-28 06:01:21 +00:00
|
|
|
self.pu_fallback and (self.current_waste_product == WASTE.PLUTONIUM) and (self.waste_product ~= WASTE.PLUTONIUM),
|
|
|
|
self.disabled_sps
|
2023-01-26 23:26:26 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2024-08-21 21:23:16 +00:00
|
|
|
-- 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
|
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
-- get RTU statuses
|
2023-02-25 04:36:16 +00:00
|
|
|
---@nodiscard
|
2022-12-10 18:58:17 +00:00
|
|
|
function public.get_rtu_statuses()
|
|
|
|
local status = {}
|
|
|
|
|
2023-02-16 00:52:28 +00:00
|
|
|
-- total count of all connected RTUs in the facility
|
|
|
|
status.count = self.rtu_conn_count
|
|
|
|
|
2023-01-26 23:26:26 +00:00
|
|
|
-- power averages from induction matricies
|
|
|
|
status.power = {
|
2023-02-04 02:05:21 +00:00
|
|
|
self.avg_charge.compute(),
|
|
|
|
self.avg_inflow.compute(),
|
2024-04-27 20:27:01 +00:00
|
|
|
self.avg_outflow.compute(),
|
|
|
|
0
|
2023-01-26 23:26:26 +00:00
|
|
|
}
|
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
-- status of induction matricies (including tanks)
|
|
|
|
status.induction = {}
|
|
|
|
for i = 1, #self.induction do
|
2024-04-27 20:27:01 +00:00
|
|
|
local matrix = self.induction[i] ---@type unit_session
|
|
|
|
local db = matrix.get_db() ---@type imatrix_session_db
|
|
|
|
|
2023-11-06 15:21:42 +00:00
|
|
|
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
2024-04-27 20:27:01 +00:00
|
|
|
|
|
|
|
local fe_per_ms = self.avg_net.compute()
|
2024-07-27 16:34:01 +00:00
|
|
|
local remaining = util.joules_to_fe_rf(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy))
|
2024-04-27 20:27:01 +00:00
|
|
|
status.power[4] = remaining / fe_per_ms
|
2022-12-10 18:58:17 +00:00
|
|
|
end
|
|
|
|
|
2023-07-08 20:57:13 +00:00
|
|
|
-- status of sps
|
|
|
|
status.sps = {}
|
|
|
|
for i = 1, #self.sps do
|
2023-07-15 17:16:36 +00:00
|
|
|
local sps = self.sps[i] ---@type unit_session
|
|
|
|
local db = sps.get_db() ---@type sps_session_db
|
2023-11-06 15:21:42 +00:00
|
|
|
status.sps[i] = { sps.is_faulted(), db.formed, db.state, db.tanks }
|
2023-07-15 17:16:36 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- status of dynamic tanks
|
|
|
|
status.tanks = {}
|
|
|
|
for i = 1, #self.tanks do
|
|
|
|
local tank = self.tanks[i] ---@type unit_session
|
|
|
|
local db = tank.get_db() ---@type dynamicv_session_db
|
|
|
|
status.tanks[tank.get_device_idx()] = { tank.is_faulted(), db.formed, db.state, db.tanks }
|
2023-07-08 20:57:13 +00:00
|
|
|
end
|
|
|
|
|
2023-02-14 03:11:31 +00:00
|
|
|
-- radiation monitors (environment detectors)
|
2023-11-12 21:55:24 +00:00
|
|
|
status.envds = {}
|
2023-02-14 03:11:31 +00:00
|
|
|
for i = 1, #self.envd do
|
|
|
|
local envd = self.envd[i] ---@type unit_session
|
2023-11-12 16:54:47 +00:00
|
|
|
local db = envd.get_db() ---@type envd_session_db
|
2023-11-12 21:55:24 +00:00
|
|
|
status.envds[envd.get_device_idx()] = { envd.is_faulted(), db.radiation, db.radiation_raw }
|
2023-02-14 03:11:31 +00:00
|
|
|
end
|
2022-12-10 18:58:17 +00:00
|
|
|
|
|
|
|
return status
|
|
|
|
end
|
|
|
|
|
2024-03-06 02:47:14 +00:00
|
|
|
--#endregion
|
|
|
|
|
|
|
|
-- supervisor sessions reporting the list of active RTU sessions
|
|
|
|
---@param rtu_sessions table session list of all connected RTUs
|
|
|
|
function public.report_rtus(rtu_sessions) self.rtu_conn_count = #rtu_sessions end
|
|
|
|
|
2024-08-21 01:32:54 +00:00
|
|
|
-- get the facility cooling configuration
|
|
|
|
function public.get_cooling_conf() return self.cooling_conf end
|
|
|
|
|
2023-02-25 04:36:16 +00:00
|
|
|
-- get the units in this facility
|
|
|
|
---@nodiscard
|
|
|
|
function public.get_units() return self.units end
|
2022-12-18 18:56:04 +00:00
|
|
|
|
2022-12-10 18:58:17 +00:00
|
|
|
return public
|
|
|
|
end
|
|
|
|
|
|
|
|
return facility
|