Advertisement
Truga

controller.lua

May 30th, 2015
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.93 KB | None | 0 0
  1. -- This script controls Big Reactors and Big Turbines.
  2.  
  3. -- For passively cooled reactors, just run this script, no configuration neccessary.
  4.  
  5. -- For actively cooled reactors, set target turbine RPM. Optimal RPM is multiples of 900.
  6. local _targetRPM = 1800
  7.  
  8. -- Get the pid controller from:
  9. -- https://github.com/OpenPrograms/Kubuxu-Programs/blob/master/pid/pid.lua
  10. -- Put it in your libs.
  11.  
  12. -- Optionally, configure reactor and turbine PID. Defaults should be fine.
  13. local _reactorKp = 0.000005
  14. local _reactorKi = 0.000001
  15. local _reactorKd = 0.00005
  16.  
  17. local _turbineKp = 0.01
  18. local _turbineKi = 0.001
  19. local _turbineKd = 0.1
  20. -- Configuration ends here
  21.  
  22. -- Import basic opencomputers libs
  23. local term = require("term")
  24. term.clear()
  25. term.setCursorBlink(false)
  26. local component = require("component")
  27. local keyboard = require("keyboard")
  28. local computer = require("computer")
  29. local pid = require("pid")
  30.  
  31. -- Constants
  32. local _reactorMaxEnergy = 10000000
  33. local _turbineMaxEnergy = 1000000
  34. local _turbineSteamMax = 2000
  35.  
  36. -- Check if component is active
  37. local function checkComponent(componentType)
  38.   if component.isAvailable(componentType) then
  39.     return true
  40.   else
  41.     return false
  42.   end
  43. end
  44.  
  45. -- Get components, return them as a table
  46. local function getComponents(componentType)
  47.   local components = {}
  48.   local i = 0
  49.   for componentAddress, componentName in component.list() do
  50.     if componentName == componentType then
  51.       i = i + 1
  52.       components[i] = component.proxy(componentAddress)
  53.     end
  54.   end
  55.   return components
  56. end
  57.  
  58. -- Get PIDs, return them as a table
  59. local function getControllers(n)
  60.   local controller = {}
  61.   for i=1,n do
  62.     controller[i] = pid:new()
  63.   end
  64.   return controller
  65. end
  66.  
  67. -- Start main program
  68. local function main()
  69.   -- Check for present reactors
  70.   if not checkComponent("br_reactor") then
  71.     error("ERROR: No reactor connected!")
  72.   end
  73.   -- Populate reactors, reactor controllers
  74.   local reactor = getComponents("br_reactor")
  75.   local numReactors = #reactor
  76.   local reactorCtrl = getControllers(numReactors)
  77.   -- Iterate through reactors, check for any actively cooled reactors, configure PID values
  78.   local activelyCooled = false
  79.   for i=1,numReactors do
  80.     if reactor[i].isActivelyCooled() then
  81.       activelyCooled = true
  82.       reactorCtrl[i].target = math.floor(reactor[i].getCoolantAmountMax()/2)
  83.       reactorCtrl[i].minout = 0
  84.       reactorCtrl[i].maxout = 100
  85.  
  86.       reactorCtrl[i].kp = _reactorKp
  87.       reactorCtrl[i].ki = _reactorKi
  88.       reactorCtrl[i].kd = _reactorKd
  89.     end
  90.   end
  91.   -- Turbine vars
  92.   local turbine = {}
  93.   local numTurbines = 0
  94.   local turbineCtrl = {}
  95.   -- If an actively cooled reactor is present, check for turbines
  96.   if activelyCooled then
  97.     if not checkComponent("br_turbine") then
  98.       error("ERROR: Reactor is actively cooled, but no turbine detected!")
  99.     end
  100.     -- Populate turbines
  101.     turbine = getComponents("br_turbine")
  102.     numTurbines = #turbine
  103.     turbineCtrl = getControllers(numTurbines)
  104.     -- Configure turbine controllers
  105.     for i=1,numTurbines do
  106.       turbineCtrl[i].target = _targetRPM
  107.       turbineCtrl[i].minout = 0
  108.       turbineCtrl[i].maxout = _turbineSteamMax
  109.  
  110.       turbineCtrl[i].kp = _turbineKp
  111.       turbineCtrl[i].ki = _turbineKi
  112.       turbineCtrl[i].kd = _turbineKd
  113.     end
  114.   end
  115.   -- This variable is needed for rotating the display
  116.   local display = 1
  117.   while true do
  118.   -- Main loop begins here
  119.     -- Variables we'll need later
  120.     local energyRate = 0
  121.     local fuelRate = 0
  122.     local steamRate = 0
  123.     for i=1,numReactors do
  124.     -- Iterate through reactors
  125.       if not reactor[i].isActivelyCooled() then
  126.       -- Reactor is passively cooled, this is super simple. Rod insertion equals stored energy.
  127.         local storedPercent = math.floor((reactor[i].getEnergyStored()/_reactorMaxEnergy)*100)
  128.         reactor[i].setAllControlRodLevels(storedPercent)
  129.         energyRate = energyRate + reactor[i].getEnergyProducedLastTick()
  130.         fuelRate = fuelRate + reactor[i].getFuelConsumedLastTick()
  131.       else
  132.       -- Reactor is actively cooled. Use the PID controller to keep steam at about half full
  133.         reactorCtrl[i].input = reactor[i].getHotFluidAmount()
  134.         reactorCtrl[i]:compute()
  135.         if reactorCtrl[i].output then
  136.           reactor[i].setAllControlRodLevels(100-math.floor(reactorCtrl[i].output))
  137.         end
  138.         steamRate = steamRate + reactor[i].getHotFluidProducedLastTick()
  139.       end
  140.     end
  141.     -- Turbine specific variables
  142.     local energyRatePerTurbine = {}
  143.     local dutyCyclePerTurbine = {}
  144.     local dutyCycle = 0
  145.     if activelyCooled then
  146.     -- Run if the reactor is actively cooled
  147.       for tick=1,20 do
  148.       -- PWM routine runs once per tick per turbine.
  149.         for i=1,numTurbines do
  150.           if not dutyCyclePerTurbine[i] then
  151.             dutyCyclePerTurbine[i] = 0
  152.             energyRatePerTurbine[i] = 0
  153.           end
  154.           local storedPercent = math.floor((turbine[i].getEnergyStored()/_turbineMaxEnergy)*100)
  155.           -- Power stored shouldn't go over 80%, so RF (and thus fuel) doesn't get wasted.
  156.           if storedPercent > 80 then
  157.             turbine[i].setInductorEngaged(false)
  158.           else
  159.             turbine[i].setInductorEngaged(true)
  160.             dutyCyclePerTurbine[i] = dutyCyclePerTurbine[i] + 5
  161.             dutyCycle = dutyCycle+5
  162.           end
  163.           energyRatePerTurbine[i] = energyRatePerTurbine[i] + turbine[i].getEnergyProducedLastTick()
  164.         end
  165.         os.sleep(0.01)
  166.       end
  167.  
  168.       -- Turbine RPM is controlled and kept at _targetRPM by the PID controller
  169.       for i=1,numTurbines do
  170.         local RPM = turbine[i].getRotorSpeed()
  171.         if RPM > 1820 then
  172.           -- Cut steam if overspeeding
  173.           -- This shouldn't really happen, but safety first, right?
  174.           turbine[i].setFluidFlowRateMax(0)
  175.           turbineCtrl[i].input = RPM
  176.           turbineCtrl[i]:compute()
  177.         else
  178.           -- Get the power setting from the PID controller
  179.           turbineCtrl[i].input = RPM
  180.           turbineCtrl[i]:compute()
  181.           if turbineCtrl[i].output then
  182.             turbine[i].setFluidFlowRateMax(math.floor(turbineCtrl[i].output))
  183.           end
  184.         end
  185.       energyRate = energyRate + energyRatePerTurbine[i]/20
  186.       end
  187.     end
  188.  
  189.     os.sleep(1)
  190.  
  191.     RFpermB = math.floor(energyRate/1000/fuelRate)
  192.     dutyCycle = math.floor(dutyCycle/numTurbines)
  193.     term.clear()
  194.     print([[
  195.    
  196.    
  197.     Total power output: ]] .. energyRate .. [[ RF/t
  198.     Fuel efficiency: ]] .. RFpermB .. " MRF/ingot")
  199.     if activelyCooled then
  200.     print("    Duty cycle:" .. dutyCycle .. "%")
  201.     end
  202.     -- View changes with space
  203.     if keyboard.isKeyDown(keyboard.keys.space) then
  204.       display = display + 1
  205.       if display > numReactors+numTurbines then
  206.         display = 1
  207.       end
  208.     end
  209.     if display <= numReactors then
  210.       local i = display
  211.       local coreTemp = math.floor(reactor[i].getFuelTemperature())
  212.       local energyRate = math.floor(reactor[i].getEnergyProducedLastTick())
  213.       if not reactor[i].isActivelyCooled() then
  214.         local storedPercent = math.floor((reactor[i].getEnergyStored()/_reactorMaxEnergy)*100)
  215.         print([[
  216.   Reactor ]] .. i .. [[
  217.     Core temperature: ]] .. coreTemp .. [[
  218.     Energy rate: ]] .. energyRate .. [[ RF/t
  219.     Reactor power: ]] .. 100 - storedPercent .. "%")
  220.       else
  221.         local steamPercent = math.floor((reactor[i].getHotFluidAmount()/reactor[i].getHotFluidAmountMax())*100)
  222.         local rodPercent = reactor[i].getControlRodLevel(0)
  223.         print([[
  224.   Reactor ]] .. i .. [[
  225.     Core temperature: ]] .. coreTemp .. [[
  226.     Steam rate: ]] .. energyRate .. [[ mB/t
  227.     Steam percent: ]] .. steamPercent .. [[%
  228.     Reactor power: ]] .. 100 - rodPercent .. "%")
  229.       end
  230.     elseif numTurbines > 0 then
  231.       local i = display - numReactors
  232.       local RPM = turbine[i].getRotorSpeed()
  233.       local steamRate = math.floor(turbineCtrl[i].output)
  234.       local dutyCycle = dutyCyclePerTurbine[i]/20*100
  235.       local energyRate = energyRatePerTurbine[i]/20
  236.         print([[
  237.   Turbine ]] .. i .. string.format([[
  238.     RPM: %.4f]], RPM) ..  [[
  239.     Duty cycle: ]] .. dutyCycle .. [[%
  240.     Energy rate: ]] .. energyRate .. [[ RF/t
  241.     Steam rate: ]] .. steamRate .. [[ mB/t
  242.     ]])
  243.     end
  244.    
  245.     if keyboard.isKeyDown(keyboard.keys.q) and keyboard.isControlDown() then
  246.       for i=1,numReactors do
  247.         reactor[i].setActive(false)
  248.         print("\nReactor " .. i .. " shutting down.\n")
  249.         os.sleep(3)
  250.         term.clear()
  251.         term.setCursorBlink(true)
  252.         os.exit()
  253.       end
  254.     end
  255.  
  256.     if (computer.energy() < 100) then
  257.       for i=1,numReactors do
  258.         reactor[i].setActive(false)
  259.         print("\nEnergy level critical\nShutting down reactor " .. i)
  260.         os.sleep(3)
  261.         computer.shutdown()
  262.       end
  263.     end
  264.     if not activelyCooled then
  265.       sleep(1)
  266.     end
  267.   end
  268. end
  269.  
  270. main()
  271. term.clear()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement