zodiak707

Reactor v1.2

Dec 12th, 2025 (edited)
47
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.44 KB | None | 0 0
  1. -- reactorctl.lua
  2. -- Auto-Regler für BigReactors/ExtremeReactors (ComputerCraft)
  3. -- Features: Pufferregelung (Hysterese), Start/Stop-Schwellen, SCRAM bei Temp/Fuel, Monitor-Ausgabe optional
  4.  
  5. -------------------------
  6. -- Konfiguration
  7. -------------------------
  8. local CFG = {
  9.   tickSeconds = 1.0,
  10.  
  11.   -- Energie-Puffer in % (0..100)
  12.   targetBuffer = 60,      -- um diesen Wert herum regeln
  13.   hysteresis   = 4,       -- Totband +/- Prozent um targetBuffer
  14.   startAt      = 35,      -- wenn Puffer darunter: Reaktor einschalten
  15.   stopAt       = 90,      -- wenn Puffer darüber: Reaktor ausschalten (optional)
  16.  
  17.   -- Steuerstab-Limits (0 = ganz raus = maximale Leistung, 100 = ganz rein)
  18.   minRod = 0,
  19.   maxRod = 100,
  20.  
  21.   -- Temperaturgrenzen (je nach Setup anpassen!)
  22.   maxFuelTemp   = 2000,   -- in °C (BigReactors API)
  23.   maxCasingTemp = 1500,   -- in °C
  24.  
  25.   -- Fuel-Schutz
  26.   minFuelPercent = 2,     -- wenn Fuel < 2%: SCRAM
  27.  
  28.   -- Anzeige
  29.   useMonitor = true,      -- wenn Monitor angeschlossen: mitnutzen
  30.   monitorScale = 0.5
  31. }
  32.  
  33. -------------------------
  34. -- Hilfsfunktionen
  35. -------------------------
  36. local function clamp(x, a, b)
  37.   if x < a then return a end
  38.   if x > b then return b end
  39.   return x
  40. end
  41.  
  42. local function getEnergyCapacity(reactor)
  43.   -- je nach Mod/Version verschieden benannt
  44.   local candidates = {
  45.     "getEnergyCapacity",
  46.     "getEnergyStoredMax",
  47.     "getMaxEnergyStored",
  48.     "getEnergyBufferSize",
  49.     "getEnergyMax"
  50.   }
  51.  
  52.   for _, m in ipairs(candidates) do
  53.     if type(reactor[m]) == "function" then
  54.       local ok, v = pcall(reactor[m])
  55.       if ok and type(v) == "number" and v > 0 then
  56.         return v, m
  57.       end
  58.     end
  59.   end
  60.  
  61.   return nil, nil
  62. end
  63.  
  64.  
  65. local function round(x)
  66.   return math.floor(x + 0.5)
  67. end
  68.  
  69. local function findReactor()
  70.   -- Verschiedene Typstrings je nach Mod/Version
  71.   local candidates = {
  72.     "BigReactors-Reactor",
  73.     "ExtremeReactors-Reactor",
  74.     "BiggerReactors_Reactor",
  75.     "Reactor"
  76.   }
  77.  
  78.   for _, t in ipairs(candidates) do
  79.     local p = peripheral.find(t)
  80.     if p then return p, t end
  81.   end
  82.  
  83.   -- Fallback: irgendwas, das typische Methoden hat
  84.   for _, name in ipairs(peripheral.getNames()) do
  85.     local p = peripheral.wrap(name)
  86.     if p and type(p.getEnergyStored) == "function" and type(p.setActive) == "function" and type(p.getNumberOfControlRods) == "function" then
  87.       return p, peripheral.getType(name) or name
  88.     end
  89.   end
  90.  
  91.   return nil, nil
  92. end
  93.  
  94. local function findMonitor()
  95.   local m = peripheral.find("monitor")
  96.   if m then
  97.     pcall(function() m.setTextScale(CFG.monitorScale) end)
  98.   end
  99.   return m
  100. end
  101.  
  102. local function setAllRods(reactor, level)
  103.   local n = reactor.getNumberOfControlRods()
  104.   for i = 0, n - 1 do
  105.     reactor.setControlRodLevel(i, level)
  106.   end
  107. end
  108.  
  109. local function getAvgRodLevel(reactor)
  110.   local n = reactor.getNumberOfControlRods()
  111.   if n <= 0 then return 0 end
  112.   local sum = 0
  113.   for i = 0, n - 1 do
  114.     sum = sum + reactor.getControlRodLevel(i)
  115.   end
  116.   return sum / n
  117. end
  118.  
  119. local function safeCall(fn, default)
  120.   local ok, val = pcall(fn)
  121.   if ok then return val end
  122.   return default
  123. end
  124.  
  125. local function fmtPercent(x) return tostring(round(x)) .. "%" end
  126.  
  127. -------------------------
  128. -- Anzeige
  129. -------------------------
  130. local function draw(lines, termObj)
  131.   termObj.clear()
  132.   termObj.setCursorPos(1, 1)
  133.   for i = 1, #lines do
  134.     termObj.write(lines[i] or "")
  135.     local _, y = termObj.getCursorPos()
  136.     termObj.setCursorPos(1, y + 1)
  137.   end
  138. end
  139.  
  140. -------------------------
  141. -- Hauptlogik
  142. -------------------------
  143. local reactor, rType = findReactor()
  144. if not reactor then
  145.   print("Kein Reaktor-Peripheral gefunden.")
  146.   print("Tipps:")
  147.   print("- Computer direkt an den Reaktor stellen oder per Modem verbinden")
  148.   print("- Prüfen: peripheral.getNames()")
  149.   return
  150. end
  151.  
  152. local monitor = CFG.useMonitor and findMonitor() or nil
  153.  
  154. local scrammed = false
  155. local lastRod = nil
  156.  
  157. local function scram(reason)
  158.   scrammed = true
  159.   pcall(function() reactor.setActive(false) end)
  160.   pcall(function() setAllRods(reactor, 100) end)
  161.   return reason or "SCRAM"
  162. end
  163.  
  164. local function controlStep()
  165.   -- Daten holen (robust, mit Defaults)
  166.   local active     = safeCall(function() return reactor.getActive() end, false)
  167.   local eStored = safeCall(function() return reactor.getEnergyStored() end, 0)
  168.   local eMax, capMethod = getEnergyCapacity(reactor)
  169.  
  170.   local bufferPct
  171.   if not eMax then
  172.     -- Keine Kapazität gefunden -> Prozent ist nicht berechenbar
  173.     bufferPct = 0
  174.   else
  175.     bufferPct = (eStored / eMax) * 100
  176.   end
  177.   bufferPct = clamp(bufferPct, 0, 100)
  178.  
  179.  
  180.   local fuelAmt    = safeCall(function() return reactor.getFuelAmount() end, 0)
  181.   local fuelMax    = safeCall(function() return reactor.getFuelAmountMax() end, 1)
  182.   local fuelPct    = (fuelAmt / math.max(fuelMax, 1)) * 100
  183.  
  184.   local fuelTemp   = safeCall(function() return reactor.getFuelTemperature() end, 0)
  185.   local casingTemp = safeCall(function() return reactor.getCasingTemperature() end, 0)
  186.  
  187.   local rfTick     = safeCall(function() return reactor.getEnergyProducedLastTick() end, 0)
  188.   local rodAvg     = safeCall(function() return getAvgRodLevel(reactor) end, 0)
  189.  
  190.   -- Safety first: Temperatur / Fuel
  191.   if fuelPct < CFG.minFuelPercent then
  192.     return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, scram("SCRAM: Fuel zu niedrig")
  193.   end
  194.   if fuelTemp > CFG.maxFuelTemp then
  195.     return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, scram("SCRAM: FuelTemp zu hoch")
  196.   end
  197.   if casingTemp > CFG.maxCasingTemp then
  198.     return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, scram("SCRAM: CasingTemp zu hoch")
  199.   end
  200.  
  201.   scrammed = false
  202.  
  203.   -- Start/Stop-Logik
  204.   if bufferPct <= CFG.startAt and not active then
  205.     pcall(function() reactor.setActive(true) end)
  206.     active = true
  207.   elseif bufferPct >= CFG.stopAt and active then
  208.     pcall(function() reactor.setActive(false) end)
  209.     active = false
  210.   end
  211.  
  212.   -- Wenn aus, nichts weiter regeln
  213.   if not active then
  214.     return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rodAvg, active, "OFF (Puffer hoch genug)"
  215.   end
  216.  
  217.   -- Hysterese-Regelung um targetBuffer
  218.   local low  = CFG.targetBuffer - CFG.hysteresis
  219.   local high = CFG.targetBuffer + CFG.hysteresis
  220.  
  221.   local newRod = rodAvg
  222.  
  223.   if bufferPct < low then
  224.     -- Mehr Leistung -> Stäbe raus (Level runter)
  225.     local err = (low - bufferPct) -- in %
  226.     local step = clamp(math.floor(err * 0.6) + 1, 1, 10)
  227.     newRod = rodAvg - step
  228.   elseif bufferPct > high then
  229.     -- Weniger Leistung -> Stäbe rein (Level rauf)
  230.     local err = (bufferPct - high)
  231.     local step = clamp(math.floor(err * 0.6) + 1, 1, 10)
  232.     newRod = rodAvg + step
  233.   else
  234.     -- im Totband: nix tun
  235.     newRod = rodAvg
  236.   end
  237.  
  238.   newRod = clamp(newRod, CFG.minRod, CFG.maxRod)
  239.  
  240.   -- Nur setzen, wenn sich wirklich was ändert (reduziert Spam)
  241.   if lastRod == nil or math.abs(newRod - lastRod) >= 1 then
  242.     pcall(function() setAllRods(reactor, newRod) end)
  243.     lastRod = newRod
  244.   end
  245.  
  246.   return bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, newRod, active, "OK"
  247. end
  248.  
  249. -- UI Loop
  250. local function uiLoop()
  251.   while true do
  252.     local bufferPct, fuelPct, fuelTemp, casingTemp, rfTick, rod, active, status = controlStep()
  253.  
  254.     local lines = {
  255.       "Reactor Control (" .. tostring(rType) .. ")",
  256.       "-----------------------------",
  257.       "Active:       " .. tostring(active),
  258.       "Status:       " .. tostring(status),
  259.       "",
  260.       "Buffer:       " .. fmtPercent(bufferPct),
  261.       "Fuel:         " .. fmtPercent(fuelPct),
  262.       "Fuel Temp:    " .. round(fuelTemp) .. " C",
  263.       "Casing Temp:  " .. round(casingTemp) .. " C",
  264.       "",
  265.       "Rod Level:    " .. round(rod) .. " (0=raus, 100=rein)",
  266.       "RF/tick:      " .. round(rfTick),
  267.       "",
  268.       "Keys: Q = quit | S = SCRAM"
  269.     }
  270.  
  271.     draw(lines, term)
  272.     if monitor then
  273.       draw(lines, monitor)
  274.     end
  275.  
  276.     -- Non-blocking Keycheck während sleep
  277.     local timer = os.startTimer(CFG.tickSeconds)
  278.     while true do
  279.       local ev, p1 = os.pullEvent()
  280.       if ev == "timer" and p1 == timer then
  281.         break
  282.       elseif ev == "char" then
  283.         local c = string.lower(p1)
  284.         if c == "q" then
  285.           term.setCursorPos(1, 1)
  286.           term.clear()
  287.           print("Beendet.")
  288.           return
  289.         elseif c == "s" then
  290.           scram("SCRAM: Manuell")
  291.         end
  292.       end
  293.     end
  294.   end
  295. end
  296.  
  297. uiLoop()
  298.  
Advertisement
Add Comment
Please, Sign In to add comment