-- 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