#25 sna/sps integration, plutonium fallback, waste rate reporting

This commit is contained in:
Mikayla Fischler
2023-07-08 16:57:13 -04:00
parent 8f54e95519
commit ba0900ac65
12 changed files with 443 additions and 91 deletions

View File

@@ -11,6 +11,9 @@ local rsctl = require("supervisor.session.rsctl")
local PROCESS = types.PROCESS
local PROCESS_NAMES = types.PROCESS_NAMES
local PRIO = types.ALARM_PRIORITY
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local WASTE = types.WASTE_PRODUCT
local WASTE_MODE = types.WASTE_MODE
local IO = rsio.IO
@@ -61,6 +64,7 @@ function facility.new(num_reactors, cooling_conf)
rtu_conn_count = 0,
redstone = {},
induction = {},
sps = {},
envd = {},
-- redstone I/O control
io_ctl = nil, ---@type rs_controller
@@ -99,6 +103,10 @@ function facility.new(num_reactors, cooling_conf)
last_update = 0,
last_error = 0.0,
last_time = 0.0,
-- waste processing
waste_product = WASTE.PLUTONIUM,
current_waste_product = WASTE.PLUTONIUM,
pu_fallback = false,
-- statistics
im_stat_init = false,
avg_charge = util.mov_avg(3, 0.0),
@@ -211,6 +219,12 @@ function facility.new(num_reactors, cooling_conf)
table.insert(self.induction, imatrix)
end
-- link an SPS RTU session
---@param sps unit_session
function public.add_sps(sps)
table.insert(self.sps, sps)
end
-- link an environment detector RTU session
---@param envd unit_session
function public.add_envd(envd)
@@ -222,6 +236,7 @@ function facility.new(num_reactors, cooling_conf)
function public.purge_rtu_devices(session)
util.filter_table(self.redstone, function (s) return s.get_session_id() ~= session end)
util.filter_table(self.induction, function (s) return s.get_session_id() ~= session end)
util.filter_table(self.sps, function (s) return s.get_session_id() ~= session end)
util.filter_table(self.envd, function (s) return s.get_session_id() ~= session end)
end
@@ -238,6 +253,7 @@ function facility.new(num_reactors, cooling_conf)
-- unlink RTU unit sessions if they are closed
_unlink_disconnected_units(self.redstone)
_unlink_disconnected_units(self.induction)
_unlink_disconnected_units(self.sps)
_unlink_disconnected_units(self.envd)
-- current state for process control
@@ -277,6 +293,8 @@ function facility.new(num_reactors, cooling_conf)
-- Run Process Control --
-------------------------
--#region Process Control
local avg_charge = self.avg_charge.compute()
local avg_inflow = self.avg_inflow.compute()
@@ -542,10 +560,14 @@ function facility.new(num_reactors, cooling_conf)
next_mode = PROCESS.INACTIVE
end
--#endregion
------------------------------
-- Evaluate Automatic SCRAM --
------------------------------
--#region Automatic SCRAM
local astatus = self.ascram_status
if self.induction[1] ~= nil then
@@ -659,6 +681,8 @@ function facility.new(num_reactors, cooling_conf)
end
end
--#endregion
-- update last mode and set next mode
self.last_mode = self.mode
self.mode = next_mode
@@ -692,12 +716,33 @@ function facility.new(num_reactors, cooling_conf)
self.io_ctl.digital_write(IO.F_ALARM, has_alarm)
end
-----------------------------
-- Update Waste Processing --
-----------------------------
local insufficent_po_rate = false
for i = 1, #self.units do
local u = self.units[i] ---@type reactor_unit
if u.get_control_inf().waste_mode == WASTE_MODE.AUTO then
if (u.get_sna_rate() * 10.0) < u.get_burn_rate() then
insufficent_po_rate = true
break
end
end
end
if self.waste_product == WASTE.PLUTONIUM or (self.pu_fallback and insufficent_po_rate) then
self.current_waste_product = WASTE.PLUTONIUM
else self.current_waste_product = self.waste_product end
end
-- call the update function of all units in the facility
-- 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] ---@type reactor_unit
u.auto_set_waste(self.current_waste_product)
u.update()
end
end
@@ -721,15 +766,15 @@ function facility.new(num_reactors, cooling_conf)
end
-- stop auto control
function public.auto_stop()
self.mode = PROCESS.INACTIVE
end
function public.auto_stop() self.mode = PROCESS.INACTIVE end
-- set automatic control configuration and start the process
---@param config coord_auto_config configuration
---@return table response ready state (successfully started) and current configuration (after updating)
function public.auto_start(config)
local ready = false
local charge_scaler = 1000000 -- convert MFE to FE
local gen_scaler = 1000 -- convert kFE to FE
local ready = false
-- load up current limits
local limits = {}
@@ -749,11 +794,11 @@ function facility.new(num_reactors, cooling_conf)
end
if (type(config.charge_target) == "number") and config.charge_target >= 0 then
self.charge_setpoint = config.charge_target * 1000000 -- convert MFE to FE
self.charge_setpoint = config.charge_target * charge_scaler
end
if (type(config.gen_target) == "number") and config.gen_target >= 0 then
self.gen_rate_setpoint = config.gen_target * 1000 -- convert kFE to FE
self.gen_rate_setpoint = config.gen_target * gen_scaler
end
if (type(config.limits) == "table") and (#config.limits == num_reactors) then
@@ -782,7 +827,14 @@ function facility.new(num_reactors, cooling_conf)
if ready then self.mode = self.mode_set end
end
return { ready, self.mode_set, self.burn_target, self.charge_setpoint, self.gen_rate_setpoint, limits }
return {
ready,
self.mode_set,
self.burn_target,
self.charge_setpoint / charge_scaler,
self.gen_rate_setpoint / gen_scaler,
limits
}
end
-- SETTINGS --
@@ -807,15 +859,35 @@ function facility.new(num_reactors, cooling_conf)
end
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
-- READ STATES/PROPERTIES --
-- get build properties of all machines
-- get build properties of all facility devices
---@nodiscard
---@param inc_imatrix boolean? true/nil to include induction matrix build, false to exclude
function public.get_build(inc_imatrix)
---@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 inc_imatrix ~= false then
if all or type == RTU_UNIT_TYPE.IMATRIX then
build.induction = {}
for i = 1, #self.induction do
local matrix = self.induction[i] ---@type unit_session
@@ -823,6 +895,14 @@ function facility.new(num_reactors, cooling_conf)
end
end
if all or type == RTU_UNIT_TYPE.SPS then
build.sps = {}
for i = 1, #self.sps do
local sps = self.sps[i] ---@type unit_session
build.sps[sps.get_device_idx()] = { sps.get_db().formed, sps.get_db().build }
end
end
return build
end
@@ -844,7 +924,9 @@ function facility.new(num_reactors, cooling_conf)
astat.gen_fault or self.mode == PROCESS.GEN_RATE_FAULT_IDLE,
self.status_text[1],
self.status_text[2],
self.group_map
self.group_map,
self.current_waste_product,
(self.current_waste_product == WASTE.PLUTONIUM) and (self.waste_product ~= WASTE.PLUTONIUM)
}
end
@@ -875,6 +957,18 @@ function facility.new(num_reactors, cooling_conf)
}
end
-- status of sps
status.sps = {}
for i = 1, #self.sps do
local sps = self.sps[i] ---@type unit_session
status.sps[sps.get_device_idx()] = {
sps.is_faulted(),
sps.get_db().formed,
sps.get_db().state,
sps.get_db().tanks
}
end
-- radiation monitors (environment detectors)
status.rad_mon = {}
for i = 1, #self.envd do

View File

@@ -258,6 +258,18 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
elseif cmd == FAC_COMMAND.ACK_ALL_ALARMS then
facility.ack_all()
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, true })
elseif cmd == FAC_COMMAND.SET_WASTE_MODE then
if pkt.length == 2 then
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, facility.set_waste_product(pkt.data[2]) })
else
log.debug(log_header .. "CRDN set waste mode packet length mismatch")
end
elseif cmd == FAC_COMMAND.SET_PU_FB then
if pkt.length == 2 then
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, facility.set_pu_fallback(pkt.data[2]) })
else
log.debug(log_header .. "CRDN set pu fallback packet length mismatch")
end
else
log.debug(log_header .. "CRDN facility command unknown")
end
@@ -417,7 +429,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
self.retry_times.f_builds_packet = util.time() + PARTIAL_RETRY_PERIOD
self.acks.fac_builds = false
_send(SCADA_CRDN_TYPE.FAC_BUILDS, { facility.get_build(cmd.val.type == RTU_UNIT_TYPE.IMATRIX) })
_send(SCADA_CRDN_TYPE.FAC_BUILDS, { facility.get_build(cmd.val.type) })
end
else
log.error(log_header .. "unsupported data command received in in_queue (this is a bug)", true)

View File

@@ -165,6 +165,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
elseif u_type == RTU_UNIT_TYPE.SPS then
-- super-critical phase shifter
unit = svrs_sps.new(id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then facility.add_sps(unit) end
elseif u_type == RTU_UNIT_TYPE.ENV_DETECTOR then
-- environment detector
unit = svrs_envd.new(id, i, unit_advert, self.modbus_q)

View File

@@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v0.18.0"
local SUPERVISOR_VERSION = "v0.19.0"
local println = util.println
local println_ts = util.println_ts

View File

@@ -74,6 +74,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
turbines = {},
sna = {},
envd = {},
sna_prod_rate = 0,
-- redstone control
io_ctl = nil, ---@type rs_controller
valves = {}, ---@type unit_valves
@@ -91,7 +92,6 @@ function unit.new(reactor_id, num_boilers, num_turbines)
damage_start = 0,
damage_last = 0,
damage_est_last = 0,
waste_mode = WASTE_MODE.AUTO, ---@type WASTE_MODE
waste_product = WASTE.PLUTONIUM, ---@type WASTE_PRODUCT
status_text = { "UNKNOWN", "awaiting connection..." },
-- logic for alarms
@@ -224,7 +224,8 @@ function unit.new(reactor_id, num_boilers, num_turbines)
degraded = false,
blade_count = 0,
br100 = 0,
lim_br100 = 0
lim_br100 = 0,
waste_mode = WASTE_MODE.AUTO ---@type WASTE_MODE
}
}
}
@@ -616,7 +617,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
-- set automatic waste product if mode is set to auto
---@param product WASTE_PRODUCT waste product to generate
function public.auto_set_waste(product)
if self.waste_mode == WASTE_MODE.AUTO then
if self.db.control.waste_mode == WASTE_MODE.AUTO then
self.waste_product = product
_set_waste_valves(product)
end
@@ -669,7 +670,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
-- set waste processing mode
---@param mode WASTE_MODE processing mode
function public.set_waste_mode(mode)
self.waste_mode = mode
self.db.control.waste_mode = mode
if mode == WASTE_MODE.MANUAL_PLUTONIUM then
_set_waste_valves(WASTE.PLUTONIUM)
@@ -759,6 +760,14 @@ function unit.new(reactor_id, num_boilers, num_turbines)
return status
end
-- get the current burn rate (actual rate)
---@nodiscard
function public.get_burn_rate()
local rate = 0
if self.plc_i ~= nil then rate = self.plc_i.get_status().act_burn_rate end
return rate or 0
end
-- get RTU statuses
---@nodiscard
function public.get_rtu_statuses()
@@ -779,7 +788,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
-- status of turbines (including tanks)
status.turbines = {}
for i = 1, #self.turbines do
local turbine = self.turbines[i] ---@type unit_session
local turbine = self.turbines[i] ---@type unit_session
status.turbines[turbine.get_device_idx()] = {
turbine.is_faulted(),
turbine.get_db().formed,
@@ -788,10 +797,13 @@ function unit.new(reactor_id, num_boilers, num_turbines)
}
end
-- basic SNA statistical information, don't send everything, it's not necessary
status.sna = { #self.sna, public.get_sna_rate() }
-- radiation monitors (environment detectors)
status.rad_mon = {}
for i = 1, #self.envd do
local envd = self.envd[i] ---@type unit_session
local envd = self.envd[i] ---@type unit_session
status.rad_mon[envd.get_device_idx()] = {
envd.is_faulted(),
envd.get_db().radiation
@@ -801,6 +813,36 @@ function unit.new(reactor_id, num_boilers, num_turbines)
return status
end
-- get the current total [max] production rate is
---@nodiscard
---@return number total_avail_rate
function public.get_sna_rate()
local total_avail_rate = 0
for i = 1, #self.sna do
local db = self.sna[i].get_db() ---@type sna_session_db
total_avail_rate = total_avail_rate + db.state.production_rate
end
return total_avail_rate
end
-- check plutonium and polonium estimated production rates
---@nodiscard
---@return number pu_rate, number po_rate
function public.get_waste_rates()
local pu, po = 0.0, 0.0
local br = public.get_burn_rate()
if self.waste_product == WASTE.PLUTONIUM then
pu = br / 10.0
else
po = math.min(br / 10.0, public.get_sna_rate())
end
return pu, po
end
-- get the annunciator status
---@nodiscard
function public.get_annunciator() return self.db.annunciator end
@@ -821,7 +863,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
self.status_text[2],
self.db.control.ready,
self.db.control.degraded,
self.waste_mode,
self.db.control.waste_mode,
self.waste_product
}
end