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