mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
Merge pull request #471 from MikaylaFischler/366-charge-control-mode-idling
366 charge control mode idling
This commit is contained in:
commit
a99b7912f8
@ -89,7 +89,7 @@ function coordinator.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfv.assert_type_int(config.LogMode)
|
cfv.assert_type_int(config.LogMode)
|
||||||
|
@ -254,6 +254,11 @@ function renderer.close_ui()
|
|||||||
-- clear unit monitors
|
-- clear unit monitors
|
||||||
for _, monitor in ipairs(engine.monitors.unit_displays) do monitor.clear() end
|
for _, monitor in ipairs(engine.monitors.unit_displays) do monitor.clear() end
|
||||||
|
|
||||||
|
if not engine.disable_flow_view then
|
||||||
|
-- clear flow monitor
|
||||||
|
engine.monitors.flow.clear()
|
||||||
|
end
|
||||||
|
|
||||||
-- re-draw dmesg
|
-- re-draw dmesg
|
||||||
engine.dmesg_window.setVisible(true)
|
engine.dmesg_window.setVisible(true)
|
||||||
engine.dmesg_window.redraw()
|
engine.dmesg_window.redraw()
|
||||||
|
@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer")
|
|||||||
local sounder = require("coordinator.sounder")
|
local sounder = require("coordinator.sounder")
|
||||||
local threads = require("coordinator.threads")
|
local threads = require("coordinator.threads")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v1.4.0"
|
local COORDINATOR_VERSION = "v1.4.2"
|
||||||
|
|
||||||
local CHUNK_LOAD_DELAY_S = 30.0
|
local CHUNK_LOAD_DELAY_S = 30.0
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ function threads.thread__main(smem)
|
|||||||
local ok, start_ui = coord_comms.try_connect()
|
local ok, start_ui = coord_comms.try_connect()
|
||||||
if not ok then
|
if not ok then
|
||||||
crd_state.link_fail = true
|
crd_state.link_fail = true
|
||||||
|
crd_state.shutdown = true
|
||||||
log_sys("supervisor connection failed, shutting down...")
|
log_sys("supervisor connection failed, shutting down...")
|
||||||
log.fatal("failed to connect to supervisor")
|
log.fatal("failed to connect to supervisor")
|
||||||
break
|
break
|
||||||
@ -271,6 +272,13 @@ function threads.thread__render(smem)
|
|||||||
if msg.qtype == mqueue.TYPE.COMMAND then
|
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
-- received a command
|
-- received a command
|
||||||
if msg.message == MQ__RENDER_CMD.START_MAIN_UI then
|
if msg.message == MQ__RENDER_CMD.START_MAIN_UI then
|
||||||
|
-- stop the UI if it was already started
|
||||||
|
-- this may occur on a quick supervisor disconnect -> connect
|
||||||
|
if renderer.ui_ready() then
|
||||||
|
log_render("closing main UI before executing new request to start")
|
||||||
|
renderer.close_ui()
|
||||||
|
end
|
||||||
|
|
||||||
-- start up the main UI
|
-- start up the main UI
|
||||||
log_render("starting main UI...")
|
log_render("starting main UI...")
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ local function new_view(root, x, y)
|
|||||||
local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
||||||
|
|
||||||
c_target.register(facility.ps, "process_charge_target", c_target.set_value)
|
c_target.register(facility.ps, "process_charge_target", c_target.set_value)
|
||||||
cur_charge.register(facility.induction_ps_tbl[1], "energy", function (j) cur_charge.update(util.joules_to_fe(j) / 1000000) end)
|
cur_charge.register(facility.induction_ps_tbl[1], "avg_charge", function (fe) cur_charge.update(fe / 1000000) end)
|
||||||
|
|
||||||
local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur}
|
local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur}
|
||||||
TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2}
|
TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2}
|
||||||
|
@ -74,7 +74,7 @@ function plc.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc")
|
|||||||
local renderer = require("reactor-plc.renderer")
|
local renderer = require("reactor-plc.renderer")
|
||||||
local threads = require("reactor-plc.threads")
|
local threads = require("reactor-plc.threads")
|
||||||
|
|
||||||
local R_PLC_VERSION = "v1.7.5"
|
local R_PLC_VERSION = "v1.7.7"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
@ -662,8 +662,9 @@ function threads.thread__setpoint_control(smem)
|
|||||||
if (type(cur_burn_rate) == "number") and (setpoints.burn_rate ~= cur_burn_rate) and rps.is_active() then
|
if (type(cur_burn_rate) == "number") and (setpoints.burn_rate ~= cur_burn_rate) and rps.is_active() then
|
||||||
last_burn_sp = setpoints.burn_rate
|
last_burn_sp = setpoints.burn_rate
|
||||||
|
|
||||||
-- update without ramp if <= 2.5 mB/t change
|
-- update without ramp if <= 2.5 mB/t increase
|
||||||
running = math.abs(setpoints.burn_rate - cur_burn_rate) > 2.5
|
-- no need to ramp down, as the ramp up poses the safety risks
|
||||||
|
running = (setpoints.burn_rate - cur_burn_rate) > 2.5
|
||||||
|
|
||||||
if running then
|
if running then
|
||||||
log.debug(util.c("SPCTL: starting burn rate ramp from ", cur_burn_rate, " mB/t to ", setpoints.burn_rate, " mB/t"))
|
log.debug(util.c("SPCTL: starting burn rate ramp from ", cur_burn_rate, " mB/t to ", setpoints.burn_rate, " mB/t"))
|
||||||
|
@ -60,7 +60,7 @@ function rtu.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfv.assert_type_int(config.LogMode)
|
cfv.assert_type_int(config.LogMode)
|
||||||
|
@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
|
|||||||
local sps_rtu = require("rtu.dev.sps_rtu")
|
local sps_rtu = require("rtu.dev.sps_rtu")
|
||||||
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
||||||
|
|
||||||
local RTU_VERSION = "v1.9.3"
|
local RTU_VERSION = "v1.9.4"
|
||||||
|
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||||
|
@ -68,8 +68,8 @@ constants.ALARM_LIMITS = alarms
|
|||||||
|
|
||||||
--#region Supervisor Constants
|
--#region Supervisor Constants
|
||||||
|
|
||||||
-- milliseconds until turbine flow is assumed to be stable enough to enable coolant checks
|
-- milliseconds until coolant flow is assumed to be stable enough to enable certain coolant checks
|
||||||
constants.FLOW_STABILITY_DELAY_MS = 15000
|
constants.FLOW_STABILITY_DELAY_MS = 10000
|
||||||
|
|
||||||
-- Notes on Radiation
|
-- Notes on Radiation
|
||||||
-- - background radiation 0.0000001 Sv/h (99.99 nSv/h)
|
-- - background radiation 0.0000001 Sv/h (99.99 nSv/h)
|
||||||
@ -84,4 +84,17 @@ constants.EXTREME_RADIATION = 100.0
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Mekanism Configuration Constants
|
||||||
|
|
||||||
|
---@class _mek_constants
|
||||||
|
local mek = {}
|
||||||
|
|
||||||
|
mek.TURBINE_GAS_PER_TANK = 64000 -- mekanism: turbineGasPerTank
|
||||||
|
mek.TURBINE_DISPERSER_FLOW = 1280 -- mekanism: turbineDisperserGasFlow
|
||||||
|
mek.TURBINE_VENT_FLOW = 32000 -- mekanism: turbineVentGasFlow
|
||||||
|
|
||||||
|
constants.mek = mek
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
return constants
|
return constants
|
||||||
|
@ -91,6 +91,7 @@ local tmp_cfg = {
|
|||||||
CoolingConfig = {},
|
CoolingConfig = {},
|
||||||
FacilityTankMode = 0,
|
FacilityTankMode = 0,
|
||||||
FacilityTankDefs = {},
|
FacilityTankDefs = {},
|
||||||
|
ExtChargeIdling = false,
|
||||||
SVR_Channel = nil, ---@type integer
|
SVR_Channel = nil, ---@type integer
|
||||||
PLC_Channel = nil, ---@type integer
|
PLC_Channel = nil, ---@type integer
|
||||||
RTU_Channel = nil, ---@type integer
|
RTU_Channel = nil, ---@type integer
|
||||||
@ -120,6 +121,7 @@ local fields = {
|
|||||||
{ "CoolingConfig", "Cooling Configuration", {} },
|
{ "CoolingConfig", "Cooling Configuration", {} },
|
||||||
{ "FacilityTankMode", "Facility Tank Mode", 0 },
|
{ "FacilityTankMode", "Facility Tank Mode", 0 },
|
||||||
{ "FacilityTankDefs", "Facility Tank Definitions", {} },
|
{ "FacilityTankDefs", "Facility Tank Definitions", {} },
|
||||||
|
{ "ExtChargeIdling", "Extended Charge Idling", false },
|
||||||
{ "SVR_Channel", "SVR Channel", 16240 },
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
||||||
{ "PLC_Channel", "PLC Channel", 16241 },
|
{ "PLC_Channel", "PLC Channel", 16241 },
|
||||||
{ "RTU_Channel", "RTU Channel", 16242 },
|
{ "RTU_Channel", "RTU Channel", 16242 },
|
||||||
@ -222,8 +224,9 @@ local function config_view(display)
|
|||||||
local svr_c_4 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
local svr_c_4 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
local svr_c_5 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
local svr_c_5 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
local svr_c_6 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
local svr_c_6 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
|
local svr_c_7 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
local svr_pane = MultiPane{parent=svr_cfg,x=1,y=4,panes={svr_c_1,svr_c_2,svr_c_3,svr_c_4,svr_c_5,svr_c_6}}
|
local svr_pane = MultiPane{parent=svr_cfg,x=1,y=4,panes={svr_c_1,svr_c_2,svr_c_3,svr_c_4,svr_c_5,svr_c_6,svr_c_7}}
|
||||||
|
|
||||||
TextBox{parent=svr_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
TextBox{parent=svr_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
|
|
||||||
@ -329,7 +332,7 @@ local function config_view(display)
|
|||||||
else
|
else
|
||||||
tmp_cfg.FacilityTankMode = 0
|
tmp_cfg.FacilityTankMode = 0
|
||||||
tmp_cfg.FacilityTankDefs = {}
|
tmp_cfg.FacilityTankDefs = {}
|
||||||
main_pane.set_value(3)
|
svr_pane.set_value(7)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -563,7 +566,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local function submit_mode()
|
local function submit_mode()
|
||||||
tmp_cfg.FacilityTankMode = tank_mode.get_value()
|
tmp_cfg.FacilityTankMode = tank_mode.get_value()
|
||||||
main_pane.set_value(3)
|
svr_pane.set_value(7)
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=svr_c_5,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_5,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
@ -577,6 +580,23 @@ local function config_view(display)
|
|||||||
|
|
||||||
PushButton{parent=svr_c_6,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_6,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=svr_c_7,height=6,text="Charge control provides automatic control to maintain an induction matrix charge level. In order to have smoother control, reactors that were activated will be held on at 0.01 mB/t for a short period before allowing them to turn off. This minimizes overshooting the charge target."}
|
||||||
|
TextBox{parent=svr_c_7,y=8,height=3,text="You can extend this to a full minute to minimize reactors flickering on/off, but there may be more overshoot of the target."}
|
||||||
|
|
||||||
|
local ext_idling = CheckBox{parent=svr_c_7,x=1,y=12,label="Enable Extended Idling",default=ini_cfg.ExtChargeIdling,box_fg_bg=cpair(colors.yellow,colors.black)}
|
||||||
|
|
||||||
|
local function back_from_idling()
|
||||||
|
svr_pane.set_value(util.trinary(tmp_cfg.FacilityTankMode == 0, 3, 5))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_idling()
|
||||||
|
tmp_cfg.ExtChargeIdling = ext_idling.get_value()
|
||||||
|
main_pane.set_value(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=svr_c_7,x=1,y=14,text="\x1b Back",callback=back_from_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=svr_c_7,x=44,y=14,text="Next \x1a",callback=submit_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Network
|
--#region Network
|
||||||
|
@ -50,9 +50,9 @@ local START_STATUS = {
|
|||||||
BLADE_MISMATCH = 2
|
BLADE_MISMATCH = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
local charge_Kp = 0.275
|
local charge_Kp = 0.15
|
||||||
local charge_Ki = 0.0
|
local charge_Ki = 0.0
|
||||||
local charge_Kd = 4.5
|
local charge_Kd = 0.6
|
||||||
|
|
||||||
local rate_Kp = 2.45
|
local rate_Kp = 2.45
|
||||||
local rate_Ki = 0.4825
|
local rate_Ki = 0.4825
|
||||||
@ -63,9 +63,9 @@ local facility = {}
|
|||||||
|
|
||||||
-- create a new facility management object
|
-- create a new facility management object
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param num_reactors integer number of reactor units
|
---@param config svr_config supervisor configuration
|
||||||
---@param cooling_conf sv_cooling_conf cooling configurations of reactor units
|
---@param cooling_conf sv_cooling_conf cooling configurations of reactor units
|
||||||
function facility.new(num_reactors, cooling_conf)
|
function facility.new(config, cooling_conf)
|
||||||
local self = {
|
local self = {
|
||||||
units = {},
|
units = {},
|
||||||
status_text = { "START UP", "initializing..." },
|
status_text = { "START UP", "initializing..." },
|
||||||
@ -134,8 +134,8 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- create units
|
-- create units
|
||||||
for i = 1, num_reactors do
|
for i = 1, config.UnitCount do
|
||||||
table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BoilerCount, cooling_conf.r_cool[i].TurbineCount))
|
table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BoilerCount, cooling_conf.r_cool[i].TurbineCount, config.ExtChargeIdling))
|
||||||
table.insert(self.group_map, 0)
|
table.insert(self.group_map, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -225,6 +225,14 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
return unallocated, false
|
return unallocated, false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- set idle state of all assigned reactors
|
||||||
|
---@param idle boolean idle state
|
||||||
|
local function _set_idling(idle)
|
||||||
|
for i = 1, #self.prio_defs do
|
||||||
|
for _, u in pairs(self.prio_defs[i]) do u.auto_set_idle(idle) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
|
|
||||||
---@class facility
|
---@class facility
|
||||||
@ -327,8 +335,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
|
|
||||||
local avg_charge = self.avg_charge.compute()
|
local avg_charge = self.avg_charge.compute()
|
||||||
local avg_inflow = self.avg_inflow.compute()
|
local avg_inflow = self.avg_inflow.compute()
|
||||||
|
local avg_outflow = self.avg_outflow.compute()
|
||||||
|
|
||||||
local now = util.time_s()
|
local now = os.clock()
|
||||||
|
|
||||||
local state_changed = self.mode ~= self.last_mode
|
local state_changed = self.mode ~= self.last_mode
|
||||||
local next_mode = self.mode
|
local next_mode = self.mode
|
||||||
@ -390,6 +399,7 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
-- disable reactors and disengage auto control
|
-- disable reactors and disengage auto control
|
||||||
for _, u in pairs(self.prio_defs[i]) do
|
for _, u in pairs(self.prio_defs[i]) do
|
||||||
u.disable()
|
u.disable()
|
||||||
|
u.auto_set_idle(false)
|
||||||
u.auto_disengage()
|
u.auto_disengage()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -460,6 +470,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
self.last_error = 0
|
self.last_error = 0
|
||||||
self.accumulator = 0
|
self.accumulator = 0
|
||||||
|
|
||||||
|
-- enabling idling on all assigned units
|
||||||
|
_set_idling(true)
|
||||||
|
|
||||||
self.status_text = { "CHARGE MODE", "running control loop" }
|
self.status_text = { "CHARGE MODE", "running control loop" }
|
||||||
log.info("FAC: CHARGE mode starting PID control")
|
log.info("FAC: CHARGE mode starting PID control")
|
||||||
elseif self.last_update ~= charge_update then
|
elseif self.last_update ~= charge_update then
|
||||||
@ -475,9 +488,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
local integral = self.accumulator
|
local integral = self.accumulator
|
||||||
local derivative = (error - self.last_error) / (now - self.last_time)
|
local derivative = (error - self.last_error) / (now - self.last_time)
|
||||||
|
|
||||||
local P = (charge_Kp * error)
|
local P = charge_Kp * error
|
||||||
local I = (charge_Ki * integral)
|
local I = charge_Ki * integral
|
||||||
local D = (charge_Kd * derivative)
|
local D = charge_Kd * derivative
|
||||||
|
|
||||||
local output = P + I + D
|
local output = P + I + D
|
||||||
|
|
||||||
@ -486,7 +499,12 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
|
|
||||||
self.saturated = output ~= out_c
|
self.saturated = output ~= out_c
|
||||||
|
|
||||||
-- log.debug(util.sprintf("CHARGE[%f] { CHRG[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%d] }",
|
if not config.ExtChargeIdling then
|
||||||
|
-- stop idling early if the output is zero, we are at or above the setpoint, and are not losing charge
|
||||||
|
_set_idling(not ((out_c == 0) and (error <= 0) and (avg_outflow <= 0)))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- log.debug(util.sprintf("CHARGE[%f] { CHRG[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%f] }",
|
||||||
-- runtime, avg_charge, error, integral, output, out_c, P, I, D))
|
-- runtime, avg_charge, error, integral, output, out_c, P, I, D))
|
||||||
|
|
||||||
_allocate_burn_rate(out_c, true)
|
_allocate_burn_rate(out_c, true)
|
||||||
@ -544,9 +562,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
local integral = self.accumulator
|
local integral = self.accumulator
|
||||||
local derivative = (error - self.last_error) / (now - self.last_time)
|
local derivative = (error - self.last_error) / (now - self.last_time)
|
||||||
|
|
||||||
local P = (rate_Kp * error)
|
local P = rate_Kp * error
|
||||||
local I = (rate_Ki * integral)
|
local I = rate_Ki * integral
|
||||||
local D = (rate_Kd * derivative)
|
local D = rate_Kd * derivative
|
||||||
|
|
||||||
-- velocity (rate) (derivative of charge level => rate) feed forward
|
-- velocity (rate) (derivative of charge level => rate) feed forward
|
||||||
local FF = self.gen_rate_setpoint / self.charge_conversion
|
local FF = self.gen_rate_setpoint / self.charge_conversion
|
||||||
@ -936,41 +954,41 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
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
|
-- set automatic control configuration and start the process
|
||||||
---@param config coord_auto_config configuration
|
---@param auto_cfg coord_auto_config configuration
|
||||||
---@return table response ready state (successfully started) and current configuration (after updating)
|
---@return table response ready state (successfully started) and current configuration (after updating)
|
||||||
function public.auto_start(config)
|
function public.auto_start(auto_cfg)
|
||||||
local charge_scaler = 1000000 -- convert MFE to FE
|
local charge_scaler = 1000000 -- convert MFE to FE
|
||||||
local gen_scaler = 1000 -- convert kFE to FE
|
local gen_scaler = 1000 -- convert kFE to FE
|
||||||
local ready = false
|
local ready = false
|
||||||
|
|
||||||
-- load up current limits
|
-- load up current limits
|
||||||
local limits = {}
|
local limits = {}
|
||||||
for i = 1, num_reactors do
|
for i = 1, config.UnitCount do
|
||||||
local u = self.units[i] ---@type reactor_unit
|
local u = self.units[i] ---@type reactor_unit
|
||||||
limits[i] = u.get_control_inf().lim_br100 * 100
|
limits[i] = u.get_control_inf().lim_br100 * 100
|
||||||
end
|
end
|
||||||
|
|
||||||
-- only allow changes if not running
|
-- only allow changes if not running
|
||||||
if self.mode == PROCESS.INACTIVE then
|
if self.mode == PROCESS.INACTIVE then
|
||||||
if (type(config.mode) == "number") and (config.mode > PROCESS.INACTIVE) and (config.mode <= PROCESS.GEN_RATE) then
|
if (type(auto_cfg.mode) == "number") and (auto_cfg.mode > PROCESS.INACTIVE) and (auto_cfg.mode <= PROCESS.GEN_RATE) then
|
||||||
self.mode_set = config.mode
|
self.mode_set = auto_cfg.mode
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.burn_target) == "number") and config.burn_target >= 0.1 then
|
if (type(auto_cfg.burn_target) == "number") and auto_cfg.burn_target >= 0.1 then
|
||||||
self.burn_target = config.burn_target
|
self.burn_target = auto_cfg.burn_target
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.charge_target) == "number") and config.charge_target >= 0 then
|
if (type(auto_cfg.charge_target) == "number") and auto_cfg.charge_target >= 0 then
|
||||||
self.charge_setpoint = config.charge_target * charge_scaler
|
self.charge_setpoint = auto_cfg.charge_target * charge_scaler
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.gen_target) == "number") and config.gen_target >= 0 then
|
if (type(auto_cfg.gen_target) == "number") and auto_cfg.gen_target >= 0 then
|
||||||
self.gen_rate_setpoint = config.gen_target * gen_scaler
|
self.gen_rate_setpoint = auto_cfg.gen_target * gen_scaler
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.limits) == "table") and (#config.limits == num_reactors) then
|
if (type(auto_cfg.limits) == "table") and (#auto_cfg.limits == config.UnitCount) then
|
||||||
for i = 1, num_reactors do
|
for i = 1, config.UnitCount do
|
||||||
local limit = config.limits[i]
|
local limit = auto_cfg.limits[i]
|
||||||
|
|
||||||
if (type(limit) == "number") and (limit >= 0.1) then
|
if (type(limit) == "number") and (limit >= 0.1) then
|
||||||
limits[i] = limit
|
limits[i] = limit
|
||||||
@ -1010,7 +1028,7 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
---@param unit_id integer unit ID
|
---@param unit_id integer unit ID
|
||||||
---@param group integer group ID or 0 for independent
|
---@param group integer group ID or 0 for independent
|
||||||
function public.set_group(unit_id, group)
|
function public.set_group(unit_id, group)
|
||||||
if (group >= 0 and group <= 4) and (unit_id > 0 and unit_id <= num_reactors) and self.mode == PROCESS.INACTIVE then
|
if (group >= 0 and group <= 4) and (unit_id > 0 and unit_id <= config.UnitCount) and self.mode == PROCESS.INACTIVE then
|
||||||
-- remove from old group if previously assigned
|
-- remove from old group if previously assigned
|
||||||
local old_group = self.group_map[unit_id]
|
local old_group = self.group_map[unit_id]
|
||||||
if old_group ~= 0 then
|
if old_group ~= 0 then
|
||||||
|
@ -201,7 +201,7 @@ function svsessions.init(nic, fp_ok, config, cooling_conf)
|
|||||||
self.nic = nic
|
self.nic = nic
|
||||||
self.fp_ok = fp_ok
|
self.fp_ok = fp_ok
|
||||||
self.config = config
|
self.config = config
|
||||||
self.facility = facility.new(config.UnitCount, cooling_conf)
|
self.facility = facility.new(config, cooling_conf)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- find an RTU session by the computer ID
|
-- find an RTU session by the computer ID
|
||||||
|
@ -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.3.5"
|
local SUPERVISOR_VERSION = "v1.3.6"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
@ -26,6 +26,7 @@ function supervisor.load_config()
|
|||||||
config.CoolingConfig = settings.get("CoolingConfig")
|
config.CoolingConfig = settings.get("CoolingConfig")
|
||||||
config.FacilityTankMode = settings.get("FacilityTankMode")
|
config.FacilityTankMode = settings.get("FacilityTankMode")
|
||||||
config.FacilityTankDefs = settings.get("FacilityTankDefs")
|
config.FacilityTankDefs = settings.get("FacilityTankDefs")
|
||||||
|
config.ExtChargeIdling = settings.get("ExtChargeIdling")
|
||||||
|
|
||||||
config.SVR_Channel = settings.get("SVR_Channel")
|
config.SVR_Channel = settings.get("SVR_Channel")
|
||||||
config.PLC_Channel = settings.get("PLC_Channel")
|
config.PLC_Channel = settings.get("PLC_Channel")
|
||||||
@ -58,6 +59,8 @@ function supervisor.load_config()
|
|||||||
cfv.assert_type_int(config.FacilityTankMode)
|
cfv.assert_type_int(config.FacilityTankMode)
|
||||||
cfv.assert_range(config.FacilityTankMode, 0, 8)
|
cfv.assert_range(config.FacilityTankMode, 0, 8)
|
||||||
|
|
||||||
|
cfv.assert_type_bool(config.ExtChargeIdling)
|
||||||
|
|
||||||
cfv.assert_channel(config.SVR_Channel)
|
cfv.assert_channel(config.SVR_Channel)
|
||||||
cfv.assert_channel(config.PLC_Channel)
|
cfv.assert_channel(config.PLC_Channel)
|
||||||
cfv.assert_channel(config.RTU_Channel)
|
cfv.assert_channel(config.RTU_Channel)
|
||||||
@ -78,7 +81,7 @@ function supervisor.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfv.assert_type_int(config.LogMode)
|
cfv.assert_type_int(config.LogMode)
|
||||||
|
@ -8,9 +8,6 @@ local logic = require("supervisor.unitlogic")
|
|||||||
local plc = require("supervisor.session.plc")
|
local plc = require("supervisor.session.plc")
|
||||||
local rsctl = require("supervisor.session.rsctl")
|
local rsctl = require("supervisor.session.rsctl")
|
||||||
|
|
||||||
---@class reactor_control_unit
|
|
||||||
local unit = {}
|
|
||||||
|
|
||||||
local WASTE_MODE = types.WASTE_MODE
|
local WASTE_MODE = types.WASTE_MODE
|
||||||
local WASTE = types.WASTE_PRODUCT
|
local WASTE = types.WASTE_PRODUCT
|
||||||
local ALARM = types.ALARM
|
local ALARM = types.ALARM
|
||||||
@ -55,12 +52,22 @@ local AISTATE = {
|
|||||||
---@field id ALARM alarm ID
|
---@field id ALARM alarm ID
|
||||||
---@field tier integer alarm urgency tier (0 = highest)
|
---@field tier integer alarm urgency tier (0 = highest)
|
||||||
|
|
||||||
|
-- burn rate to idle at
|
||||||
|
local IDLE_RATE = 0.01
|
||||||
|
|
||||||
|
---@class reactor_control_unit
|
||||||
|
local unit = {}
|
||||||
|
|
||||||
-- create a new reactor unit
|
-- create a new reactor unit
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param reactor_id integer reactor unit number
|
---@param reactor_id integer reactor unit number
|
||||||
---@param num_boilers integer number of boilers expected
|
---@param num_boilers integer number of boilers expected
|
||||||
---@param num_turbines integer number of turbines expected
|
---@param num_turbines integer number of turbines expected
|
||||||
function unit.new(reactor_id, num_boilers, num_turbines)
|
---@param ext_idle boolean extended idling mode
|
||||||
|
function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
||||||
|
-- time (ms) to idle for auto idling
|
||||||
|
local IDLE_TIME = util.trinary(ext_idle, 60000, 10000)
|
||||||
|
|
||||||
---@class _unit_self
|
---@class _unit_self
|
||||||
local self = {
|
local self = {
|
||||||
r_id = reactor_id,
|
r_id = reactor_id,
|
||||||
@ -83,6 +90,9 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
emcool_opened = false,
|
emcool_opened = false,
|
||||||
-- auto control
|
-- auto control
|
||||||
auto_engaged = false,
|
auto_engaged = false,
|
||||||
|
auto_idle = false,
|
||||||
|
auto_idling = false,
|
||||||
|
auto_idle_start = 0,
|
||||||
auto_was_alarmed = false,
|
auto_was_alarmed = false,
|
||||||
ramp_target_br100 = 0,
|
ramp_target_br100 = 0,
|
||||||
-- state tracking
|
-- state tracking
|
||||||
@ -98,6 +108,8 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
status_text = { "UNKNOWN", "awaiting connection..." },
|
status_text = { "UNKNOWN", "awaiting connection..." },
|
||||||
-- logic for alarms
|
-- logic for alarms
|
||||||
had_reactor = false,
|
had_reactor = false,
|
||||||
|
turbine_flow_stable = false,
|
||||||
|
turbine_stability_data = {},
|
||||||
last_rate_change_ms = 0,
|
last_rate_change_ms = 0,
|
||||||
---@type rps_status
|
---@type rps_status
|
||||||
last_rps_trips = {
|
last_rps_trips = {
|
||||||
@ -251,6 +263,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
table.insert(self.db.annunciator.TurbineOverSpeed, false)
|
table.insert(self.db.annunciator.TurbineOverSpeed, false)
|
||||||
table.insert(self.db.annunciator.GeneratorTrip, false)
|
table.insert(self.db.annunciator.GeneratorTrip, false)
|
||||||
table.insert(self.db.annunciator.TurbineTrip, false)
|
table.insert(self.db.annunciator.TurbineTrip, false)
|
||||||
|
table.insert(self.turbine_stability_data, { time_state = 0, time_tanks = 0, rotation = 1 })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PRIVATE FUNCTIONS --
|
-- PRIVATE FUNCTIONS --
|
||||||
@ -530,6 +543,13 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
|
|
||||||
-- re-engage auto lock if it reconnected without it
|
-- re-engage auto lock if it reconnected without it
|
||||||
if self.auto_engaged and not self.plc_i.is_auto_locked() then self.plc_i.auto_lock(true) end
|
if self.auto_engaged and not self.plc_i.is_auto_locked() then self.plc_i.auto_lock(true) end
|
||||||
|
|
||||||
|
-- stop idling when completed
|
||||||
|
if self.auto_idling and (((util.time_ms() - self.auto_idle_start) > IDLE_TIME) or not self.auto_idle) then
|
||||||
|
log.info(util.c("UNIT ", self.r_id, ": completed idling period"))
|
||||||
|
self.auto_idling = false
|
||||||
|
self.plc_i.auto_set_burn(0, false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update deltas
|
-- update deltas
|
||||||
@ -578,6 +598,23 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- set automatic control idling mode to change behavior when given a burn rate command of zero<br>
|
||||||
|
-- - enabling it will hold the reactor at 0.01 mB/t for a period when commanded zero before disabling
|
||||||
|
-- - disabling it will stop the reactor when commanded zero
|
||||||
|
---@param idle boolean true to enable, false to disable (and stop)
|
||||||
|
function public.auto_set_idle(idle)
|
||||||
|
if idle and not self.auto_idle then
|
||||||
|
self.auto_idling = false
|
||||||
|
self.auto_idle_start = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if idle ~= self.auto_idle then
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": idling mode changed to ", idle))
|
||||||
|
end
|
||||||
|
|
||||||
|
self.auto_idle = idle
|
||||||
|
end
|
||||||
|
|
||||||
-- get the actual limit of this unit<br>
|
-- get the actual limit of this unit<br>
|
||||||
-- if it is degraded or not ready, the limit will be 0
|
-- if it is degraded or not ready, the limit will be 0
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@ -597,7 +634,35 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
if self.auto_engaged then
|
if self.auto_engaged then
|
||||||
if self.plc_i ~= nil then
|
if self.plc_i ~= nil then
|
||||||
log.debug(util.c("UNIT ", self.r_id, ": commit br100 of ", self.db.control.br100, " with ramp set to ", ramp))
|
log.debug(util.c("UNIT ", self.r_id, ": commit br100 of ", self.db.control.br100, " with ramp set to ", ramp))
|
||||||
self.plc_i.auto_set_burn(self.db.control.br100 / 100, ramp)
|
|
||||||
|
local rate = self.db.control.br100 / 100
|
||||||
|
|
||||||
|
if self.auto_idle then
|
||||||
|
if rate <= IDLE_RATE then
|
||||||
|
if self.auto_idle_start == 0 then
|
||||||
|
self.auto_idling = true
|
||||||
|
self.auto_idle_start = util.time_ms()
|
||||||
|
log.info(util.c("UNIT ", self.r_id, ": started idling at ", IDLE_RATE, " mB/t"))
|
||||||
|
|
||||||
|
rate = IDLE_RATE
|
||||||
|
elseif (util.time_ms() - self.auto_idle_start) > IDLE_TIME then
|
||||||
|
if self.auto_idling then
|
||||||
|
self.auto_idling = false
|
||||||
|
log.info(util.c("UNIT ", self.r_id, ": completed idling period"))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": continuing idle at ", IDLE_RATE, " mB/t"))
|
||||||
|
|
||||||
|
rate = IDLE_RATE
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.auto_idling = false
|
||||||
|
self.auto_idle_start = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.plc_i.auto_set_burn(rate, ramp)
|
||||||
|
|
||||||
if ramp then self.ramp_target_br100 = self.db.control.br100 end
|
if ramp then self.ramp_target_br100 = self.db.control.br100 end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -39,6 +39,21 @@ local ALARM_LIMS = const.ALARM_LIMITS
|
|||||||
---@class unit_logic_extension
|
---@class unit_logic_extension
|
||||||
local logic = {}
|
local logic = {}
|
||||||
|
|
||||||
|
-- compute Mekanism's rotation rate for a turbine
|
||||||
|
---@param turbine turbinev_session_db
|
||||||
|
local function turbine_rotation(turbine)
|
||||||
|
local build = turbine.build
|
||||||
|
|
||||||
|
local inner_vol = build.steam_cap / const.mek.TURBINE_GAS_PER_TANK
|
||||||
|
local disp_rate = (build.dispersers * const.mek.TURBINE_DISPERSER_FLOW) * inner_vol
|
||||||
|
local vent_rate = build.vents * const.mek.TURBINE_VENT_FLOW
|
||||||
|
|
||||||
|
local max_rate = math.min(disp_rate, vent_rate)
|
||||||
|
local flow = math.min(max_rate, turbine.tanks.steam.amount)
|
||||||
|
|
||||||
|
return (flow * (turbine.tanks.steam.amount / build.steam_cap)) / max_rate
|
||||||
|
end
|
||||||
|
|
||||||
-- update the annunciator
|
-- update the annunciator
|
||||||
---@param self _unit_self
|
---@param self _unit_self
|
||||||
function logic.update_annunciator(self)
|
function logic.update_annunciator(self)
|
||||||
@ -81,6 +96,11 @@ function logic.update_annunciator(self)
|
|||||||
-- some alarms wait until the burn rate has stabilized, so keep track of that
|
-- some alarms wait until the burn rate has stabilized, so keep track of that
|
||||||
if math.abs(_get_dt(DT_KEYS.ReactorBurnR)) > 0 then
|
if math.abs(_get_dt(DT_KEYS.ReactorBurnR)) > 0 then
|
||||||
self.last_rate_change_ms = util.time_ms()
|
self.last_rate_change_ms = util.time_ms()
|
||||||
|
self.turbine_flow_stable = false
|
||||||
|
|
||||||
|
for t = 1, self.num_turbines do
|
||||||
|
self.turbine_stability_data[t] = { time_state = 0, time_tanks = 0, rotation = 1 }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- record reactor stats
|
-- record reactor stats
|
||||||
@ -274,6 +294,7 @@ function logic.update_annunciator(self)
|
|||||||
local total_flow_rate = 0
|
local total_flow_rate = 0
|
||||||
local total_input_rate = 0
|
local total_input_rate = 0
|
||||||
local max_water_return_rate = 0
|
local max_water_return_rate = 0
|
||||||
|
local turbines_stable = true
|
||||||
|
|
||||||
-- recompute blade count on the chance that it may have changed
|
-- recompute blade count on the chance that it may have changed
|
||||||
self.db.control.blade_count = 0
|
self.db.control.blade_count = 0
|
||||||
@ -282,8 +303,10 @@ function logic.update_annunciator(self)
|
|||||||
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
|
||||||
|
local idx = session.get_device_idx()
|
||||||
|
|
||||||
annunc.RCSFault = annunc.RCSFault or (not turbine.formed) or session.is_faulted()
|
annunc.RCSFault = annunc.RCSFault or (not turbine.formed) or session.is_faulted()
|
||||||
|
annunc.TurbineOnline[idx] = true
|
||||||
|
|
||||||
-- update ready state
|
-- update ready state
|
||||||
-- - must be formed
|
-- - must be formed
|
||||||
@ -296,11 +319,56 @@ function logic.update_annunciator(self)
|
|||||||
total_flow_rate = total_flow_rate + turbine.state.flow_rate
|
total_flow_rate = total_flow_rate + turbine.state.flow_rate
|
||||||
total_input_rate = total_input_rate + turbine.state.steam_input_rate
|
total_input_rate = total_input_rate + turbine.state.steam_input_rate
|
||||||
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
|
||||||
|
|
||||||
annunc.TurbineOnline[session.get_device_idx()] = true
|
local last = self.turbine_stability_data[i]
|
||||||
|
|
||||||
|
if (not self.turbine_flow_stable) and (turbine.state.steam_input_rate > 0) then
|
||||||
|
local rotation = turbine_rotation(turbine)
|
||||||
|
local rotation_stable = false
|
||||||
|
|
||||||
|
-- see if data updated, and if so, check rotation speed change
|
||||||
|
-- minimal change indicates the turbine is converging on a flow rate
|
||||||
|
if last.time_tanks < turbine.tanks.last_update then
|
||||||
|
if last.time_tanks > 0 then
|
||||||
|
rotation_stable = math.abs(rotation - last.rotation) < 0.00000003
|
||||||
end
|
end
|
||||||
|
|
||||||
|
last.time_tanks = turbine.tanks.last_update
|
||||||
|
last.rotation = rotation
|
||||||
|
end
|
||||||
|
|
||||||
|
-- flow is stable if the flow rate is at the input rate or at the max (±1 mB/t)
|
||||||
|
local flow_stable = false
|
||||||
|
if last.time_state < turbine.state.last_update then
|
||||||
|
if (last.time_state > 0) and (turbine.state.flow_rate > 0) then
|
||||||
|
flow_stable = math.abs(turbine.state.flow_rate - math.min(turbine.state.steam_input_rate, turbine.build.max_flow_rate)) < 2
|
||||||
|
end
|
||||||
|
|
||||||
|
last.time_state = turbine.state.last_update
|
||||||
|
end
|
||||||
|
|
||||||
|
if rotation_stable then
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": turbine ", idx, " reached rotational stability (", rotation, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
if flow_stable then
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": turbine ", idx, " reached flow stability (", turbine.state.flow_rate, " mB/t)"))
|
||||||
|
end
|
||||||
|
|
||||||
|
turbines_stable = turbines_stable and (rotation_stable or flow_stable)
|
||||||
|
else
|
||||||
|
last.time_state = 0
|
||||||
|
last.time_tanks = 0
|
||||||
|
last.rotation = 1
|
||||||
|
|
||||||
|
turbines_stable = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.turbine_flow_stable = self.turbine_flow_stable or turbines_stable
|
||||||
|
|
||||||
-- 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
|
||||||
annunc.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)
|
||||||
|
|
||||||
@ -508,11 +576,25 @@ function logic.update_alarms(self)
|
|||||||
|
|
||||||
local rcs_trans = any_low or any_over or gen_trip or annunc.RCPTrip or annunc.MaxWaterReturnFeed
|
local rcs_trans = any_low or any_over or gen_trip or annunc.RCPTrip or annunc.MaxWaterReturnFeed
|
||||||
|
|
||||||
-- annunciator indicators for these states may not indicate a real issue when:
|
if plc_cache.active then
|
||||||
-- > flow is ramping up right after reactor start
|
-- these conditions may not indicate an issue when flow is changing after a burn rate change
|
||||||
-- > flow is ramping down after reactor shutdown
|
if self.num_boilers == 0 then
|
||||||
if ((util.time_ms() - self.last_rate_change_ms) > FLOW_STABILITY_DELAY_MS) and plc_cache.active then
|
if (util.time_ms() - self.last_rate_change_ms) > FLOW_STABILITY_DELAY_MS then
|
||||||
rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.BoilRateMismatch or annunc.CoolantFeedMismatch or annunc.SteamFeedMismatch
|
rcs_trans = rcs_trans or annunc.BoilRateMismatch
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.turbine_flow_stable then
|
||||||
|
rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.CoolantFeedMismatch or annunc.SteamFeedMismatch
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (util.time_ms() - self.last_rate_change_ms) > FLOW_STABILITY_DELAY_MS then
|
||||||
|
rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.BoilRateMismatch or annunc.CoolantFeedMismatch
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.turbine_flow_stable then
|
||||||
|
rcs_trans = rcs_trans or annunc.SteamFeedMismatch
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if _update_alarm_state(self, rcs_trans, self.alarms.RCSTransient) then
|
if _update_alarm_state(self, rcs_trans, self.alarms.RCSTransient) then
|
||||||
@ -666,7 +748,9 @@ function logic.update_status_text(self)
|
|||||||
elseif annunc.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 coolant flow stability"
|
||||||
|
elseif not self.turbine_flow_stable then
|
||||||
|
self.status_text[2] = "awaiting turbine flow stability"
|
||||||
else
|
else
|
||||||
self.status_text[2] = "system nominal"
|
self.status_text[2] = "system nominal"
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user