#306 #362 supervisor updates for RTU config changes

This commit is contained in:
Mikayla Fischler 2023-11-06 10:21:42 -05:00
parent dc0408881e
commit 838f80c30c
16 changed files with 94 additions and 103 deletions

View File

@ -17,7 +17,7 @@ local max_distance = nil
local comms = {} local comms = {}
-- protocol/data version (protocol/data independent changes tracked by util.lua version) -- protocol/data version (protocol/data independent changes tracked by util.lua version)
comms.version = "2.4.1" comms.version = "2.4.2"
---@enum PROTOCOL ---@enum PROTOCOL
local PROTOCOL = { local PROTOCOL = {

View File

@ -63,7 +63,7 @@ function types.new_zero_coordinate() return { x = 0, y = 0, z = 0 } end
---@class rtu_advertisement ---@class rtu_advertisement
---@field type RTU_UNIT_TYPE ---@field type RTU_UNIT_TYPE
---@field index integer ---@field index integer|false
---@field reactor integer ---@field reactor integer
---@field rsio table|nil ---@field rsio table|nil

View File

@ -1093,22 +1093,22 @@ function facility.new(num_reactors, cooling_conf)
build.induction = {} build.induction = {}
for i = 1, #self.induction do for i = 1, #self.induction do
local matrix = self.induction[i] ---@type unit_session local matrix = self.induction[i] ---@type unit_session
build.induction[matrix.get_device_idx()] = { matrix.get_db().formed, matrix.get_db().build } build.induction[i] = { matrix.get_db().formed, matrix.get_db().build }
end end
end end
if all or type == RTU_UNIT_TYPE.SPS then if all or type == RTU_UNIT_TYPE.SPS then
build.sps = {} build.sps = {}
for i = 1, #self.sps do for i = 1, #self.sps do
local sps = self.sps[i] ---@type unit_session local sps = self.sps[i] ---@type unit_session
build.sps[sps.get_device_idx()] = { sps.get_db().formed, sps.get_db().build } build.sps[i] = { sps.get_db().formed, sps.get_db().build }
end end
end end
if all or type == RTU_UNIT_TYPE.DYNAMIC_VALVE then if all or type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
build.tanks = {} build.tanks = {}
for i = 1, #self.tanks do for i = 1, #self.tanks do
local tank = self.tanks[i] ---@type unit_session local tank = self.tanks[i] ---@type unit_session
build.tanks[tank.get_device_idx()] = { tank.get_db().formed, tank.get_db().build } build.tanks[tank.get_device_idx()] = { tank.get_db().formed, tank.get_db().build }
end end
end end
@ -1160,7 +1160,7 @@ function facility.new(num_reactors, cooling_conf)
for i = 1, #self.induction do for i = 1, #self.induction do
local matrix = self.induction[i] ---@type unit_session local matrix = self.induction[i] ---@type unit_session
local db = matrix.get_db() ---@type imatrix_session_db local db = matrix.get_db() ---@type imatrix_session_db
status.induction[matrix.get_device_idx()] = { matrix.is_faulted(), db.formed, db.state, db.tanks } status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
end end
-- status of sps -- status of sps
@ -1168,7 +1168,7 @@ function facility.new(num_reactors, cooling_conf)
for i = 1, #self.sps do for i = 1, #self.sps do
local sps = self.sps[i] ---@type unit_session local sps = self.sps[i] ---@type unit_session
local db = sps.get_db() ---@type sps_session_db local db = sps.get_db() ---@type sps_session_db
status.sps[sps.get_device_idx()] = { sps.is_faulted(), db.formed, db.state, db.tanks } status.sps[i] = { sps.is_faulted(), db.formed, db.state, db.tanks }
end end
-- status of dynamic tanks -- status of dynamic tanks
@ -1183,7 +1183,7 @@ function facility.new(num_reactors, cooling_conf)
status.rad_mon = {} status.rad_mon = {}
for i = 1, #self.envd do 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 } status.rad_mon[i] = { envd.is_faulted(), envd.get_db().radiation }
end end
return status return status

View File

@ -100,7 +100,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
-- validate unit advertisement -- validate unit advertisement
local advert_validator = util.new_validator() local advert_validator = util.new_validator()
advert_validator.assert_type_int(unit_advert.index) advert_validator.assert(util.is_int(unit_advert.index) or (unit_advert.index == false))
advert_validator.assert_type_int(unit_advert.reactor) advert_validator.assert_type_int(unit_advert.reactor)
if u_type == RTU_UNIT_TYPE.REDSTONE then if u_type == RTU_UNIT_TYPE.REDSTONE then
@ -108,7 +108,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
end end
if advert_validator.valid() then if advert_validator.valid() then
advert_validator.assert_min(unit_advert.index, 1) if util.is_int(unit_advert.index) then advert_validator.assert_min(unit_advert.index, 1) end
advert_validator.assert_min(unit_advert.reactor, 0) advert_validator.assert_min(unit_advert.reactor, 0)
advert_validator.assert_max(unit_advert.reactor, #self.fac_units) advert_validator.assert_max(unit_advert.reactor, #self.fac_units)
if not advert_validator.valid() then u_type = false end if not advert_validator.valid() then u_type = false end

View File

@ -43,7 +43,7 @@ function boilerv.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
local log_tag = "session.rtu(" .. session_id .. ").boilerv(" .. advert.index .. "): " local log_tag = util.c("session.rtu(", session_id, ").boilerv(", advert.index, ")[@", unit_id, "]: ")
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -55,7 +55,7 @@ function dynamicv.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
local log_tag = "session.rtu(" .. session_id .. ").dynamicv(" .. advert.index .. "): " local log_tag = util.c("session.rtu(", session_id, ").dynamicv(", advert.index, ")[@", unit_id, "]: ")
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -34,7 +34,7 @@ function envd.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
local log_tag = "session.rtu(" .. session_id .. ").envd(" .. advert.index .. "): " local log_tag = util.c("session.rtu(", session_id, ").envd[@", unit_id, "]: ")
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -43,7 +43,7 @@ function imatrix.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
local log_tag = "session.rtu(" .. session_id .. ").imatrix(" .. advert.index .. "): " local log_tag = util.c("session.rtu(", session_id, ").imatrix[@", unit_id, "]: ")
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -56,8 +56,7 @@ function redstone.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
-- for redstone, use unit ID not device index local log_tag = util.c("session.rtu(", session_id, ").redstone[@", unit_id, "]: ")
local log_tag = "session.rtu(" .. session_id .. ").redstone(" .. unit_id .. "): "
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -40,7 +40,7 @@ function sna.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
local log_tag = "session.rtu(" .. session_id .. ").sna(" .. advert.index .. "): " local log_tag = util.c("session.rtu(", session_id, ").sna[@", unit_id, "]: ")
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -43,7 +43,7 @@ function sps.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
local log_tag = "session.rtu(" .. session_id .. ").sps(" .. advert.index .. "): " local log_tag = util.c("session.rtu(", session_id, ").sps[@", unit_id, "]: ")
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -55,7 +55,7 @@ function turbinev.new(session_id, unit_id, advert, out_queue)
return nil return nil
end end
local log_tag = "session.rtu(" .. session_id .. ").turbinev(" .. advert.index .. "): " local log_tag = util.c("session.rtu(", session_id, ").turbinev(", advert.index, ")[@", unit_id, "]: ")
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),

View File

@ -152,7 +152,7 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t
function public.get_unit_id() return unit_id end function public.get_unit_id() return unit_id end
-- get the device index -- get the device index
---@nodiscard ---@nodiscard
function public.get_device_idx() return self.device_index end function public.get_device_idx() return self.device_index or 0 end
-- get the reactor ID -- get the reactor ID
---@nodiscard ---@nodiscard
function public.get_reactor() return self.reactor end function public.get_reactor() return self.reactor end

View File

@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions") local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v1.0.9" local SUPERVISOR_VERSION = "v1.1.0"
local println = util.println local println = util.println
local println_ts = util.println_ts local println_ts = util.println_ts

View File

@ -867,7 +867,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
status.rad_mon = {} status.rad_mon = {}
for i = 1, #self.envd do 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 } status.rad_mon[i] = { envd.is_faulted(), envd.get_db().radiation }
end end
return status return status

View File

@ -47,8 +47,9 @@ function logic.update_annunciator(self)
local num_boilers = self.num_boilers local num_boilers = self.num_boilers
local num_turbines = self.num_turbines local num_turbines = self.num_turbines
local annunc = self.db.annunciator
self.db.annunciator.RCSFault = false annunc.RCSFault = false
-- variables for boiler, or reactor if no boilers used -- variables for boiler, or reactor if no boilers used
local total_boil_rate = 0.0 local total_boil_rate = 0.0
@ -57,14 +58,14 @@ function logic.update_annunciator(self)
-- REACTOR -- -- REACTOR --
------------- -------------
self.db.annunciator.AutoControl = self.auto_engaged annunc.AutoControl = self.auto_engaged
-- check PLC status -- check PLC status
self.db.annunciator.PLCOnline = self.plc_i ~= nil annunc.PLCOnline = self.plc_i ~= nil
local plc_ready = self.db.annunciator.PLCOnline local plc_ready = annunc.PLCOnline
if self.db.annunciator.PLCOnline then if plc_ready then
local plc_db = self.plc_i.get_db() local plc_db = self.plc_i.get_db()
-- update ready state -- update ready state
@ -110,29 +111,29 @@ function logic.update_annunciator(self)
-- heartbeat blink about every second -- heartbeat blink about every second
if self.last_heartbeat + 1000 < plc_db.last_status_update then if self.last_heartbeat + 1000 < plc_db.last_status_update then
self.db.annunciator.PLCHeartbeat = not self.db.annunciator.PLCHeartbeat annunc.PLCHeartbeat = not annunc.PLCHeartbeat
self.last_heartbeat = plc_db.last_status_update self.last_heartbeat = plc_db.last_status_update
end end
local flow_low = util.trinary(plc_db.mek_status.ccool_type == types.FLUID.SODIUM, ANNUNC_LIMS.RCSFlowLow_NA, ANNUNC_LIMS.RCSFlowLow_H2O) local flow_low = util.trinary(plc_db.mek_status.ccool_type == types.FLUID.SODIUM, ANNUNC_LIMS.RCSFlowLow_NA, ANNUNC_LIMS.RCSFlowLow_H2O)
-- update other annunciator fields -- update other annunciator fields
self.db.annunciator.ReactorSCRAM = plc_db.rps_tripped annunc.ReactorSCRAM = plc_db.rps_tripped
self.db.annunciator.ManualReactorSCRAM = plc_db.rps_trip_cause == types.RPS_TRIP_CAUSE.MANUAL annunc.ManualReactorSCRAM = plc_db.rps_trip_cause == types.RPS_TRIP_CAUSE.MANUAL
self.db.annunciator.AutoReactorSCRAM = plc_db.rps_trip_cause == types.RPS_TRIP_CAUSE.AUTOMATIC annunc.AutoReactorSCRAM = plc_db.rps_trip_cause == types.RPS_TRIP_CAUSE.AUTOMATIC
self.db.annunciator.RCPTrip = plc_db.rps_tripped and (plc_db.rps_status.ex_hcool or plc_db.rps_status.low_cool) annunc.RCPTrip = plc_db.rps_tripped and (plc_db.rps_status.ex_hcool or plc_db.rps_status.low_cool)
self.db.annunciator.RCSFlowLow = _get_dt(DT_KEYS.ReactorCCool) < flow_low annunc.RCSFlowLow = _get_dt(DT_KEYS.ReactorCCool) < flow_low
self.db.annunciator.CoolantLevelLow = plc_db.mek_status.ccool_fill < ANNUNC_LIMS.CoolantLevelLow annunc.CoolantLevelLow = plc_db.mek_status.ccool_fill < ANNUNC_LIMS.CoolantLevelLow
self.db.annunciator.ReactorTempHigh = plc_db.mek_status.temp > ANNUNC_LIMS.ReactorTempHigh annunc.ReactorTempHigh = plc_db.mek_status.temp > ANNUNC_LIMS.ReactorTempHigh
self.db.annunciator.ReactorHighDeltaT = _get_dt(DT_KEYS.ReactorTemp) > ANNUNC_LIMS.ReactorHighDeltaT annunc.ReactorHighDeltaT = _get_dt(DT_KEYS.ReactorTemp) > ANNUNC_LIMS.ReactorHighDeltaT
self.db.annunciator.FuelInputRateLow = _get_dt(DT_KEYS.ReactorFuel) < -1.0 or plc_db.mek_status.fuel_fill <= ANNUNC_LIMS.FuelLevelLow annunc.FuelInputRateLow = _get_dt(DT_KEYS.ReactorFuel) < -1.0 or plc_db.mek_status.fuel_fill <= ANNUNC_LIMS.FuelLevelLow
self.db.annunciator.WasteLineOcclusion = _get_dt(DT_KEYS.ReactorWaste) > 1.0 or plc_db.mek_status.waste_fill >= ANNUNC_LIMS.WasteLevelHigh annunc.WasteLineOcclusion = _get_dt(DT_KEYS.ReactorWaste) > 1.0 or plc_db.mek_status.waste_fill >= ANNUNC_LIMS.WasteLevelHigh
local heating_rate_conv = util.trinary(plc_db.mek_status.ccool_type == types.FLUID.SODIUM, 200000, 20000) local heating_rate_conv = util.trinary(plc_db.mek_status.ccool_type == types.FLUID.SODIUM, 200000, 20000)
local high_rate = plc_db.mek_status.burn_rate >= (plc_db.mek_status.ccool_amnt * 0.27 / heating_rate_conv) local high_rate = plc_db.mek_status.burn_rate >= (plc_db.mek_status.ccool_amnt * 0.27 / heating_rate_conv)
-- this advisory applies when no coolant is buffered (which we can't easily determine)<br> -- this advisory applies when no coolant is buffered (which we can't easily determine)<br>
-- it's a rough estimation, see GitHub cc-mek-scada/wiki/High-Rate-Calculation -- it's a rough estimation, see GitHub cc-mek-scada/wiki/High-Rate-Calculation
self.db.annunciator.HighStartupRate = not plc_db.mek_status.status and high_rate annunc.HighStartupRate = not plc_db.mek_status.status and high_rate
-- if no boilers, use reactor heating rate to check for boil rate mismatch -- if no boilers, use reactor heating rate to check for boil rate mismatch
if num_boilers == 0 then if num_boilers == 0 then
@ -146,21 +147,25 @@ function logic.update_annunciator(self)
-- MISC RTUs -- -- MISC RTUs --
--------------- ---------------
self.db.annunciator.RadiationMonitor = 1 local max_rad, any_faulted = 0, false
self.db.annunciator.RadiationWarning = false
for i = 1, #self.envd do for i = 1, #self.envd do
local envd = self.envd[i] ---@type unit_session local envd = self.envd[i] ---@type unit_session
self.db.annunciator.RadiationMonitor = util.trinary(envd.is_faulted(), 2, 3) local db = envd.get_db() ---@type envd_session_db
self.db.annunciator.RadiationWarning = envd.get_db().radiation_raw >= ANNUNC_LIMS.RadiationWarning any_faulted = any_faulted or envd.is_faulted()
break if db.radiation_raw > max_rad then max_rad = db.radiation_raw end
end end
self.db.annunciator.EmergencyCoolant = 1 annunc.RadiationMonitor = util.trinary(#self.envd == 0, 1, util.trinary(any_faulted, 2, 3))
annunc.RadiationWarning = max_rad >= ANNUNC_LIMS.RadiationWarning
annunc.EmergencyCoolant = 1
for i = 1, #self.redstone do for i = 1, #self.redstone do
local db = self.redstone[i].get_db() ---@type redstone_session_db local db = self.redstone[i].get_db() ---@type redstone_session_db
local io = db.io[IO.U_EMER_COOL] ---@type rs_db_dig_io|nil local io = db.io[IO.U_EMER_COOL] ---@type rs_db_dig_io|nil
if io ~= nil then if io ~= nil then
self.db.annunciator.EmergencyCoolant = util.trinary(io.read(), 3, 2) annunc.EmergencyCoolant = util.trinary(io.read(), 3, 2)
break break
end end
end end
@ -172,7 +177,7 @@ function logic.update_annunciator(self)
local boilers_ready = num_boilers == #self.boilers local boilers_ready = num_boilers == #self.boilers
-- clear boiler online flags -- clear boiler online flags
for i = 1, num_boilers do self.db.annunciator.BoilerOnline[i] = false end for i = 1, num_boilers do annunc.BoilerOnline[i] = false end
-- aggregated statistics -- aggregated statistics
local boiler_steam_dt_sum = 0.0 local boiler_steam_dt_sum = 0.0
@ -185,7 +190,7 @@ function logic.update_annunciator(self)
local boiler = session.get_db() ---@type boilerv_session_db local boiler = session.get_db() ---@type boilerv_session_db
local idx = session.get_device_idx() local idx = session.get_device_idx()
self.db.annunciator.RCSFault = self.db.annunciator.RCSFault or (not boiler.formed) or session.is_faulted() annunc.RCSFault = annunc.RCSFault or (not boiler.formed) or session.is_faulted()
-- update ready state -- update ready state
-- - must be formed -- - must be formed
@ -199,8 +204,8 @@ function logic.update_annunciator(self)
boiler_steam_dt_sum = _get_dt(DT_KEYS.BoilerSteam .. idx) boiler_steam_dt_sum = _get_dt(DT_KEYS.BoilerSteam .. idx)
boiler_water_dt_sum = _get_dt(DT_KEYS.BoilerWater .. idx) boiler_water_dt_sum = _get_dt(DT_KEYS.BoilerWater .. idx)
self.db.annunciator.BoilerOnline[idx] = true annunc.BoilerOnline[idx] = true
self.db.annunciator.WaterLevelLow[idx] = boiler.tanks.water_fill < ANNUNC_LIMS.WaterLevelLow annunc.WaterLevelLow[idx] = boiler.tanks.water_fill < ANNUNC_LIMS.WaterLevelLow
end end
-- check heating rate low -- check heating rate low
@ -209,14 +214,14 @@ function logic.update_annunciator(self)
-- check for inactive boilers while reactor is active -- check for inactive boilers while reactor is active
for i = 1, #self.boilers do for i = 1, #self.boilers do
local boiler = self.boilers[i] ---@type unit_session local boiler = self.boilers[i] ---@type unit_session
local idx = boiler.get_device_idx() local idx = boiler.get_device_idx()
local db = boiler.get_db() ---@type boilerv_session_db local db = boiler.get_db() ---@type boilerv_session_db
if r_db.mek_status.status then if r_db.mek_status.status then
self.db.annunciator.HeatingRateLow[idx] = db.state.boil_rate == 0 annunc.HeatingRateLow[idx] = db.state.boil_rate == 0
else else
self.db.annunciator.HeatingRateLow[idx] = false annunc.HeatingRateLow[idx] = false
end end
end end
end end
@ -234,9 +239,9 @@ function logic.update_annunciator(self)
if num_boilers > 0 then if num_boilers > 0 then
for i = 1, #self.boilers do for i = 1, #self.boilers do
local boiler = self.boilers[i] ---@type unit_session local boiler = self.boilers[i] ---@type unit_session
local idx = boiler.get_device_idx() local idx = boiler.get_device_idx()
local db = boiler.get_db() ---@type boilerv_session_db local db = boiler.get_db() ---@type boilerv_session_db
local gaining_hc = _get_dt(DT_KEYS.BoilerHCool .. idx) > 10.0 or db.tanks.hcool_fill == 1 local gaining_hc = _get_dt(DT_KEYS.BoilerHCool .. idx) > 10.0 or db.tanks.hcool_fill == 1
@ -256,7 +261,7 @@ function logic.update_annunciator(self)
cfmismatch = cfmismatch or _get_dt(DT_KEYS.ReactorCCool) < -10.0 or (gaining_hc and r_db.mek_status.ccool_fill == 0) cfmismatch = cfmismatch or _get_dt(DT_KEYS.ReactorCCool) < -10.0 or (gaining_hc and r_db.mek_status.ccool_fill == 0)
end end
self.db.annunciator.CoolantFeedMismatch = cfmismatch annunc.CoolantFeedMismatch = cfmismatch
-------------- --------------
-- TURBINES -- -- TURBINES --
@ -265,7 +270,7 @@ function logic.update_annunciator(self)
local turbines_ready = num_turbines == #self.turbines local turbines_ready = num_turbines == #self.turbines
-- clear turbine online flags -- clear turbine online flags
for i = 1, num_turbines do self.db.annunciator.TurbineOnline[i] = false end for i = 1, num_turbines do annunc.TurbineOnline[i] = false end
-- aggregated statistics -- aggregated statistics
local total_flow_rate = 0 local total_flow_rate = 0
@ -277,10 +282,10 @@ function logic.update_annunciator(self)
-- go through turbines for stats and online -- go through turbines for stats and online
for i = 1, #self.turbines do for i = 1, #self.turbines do
local session = self.turbines[i] ---@type unit_session local session = self.turbines[i] ---@type unit_session
local turbine = session.get_db() ---@type turbinev_session_db local turbine = session.get_db() ---@type turbinev_session_db
self.db.annunciator.RCSFault = self.db.annunciator.RCSFault or (not turbine.formed) or session.is_faulted() annunc.RCSFault = annunc.RCSFault or (not turbine.formed) or session.is_faulted()
-- update ready state -- update ready state
-- - must be formed -- - must be formed
@ -295,59 +300,44 @@ function logic.update_annunciator(self)
max_water_return_rate = max_water_return_rate + turbine.build.max_water_output max_water_return_rate = max_water_return_rate + turbine.build.max_water_output
self.db.control.blade_count = self.db.control.blade_count + turbine.build.blades self.db.control.blade_count = self.db.control.blade_count + turbine.build.blades
self.db.annunciator.TurbineOnline[session.get_device_idx()] = true annunc.TurbineOnline[session.get_device_idx()] = true
end end
-- check for boil rate mismatch (> 4% error) either between reactor and turbine or boiler and turbine -- check for boil rate mismatch (> 4% error) either between reactor and turbine or boiler and turbine
self.db.annunciator.BoilRateMismatch = math.abs(total_boil_rate - total_input_rate) > (0.04 * total_boil_rate) annunc.BoilRateMismatch = math.abs(total_boil_rate - total_input_rate) > (0.04 * total_boil_rate)
-- check for steam feed mismatch and max return rate -- check for steam feed mismatch and max return rate
local steam_dt_max = util.trinary(num_boilers == 0, ANNUNC_LIMS.SFM_MaxSteamDT_H20, ANNUNC_LIMS.SFM_MaxSteamDT_NA) local steam_dt_max = util.trinary(num_boilers == 0, ANNUNC_LIMS.SFM_MaxSteamDT_H20, ANNUNC_LIMS.SFM_MaxSteamDT_NA)
local water_dt_min = util.trinary(num_boilers == 0, ANNUNC_LIMS.SFM_MinWaterDT_H20, ANNUNC_LIMS.SFM_MinWaterDT_NA) local water_dt_min = util.trinary(num_boilers == 0, ANNUNC_LIMS.SFM_MinWaterDT_H20, ANNUNC_LIMS.SFM_MinWaterDT_NA)
local sfmismatch = math.abs(total_flow_rate - total_input_rate) > ANNUNC_LIMS.SteamFeedMismatch local sfmismatch = math.abs(total_flow_rate - total_input_rate) > ANNUNC_LIMS.SteamFeedMismatch
sfmismatch = sfmismatch or boiler_steam_dt_sum > steam_dt_max or boiler_water_dt_sum < water_dt_min sfmismatch = sfmismatch or boiler_steam_dt_sum > steam_dt_max or boiler_water_dt_sum < water_dt_min
self.db.annunciator.SteamFeedMismatch = sfmismatch annunc.SteamFeedMismatch = sfmismatch
self.db.annunciator.MaxWaterReturnFeed = max_water_return_rate == total_flow_rate and total_flow_rate ~= 0 annunc.MaxWaterReturnFeed = max_water_return_rate == total_flow_rate and total_flow_rate ~= 0
-- turbine safety checks -- turbine safety checks
for i = 1, #self.turbines do for i = 1, #self.turbines do
local turbine = self.turbines[i] ---@type unit_session local turbine = self.turbines[i] ---@type unit_session
local db = turbine.get_db() ---@type turbinev_session_db local db = turbine.get_db() ---@type turbinev_session_db
local idx = turbine.get_device_idx() local idx = turbine.get_device_idx()
-- check if steam dumps are open -- check if steam dumps are open
if db.state.dumping_mode == DUMPING_MODE.IDLE then if db.state.dumping_mode == DUMPING_MODE.IDLE then
self.db.annunciator.SteamDumpOpen[idx] = TRI_FAIL.OK annunc.SteamDumpOpen[idx] = TRI_FAIL.OK
elseif db.state.dumping_mode == DUMPING_MODE.DUMPING_EXCESS then elseif db.state.dumping_mode == DUMPING_MODE.DUMPING_EXCESS then
self.db.annunciator.SteamDumpOpen[idx] = TRI_FAIL.PARTIAL annunc.SteamDumpOpen[idx] = TRI_FAIL.PARTIAL
else else
self.db.annunciator.SteamDumpOpen[idx] = TRI_FAIL.FULL annunc.SteamDumpOpen[idx] = TRI_FAIL.FULL
end end
-- check if turbines are at max speed but not keeping up -- check if turbines are at max speed but not keeping up
self.db.annunciator.TurbineOverSpeed[idx] = (db.state.flow_rate == db.build.max_flow_rate) and (_get_dt(DT_KEYS.TurbineSteam .. idx) > 0.0) annunc.TurbineOverSpeed[idx] = (db.state.flow_rate == db.build.max_flow_rate) and (_get_dt(DT_KEYS.TurbineSteam .. idx) > 0.0)
--[[ -- see notes at cc-mek-scada/wiki/Annunciator-Panels#Generator-Trip
Generator Trip annunc.GeneratorTrip[idx] = (_get_dt(DT_KEYS.TurbinePower .. idx) > 0.0) and (db.tanks.energy_fill > 0.05)
a generator trip is when a generator suddenly and unexpectedly loses it's external load, which occurs when a power plant
is disconnected from the grid. in our case, this is when the turbine is disconnected, or what it's connected to becomes
fully charged. this is identified by detecting if:
- the internal power storage of the turbine is increasing AND
- there is at least 5% energy fill (preventing false trips with periodic power extraction from other mods)
this would then mean there is no external load and there will be a turbine trip soon if this is not resolved
]]--
self.db.annunciator.GeneratorTrip[idx] = (_get_dt(DT_KEYS.TurbinePower .. idx) > 0.0) and (db.tanks.energy_fill > 0.05)
--[[ -- see notes at cc-mek-scada/wiki/Annunciator-Panels#Turbine-Trip
Turbine Trip
a turbine trip is when the turbine stops, which means we are no longer receiving water and lose the ability to cool.
this can be identified by these conditions:
- the current flow rate is 0 mB/t and it should not be
- can initially catch this by detecting a 0 flow rate with a non-zero input rate, but eventually the steam will fill up
- can later identified by presence of steam in tank with a 0 flow rate
]]--
local has_steam = db.state.steam_input_rate > 0 or db.tanks.steam_fill > 0.01 local has_steam = db.state.steam_input_rate > 0 or db.tanks.steam_fill > 0.01
self.db.annunciator.TurbineTrip[idx] = has_steam and db.state.flow_rate == 0 annunc.TurbineTrip[idx] = has_steam and db.state.flow_rate == 0
end end
-- update auto control ready state for this unit -- update auto control ready state for this unit
@ -577,6 +567,7 @@ end
---@param self _unit_self unit instance ---@param self _unit_self unit instance
function logic.update_status_text(self) function logic.update_status_text(self)
local AISTATE = self.types.AISTATE local AISTATE = self.types.AISTATE
local annunc = self.db.annunciator
-- check if an alarm is active (tripped or ack'd) -- check if an alarm is active (tripped or ack'd)
---@nodiscard ---@nodiscard
@ -666,13 +657,13 @@ function logic.update_status_text(self)
if plc_db.mek_status.status then if plc_db.mek_status.status then
self.status_text[1] = "ACTIVE" self.status_text[1] = "ACTIVE"
if self.db.annunciator.ReactorHighDeltaT then if annunc.ReactorHighDeltaT then
self.status_text[2] = "core temperature rising" self.status_text[2] = "core temperature rising"
elseif self.db.annunciator.ReactorTempHigh then elseif annunc.ReactorTempHigh then
self.status_text[2] = "core temp high, system nominal" self.status_text[2] = "core temp high, system nominal"
elseif self.db.annunciator.FuelInputRateLow then elseif annunc.FuelInputRateLow then
self.status_text[2] = "insufficient fuel input rate" self.status_text[2] = "insufficient fuel input rate"
elseif self.db.annunciator.WasteLineOcclusion then elseif annunc.WasteLineOcclusion then
self.status_text[2] = "insufficient waste output rate" self.status_text[2] = "insufficient waste output rate"
elseif (util.time_ms() - self.last_rate_change_ms) <= FLOW_STABILITY_DELAY_MS then elseif (util.time_ms() - self.last_rate_change_ms) <= FLOW_STABILITY_DELAY_MS then
self.status_text[2] = "awaiting flow stability" self.status_text[2] = "awaiting flow stability"
@ -711,7 +702,7 @@ function logic.update_status_text(self)
end end
self.status_text = { "RPS SCRAM", cause } self.status_text = { "RPS SCRAM", cause }
elseif self.db.annunciator.RadiationWarning then elseif annunc.RadiationWarning then
-- elevated, non-hazardous level of radiation is low priority, so display it now if everything else was fine -- elevated, non-hazardous level of radiation is low priority, so display it now if everything else was fine
self.status_text = { "RADIATION DETECTED", "elevated level of radiation" } self.status_text = { "RADIATION DETECTED", "elevated level of radiation" }
else else
@ -726,7 +717,7 @@ function logic.update_status_text(self)
self.status_text[2] = "core hot" self.status_text[2] = "core hot"
end end
end end
elseif self.db.annunciator.RadiationWarning then elseif annunc.RadiationWarning then
-- in case PLC was disconnected but radiation is present -- in case PLC was disconnected but radiation is present
self.status_text = { "RADIATION DETECTED", "elevated level of radiation" } self.status_text = { "RADIATION DETECTED", "elevated level of radiation" }
else else
@ -738,6 +729,7 @@ end
---@param self _unit_self unit instance ---@param self _unit_self unit instance
function logic.handle_redstone(self) function logic.handle_redstone(self)
local AISTATE = self.types.AISTATE local AISTATE = self.types.AISTATE
local annunc = self.db.annunciator
-- check if an alarm is active (tripped or ack'd) -- check if an alarm is active (tripped or ack'd)
---@nodiscard ---@nodiscard
@ -806,7 +798,7 @@ function logic.handle_redstone(self)
----------------------- -----------------------
local enable_emer_cool = self.plc_cache.rps_status.low_cool or local enable_emer_cool = self.plc_cache.rps_status.low_cool or
(self.auto_engaged and self.db.annunciator.CoolantLevelLow and is_active(self.alarms.ReactorOverTemp)) (self.auto_engaged and annunc.CoolantLevelLow and is_active(self.alarms.ReactorOverTemp))
-- don't turn off emergency coolant on sufficient coolant level since it might drop again -- don't turn off emergency coolant on sufficient coolant level since it might drop again
-- turn off once system is OK again -- turn off once system is OK again
@ -822,7 +814,7 @@ function logic.handle_redstone(self)
end end
end end
if self.db.annunciator.EmergencyCoolant > 1 and self.emcool_opened then if annunc.EmergencyCoolant > 1 and self.emcool_opened then
log.info(util.c("UNIT ", self.r_id, " emergency coolant valve closed")) log.info(util.c("UNIT ", self.r_id, " emergency coolant valve closed"))
log.info(util.c("UNIT ", self.r_id, " turbines set to not dump steam")) log.info(util.c("UNIT ", self.r_id, " turbines set to not dump steam"))
end end
@ -849,7 +841,7 @@ function logic.handle_redstone(self)
end end
end end
if self.db.annunciator.EmergencyCoolant > 1 and not self.emcool_opened then if annunc.EmergencyCoolant > 1 and not self.emcool_opened then
log.info(util.c("UNIT ", self.r_id, " emergency coolant valve opened")) log.info(util.c("UNIT ", self.r_id, " emergency coolant valve opened"))
log.info(util.c("UNIT ", self.r_id, " turbines set to dump excess steam")) log.info(util.c("UNIT ", self.r_id, " turbines set to dump excess steam"))
end end