Advertisement
aidenmagrath

PID Reactor and Turbine Controller w/ Display - Big Reactors

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