Advertisement
1ng0

CC - Reactor Controler - by lezchap

Dec 2nd, 2015
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.84 KB | None | 0 0
  1. os.loadAPI("button")
  2.  
  3. --Based on the PID Controller first posted by Copper280z on Reddit:
  4. --http://www.reddit.com/r/feedthebeast/comments/2pxwzo/big_reactors_like_br_turbines_but_dont_like/
  5. --Heavily Modified by LezChap for use with Multiple Turbines and Turbine Control
  6. --Turbine Graphic Code used from Direwolf20's Reactor Controler (youtube.com/watch?v=l7ZwSFVYITU)
  7. --Button API modified from Direwolf20 (youtube link above)
  8.  
  9. local turbines = {peripheral.find("BigReactors-Turbine")}
  10. local reactor = peripheral.find("BigReactors-Reactor")
  11. local mon = peripheral.find("monitor")
  12. local totalTurbines = #turbines
  13.  
  14. --Change number below for your setup
  15. rednet.open("bottom") --modem connected to Capacitor Relay Computer
  16. local rodsPerTurbine = 5.5  --how much Control Rod needed to produce >2000mb of steam in your reactor, helps prevent reactor from jumping between on/off quickly
  17. local goalRPM = 1792  --all RPM-based settings based on this number
  18. local shutdownPower = 200000000  --make sure this is less then max Capacitor capacity
  19.  
  20. --Tweak these numbers if needed, as per Copper280z's post on Reddit (linked in header)
  21. local P = .4
  22. local I = .000001
  23. local D = 12
  24.  
  25. --initiating variables
  26. local integral = 0
  27. local derivative = 0
  28.  
  29. local error = 0
  30. local prevError = 0
  31.  
  32. local table = {}
  33. local needEngaged = false
  34. local needShutdown = false
  35. local turbineSpeed = 0
  36.  
  37. local slowdown = {}
  38. for k,v in ipairs(turbines) do
  39.   slowdown[k] = false
  40.   v.setActive(true)
  41.   v.setInductorEngaged(false)
  42.   v.setVentOverflow(true)
  43.   v.setFluidFlowRateMax(0)
  44. end
  45.  
  46. local reactorCooldown = false
  47.  
  48. local prevControlRod = 0
  49. local ControlRod = 0  
  50.  
  51. local CRchange = 0
  52. local newCR = 0
  53.  
  54. reactor.setAllControlRodLevels(100)
  55. reactor.setActive(true)
  56.  
  57. local turbinePage = 0
  58. local lastPage = 1
  59. local pages = math.ceil(totalTurbines / 4)
  60. mon.clear()
  61.  
  62. local turbineSpeed = 0
  63. local countTurbines = 0
  64. local timerCounter = 0
  65.  
  66. function PID()
  67.   turbineSpeed = 0
  68.   local turbineNeedSteam = false
  69.   for k,v in pairs(turbines) do
  70.     local tempSpeed = v.getRotorSpeed()
  71.     turbineSpeed = turbineSpeed + tempSpeed
  72.     --if one turbine is too low, make sure steam is produced
  73.     if tempSpeed < (goalRPM - 6) then
  74.       turbineNeedSteam = true
  75.     end
  76.   end
  77.   turbineSpeed = turbineSpeed / totalTurbines
  78.   error = goalRPM - turbineSpeed
  79.  
  80.   ControlRod = reactor.getControlRodLevel(0)
  81.   local reactorTemp = reactor.getFuelTemperature()
  82.  
  83.   integral = integral + error
  84.  
  85.   derivative = error - prevError
  86.  
  87.   if integral > 1000 then
  88.     integral = 1000
  89.   end
  90.  
  91.   if integral < -1000 then
  92.     integral = -1000
  93.   end
  94.  
  95.   CRchange = P*error + I*integral + D*derivative
  96.  
  97.   prevError = error
  98.  
  99.   newCR = ControlRod - CRchange
  100.  
  101.   --check how many Turbines are using steam, only make enough for that # of Turbines
  102.   countTurbines = 0
  103.   for k,v in ipairs(slowdown) do
  104.     if not v then
  105.       countTurbines = countTurbines + 1
  106.     end
  107.   end
  108.   local maxControlRods = 100-(countTurbines * rodsPerTurbine)
  109.  
  110.   if newCR < maxControlRods then
  111.     newCR = maxControlRods
  112.   end
  113.  
  114.   if newCR > 100 then
  115.     newCR = 100
  116.   end
  117.  
  118.   if turbineNeedSteam then
  119.     newCR = maxControlRods
  120.   end
  121.  
  122.   --prevents fuel waste from too-hot reactors - normally used during turbine Spin-up
  123.   if reactorTemp < 200 and reactorCooldown then
  124.     reactorCooldown = false
  125.   end
  126.     if reactorTemp > 1000 or reactorCooldown then
  127.     newCR = 100
  128.     reactorCooldown = true
  129.   end
  130.  
  131.   reactor.setAllControlRodLevels(newCR)
  132.   print("Control Rods: "..newCR.." Error: "..error)
  133.   --write("...")
  134.   --write("P: "..P*error)
  135.   --write("...")
  136.   --write("  I: "..I*integral)
  137.   --write("...")
  138.   --print("  D: "..D*derivative)
  139.   --print(newCR)
  140. end
  141.  
  142. function doTurbineLogic()
  143.   for k,v in ipairs(turbines) do
  144.     local turbSpeed = v.getRotorSpeed()
  145.     local flowRate = v.getFluidFlowRateMax()
  146.     local maxFlowRate = v.getFluidFlowRateMaxMax()
  147.     local safeShutdown = false
  148.     local tooSlow = false
  149.     local coils = v.getInductorEngaged()
  150.  
  151.     --Failsafes for turbines running too fast at shutdown (shouldn't happen)
  152.     --or running too slow (during spin-up) to make sure coils engage/disengage at optimum range
  153.     if turbSpeed > goalRPM + 50 then
  154.       safeShutdown = false
  155.     elseif turbSpeed < goalRPM - 10 then
  156.       tooSlow = true
  157.     else
  158.       tooSlow = false
  159.       safeShutdown = true
  160.     end
  161.     needEngaged = false
  162.     needShutdown = false
  163.  
  164.     --does the system need power, or have too much?
  165.     if table.monitor and table.currPower < 10000000 then
  166.       needEngaged = true
  167.       needShutdown = false
  168.     elseif not table.montitor and table.currPower > shutdownPower then
  169.       needEngaged = false
  170.       needShutdown = true
  171.     end
  172.  
  173.     --Adjust Steam Usage based on goalRPM (to make sure multiple-turbine setups split steam more evenly)
  174.     if turbSpeed > goalRPM + 5 then
  175.       slowdown[k] = true
  176.     end
  177.     if turbSpeed < goalRPM then
  178.       slowdown[k] = false
  179.     end
  180.  
  181.     --turn coils on/off based on conditions above
  182.     if needEngaged and not tooSlow and not coils then
  183.       v.setInductorEngaged(true)
  184.     elseif needShutdown and safeShutdown and coils then
  185.       v.setInductorEngaged(false)
  186.     elseif tooSlow and coils then
  187.       v.setInductorEngaged(false)
  188.     end
  189.  
  190.     --turn flowrate for steam usage on/off based on goalRPM (helps keep RPMs even in multiple-turbine setups)
  191.     if slowdown[k] then
  192.       if flowRate ~= 0 then
  193.         v.setFluidFlowRateMax(0)
  194.       end
  195.     else
  196.       if flowRate ~= 2000 then
  197.         local setRate = math.min(2000, maxFlowRate)
  198.         v.setFluidFlowRateMax(setRate)
  199.       end
  200.     end
  201.   end
  202. end
  203.  
  204. function toint(num)  --removes decimals from strings
  205.   return string.format("%d", tostring(num))
  206. end  
  207.  
  208. function commaformat(num)  --puts a comma every 3 digits in a number (From Direwolf20)
  209.   local formatted = num
  210.   local neg = false
  211.   if formatted < 0 then
  212.     formatted = math.abs(formatted)
  213.     neg = true
  214.   end
  215.   while true do
  216.     formatted, k = string.gsub(formatted, "^(%d+)(%d%d%d)", '%1,%2')
  217.     if k == 0 then
  218.       break
  219.     end
  220.   end
  221.   if neg then
  222.     formatted = "-"..formatted
  223.   end
  224.  
  225.   return formatted
  226. end
  227.    
  228.  
  229. function displayHeader()
  230.   mon.setTextScale(1)
  231.   mon.setBackgroundColor(colors.black)
  232.   mon.setTextColor(colors.white)
  233.   mon.setCursorPos(1,1)
  234.   mon.clearLine()
  235.  
  236.   --Start First Line of Header
  237.   mon.write("Average RPM: ")
  238.   if error > 4 or error < -4 then
  239.     mon.setTextColor(colors.red)
  240.   else
  241.     mon.setTextColor(colors.green)
  242.   end
  243.   mon.write(toint(turbineSpeed))
  244.   local x, y = mon.getSize()
  245.   local middle = (x/2) + 3
  246.   mon.setTextColor(colors.white)
  247.   mon.setCursorPos(middle, 1)
  248.   mon.write("Control Rods: ")
  249.   if math.ceil(newCR) == 100 then
  250.     mon.setTextColor(colors.green)
  251.   else
  252.     mon.setTextColor(colors.red)
  253.   end
  254.   mon.write(toint(math.ceil(newCR)))
  255.   mon.write("%")
  256.  
  257.   --start second line of Header
  258.   mon.setTextColor(colors.white)
  259.   mon.setCursorPos(1,2)
  260.   mon.clearLine()
  261.   mon.write("Goal RPM: "..goalRPM)
  262.   mon.setCursorPos(middle, 2)
  263.   mon.write("Fuel Usage: ")
  264.   local fuelUse = reactor.getFuelConsumedLastTick()
  265.   fuelUse = math.floor(fuelUse*100)
  266.   fuelUse = fuelUse/100
  267.   if fuelUse > 0 then
  268.     mon.setTextColor(colors.red)
  269.   else
  270.     mon.setTextColor(colors.green)
  271.   end
  272.   mon.write(fuelUse.."mb/t")
  273.  
  274.   --start third line of Header
  275.   mon.setTextColor(colors.white)
  276.   mon.setCursorPos(1,3)
  277.   mon.clearLine()
  278.   mon.write("Turbines Active: ")
  279.   mon.write(toint(countTurbines).." of "..totalTurbines)
  280.   mon.setTextColor(colors.white)
  281.   mon.setCursorPos(middle, 3)
  282.   mon.write("Fuel Level: ")
  283.   local fuelLevel = (reactor.getFuelAmount() / reactor.getFuelAmountMax()) * 100
  284.   if fuelLevel > 99 then
  285.     mon.setTextColor(colors.green)
  286.   else
  287.     mon.setTextColor(colors.red)
  288.   end
  289.   mon.write(fuelLevel.."%")
  290.  
  291.   --start fourth line of Header
  292.   mon.setTextColor(colors.white)
  293.   mon.setCursorPos(1,4)
  294.   mon.clearLine()
  295.   mon.write("Power Produced: ")
  296.   local totalEnergyProduced = 0
  297.   for k,v in ipairs(turbines) do
  298.     totalEnergyProduced = totalEnergyProduced + v.getEnergyProducedLastTick()
  299.   end
  300.   if totalEnergyProduced > (23500 * totalTurbines) then
  301.     mon.setTextColor(colors.green)
  302.   else
  303.     mon.setTextColor(colors.red)
  304.   end
  305.   totalEnergyProduced = toint(totalEnergyProduced)
  306.   totalEnergyProduced = tonumber(totalEnergyProduced)
  307.   mon.write(commaformat(totalEnergyProduced).."RF/t")
  308.   mon.setTextColor(colors.white)
  309.   mon.setCursorPos(middle, 4)
  310.   mon.write("Core Temp: ")
  311.   local coreTemp = reactor.getFuelTemperature()
  312.   if coreTemp > 750 then
  313.     mon.setTextColor(colors.red)
  314.   elseif coreTemp < 250 then
  315.     mon.setTextColor(colors.green)
  316.   else
  317.     mon.setTextColor(colors.orange)
  318.   end
  319.   mon.write(math.floor(coreTemp).."c")
  320. end
  321.  
  322. function displayTurbines(page)
  323.   local turbineStartNum = (page * 4) + 1
  324.   local turbinesOnPage = totalTurbines - turbineStartNum + 1
  325.   if turbinesOnPage > 4 then
  326.     turbinesOnPage = 4
  327.   end
  328.   if turbinesOnPage < 1 then
  329.     turbinesOnPage = 1
  330.   end
  331.   local x,y = mon.getSize()
  332.   local xInterval = math.floor(x/(turbinesOnPage + 1))
  333.   local turbinePagePeriph = {}
  334.   local temp = 1
  335.   for i = turbineStartNum, (turbineStartNum + turbinesOnPage - 1) do
  336.     turbinePagePeriph[temp] = turbines[i]
  337.     --print(i.." "..turbines[i])
  338.     temp = temp + 1
  339.   end
  340.   --clear text variables of Turbines
  341.   --mon.setCursorPos(1,5)
  342.   --mon.clearLine()
  343.   mon.setCursorPos(1,18)
  344.   mon.clearLine()
  345.   mon.setCursorPos(1,19)
  346.   mon.clearLine()
  347.  
  348.   for k,v in ipairs(turbinePagePeriph) do
  349.     --display Turbine info on Monitor
  350.     local xCenter = (k * xInterval)
  351.  
  352.     --display Turbine Title (Turbine #)
  353.     local turbineTitle = "Turbine "..tostring(k + turbineStartNum - 1)
  354.     local titleXStart = xCenter - math.floor(string.len(turbineTitle) / 2)
  355.     mon.setTextColor(colors.lightBlue)
  356.     mon.setCursorPos(titleXStart, 5)
  357.     mon.write(turbineTitle)
  358.  
  359.     --display individual turbine RPMs
  360.     local turbineRPM = toint(v.getRotorSpeed())
  361.     local RPMXStart = xCenter - math.floor(string.len(tostring(turbineRPM)) / 2)
  362.     mon.setCursorPos(RPMXStart, 18)
  363.     if goalRPM - turbineRPM > 4 or goalRPM - turbineRPM < -4 then
  364.       mon.setTextColor(colors.red)
  365.     else
  366.       mon.setTextColor(colors.green)
  367.     end
  368.     mon.write(turbineRPM)
  369.  
  370.     --display individual turbine power outputs
  371.     local powerMade = v.getEnergyProducedLastTick()
  372.     if powerMade > 0 then
  373.       mon.setTextColor(colors.green)
  374.     else
  375.       mon.setTextColor(colors.red)
  376.     end
  377.     powerMade = toint(powerMade)
  378.     powerMade = tonumber(powerMade)
  379.     powerMade = commaformat(powerMade).."RF"
  380.     local powerXStart = xCenter - math.floor(string.len(powerMade) / 2)
  381.     mon.setCursorPos(powerXStart, 19)
  382.     mon.write(powerMade)
  383.   end
  384.  
  385.   --display turbine graphics/coils
  386.   for k,v in ipairs(turbinePagePeriph) do
  387.     local xCenter = (k * xInterval)
  388.     local turbineXStart = xCenter - 3
  389.     local coilColor
  390.     if v.getInductorEngaged() then
  391.       coilColor = colors.red
  392.     else
  393.       coilColor = colors.blue
  394.     end
  395.     mon.setBackgroundColor(colors.gray)
  396.     mon.setCursorPos(turbineXStart, 6)
  397.     mon.write("       ")
  398.     for i=7,13 do
  399.       mon.setCursorPos(turbineXStart, i)
  400.       mon.setBackgroundColor(colors.gray)
  401.       mon.write(" ")
  402.       mon.setBackgroundColor(colors.lightGray)
  403.       mon.write(" ")
  404.       if i % 2 == 0 then
  405.         mon.setBackgroundColor(colors.gray)
  406.       end
  407.       mon.write(" ")
  408.       mon.setBackgroundColor(colors.gray)
  409.       mon.write(" ")
  410.       if i % 2 ~= 0 then
  411.         mon.setBackgroundColor(colors.lightGray)
  412.       end
  413.       mon.write(" ")
  414.       mon.setBackgroundColor(colors.lightGray)
  415.       mon.write(" ")
  416.       mon.setBackgroundColor(colors.gray)
  417.       mon.write(" ")
  418.     end
  419.     for i=14,16 do
  420.       mon.setCursorPos(turbineXStart, i)
  421.       mon.setBackgroundColor(colors.gray)
  422.       mon.write(" ")
  423.       mon.setBackgroundColor(colors.lightGray)
  424.       mon.write(" ")
  425.       mon.setBackgroundColor(coilColor)
  426.       mon.write(" ")
  427.       mon.setBackgroundColor(colors.gray)
  428.       mon.write(" ")
  429.       mon.setBackgroundColor(coilColor)
  430.       mon.write(" ")
  431.       mon.setBackgroundColor(colors.lightGray)
  432.       mon.write(" ")
  433.       mon.setBackgroundColor(colors.gray)
  434.       mon.write(" ")
  435.     end
  436.   mon.setCursorPos(turbineXStart, 17)
  437.   mon.write("       ")
  438.   mon.setBackgroundColor(colors.black)
  439.   end
  440. end
  441.  
  442. --change Turbine page
  443. function turnPage(num)
  444.   turbinePage = num
  445.   mon.clear()
  446.   displayScreen()
  447. end
  448.  
  449. --show buttons to change pages
  450. function displayPageButtons(page)
  451.   button.clearTable()
  452.   mon.setTextColor(colors.black)
  453.   if page > 0 then
  454.     local param = page - 1
  455.     button.setTable("<<", turnPage, param, 1, 3, 10, 12)
  456.   end
  457.   if page < (pages-1) then
  458.     local param = page + 1
  459.     button.setTable(">>", turnPage, param, 48, 50, 10, 12)
  460.   end
  461.   button.screen()
  462. end
  463.  
  464. function displayScreen()
  465.   displayHeader()
  466.   displayTurbines(turbinePage)
  467.   if pages > 1 and lastPage ~= turbinePage then  --only refresh buttons if page changes
  468.     displayPageButtons(turbinePage)
  469.     lastPage = turbinePage
  470.   end
  471. end
  472.  
  473. displayScreen()
  474. os.startTimer(.1)
  475.  
  476. while true do
  477.   local event, param1, param2, param3 = os.pullEvent()
  478.  
  479.   --do timed events, restart timer
  480.   if event == "timer" then
  481.     os.startTimer(.1)
  482.     PID()
  483.     if not (table.monitor == nil) then
  484.       doTurbineLogic()
  485.     end
  486.     timerCounter = timerCounter + 1
  487.     --update display every half second
  488.     if timerCounter % 5 == 0 then
  489.       timerCounter = 0
  490.       displayScreen()
  491.     end
  492.   end
  493.  
  494.   --import data from Capacitor Relay Computer
  495.   if event == "rednet_message" then
  496.     table = textutils.unserialize(param2)
  497.   end
  498.  
  499.   --parse monitor clicks to API
  500.   if event == "monitor_touch" then
  501.     button.checkxy(param2, param3)
  502.   end
  503. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement