Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- reactorctl.lua
- -- Auto-Regler für BigReactors/ExtremeReactors (ComputerCraft)
- -- Features: Pufferregelung (Hysterese), Start/Stop-Schwellen, SCRAM bei Temp/Fuel, Monitor-Ausgabe optional
- -------------------------
- -- Konfiguration
- -------------------------
- local CFG = {
- tickSeconds = 1.0,
- -- Energie-Puffer in % (0..100)
- targetBuffer = 60, -- um diesen Wert herum regeln
- hysteresis = 4, -- Totband +/- Prozent um targetBuffer
- startAt = 35, -- wenn Puffer darunter: Reaktor einschalten
- stopAt = 90, -- wenn Puffer darüber: Reaktor ausschalten (optional)
- -- Steuerstab-Limits (0 = ganz raus = maximale Leistung, 100 = ganz rein)
- minRod = 0,
- maxRod = 100,
- -- Temperaturgrenzen (je nach Setup anpassen!)
- maxFuelTemp = 2000, -- in °C (BigReactors API)
- maxCasingTemp = 1500, -- in °C
- -- Fuel-Schutz
- minFuelPercent = 2, -- wenn Fuel < 2%: SCRAM
- -- Anzeige
- useMonitor = true, -- wenn Monitor angeschlossen: mitnutzen
- monitorScale = 0.5
- }
- -------------------------
- -- Hilfsfunktionen
- -------------------------
- local function clamp(x, a, b)
- if x < a then return a end
- if x > b then return b end
- return x
- end
- local function getEnergyCapacity(reactor)
- -- je nach Mod/Version verschieden benannt
- local candidates = {
- "getEnergyCapacity",
- "getEnergyStoredMax",
- "getMaxEnergyStored",
- "getEnergyBufferSize",
- "getEnergyMax"
- }
- for _, m in ipairs(candidates) do
- if type(reactor[m]) == "function" then
- local ok, v = pcall(reactor[m])
- if ok and type(v) == "number" and v > 0 then
- return v, m
- end
- end
- end
- return nil, nil
- end
- local function round(x)
- return math.floor(x + 0.5)
- end
- local function findReactor()
- -- Verschiedene Typstrings je nach Mod/Version
- local candidates = {
- "BigReactors-Reactor",
- "ExtremeReactors-Reactor",
- "BiggerReactors_Reactor",
- "Reactor"
- }
- for _, t in ipairs(candidates) do
- local p = peripheral.find(t)
- if p then return p, t end
- end
- -- Fallback: irgendwas, das typische Methoden hat
- for _, name in ipairs(peripheral.getNames()) do
- local p = peripheral.wrap(name)
- if p and type(p.getEnergyStored) == "function" and type(p.setActive) == "function" and type(p.getNumberOfControlRods) == "function" then
- return p, peripheral.getType(name) or name
- end
- end
- return nil, nil
- end
- local function findMonitor()
- local m = peripheral.find("monitor")
- if m then
- pcall(function() m.setTextScale(CFG.monitorScale) end)
- end
- return m
- end
- local function setAllRods(reactor, level)
- local n = reactor.getNumberOfControlRods()
- for i = 0, n - 1 do
- reactor.setControlRodLevel(i, level)
- end
- end
- local function getAvgRodLevel(reactor)
- local n = reactor.getNumberOfControlRods()
- if n <= 0 then return 0 end
- local sum = 0
- for i = 0, n - 1 do
- sum = sum + reactor.getControlRodLevel(i)
- end
- return sum / n
- end
- local function safeCall(fn, default)
- local ok, val = pcall(fn)
- if ok then return val end
- return default
- end
- local function fmtPercent(x) return tostring(round(x)) .. "%" end
- -------------------------
- -- Anzeige
- -------------------------
- local function draw(lines, termObj)
- termObj.clear()
- termObj.setCursorPos(1, 1)
- for i = 1, #lines do
- termObj.write(lines[i] or "")
- local _, y = termObj.getCursorPos()
- termObj.setCursorPos(1, y + 1)
- end
- end
- -------------------------
- -- Hauptlogik
- -------------------------
- local reactor, rType = findReactor()
- if not reactor then
- print("Kein Reaktor-Peripheral gefunden.")
- print("Tipps:")
- print("- Computer direkt an den Reaktor stellen oder per Modem verbinden")
- print("- Prüfen: peripheral.getNames()")
- return
- end
- local monitor = CFG.useMonitor and findMonitor() or nil
- local scrammed = false
- local lastRod = nil
- local function scram(reason)
- scrammed = true
- pcall(function() reactor.setActive(false) end)
- pcall(function() setAllRods(reactor, 100) end)
- return reason or "SCRAM"
- end
- local function controlStep()
- -- Daten holen (robust, mit Defaults)
- local active = safeCall(function() return reactor.getActive() end, false)
- local eStored = safeCall(function() return reactor.getEnergyStored() end, 0)
- local eMax, capMethod = getEnergyCapacity(reactor)
- local bufferPct
- if not eMax then
- -- Keine Kapazität gefunden -> Prozent ist nicht berechenbar
- bufferPct = 0
- else
- bufferPct = (eStored / eMax) * 100
- end
- bufferPct = clamp(bufferPct, 0, 100)
- local fuelAmt = safeCall(function() return reactor.getFuelAmount() end, 0)
- local fuelMax = safeCall(function() return reactor.getFuelAmountMax() end, 1)
- local fuelPct = (fuelAmt / math.max(fuelMax, 1)) * 100
- local fuelTemp = safeCall(function() return reactor.getFuelTemperature() end, 0)
- local casingTemp = safeCall(function() return reactor.getCasingTemperature() end, 0)
- local rfTick = safeCall(function() return reactor.getEnergyProducedLastTick() end, 0)
- local rodAvg = safeCall(function() return getAvgRodLevel(reactor) end, 0)
- -- Safety first: Temperatur / Fuel
- if fuelPct < CFG.minFuelPercent then
- return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, scram("SCRAM: Fuel zu niedrig")
- end
- if fuelTemp > CFG.maxFuelTemp then
- return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, scram("SCRAM: FuelTemp zu hoch")
- end
- if casingTemp > CFG.maxCasingTemp then
- return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, scram("SCRAM: CasingTemp zu hoch")
- end
- scrammed = false
- -- Start/Stop-Logik
- if bufferPct <= CFG.startAt and not active then
- pcall(function() reactor.setActive(true) end)
- active = true
- elseif bufferPct >= CFG.stopAt and active then
- pcall(function() reactor.setActive(false) end)
- active = false
- end
- -- Wenn aus, nichts weiter regeln
- if not active then
- return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, "OFF (Puffer hoch genug)"
- end
- -- Hysterese-Regelung um targetBuffer
- local low = CFG.targetBuffer - CFG.hysteresis
- local high = CFG.targetBuffer + CFG.hysteresis
- local newRod = rodAvg
- if bufferPct < low then
- -- Mehr Leistung -> Stäbe raus (Level runter)
- local err = (low - bufferPct) -- in %
- local step = clamp(math.floor(err * 0.6) + 1, 1, 10)
- newRod = rodAvg - step
- elseif bufferPct > high then
- -- Weniger Leistung -> Stäbe rein (Level rauf)
- local err = (bufferPct - high)
- local step = clamp(math.floor(err * 0.6) + 1, 1, 10)
- newRod = rodAvg + step
- else
- -- im Totband: nix tun
- newRod = rodAvg
- end
- newRod = clamp(newRod, CFG.minRod, CFG.maxRod)
- -- Nur setzen, wenn sich wirklich was ändert (reduziert Spam)
- if lastRod == nil or math.abs(newRod - lastRod) >= 1 then
- pcall(function() setAllRods(reactor, newRod) end)
- lastRod = newRod
- end
- return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, newRod, active, "OK"
- end
- -- UI Loop
- local function uiLoop()
- while true do
- local bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rod, active, status = controlStep()
- local lines = {
- "Reactor Control (" .. tostring(rType) .. ")",
- "-----------------------------",
- "Active: " .. tostring(active),
- "Status: " .. tostring(status),
- "",
- "Buffer: " .. fmtPercent(bufferPct),
- "Fuel: " .. fmtPercent(fuelPct),
- "Fuel Temp: " .. round(fuelTemp) .. " C",
- "Casing Temp: " .. round(casingTemp) .. " C",
- "",
- "Rod Level: " .. round(rod) .. " (0=raus, 100=rein)",
- "RF/tick: " .. round(rfTick),
- "",
- "Keys: Q = quit | S = SCRAM"
- }
- draw(lines, term)
- if monitor then
- draw(lines, monitor)
- end
- -- Non-blocking Keycheck während sleep
- local timer = os.startTimer(CFG.tickSeconds)
- while true do
- local ev, p1 = os.pullEvent()
- if ev == "timer" and p1 == timer then
- break
- elseif ev == "char" then
- local c = string.lower(p1)
- if c == "q" then
- term.setCursorPos(1, 1)
- term.clear()
- print("Beendet.")
- return
- elseif c == "s" then
- scram("SCRAM: Manuell")
- end
- end
- end
- end
- end
- uiLoop()
Advertisement
Add Comment
Please, Sign In to add comment