-- draconic_autolimit.lua
-- Set output flux gate automatically to hold reactor at T_set (7600C), maximize draw.
-- Place this on your advanced computer and edit sides if needed.
-- CONFIG
local T_SET = 7600
local SAMPLE_INTERVAL = 2 -- seconds between fast samples
local KP_RECALC_INTERVAL = 60 -- seconds to recalc Kp
local SAFETY_FACTOR = 0.85 -- use 85% of instantaneous available power
local MIN_LIMIT = 0
local MAX_LIMIT = 2000000000 -- upper clamp for flux gate (2 billion RF/t)
local EMERGENCY_TEMP = 8000 -- immediate cut if exceeded
local MIN_FIELD_PERCENT = 10 -- emergency if field drops below this percent
-- discovery helpers (tries many common method names)
local function findPeripheralWithMethods(methodsList)
for _,name in ipairs(peripheral.getNames()) do
local ok, meths = pcall(peripheral.getMethods, name)
if ok and meths then
local sig = {}
for _,m in ipairs(meths) do sig[m] = true end
local allFound = true
for _,m in ipairs(methodsList) do
if not sig[m] then allFound = false; break end
end
if allFound then return peripheral.wrap(name), name end
end
end
return nil
end
-- choose reactor peripheral (search for common method sets)
local reactor = findPeripheralWithMethods({"getTemperature","getGenerationRate"}) or findPeripheralWithMethods({"getTemperature","getEnergyStored"})
if not reactor then error("No reactor peripheral found (wrap/attach fluxgate to computer or use wired modem).") end
-- find output flux gate peripheral (we expect setFlowOverride or equivalent)
local fluxOut = findPeripheralWithMethods({"setFlowOverride"}) or findPeripheralWithMethods({"setFlow"})
if not fluxOut then
-- try to find any peripheral named like fluxgate
for _,n in ipairs(peripheral.getNames()) do
if n:lower():find("flux") or n:lower():find("flow") then
fluxOut = peripheral.wrap(n); break
end
end
end
if not fluxOut then error("No output flux gate peripheral found. Bind a flux gate to the computer or wire it.") end
-- helper fetchers (try multiple possible reactor APIs)
local function getTemperature()
local s = { "getTemperature","getTemp","getHeat","getCoreTemperature" }
for _,m in ipairs(s) do
if reactor[m] then local ok,res = pcall(reactor[m], reactor); if ok and type(res) == "number" then return res end end
end
return nil
end
local function getGeneration()
local s = { "getGenerationRate","getFlux","getEnergyChange","getGeneration" }
for _,m in ipairs(s) do
if reactor[m] then local ok,res = pcall(reactor[m], reactor); if ok and type(res) == "number" then return res end end
end
return nil
end
local function getFieldLoad()
local s = { "getFieldLoadRate","getFieldDrainRate","getFieldLoad" }
for _,m in ipairs(s) do
if reactor[m] then local ok,res = pcall(reactor[m], reactor); if ok and type(res) == "number" then return res end end
end
return nil
end
local function getFieldPercent()
local s = { "getField","getFieldStrength","getContainmentPercent" }
for _,m in ipairs(s) do
if reactor[m] then local ok,res = pcall(reactor[m], reactor); if ok and type(res) == "number" then return res end end
end
return nil
end
local function getEnergySaturation()
local s = { "getEnergySaturation","getSaturation","getInternalEnergy" }
for _,m in ipairs(s) do
if reactor[m] then local ok,res = pcall(reactor[m], reactor); if ok and type(res) == "number" then return res end end
end
return nil
end
-- set flux gate (attempt setFlowOverride; if it does not exist try alternatives)
local function setFluxLimit(v)
if v < MIN_LIMIT then v = MIN_LIMIT end
if v > MAX_LIMIT then v = MAX_LIMIT end
-- prefer exact override API
if fluxOut.setFlowOverride then
pcall(fluxOut.setFlowOverride, fluxOut, math.floor(v))
return true
end
if fluxOut.setFlow then
pcall(fluxOut.setFlow, fluxOut, math.floor(v))
return true
end
-- fallback: try using redstone on a side (you must wire your flux gate to accept redstone)
return false
end
-- initial Kp guess
local Kp = 20000 -- RF per °C (will be recalculated)
local lastKpCalc = 0
local lastTemp, lastAllowed = nil, nil
-- auto Kp calc routine: sample two points and compute slope S
local function recalcKp()
local t1 = getTemperature(); local g1 = getGeneration(); local f1 = getFieldLoad()
os.sleep(2)
local t2 = getTemperature(); local g2 = getGeneration(); local f2 = getFieldLoad()
if not t1 or not t2 or t1 == t2 then return end
local allowed1 = (g1 or 0) - (f1 or 0)
local allowed2 = (g2 or 0) - (f2 or 0)
local S = (allowed2 - allowed1) / (t2 - t1) -- RF per °C
-- if injector is externally powered, we should use (g2-g1)/(t2-t1) instead. Adjust if necessary.
if S < 0 then S = math.abs(S) end
local newKp = S * SAFETY_FACTOR
if newKp < 1000 then newKp = 1000 end
if newKp > 2000000 then newKp = 2000000 end
Kp = newKp
lastKpCalc = os.time()
end
-- main loop
local running = true
local lastRecalc = 0
while running do
pcall(function()
local T = getTemperature()
local G = getGeneration() or 0
local F = getFieldLoad() or 0
local fieldPct = getFieldPercent() or 100
if not T then error("Cannot read temperature; check peripheral.") end
-- emergency checks
if T >= EMERGENCY_TEMP or fieldPct <= MIN_FIELD_PERCENT then
-- immediate shutdown: set flux limit to 0 and optionally call reactor.scram if available
setFluxLimit(0)
-- attempt to deactivate reactor (if available)
if reactor.setActive then pcall(reactor.setActive, reactor, false) end
error(("Emergency stop: T=%.1f, Field=%.1f%%"):format(T, fieldPct))
end
-- recompute Kp periodically
if os.time() - lastKpCalc > KP_RECALC_INTERVAL then
recalcKp()
end
-- instant available power
-- choose whether you power the injector from output or from external grid
local injectorFromOutput = true -- EDIT THIS if your injector is externally powered
local A = G
if injectorFromOutput then A = G - F end
-- compute limit with proportional correction on temp error
local tempErr = T - T_SET
local correction = Kp * tempErr
local rawLimit = SAFETY_FACTOR * A - correction
-- clamp and write to flux gate
local limit = math.max(0, math.floor(rawLimit + 0.5))
setFluxLimit(limit)
-- small status print
term.clear(); term.setCursorPos(1,1)
print(("T: %.1f°C G: %d F: %d A: %d Kp: %d Limit: %d"):format(T, G, F, A, Kp, limit))
end)
os.sleep(SAMPLE_INTERVAL)
end