This commit is contained in:
Navid Sassan 2025-12-09 23:00:13 +01:00
parent 7eec32ad38
commit 59cfe70407

View File

@ -347,7 +347,18 @@ local function calculateFieldInput()
-- Add 10% safety margin, but ensure minimum of 1M RF/t during warmup when drain is 0
local minimumInput = math.max(drainRate * 1.1, 1000000)
return math.max(minimumInput, math.floor(totalInput))
local result = math.max(minimumInput, math.floor(totalInput))
-- Verbose output
local fieldPct = string.format("%.1f%%", currentStrength * 100)
local targetPct = string.format("%.1f%%", targetStrength * 100)
if currentStrength < targetStrength - 0.01 then
print("[FIELD] " .. fieldPct .. " < " .. targetPct .. " target -> increasing input to " .. formatNumber(result) .. " RF/t")
elseif currentStrength > targetStrength + 0.01 then
print("[FIELD] " .. fieldPct .. " > " .. targetPct .. " target -> reducing input to " .. formatNumber(result) .. " RF/t")
end
return result
end
-- Calculate safe output rate based on temperature and saturation
@ -368,52 +379,66 @@ local function calculateOutputRate()
local temp = reactorInfo.temperature or 0
local saturation = reactorInfo.energySaturation / reactorInfo.maxEnergySaturation
local generationRate = reactorInfo.generationRate or 0
local result = 0
local reason = ""
-- If we're in emergency or cooling, limit output
if state == STATES.EMERGENCY then
return 0
result = 0
reason = "EMERGENCY state"
elseif state == STATES.COOLING then
result = math.floor(generationRate * 0.5)
reason = "COOLING - 50% of generation"
elseif state == STATES.CHARGING or state == STATES.WARMING_UP then
result = math.floor(generationRate * 0.8)
reason = state .. " - 80% of generation"
else
-- Normal operation
local tempFactor = 1.0
if temp > CONFIG.critical_temperature then
local overTemp = temp - CONFIG.critical_temperature
local tempRange = CONFIG.max_temperature - CONFIG.critical_temperature
tempFactor = 1.0 - (overTemp / tempRange) * 0.5
reason = "temp " .. formatTemp(temp) .. " > critical, factor=" .. string.format("%.2f", tempFactor)
end
local outputRate = generationRate * tempFactor
if saturation > 0.8 then
outputRate = generationRate * 1.1 * tempFactor
if reason == "" then reason = "high saturation (110%)" end
elseif reason == "" then
reason = "normal operation"
end
result = math.max(0, math.floor(outputRate))
end
if state == STATES.COOLING then
-- During cooling, output less than we generate to build saturation
return math.floor(generationRate * 0.5)
if generationRate > 0 then
print("[OUTPUT] " .. formatNumber(result) .. " RF/t (" .. reason .. ")")
end
if state == STATES.CHARGING or state == STATES.WARMING_UP then
-- During warmup, output slightly less than generation
return math.floor(generationRate * 0.8)
end
-- Normal operation: output slightly more than generation to keep temp rising slowly
-- But cap it if temperature is getting high
local tempFactor = 1.0
if temp > CONFIG.critical_temperature then
-- Reduce output as we approach max temp
local overTemp = temp - CONFIG.critical_temperature
local tempRange = CONFIG.max_temperature - CONFIG.critical_temperature
tempFactor = 1.0 - (overTemp / tempRange) * 0.5
end
-- Output rate based on generation and saturation
local outputRate = generationRate * tempFactor
-- If saturation is high, we can output more
if saturation > 0.8 then
outputRate = generationRate * 1.1 * tempFactor
end
return math.max(0, math.floor(outputRate))
return result
end
-- ============================================================================
-- STATE MACHINE LOGIC
-- ============================================================================
local lastState = nil
local function setState(newState, reason)
if state ~= newState then
print("[STATE] " .. state .. " -> " .. newState .. " (" .. reason .. ")")
state = newState
end
end
local function processStateMachine()
if not reactorInfo or not reactorInfo.status then
-- SAFETY: Can't read reactor, go to emergency to maintain field
if state ~= STATES.OFFLINE and state ~= STATES.ERROR then
state = STATES.EMERGENCY
setState(STATES.EMERGENCY, "cannot read reactor data")
end
return
end
@ -429,15 +454,16 @@ local function processStateMachine()
local fieldStrength = (reactorInfo.fieldStrength or 0) / maxField
local saturation = (reactorInfo.energySaturation or 0) / maxSat
local fieldPct = string.format("%.1f%%", fieldStrength * 100)
-- Check for emergency conditions first (always)
if reactorStatus ~= "cold" and reactorStatus ~= "cooling" then
if fieldStrength < CONFIG.min_field_strength then
state = STATES.EMERGENCY
setState(STATES.EMERGENCY, "field " .. fieldPct .. " below minimum " .. formatPercent(CONFIG.min_field_strength))
return
end
if temp > CONFIG.max_temperature then
state = STATES.EMERGENCY
setState(STATES.EMERGENCY, "temp " .. formatTemp(temp) .. " exceeds max " .. formatTemp(CONFIG.max_temperature))
return
end
end
@ -445,26 +471,25 @@ local function processStateMachine()
-- Check for shutdown request
if shutdownRequested then
if reactorStatus == "cold" or reactorStatus == "cooling" then
state = STATES.SHUTDOWN
setState(STATES.SHUTDOWN, "shutdown requested, reactor cooling")
shutdownRequested = false
elseif state ~= STATES.EMERGENCY then
state = STATES.SHUTDOWN
setState(STATES.SHUTDOWN, "shutdown requested")
end
return
end
-- State transitions based on reactor status
if reactorStatus == "cold" or reactorStatus == "invalid" then
state = STATES.OFFLINE
setState(STATES.OFFLINE, "reactor status: " .. reactorStatus)
return
end
-- Clear charge request flag once field is sufficiently charged
if chargeRequested then
local fieldPercent = fieldStrength -- Already calculated above as ratio
if fieldPercent >= CONFIG.charge_field_target then
if fieldStrength >= CONFIG.charge_field_target then
chargeRequested = false
print("[INFO] Field charged to " .. string.format("%.1f%%", fieldPercent * 100) .. ", charge request cleared")
print("[INFO] Field charged to " .. fieldPct .. ", charge request cleared")
end
end
@ -473,33 +498,33 @@ local function processStateMachine()
-- Keep current state during cooldown
return
end
state = STATES.COOLING
setState(STATES.COOLING, "reactor cooling down")
return
end
if reactorStatus == "warming_up" then
-- Use 0.9 multiplier for hysteresis (consistent with RUNNING state)
if fieldStrength < CONFIG.charge_field_target * 0.9 then
state = STATES.CHARGING
setState(STATES.CHARGING, "field " .. fieldPct .. " below charge target")
else
state = STATES.WARMING_UP
setState(STATES.WARMING_UP, "field " .. fieldPct .. " sufficient, warming up")
end
return
end
if reactorStatus == "running" then
if fieldStrength < CONFIG.charge_field_target * 0.9 then
state = STATES.CHARGING
setState(STATES.CHARGING, "field " .. fieldPct .. " dropped below threshold while running")
elseif temp > CONFIG.critical_temperature then
state = STATES.COOLING
setState(STATES.COOLING, "temp " .. formatTemp(temp) .. " above critical " .. formatTemp(CONFIG.critical_temperature))
else
state = STATES.RUNNING
setState(STATES.RUNNING, "normal operation")
end
return
end
if reactorStatus == "stopping" then
state = STATES.SHUTDOWN
setState(STATES.SHUTDOWN, "reactor stopping")
return
end
end
@ -602,7 +627,7 @@ local function controlReactor()
-- User requested charge - provide input flux to start charging the field
inputRate = CONFIG.emergency_field_input
outputRate = 0
print("[DEBUG] OFFLINE+chargeRequested: inputRate=" .. inputRate)
print("[CHARGE] Providing " .. formatNumber(inputRate) .. " RF/t to charge field")
else
-- No action needed
inputRate = 0
@ -623,7 +648,6 @@ local function controlReactor()
lastInputRate = inputRate
lastOutputRate = outputRate
print("[DEBUG] Setting flux gates: input=" .. inputRate .. " output=" .. outputRate)
local inputSuccess = pcall(function()
inputGate.setFlowOverride(math.floor(inputRate))
end)