diff --git a/supervisor/facility.lua b/supervisor/facility.lua
index 43c6cc4..737f6f9 100644
--- a/supervisor/facility.lua
+++ b/supervisor/facility.lua
@@ -50,9 +50,9 @@ local START_STATUS = {
BLADE_MISMATCH = 2
}
-local charge_Kp = 0.275
+local charge_Kp = 0.15
local charge_Ki = 0.0
-local charge_Kd = 4.5
+local charge_Kd = 0.6
local rate_Kp = 2.45
local rate_Ki = 0.4825
@@ -225,6 +225,14 @@ function facility.new(num_reactors, cooling_conf)
return unallocated, false
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 --
---@class facility
@@ -325,8 +333,9 @@ function facility.new(num_reactors, cooling_conf)
--#region
- local avg_charge = self.avg_charge.compute()
- local avg_inflow = self.avg_inflow.compute()
+ local avg_charge = self.avg_charge.compute()
+ local avg_inflow = self.avg_inflow.compute()
+ local avg_outflow = self.avg_outflow.compute()
local now = util.time_s()
@@ -390,6 +399,7 @@ function facility.new(num_reactors, cooling_conf)
-- disable reactors and disengage auto control
for _, u in pairs(self.prio_defs[i]) do
u.disable()
+ u.auto_set_idle(false)
u.auto_disengage()
end
end
@@ -460,6 +470,9 @@ function facility.new(num_reactors, cooling_conf)
self.last_error = 0
self.accumulator = 0
+ -- enabling idling on all assigned units
+ _set_idling(true)
+
self.status_text = { "CHARGE MODE", "running control loop" }
log.info("FAC: CHARGE mode starting PID control")
elseif self.last_update ~= charge_update then
@@ -475,9 +488,9 @@ function facility.new(num_reactors, cooling_conf)
local integral = self.accumulator
local derivative = (error - self.last_error) / (now - self.last_time)
- local P = (charge_Kp * error)
- local I = (charge_Ki * integral)
- local D = (charge_Kd * derivative)
+ local P = charge_Kp * error
+ local I = charge_Ki * integral
+ local D = charge_Kd * derivative
local output = P + I + D
@@ -486,7 +499,10 @@ function facility.new(num_reactors, cooling_conf)
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] }",
+ -- 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 < avg_inflow)))
+
+ -- 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))
_allocate_burn_rate(out_c, true)
@@ -544,9 +560,9 @@ function facility.new(num_reactors, cooling_conf)
local integral = self.accumulator
local derivative = (error - self.last_error) / (now - self.last_time)
- local P = (rate_Kp * error)
- local I = (rate_Ki * integral)
- local D = (rate_Kd * derivative)
+ local P = rate_Kp * error
+ local I = rate_Ki * integral
+ local D = rate_Kd * derivative
-- velocity (rate) (derivative of charge level => rate) feed forward
local FF = self.gen_rate_setpoint / self.charge_conversion
diff --git a/supervisor/startup.lua b/supervisor/startup.lua
index b63b399..1694ebe 100644
--- a/supervisor/startup.lua
+++ b/supervisor/startup.lua
@@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions")
-local SUPERVISOR_VERSION = "v1.3.5"
+local SUPERVISOR_VERSION = "v1.3.6"
local println = util.println
local println_ts = util.println_ts
diff --git a/supervisor/unit.lua b/supervisor/unit.lua
index afdf6f3..10e028a 100644
--- a/supervisor/unit.lua
+++ b/supervisor/unit.lua
@@ -8,9 +8,6 @@ local logic = require("supervisor.unitlogic")
local plc = require("supervisor.session.plc")
local rsctl = require("supervisor.session.rsctl")
----@class reactor_control_unit
-local unit = {}
-
local WASTE_MODE = types.WASTE_MODE
local WASTE = types.WASTE_PRODUCT
local ALARM = types.ALARM
@@ -55,6 +52,14 @@ local AISTATE = {
---@field id ALARM alarm ID
---@field tier integer alarm urgency tier (0 = highest)
+-- burn rate to idle at
+local IDLE_RATE = 0.01
+-- time (ms) to idle
+local IDLE_TIME = 15000
+
+---@class reactor_control_unit
+local unit = {}
+
-- create a new reactor unit
---@nodiscard
---@param reactor_id integer reactor unit number
@@ -83,6 +88,9 @@ function unit.new(reactor_id, num_boilers, num_turbines)
emcool_opened = false,
-- auto control
auto_engaged = false,
+ auto_idle = false,
+ auto_idling = false,
+ auto_idle_start = 0,
auto_was_alarmed = false,
ramp_target_br100 = 0,
-- state tracking
@@ -578,6 +586,23 @@ function unit.new(reactor_id, num_boilers, num_turbines)
end
end
+ -- set automatic control idling mode to change behavior when given a burn rate command of zero
+ -- - enabling will hold the reactor at 0.01 mB/t for a period before disabling when commanded zero
+ -- - disabling will stop the reactor when commanded zero
+ ---@param idle boolean true to enable, false to disable and stop right away
+ function public.auto_set_idle(idle)
+ if not (idle and 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
-- if it is degraded or not ready, the limit will be 0
---@nodiscard
@@ -597,7 +622,35 @@ function unit.new(reactor_id, num_boilers, num_turbines)
if self.auto_engaged 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))
- 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
end
end