Advertisement
Hastwell

NUKE-DC, pre 1.6CC, HighBlockLag tolerant

Nov 11th, 2014
187
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 95.70 KB | None | 0 0
  1. --[[
  2. I AM NOT THE ORIGINAL AUTHOR OF THIS PROGRAM. ALL I DID WAS PATCH IT FOR FTB MONSTER and it's old CC version.
  3. Program name: Lolmer's EZ-NUKE reactor control system
  4. Version: v0.3.13 -pre16-0.2
  5. Programmer: Lolmer
  6. Great assistance by Mechaet
  7. Pre-CC 1.6 patch by Hastwell
  8. Last update: 2014-09-24
  9. Pastebin (original unadulterated version): http://pastebin.com/fguScPBQ
  10. Pastebin (patched version): http://pastebin.com/vQy0Bbaz
  11.  
  12. Description:
  13. This program controls a Big Reactors nuclear reactor in Minecraft with a Computercraft computer, using Computercraft's own wired modem connected to the reactors computer control port.
  14.  
  15. This program was designed to work with the mods and versions installed on Never Stop Toasting (NST) Diet http://www.technicpack.net/modpack/details/never-stop-toasting-diet.254882 Endeavour: Never Stop Toasting: Diet official Minecraft server http://forums.somethingawful.com/showthread.php?threadid=3603757
  16.  
  17. To simplify the code and guesswork, I assume the following monitor layout, where each "monitor" listed below is a collection of 3 wide by two high Advanced Monitors:
  18. 1) One Advanced Monitor for overall status display plus
  19.     one or more Reactors plus
  20.     none or more Turbines.
  21. 2) One Advanced Monitor for overall status display plus (furthest monitor from computer by cable length)
  22.     one Advanced Monitor for each connected Reactor plus (subsequent found monitors)
  23.     one Advanced Monitor for each connected Turbine (last group of monitors found).
  24. If you enable debug mode, add one additional Advanced Monitor for #1 or #2.
  25.  
  26. Notes:
  27.     Only one reactor and one, two, and three turbines have been tested with the above, but IN THEORY any number is supported.
  28.     Devices are found in the reverse order they are plugged in, so monitor_10 will be found before monitor_9.
  29.     Two 15x15x14 Turbines can output 260K RF/t by just one 7^3 (four rods) reactor putting out 4k mB steam.
  30.  
  31. When using actively cooled reactors with turbines, keep the following in mind:
  32.     - 1 mB steam carries up to 10RF of potential energy to extract in a turbine.
  33.     - Actively cooled reactors produce steam, not power.
  34.     - You will need about 10 mB of water for each 1 mB of steam that you want to create in a 7^3 reactor.
  35.  
  36. Features:
  37.     Configurable min/max energy buffer and min/max temperature via ReactorOptions file.
  38.     ReactorOptions is read on start and then current values are saved every program cycle.
  39.     Rod Control value in ReactorOptions is only useful for initial start, after that the program saves the current Rod Control average over all Fuel Rods for next boot.
  40.     Auto-adjusts control rods per reactor to maintain temperature.
  41.     Will display reactor data to all attached monitors of correct dimensions.
  42.         For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
  43.     For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
  44.     A new cruise mode from mechaet, ONLINE will be "blue" when active, to keep your actively cooled reactors running smoothly.
  45.  
  46. GUI Usage:
  47.     The "<" and ">" buttons, when right-clicked with the mouse, will decrease and increase, respectively, the values assigned to the monitor:
  48.         "Rod (%)" will lower/raise the Reactor Control Rods for that Reactor
  49.         "mB/t" will lower/raise the Turbine Flow Rate maximum for that Turbine
  50.         "RPM" will lower/raise the target Turbine RPM for that Turbine
  51.     Right-clicking between the "<" and ">" (not on them) will disable auto-adjust of that value for attached device.
  52.         Right-clicking on the "Enabled" or "Disabled" text for auto-adjust will do the same.
  53.     Right-clicking on "ONLINE" or "OFFLINE" at the top-right will toggle the state of attached device.
  54.  
  55. Default values:
  56.     Rod Control: 90% (Let's start off safe and then power up as we can)
  57.     Minimum Energy Buffer: 15% (will power on below this value)
  58.     Maximum Energy Buffer: 85% (will power off above this value)
  59.     Minimum Passive Cooling Temperature: 950^C (will raise control rods below this value)
  60.     Maximum Passive Cooling Temperature: 1,400^C (will lower control rods above this value)
  61.     Minimum Active Cooling Temperature: 300^C (will raise the control rods below this value)
  62.     Maximum Active Cooling Temperature: 420^C (will lower control rods above this value)
  63.     Optimal Turbine RPM:  900, 1,800, or 2,700 (divisible by 900)
  64.     New user-controlled option for target speed of turbines, defaults to 2726RPM, which is high-optimal.
  65.  
  66. Requirements:
  67.     Advanced Monitor size is X: 29, Y: 12 with a 3x2 size
  68.     Computer or Advanced Computer
  69.     Modems (not wireless) connecting each of the Computer to both the Advanced Monitor and Reactor Computer Port.
  70.     Big Reactors (http://www.big-reactors.com/) 0.3.2A+
  71.     Computercraft (http://computercraft.info/) 1.63+
  72.     Reset the computer any time number of connected devices change.
  73.  
  74. Resources:
  75. This script is available from:
  76.     http://pastebin.com/fguScPBQ
  77.     https://github.com/sandalle/minecraft_bigreactor_control
  78. Start-up script is available from:
  79.     http://pastebin.com/ZTMzRLez
  80.     https://github.com/sandalle/minecraft_bigreactor_control
  81. Other reactor control program which I based my program on:
  82.     http://pastebin.com/aMAu4X5J (ScatmanJohn)
  83.     http://pastebin.com/HjUVNDau (version ScatmanJohn based his on)
  84. A simpler Big Reactor control program is available from:
  85.     http://pastebin.com/7S5xCvgL (IronClaymore only for passively cooled reactors)
  86.  
  87.     Reactor Computer Port API: http://wiki.technicpack.net/Reactor_Computer_Port
  88.     Computercraft API: http://computercraft.info/wiki/Category:APIs
  89.     Big Reactors Efficiency, Speculation and Questions! http://www.reddit.com/r/feedthebeast/comments/1vzds0/big_reactors_efficiency_speculation_and_questions/
  90.     Big Reactors API code: https://github.com/erogenousbeef/BigReactors/blob/master/erogenousbeef/bigreactors/common/multiblock/tileentity/TileEntityReactorComputerPort.java
  91.     Big Reactors API: http://big-reactors.com/cc_api.html
  92.  
  93. ChangeLog:
  94. - 0.3.13
  95.     - Fix one reactor and one monitor incorrectly using status display instead of control display (Issue #35)
  96.     - Fix concatenating a string and boolean, see http://stackoverflow.com/questions/6615572/how-to-format-a-lua-string-with-a-boolean-variable
  97.     - Hopefully fix concatenating string and nul in printLog (Issue #3)
  98.  
  99. - 0.3.12
  100.     - Mechaet's changes:
  101.     - Redid some typing to correct a bug where the reactors always started with rod control disabled.
  102.  
  103. - 0.3.11
  104.     - Mechaet's changes:
  105.     - Cleaned up global variables list
  106.     - Added in per-device naming (displays a friendly name on the bottom of the monitor if configured in the device options file)
  107.     - Bigger bypasses of control routines when the control has been overridden
  108.     - Individual config files for turbines and reactors. Persistent between reboots, remembers your last saved settings.
  109.     - Cruise mode override bypass
  110.     - Changing flow rate no longer toggles flow rate override on and off. Changing the flow rate clearly indicates intent, so we put the override flag on and leave it there.
  111.     - Changed the rate at which the regular algorithm adjusts reactor rod control rates. Instead of being 1:1 we now move at 1:5 speed because there is a wide loophole where big adjustments can cause a swinging pendulum effect continually missing the target.
  112.  
  113. - 0.3.10
  114.     - Turbine algorithm pass by Mechaet.
  115.     - Updated turbine GUI.
  116.     - Fix single monitor (again) for Issue #22.
  117.  
  118. - 0.3.9
  119.     - Reactor algorithm pass by Mechaet.
  120.     - Additional user config options by Mechaet.
  121.     - Fix multiple reactors and none or more turbines with only one status monitor.
  122.     - Fix monitor scaling after one was used as debug (or in case of other modifications).
  123.     - Cruise mode implemented, defaults off but is saved between boots.
  124.     - Fix energy/% displays to match Big Reactors' GUI (Issue #9).
  125.     - Always write out found devices on computer terminal.
  126.     - Much improved round() function from mechaet (Issue #14).
  127.     - Refactoring pass/algorithm change on the reactor temperature control. Should now adjust in increments to achieve the desired temperature range quicker and more accurately.
  128.     - Optimal passive-cooled reactor temperature range changed from 850-900 to 950-1400.
  129.     - Fix display Issue #15.
  130.  
  131. - 0.3.8
  132.     - Update to ComputerCraft 1.6 API (only term.restore() -> term.native() required :)).
  133.  
  134. - 0.3.7
  135.     - Fix typo when initializing TurbineNames array.
  136.     - Fix Issue #1, turbine display is using the Reactor buffer size (10M RF) instead of the Turbine buffer size (1M RF).
  137.  
  138. - 0.3.6
  139.     - Fix multi-reactors displaying on the correct monitors (thanks HybridFusion).
  140.     - Fix rod auto-adjust text position.
  141.     - Reactors store 10M RF and Turbines store 1M RF in their buffer.
  142.     - Add more colour to displayAllStatus().
  143.     - Sleep for only two seconds instead of five.
  144.     - Fix getDeviceStoredEnergyBufferPercent() for Reactors storing 10M RF in buffer.
  145.     - Keep actively cooled reactors between 0-300^C (non-configurable for now).
  146.  
  147. - 0.3.5
  148.     - Do not discover connected devices every loop - nicer on servers. Reset computer anytime number of connected devices change.
  149.     - Fix multi-reactor setups to display the additional reactors on monitors, rather than the last one found.
  150.     - Fix passive reactor display having auto-adjust and energy buffer overwrite each other (removes rod count).
  151.  
  152. - 0.3.4
  153.     - Fix arithmetic for checking if we have enough monitors for the number of reactors.
  154.     - Turbines are optimal at 900, 1800, *and* 2700 RPM
  155.     - Increase loop timer from 1 to 5 to be nicer to servers
  156.  
  157. - 0.3.3
  158.     - Add Big Reactors Turbine support.
  159.     - First found monitor (appears to be last connected monitor) is used to display status of all found devices (if more than one valid monitor is found)
  160.     - Display monitor number on top left of each monitor as "M#" to help find which monitor is which.
  161.     - Enabling debug will use the last monitor found, if more than one, to print out debug info (also written to file)
  162.     - Only clear monitors when we're about to use them (e.g. turbine monitors no longer clear, then wait for all reactors to update)
  163.     - Fix getDeviceStoredEnergyBufferPercent(), was off by a decimal place
  164.     - Just use first Control Rod level for entire reactor, they are no longer treated individually in BR 0.3
  165.     - Allow for one monitor for n number of reactors and m number of turbines
  166.     - Auto-adjust turbine flow rate by 25 mB to keep rotor speed at 900 or 1,800 RPM.
  167.     - Clicks on monitors relate to what the monitor is showing (e.g. clicking on reactor 1's display won't modify turbine 1's nor reactor 2's values)
  168.     - Print monitor name and device (reactor|turbine) name in blue to monitor associated for easier design by users.
  169.     - Remove version number from monitors to free up space for monitor names.
  170.     - Add option of right-clicking on "Enabled"/"Disabled" of auto-adjust to toggle it.
  171.  
  172. - 0.3.2
  173.     - Allow for rod control to override (disable) auto-adjust via UI (Rhonyn)
  174.  
  175. - 0.3.1
  176.     - Add fuel consumption per tick to display
  177.  
  178. - 0.3.0
  179.     - Add multi-monitor support! Sends one reactor's data to all monitors.
  180.     - print function now takes table to support optional specified monitor
  181.     - Set "numRods" every cycle for some people (mechaet)
  182.     - Don't redirect terminal output with multiple monitor support
  183.     - Log troubleshooting data to reactorcontrol.log
  184.     - FC_API no longer used (copied and modified what I needed)
  185.     - Multi-reactor support is theoretically implemented, but it is UNTESTED!
  186.     - Updated for Big Reactor 0.3 (no longer works with 0.2)
  187.     - BR getFuelTemperature() now returns many significant digits, just use math.ceil()
  188.     - BR 0.3 removed individual rod temperatures, now it's only reactor-level temperature
  189.  
  190. - 0.2.4
  191.     - Simplify math, don't divide by a simple large number and then multiply by 100 (#/10000000*100)
  192.     - Fix direct-connected (no modem) devices. getDeviceSide -> FC_API.getDeviceSide (simple as that :))
  193.  
  194. - 0.2.3
  195.     - Check bounds on reactor.setRodControlLevel(#,#), Big Reactor doesn't check for us.
  196.  
  197. - 0.2.2
  198.     - Do not auto-start the reactor if it was manually powered off (autoStart=false)
  199.  
  200. - 0.2.1
  201.     - Lower/raise only the hottest/coldest Control Rod while trying to control the reactor temperature.
  202.     - "<" Rod Control buttons was off by one (to the left)
  203.  
  204. - 0.2.0 - Lolmer Edition :)
  205.     - Add min/max stored energy percentage (default is 15%/85%), configurable via ReactorOptions file.
  206.     - No reason to keep burning fuel if our power output is going nowhere. :)
  207.     - Use variables variable for the title and version.
  208.     - Try to keep the temperature between configured values (default is 850^C-950^C)
  209.     - Add Waste and number of Control/Fuel Rods to displayBards()
  210.  
  211. TODO:
  212. - Save parameters per reactor instead of one global set for all reactors.
  213. - Add min/max RF/t output and have it override temperature concerns (maybe?).
  214. - Add support for wireless modems, see http://computercraft.info/wiki/Modem_%28API%29, will not be secure (anyone can send/listen to your channels)!
  215. - Add support for any sized monitor (minimum 3x3), dynamic allocation/alignment.
  216. - Lookup using pcall for better error handling http://www.computercraft.info/forums2/index.php?/topic/10992-using-pcall/ .
  217. - Update cruise mode to work independently for each actively-cooled reactor.
  218.  
  219. ]]--
  220.  
  221. -- allow chunks to load around us first
  222. --print("Sleeping for 15 secs...")
  223. --sleep(15.0)
  224.  
  225.  
  226. -- Some global variables
  227. -- NUKE-DC is based on the EZNUKE 0.3.13 train
  228. -- local progVer = "0.3.13-p6"
  229. local progVer = "0.1-HBLT"
  230. local progName = "NUKE-DC"
  231. local sideClick, xClick, yClick = nil, 0, 0
  232. local loopTime = 2
  233. local controlRodAdjustAmount = 1 -- Default Reactor Rod Control % adjustment amount
  234. local flowRateAdjustAmount = 25 -- Default Turbine Flow Rate in mB adjustment amount
  235. local debugMode = false
  236. -- End multi-reactor cleanup section
  237. local minStoredEnergyPercent = nil -- Max energy % to store before activate
  238. local maxStoredEnergyPercent = nil -- Max energy % to store before shutdown
  239. local monitorList = {} -- Empty monitor array
  240. local monitorNames = {} -- Empty array of monitor names
  241. local reactorList = {} -- Empty reactor array
  242. local reactorNames = {} -- Empty array of reactor names
  243. local turbineList = {} -- Empty turbine array
  244. local turbineNames = {} -- Empty array of turbine names
  245. local turbineMonitorOffset = 0 -- Turbines are assigned monitors after reactors
  246.  
  247. -- Hastwell's L33T Capbank Extended Buffer Graft [CEBG]
  248. local capBankList = {}
  249. -- local capBankMaxCapacity = 0
  250.  
  251.  
  252. term.clear()
  253. term.setCursorPos(2,1)
  254. write("Initializing program...\n")
  255.  
  256.  
  257. -- File needs to exist for append "a" later and zero it out if it already exists
  258. -- Always initalize this file to avoid confusion with old files and the latest run
  259. local logFile = fs.open("reactorcontrol.log", "w")
  260. if logFile then
  261.     logFile.writeLine("Minecraft time: Day "..os.day().." at "..textutils.formatTime(os.time(),true))
  262.     logFile.close()
  263. else
  264.     error("Could not open file reactorcontrol.log for writing.")
  265. end
  266.  
  267.  
  268. -- Helper functions
  269.  
  270. local function termNative()
  271.     -- term.native()
  272.     term.restore()
  273. end
  274.  
  275.  
  276. local function printLog(printStr)
  277.     if debugMode then
  278.         -- If multiple monitors, use the last monitor for debugging if debug is enabled
  279.         if #monitorList > 1 then
  280.             term.redirect(monitorList[#monitorList]) -- Redirect to last monitor for debugging
  281.             monitorList[#monitorList].setTextScale(0.5) -- Fit more logs on screen
  282.             write(printStr.."\n")   -- May need to use term.scroll(x) if we output too much, not sure
  283.             termNative()
  284.         end -- if #monitorList > 1 then
  285.  
  286.         local logFile = fs.open("reactorcontrol.log", "a") -- See http://computercraft.info/wiki/Fs.open
  287.         if logFile then
  288.             logFile.writeLine(printStr)
  289.             logFile.close()
  290.         else
  291.             error("Cannot open file reactorcontrol.log for appending!")
  292.         end -- if logFile then
  293.     end -- if debugMode then
  294. end -- function printLog(printStr)
  295.  
  296. -- Trim a string
  297. function stringTrim(s)
  298.     assert(s ~= nil, "String can't be nil")
  299.     return(string.gsub(s, "^%s*(.-)%s*$", "%1"))
  300. end
  301.  
  302. config = {}
  303.  
  304. -- Save a table into a config file
  305. -- path: path of the file to write
  306. -- tab: table to save
  307. config.save = function(path, tab)
  308.     printLog("Save function called for config for "..path.." EOL")
  309.     assert(path ~= nil, "Path can't be nil")
  310.     assert(type(tab) == "table", "Second parameter must be a table")
  311.     local f = io.open(path, "w")
  312.     local i = 0
  313.     for key, value in pairs(tab) do
  314.         if i ~= 0 then
  315.             f:write("\n")
  316.         end
  317.         f:write("["..key.."]".."\n")
  318.         for key2, value2 in pairs(tab[key]) do
  319.             key2 = stringTrim(key2)
  320.             --doesn't like boolean values
  321.             if (type(value2) ~= "boolean") then
  322.             value2 = stringTrim(value2)
  323.             else
  324.             value2 = tostring(value2)
  325.             end
  326.             key2 = key2:gsub(";", "\\;")
  327.             key2 = key2:gsub("=", "\\=")
  328.             value2 = value2:gsub(";", "\\;")
  329.             value2 = value2:gsub("=", "\\=")   
  330.             f:write(key2.."="..value2.."\n")
  331.         end
  332.         i = i + 1
  333.     end
  334.     f:close()
  335. end --config.save = function(path, tab)
  336.  
  337. -- Load a config file
  338. -- path: path of the file to read
  339. config.load = function(path)
  340.     printLog("Load function called for config for "..path.." EOL")
  341.     assert(path ~= nil, "Path can't be nil")
  342.     local f = fs.open(path, "r")
  343.     if f ~= nil then
  344.         local tab = {}
  345.         local line = ""
  346.         local newLine
  347.         local i
  348.         local currentTag = nil
  349.         local found = false
  350.         local pos = 0
  351.         while line ~= nil do
  352.             found = false      
  353.             line = line:gsub("\\;", "#_!36!_#") -- to keep \;
  354.             line = line:gsub("\\=", "#_!71!_#") -- to keep \=
  355.             if line ~= "" then
  356.                 -- Delete comments
  357.                 newLine = line
  358.                 line = ""
  359.                 for i=1, string.len(newLine) do            
  360.                     if string.sub(newLine, i, i) ~= ";" then
  361.                         line = line..newLine:sub(i, i)                     
  362.                     else               
  363.                         break
  364.                     end
  365.                 end
  366.                 line = stringTrim(line)
  367.                 -- Find tag        
  368.                 if line:sub(1, 1) == "[" and line:sub(line:len(), line:len()) == "]" then
  369.                     currentTag = stringTrim(line:sub(2, line:len()-1))
  370.                     tab[currentTag] = {}
  371.                     found = true                           
  372.                 end
  373.                 -- Find key and values
  374.                 if not found and line ~= "" then               
  375.                     pos = line:find("=")               
  376.                     if pos == nil then
  377.                         error("Bad INI file structure")
  378.                     end
  379.                     line = line:gsub("#_!36!_#", ";")
  380.                     line = line:gsub("#_!71!_#", "=")
  381.                     tab[currentTag][stringTrim(line:sub(1, pos-1))] = stringTrim(line:sub(pos+1, line:len()))
  382.                     found = true           
  383.                 end        
  384.             end
  385.             line = f.readLine()
  386.         end
  387.        
  388.         f:close()
  389.        
  390.         return tab
  391.     else
  392.         return nil
  393.     end
  394. end --config.load = function(path)
  395.  
  396.  
  397.  
  398. -- round() function from mechaet
  399. local function round(num, places)
  400.     local mult = 10^places
  401.     local addon = nil
  402.     if ((num * mult) < 0) then
  403.         addon = -.5
  404.     else
  405.         addon = .5
  406.     end
  407.  
  408.     local integer, decimal = math.modf(num*mult+addon)
  409.     newNum = integer/mult
  410.     printLog("Called round(num="..num..",places="..places..") returns \""..newNum.."\".")
  411.     return newNum
  412. end -- function round(num, places)
  413.  
  414.  
  415. local function print(printParams)
  416.     -- Default to xPos=1, yPos=1, and first monitor
  417.     setmetatable(printParams,{__index={xPos=1, yPos=1, monitorIndex=1}})
  418.     local printString, xPos, yPos, monitorIndex =
  419.         printParams[1], -- Required parameter
  420.         printParams[2] or printParams.xPos,
  421.         printParams[3] or printParams.yPos,
  422.         printParams[4] or printParams.monitorIndex
  423.  
  424.     local monitor = nil
  425.     monitor = monitorList[monitorIndex]
  426.  
  427.     if not monitor then
  428.         printLog("monitor["..monitorIndex.."] in print() is NOT a valid monitor.")
  429.         return -- Invalid monitorIndex
  430.     end
  431.  
  432.     monitor.setCursorPos(xPos, yPos)
  433.     monitor.write(printString)
  434. end -- function print(printParams)
  435.  
  436.  
  437. -- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
  438. local function printCentered(printString, yPos, monitorIndex)
  439.     local monitor = nil
  440.     monitor = monitorList[monitorIndex]
  441.  
  442.     if not monitor then
  443.         printLog("monitor["..monitorIndex.."] in printCentered() is NOT a valid monitor.")
  444.         return -- Invalid monitorIndex
  445.     end
  446.  
  447.     local width, height = monitor.getSize()
  448.     local monitorNameLength = 0
  449.  
  450.     -- Special changes for title bar
  451.     if yPos == 1 then
  452.         -- Add monitor name to first line
  453.         monitorNameLength = monitorNames[monitorIndex]:len()
  454.  
  455.         -- Leave room for "offline" and "online" on the right except for overall status display
  456.         if (#monitorList ~= 1) and (monitorIndex ~= 1) then
  457.             width = width - 7
  458.         end
  459.     end
  460.  
  461.     monitor.setCursorPos(math.floor(width/2) - math.ceil(printString:len()/2) +  monitorNameLength/2, yPos)
  462.     monitor.clearLine()
  463.     monitor.write(printString)
  464.  
  465.     monitor.setTextColor(colors.blue)
  466.     print{monitorNames[monitorIndex], 1, 1, monitorIndex}
  467.     monitor.setTextColor(colors.white)
  468. end -- function printCentered(printString, yPos, monitorIndex)
  469.  
  470.  
  471. -- Print text padded from the left side
  472. -- Clear the left side of the screen
  473. local function printLeft(printString, yPos, monitorIndex)
  474.     local monitor = nil
  475.     monitor = monitorList[monitorIndex]
  476.  
  477.     if not monitor then
  478.         printLog("monitor["..monitorIndex.."] in printLeft() is NOT a valid monitor.")
  479.         return -- Invalid monitorIndex
  480.     end
  481.  
  482.     local gap = 1
  483.     local width = monitor.getSize()
  484.  
  485.     -- Clear left-half of the monitor
  486.  
  487.     for curXPos = 1, (width / 2) do
  488.         monitor.setCursorPos(curXPos, yPos)
  489.         monitor.write(" ")
  490.     end
  491.  
  492.     -- Write our string left-aligned
  493.     monitor.setCursorPos(1+gap, yPos)
  494.     monitor.write(printString)
  495. end
  496.  
  497.  
  498. -- Print text padded from the right side
  499. -- Clear the right side of the screen
  500. local function printRight(printString, yPos, monitorIndex)
  501.     local monitor = nil
  502.     monitor = monitorList[monitorIndex]
  503.  
  504.     if not monitor then
  505.         printLog("monitor["..monitorIndex.."] in printRight() is NOT a valid monitor.")
  506.         return -- Invalid monitorIndex
  507.     end
  508.  
  509.     -- Make sure printString is a string
  510.     printString = tostring(printString)
  511.  
  512.     local gap = 1
  513.     local width = monitor.getSize()
  514.  
  515.     -- Clear right-half of the monitor
  516.     for curXPos = (width/2), width do
  517.         monitor.setCursorPos(curXPos, yPos)
  518.         monitor.write(" ")
  519.     end
  520.  
  521.     -- Write our string right-aligned
  522.     monitor.setCursorPos(math.floor(width) - math.ceil(printString:len()+gap), yPos)
  523.     monitor.write(printString)
  524. end
  525.  
  526.  
  527. -- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
  528. local function clearMonitor(printString, monitorIndex)
  529.     local monitor = nil
  530.     monitor = monitorList[monitorIndex]
  531.  
  532.     printLog("Called as clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..").")
  533.  
  534.     if not monitor then
  535.         printLog("monitor["..monitorIndex.."] in clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  536.         return -- Invalid monitorIndex
  537.     end
  538.  
  539.     local gap = 2
  540.     monitor.clear()
  541.     local width, height = monitor.getSize()
  542.     monitor.setTextScale(1.0) -- Make sure scale is correct
  543.  
  544.     printCentered(printString, 1, monitorIndex)
  545.     monitor.setTextColor(colors.blue)
  546.     print{monitorNames[monitorIndex], 1, 1, monitorIndex}
  547.     monitor.setTextColor(colors.white)
  548.  
  549.     for i=1, width do
  550.         monitor.setCursorPos(i, gap)
  551.         monitor.write("-")
  552.     end
  553.  
  554.     monitor.setCursorPos(1, gap+1)
  555. end -- function clearMonitor(printString, monitorIndex)
  556.  
  557.  
  558. -- Return a list of all connected (including via wired modems) devices of "deviceType"
  559. local function getDevices(deviceType)
  560.     printLog("Called as getDevices(deviceType="..deviceType..")")
  561.  
  562.     local deviceName = nil
  563.     local deviceIndex = 1
  564.     local deviceList, deviceNames = {}, {} -- Empty array, which grows as we need
  565.     local peripheralList = peripheral.getNames() -- Get table of connected peripherals
  566.  
  567.     deviceType = deviceType:lower() -- Make sure we're matching case here
  568.  
  569.     for peripheralIndex = 1, #peripheralList do
  570.         -- Log every device found
  571.         -- printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] attached as \""..peripheralList[peripheralIndex].."\".")
  572.         if (string.lower(peripheral.getType(peripheralList[peripheralIndex])) == deviceType) then
  573.             -- Log devices found which match deviceType and which device index we give them
  574.             printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".")
  575.             write("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".\n")
  576.             deviceNames[deviceIndex] = peripheralList[peripheralIndex]
  577.             deviceList[deviceIndex] = peripheral.wrap(peripheralList[peripheralIndex])
  578.             deviceIndex = deviceIndex + 1
  579.         end
  580.     end -- for peripheralIndex = 1, #peripheralList do
  581.  
  582.     return deviceList, deviceNames
  583. end -- function getDevices(deviceType)
  584.  
  585. -- Draw a line across the entire x-axis
  586. local function drawLine(yPos, monitorIndex)
  587.     local monitor = nil
  588.     monitor = monitorList[monitorIndex]
  589.  
  590.     if not monitor then
  591.         printLog("monitor["..monitorIndex.."] in drawLine() is NOT a valid monitor.")
  592.         return -- Invalid monitorIndex
  593.     end
  594.  
  595.     local width, height = monitor.getSize()
  596.  
  597.     for i=1, width do
  598.         monitor.setCursorPos(i, yPos)
  599.         monitor.write("-")
  600.     end
  601. end -- function drawLine(yPos,monitorIndex)
  602.  
  603.  
  604. -- Display a solid bar of specified color
  605. local function drawBar(startXPos, startYPos, endXPos, endYPos, color, monitorIndex)
  606.     local monitor = nil
  607.     monitor = monitorList[monitorIndex]
  608.  
  609.     if not monitor then
  610.         printLog("monitor["..monitorIndex.."] in drawBar() is NOT a valid monitor.")
  611.         return -- Invalid monitorIndex
  612.     end
  613.  
  614.     -- PaintUtils only outputs to term., not monitor.
  615.     -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
  616.     term.redirect(monitor)
  617.     paintutils.drawLine(startXPos, startYPos, endXPos, endYPos, color)
  618.     monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
  619.     termNative()
  620. end -- function drawBar(startXPos, startYPos,endXPos,endYPos,color,monitorIndex)
  621.  
  622.  
  623. -- Display single pixel color
  624. local function drawPixel(xPos, yPos, color, monitorIndex)
  625.     local monitor = nil
  626.     monitor = monitorList[monitorIndex]
  627.  
  628.     if not monitor then
  629.         printLog("monitor["..monitorIndex.."] in drawPixel() is NOT a valid monitor.")
  630.         return -- Invalid monitorIndex
  631.     end
  632.  
  633.     -- PaintUtils only outputs to term., not monitor.
  634.     -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
  635.     term.redirect(monitor)
  636.     paintutils.drawPixel(xPos, yPos, color)
  637.     monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
  638.     termNative()
  639. end -- function drawPixel(xPos, yPos, color, monitorIndex)
  640.  
  641.  
  642. -- End helper functions
  643.  
  644.  
  645. -- Then initialize the monitors
  646. local function findMonitors()
  647.     -- Empty out old list of monitors
  648.     monitorList = {}
  649.  
  650.     printLog("Finding monitors...")
  651.     monitorList, monitorNames = getDevices("monitor")
  652.  
  653.     if #monitorList == 0 then
  654.         printLog("No monitors found!")
  655.         error("Can't find any monitors!")
  656.     else
  657.         for monitorIndex = 1, #monitorList do
  658.             local monitor, monitorX, monitorY = nil, nil, nil
  659.             monitor = monitorList[monitorIndex]
  660.  
  661.             if not monitor then
  662.                 printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid monitor.")
  663.  
  664.                 table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
  665.                 if monitorIndex ~= #monitorList then    -- If we're not at the end, clean up
  666.                     monitorIndex = monitorIndex - 1 -- We just removed an element
  667.                 end -- if monitorIndex == #monitorList then
  668.                 break -- Invalid monitorIndex
  669.             else -- valid monitor
  670.                 monitorX, monitorY = monitor.getSize()
  671.                 if (monitorX == nil) or (monitorY == nil) then -- somehow a valid monitor, but non-existent sizes? Maybe fixes Issue #3
  672.                     printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid sized monitor.")
  673.  
  674.                     table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
  675.                     if monitorIndex ~= #monitorList then    -- If we're not at the end, clean up
  676.                         monitorIndex = monitorIndex - 1 -- We just removed an element
  677.                     end -- if monitorIndex == #monitorList then
  678.                     break -- Invalid monitorIndex
  679.  
  680.                 -- Check for minimum size to allow for monitor.setTextScale(0.5) to work for 3x2 debugging monitor, changes getSize()
  681.                 elseif monitorX < 29 or monitorY < 12 then
  682.                     term.redirect(monitor)
  683.                     monitor.clear()
  684.                     printLog("Removing monitor "..monitorIndex.." for being too small.")
  685.                     monitor.setCursorPos(1,2)
  686.                     write("Monitor is the wrong size!\n")
  687.                     write("Needs to be at least 3x2.")
  688.                     termNative()
  689.  
  690.                     table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
  691.                     if monitorIndex == #monitorList then    -- If we're at the end already, break from loop
  692.                         break
  693.                     else
  694.                         monitorIndex = monitorIndex - 1 -- We just removed an element
  695.                     end -- if monitorIndex == #monitorList then
  696.  
  697.                 end -- if monitorX < 29 or monitorY < 12 then
  698.             end -- if not monitor then
  699.  
  700.             printLog("Monitor["..monitorIndex.."] named \""..monitorNames[monitorIndex].."\" is a valid monitor of size x:"..monitorX.." by y:"..monitorY..".")
  701.         end -- for monitorIndex = 1, #monitorList do
  702.     end -- if #monitorList == 0 then
  703.  
  704.     printLog("Found "..#monitorList.." monitor(s) in findMonitors().")
  705. end -- local function findMonitors()
  706.  
  707.  
  708. -- Initialize all Big Reactors - Reactors
  709. local function findReactors()
  710.     -- Empty out old list of reactors
  711.     newReactorList = {}
  712.     printLog("Finding reactors...")
  713.     newReactorList, reactorNames = getDevices("BigReactors-Reactor")
  714.  
  715.     if #newReactorList == 0 then
  716.         printLog("No reactors found!")
  717.         error("Can't find any reactors!")
  718.     else  -- Placeholder
  719.         for reactorIndex = 1, #newReactorList do
  720.             local reactor = nil
  721.             reactor = newReactorList[reactorIndex]
  722.  
  723.             if not reactor then
  724.                 printLog("reactorList["..reactorIndex.."] in findReactors() is NOT a valid Big Reactor.")
  725.  
  726.                 table.remove(newReactorList, reactorIndex) -- Remove invalid reactor from list
  727.                 if reactorIndex ~= #newReactorList then    -- If we're not at the end, clean up
  728.                     reactorIndex = reactorIndex - 1 -- We just removed an element
  729.                 end -- reactorIndex ~= #newReactorList then
  730.                 return -- Invalid reactorIndex
  731.             else
  732.                 printLog("reactor["..reactorIndex.."] in findReactors() is a valid Big Reactor.")
  733.                 --initialize the default table
  734.                 _G[reactorNames[reactorIndex]] = {}
  735.                 _G[reactorNames[reactorIndex]]["ReactorOptions"] = {}
  736.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = 80
  737.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = 0
  738.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
  739.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
  740.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = 1400 --set for passive-cooled, the active-cooled subroutine will correct it
  741.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = 1000
  742.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = false
  743.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = reactorNames[reactorIndex]
  744.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  745.                 if reactor.getConnected() then
  746.                     printLog("reactor["..reactorIndex.."] in findReactors() is connected.")
  747.                 else
  748.                     printLog("reactor["..reactorIndex.."] in findReactors() is NOT connected.")
  749.                     return -- Disconnected reactor
  750.                 end
  751.             end
  752.            
  753.             --failsafe
  754.             local tempTable = _G[reactorNames[reactorIndex]]
  755.            
  756.             --check to make sure we get a valid config
  757.             if (config.load(reactorNames[reactorIndex]..".options")) ~= nil then
  758.                 tempTable = config.load(reactorNames[reactorIndex]..".options")
  759.                
  760.                 -- check for null config caused by CC not saving config files properly D:
  761.                 if temptable == nil then
  762.                     tempTable = _G[reactorNames[reactorIndex]]
  763.                     printLog("null config detected! restoring default...")
  764.                     write("null config detected! restoring default...")
  765.                     config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  766.                 end
  767.             else
  768.                 --if we don't have a valid config from disk, make a valid config
  769.                 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  770.             end
  771.            
  772.             --load values from tempTable, checking for nil values along the way
  773.             if tempTable["ReactorOptions"]["baseControlRodLevel"] ~= nil then
  774.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tempTable["ReactorOptions"]["baseControlRodLevel"]
  775.             end
  776.            
  777.             if tempTable["ReactorOptions"]["lastTempPoll"] ~= nil then
  778.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tempTable["ReactorOptions"]["lastTempPoll"]
  779.             end
  780.            
  781.             if tempTable["ReactorOptions"]["autoStart"] ~= nil then
  782.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = tempTable["ReactorOptions"]["autoStart"]
  783.             end
  784.            
  785.             if tempTable["ReactorOptions"]["activeCooled"] ~= nil then
  786.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = tempTable["ReactorOptions"]["activeCooled"]
  787.             end
  788.            
  789.             if tempTable["ReactorOptions"]["reactorMaxTemp"] ~= nil then
  790.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tempTable["ReactorOptions"]["reactorMaxTemp"]
  791.             end
  792.            
  793.             if tempTable["ReactorOptions"]["reactorMinTemp"] ~= nil then
  794.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tempTable["ReactorOptions"]["reactorMinTemp"]
  795.             end
  796.            
  797.             if tempTable["ReactorOptions"]["rodOverride"] ~= nil then
  798.                 printLog("Got value from config file for Rod Override, the value is: "..tostring(tempTable["ReactorOptions"]["rodOverride"]).." EOL")
  799.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = tempTable["ReactorOptions"]["rodOverride"]
  800.             end
  801.            
  802.             if tempTable["ReactorOptions"]["reactorName"] ~= nil then
  803.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = tempTable["ReactorOptions"]["reactorName"]
  804.             end
  805.            
  806.             if tempTable["ReactorOptions"]["reactorCruising"] ~= nil then
  807.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = tempTable["ReactorOptions"]["reactorCruising"]
  808.             end
  809.            
  810.             --stricter typing, let's set these puppies up with the right type of value.
  811.             _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"])
  812.            
  813.             _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"])
  814.            
  815.             if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"]) == "true") then
  816.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
  817.             else
  818.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
  819.             end
  820.            
  821.             if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"]) == "true") then
  822.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
  823.             else
  824.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = false
  825.             end
  826.            
  827.             _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"])
  828.            
  829.             _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"])
  830.            
  831.             if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]) == "true") then
  832.                 printLog("Setting Rod Override for  "..reactorNames[reactorIndex].." to true because value was "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  833.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = true
  834.             else
  835.                 printLog("Setting Rod Override for  "..reactorNames[reactorIndex].." to false because value was "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  836.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = false
  837.             end
  838.            
  839.             if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"]) == "true") then
  840.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
  841.             else
  842.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  843.             end
  844.                        
  845.             --save one more time, in case we didn't have a complete config file before
  846.             config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  847.         end -- for reactorIndex = 1, #newReactorList do
  848.     end -- if #newReactorList == 0 then
  849.  
  850.     -- Overwrite old reactor list with the now updated list
  851.     reactorList = newReactorList
  852.  
  853.     -- Start turbine monitor offset after reactors get monitors
  854.     -- This assumes that there is a monitor for each turbine and reactor, plus the overall monitor display
  855.     turbineMonitorOffset = #reactorList + 1 -- #turbineList will start at "1" if turbines found and move us just beyond #reactorList and status monitor range
  856.  
  857.     printLog("Found "..#reactorList.." reactor(s) in findReactors().")
  858.     printLog("Set turbineMonitorOffset to "..turbineMonitorOffset.." in findReactors().")
  859. end -- function findReactors()
  860.  
  861.  
  862. -- Initialize all Big Reactors - Turbines
  863. local function findTurbines()
  864.     -- Empty out old list of turbines
  865.     newTurbineList = {}
  866.  
  867.     printLog("Finding turbines...")
  868.     newTurbineList, turbineNames = getDevices("BigReactors-Turbine")
  869.  
  870.     if #newTurbineList == 0 then
  871.         printLog("No turbines found") -- Not an error
  872.     else
  873.         for turbineIndex = 1, #newTurbineList do
  874.             local turbine = nil
  875.             turbine = newTurbineList[turbineIndex]
  876.  
  877.             if not turbine then
  878.                 printLog("turbineList["..turbineIndex.."] in findTurbines() is NOT a valid Big Reactors Turbine.")
  879.  
  880.                 table.remove(newTurbineList, turbineIndex) -- Remove invalid turbine from list
  881.                 if turbineIndex ~= #newTurbineList then    -- If we're not at the end, clean up
  882.                     turbineIndex = turbineIndex - 1 -- We just removed an element
  883.                 end -- turbineIndex ~= #newTurbineList then
  884.  
  885.                 return -- Invalid turbineIndex
  886.             else
  887.            
  888.                 _G[turbineNames[turbineIndex]] = {}
  889.                 _G[turbineNames[turbineIndex]]["TurbineOptions"] = {}
  890.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = 0
  891.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = 2726
  892.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = true
  893.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = 2000 --open up with all the steam wide open
  894.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
  895.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = turbineNames[turbineIndex]
  896.                 printLog("turbineList["..turbineIndex.."] in findTurbines() is a valid Big Reactors Turbine.")
  897.                 if turbine.getConnected() then
  898.                     printLog("turbine["..turbineIndex.."] in findTurbines() is connected.")
  899.                 else
  900.                     printLog("turbine["..turbineIndex.."] in findTurbines() is NOT connected.")
  901.                     return -- Disconnected turbine
  902.                 end
  903.             end
  904.            
  905.             --failsafe
  906.             local tempTable = _G[turbineNames[turbineIndex]]
  907.            
  908.             --check to make sure we get a valid config
  909.             if (config.load(turbineNames[turbineIndex]..".options")) ~= nil then
  910.                 tempTable = config.load(turbineNames[turbineIndex]..".options")
  911.             else
  912.                 --if we don't have a valid config from disk, make a valid config
  913.                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  914.             end
  915.            
  916.             --load values from tempTable, checking for nil values along the way
  917.             if tempTable["TurbineOptions"]["LastSpeed"] ~= nil then
  918.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = tempTable["TurbineOptions"]["LastSpeed"]
  919.             end
  920.            
  921.             if tempTable["TurbineOptions"]["BaseSpeed"] ~= nil then
  922.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = tempTable["TurbineOptions"]["BaseSpeed"]
  923.             end
  924.            
  925.             if tempTable["TurbineOptions"]["autoStart"] ~= nil then
  926.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = tempTable["TurbineOptions"]["autoStart"]
  927.             end
  928.            
  929.             if tempTable["TurbineOptions"]["LastFlow"] ~= nil then
  930.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = tempTable["TurbineOptions"]["LastFlow"]
  931.             end
  932.            
  933.             if tempTable["TurbineOptions"]["flowOverride"] ~= nil then
  934.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = tempTable["TurbineOptions"]["flowOverride"]
  935.             end
  936.            
  937.             if tempTable["TurbineOptions"]["turbineName"] ~= nil then
  938.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = tempTable["TurbineOptions"]["turbineName"]
  939.             end
  940.            
  941.             --save once more just to make sure we got it
  942.             config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  943.         end -- for turbineIndex = 1, #newTurbineList do
  944.  
  945.         -- Overwrite old turbine list with the now updated list
  946.         turbineList = newTurbineList
  947.     end -- if #newTurbineList == 0 then
  948.  
  949.     printLog("Found "..#turbineList.." turbine(s) in findTurbines().")
  950. end -- function findTurbines()
  951.  
  952. -- Hastwell's Capbank Extended Buffer Graft [CEBG]
  953. local function findCapBanks()
  954.     printLog("Finding capbanks...")
  955.     newCapbankList, capbankNames = getDevices("tile_enderio_blockcapacitorbank_name")
  956.     --capBankMaxCapacity = 0
  957.    
  958.     if #newCapbankList == 0 then
  959.         printLog("No capbanks found") -- Not an error
  960.     --else
  961.     --  for capIndex = 1, #newCapbankList do
  962.     --      capBankMaxCapacity = capBankMaxCapacity + newCapbankList[capIndex].getMaxEnergyStored('')
  963.     --  end
  964.     end
  965.     capBankList = newCapbankList
  966. end
  967.  
  968. local function getCapbanksTotalCharge()
  969.     local currentCharge = 0
  970.     local maxCapacity = 0
  971.     for capIndex = 1, #capBankList do
  972.         currCap = capBankList[capIndex]
  973.         currentCharge = currentCharge + currCap.getEnergyStored('')
  974.         maxCapacity = maxCapacity + currCap.getMaxEnergyStored('')
  975.     end
  976.     return currentCharge, maxCapacity
  977. end
  978.  
  979. -- Return current energy buffer in a specific reactor by %
  980. local function getReactorStoredEnergyBufferPercent(reactor)
  981.     printLog("Called as getReactorStoredEnergyBufferPercent(reactor).")
  982.  
  983.     if not reactor then
  984.         printLog("getReactorStoredEnergyBufferPercent() did NOT receive a valid Big Reactor Reactor.")
  985.         return -- Invalid reactorIndex
  986.     else
  987.         printLog("getReactorStoredEnergyBufferPercent() did receive a valid Big Reactor Reactor.")
  988.     end
  989.  
  990.     local energyBufferStorage = reactor.getEnergyStored()
  991.     local capCharge, capMax = getCapbanksTotalCharge()
  992.     return round( (energyBufferStorage+capCharge) / (10000000+capMax) * 100, 1) -- (buffer/10000000 RF)*100%
  993. end -- function getReactorStoredEnergyBufferPercent(reactor)
  994.  
  995.  
  996. -- Return current energy buffer in a specific Turbine by %
  997. local function getTurbineStoredEnergyBufferPercent(turbine)
  998.     printLog("Called as getTurbineStoredEnergyBufferPercent(turbine)")
  999.  
  1000.     if not turbine then
  1001.         printLog("getTurbineStoredEnergyBufferPercent() did NOT receive a valid Big Reactor Turbine.")
  1002.         return -- Invalid reactorIndex
  1003.     else
  1004.         printLog("getTurbineStoredEnergyBufferPercent() did receive a valid Big Reactor Turbine.")
  1005.     end
  1006.  
  1007.     local energyBufferStorage = turbine.getEnergyStored()
  1008.     local capCharge, capMax = getCapbanksTotalCharge()
  1009.     return round( (energyBufferStorage+capCharge) / (1000000+capMax) * 100, 1) -- (buffer/1000000 RF)*100%
  1010. end -- function getTurbineStoredEnergyBufferPercent(turbine)
  1011.  
  1012. local function reactorCruise(cruiseMaxTemp, cruiseMinTemp, reactorIndex)
  1013.     printLog("Called as reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp=".._G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"]..",reactorIndex="..reactorIndex..").")
  1014.    
  1015.     --sanitization
  1016.     local lastPolledTemp = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"])
  1017.     cruiseMaxTemp = tonumber(cruiseMaxTemp)
  1018.     cruiseMinTemp = tonumber(cruiseMinTemp)
  1019.    
  1020.     if ((lastPolledTemp < cruiseMaxTemp) and (lastPolledTemp > cruiseMinTemp)) then
  1021.         local reactor = nil
  1022.         reactor = reactorList[reactorIndex]
  1023.         if not reactor then
  1024.             printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is NOT a valid Big Reactor.")
  1025.             return -- Invalid reactorIndex
  1026.         else
  1027.             printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is a valid Big Reactor.")
  1028.             if reactor.getConnected() then
  1029.                 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is connected.")
  1030.             else
  1031.                 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is NOT connected.")
  1032.                 return -- Disconnected reactor
  1033.             end -- if reactor.getConnected() then
  1034.         end -- if not reactor then
  1035.  
  1036.         local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
  1037.         local reactorTemp = math.ceil(reactor.getFuelTemperature())
  1038.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = rodPercentage
  1039.        
  1040.         if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
  1041.             if (reactorTemp < lastPolledTemp) then
  1042.                 rodPercentage = (rodPercentage - 1)
  1043.                 --Boundary check
  1044.                 if rodPercentage < 0 then
  1045.                     reactor.setAllControlRodLevels(0)
  1046.                 else
  1047.                     reactor.setAllControlRodLevels(rodPercentage)
  1048.                 end
  1049.             else
  1050.                 rodPercentage = (rodPercentage + 1)
  1051.                 --Boundary check
  1052.                 if rodPercentage > 99 then
  1053.                     reactor.setAllControlRodLevels(99)
  1054.                 else
  1055.                     reactor.setAllControlRodLevels(rodPercentage)
  1056.                 end
  1057.             end -- if (reactorTemp > lastPolledTemp) then
  1058.         else
  1059.             --disengage cruise, we've fallen out of the ideal temperature range
  1060.             _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  1061.         end -- if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
  1062.     else
  1063.         --I don't know how we'd get here, but let's turn the cruise mode off
  1064.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  1065.     end -- if ((lastPolledTemp < cruiseMaxTemp) and (lastPolledTemp > cruiseMinTemp)) then
  1066.     _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
  1067.     _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
  1068.     _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = cruiseMaxTemp
  1069.     _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = cruiseMinTemp
  1070.     config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1071. end -- function reactorCruise(cruiseMaxTemp, cruiseMinTemp, lastPolledTemp, reactorIndex)
  1072.  
  1073. -- Modify reactor control rod levels to keep temperature with defined parameters, but
  1074. -- wait an in-game half-hour for the temperature to stabalize before modifying again
  1075. local function temperatureControl(reactorIndex)
  1076.     printLog("Called as temperatureControl(reactorIndex="..reactorIndex..")")
  1077.  
  1078.     local reactor = nil
  1079.     reactor = reactorList[reactorIndex]
  1080.     if not reactor then
  1081.         printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT a valid Big Reactor.")
  1082.         return -- Invalid reactorIndex
  1083.     else
  1084.         printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is a valid Big Reactor.")
  1085.  
  1086.         if reactor.getConnected() then
  1087.             printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is connected.")
  1088.         else
  1089.             printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT connected.")
  1090.             return -- Disconnected reactor
  1091.         end -- if reactor.getConnected() then
  1092.     end
  1093.  
  1094.     local reactorNum = reactorIndex
  1095.     local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
  1096.     local reactorTemp = math.ceil(reactor.getFuelTemperature())
  1097.     local localMinReactorTemp, localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"], _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
  1098.  
  1099.     --bypass if the reactor itself is set to not be auto-controlled
  1100.     if ((not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]) or (_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] == "false")) then
  1101.         -- No point modifying control rod levels for temperature if the reactor is offline
  1102.         if reactor.getActive() then
  1103.             -- Actively cooled reactors should range between 0^C-300^C
  1104.             -- Actually, active-cooled reactors should range between 300 and 420C (Mechaet)
  1105.             -- Accordingly I changed the below lines
  1106.             if reactor.isActivelyCooled() then
  1107.                 -- below was 0
  1108.                 localMinReactorTemp = 300
  1109.                 -- below was 300
  1110.                 localMaxReactorTemp = 420
  1111.             else
  1112.                 localMinReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"]
  1113.                 localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
  1114.             end
  1115.             local lastTempPoll = _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"]
  1116.             if _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] then
  1117.                 --let's bypass all this math and hit the much-more-subtle cruise feature
  1118.                 --printLog("min: "..localMinReactorTemp..", max: "..localMaxReactorTemp..", lasttemp: "..lastTempPoll..", ri: "..reactorIndex.."  EOL")
  1119.                 reactorCruise(localMaxReactorTemp, localMinReactorTemp, reactorIndex)
  1120.             else
  1121.                 -- Don't bring us to 100, that's effectively a shutdown
  1122.                 if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
  1123.                     --increase the rods, but by how much?
  1124.                     if (reactorTemp > lastTempPoll) then
  1125.                         --we're climbing, we need to get this to decrease
  1126.                         if ((reactorTemp - lastTempPoll) > 100) then
  1127.                             --we're climbing really fast, arrest it
  1128.                             if (rodPercentage + (10 * controlRodAdjustAmount)) > 99 then
  1129.                                 reactor.setAllControlRodLevels(99)
  1130.                             else
  1131.                                 reactor.setAllControlRodLevels(rodPercentage + (10 * controlRodAdjustAmount))
  1132.                             end
  1133.                         else
  1134.                             --we're not climbing by leaps and bounds, let's give it a rod adjustment based on temperature increase
  1135.                             local diffAmount = reactorTemp - lastTempPoll
  1136.                             diffAmount = (round(diffAmount/10, 0))/5
  1137.                             controlRodAdjustAmount = diffAmount
  1138.                             if (rodPercentage + controlRodAdjustAmount) > 99 then
  1139.                                 reactor.setAllControlRodLevels(99)
  1140.                             else
  1141.                                 reactor.setAllControlRodLevels(rodPercentage + controlRodAdjustAmount)
  1142.                             end
  1143.                         end --if ((reactorTemp - lastTempPoll) > 100) then
  1144.                     elseif (reactorTemp == lastTempPoll) then
  1145.                         --temperature has stagnated, kick it very lightly
  1146.                         local controlRodAdjustment = 1
  1147.                         if (rodPercentage + controlRodAdjustment) > 99 then
  1148.                             reactor.setAllControlRodLevels(99)
  1149.                         else
  1150.                             reactor.setAllControlRodLevels(rodPercentage + controlRodAdjustment)
  1151.                         end
  1152.                     end --if (reactorTemp > lastTempPoll) then
  1153.                         --worth noting that if we're above temp but decreasing, we do nothing. let it continue decreasing.
  1154.  
  1155.                 elseif (reactorTemp < localMinReactorTemp) and (rodPercentage ~=0) then
  1156.                     --we're too cold. time to warm up, but by how much?
  1157.                     if (reactorTemp < lastTempPoll) then
  1158.                         --we're descending, let's stop that.
  1159.                         if ((lastTempPoll - reactorTemp) > 100) then
  1160.                             --we're headed for a new ice age, bring the heat
  1161.                             if (rodPercentage - (10 * controlRodAdjustAmount)) < 0 then
  1162.                                 reactor.setAllControlRodLevels(0)
  1163.                             else
  1164.                                 reactor.setAllControlRodLevels(rodPercentage - (10 * controlRodAdjustAmount))
  1165.                             end
  1166.                         else
  1167.                             --we're not descending quickly, let's bump it based on descent rate
  1168.                             local diffAmount = lastTempPoll - reactorTemp
  1169.                             diffAmount = (round(diffAmount/10, 0))/5
  1170.                             controlRodAdjustAmount = diffAmount
  1171.                             if (rodPercentage - controlRodAdjustAmount) < 0 then
  1172.                                 reactor.setAllControlRodLevels(0)
  1173.                             else
  1174.                                 reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustAmount)
  1175.                             end
  1176.                         end --if ((lastTempPoll - reactorTemp) > 100) then
  1177.                     elseif (reactorTemp == lastTempPoll) then
  1178.                         --temperature has stagnated, kick it very lightly
  1179.                         local controlRodAdjustment = 1
  1180.                         if (rodPercentage - controlRodAdjustment) < 0 then
  1181.                             reactor.setAllControlRodLevels(0)
  1182.                         else
  1183.                             reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustment)
  1184.                         end --if (rodPercentage - controlRodAdjustment) < 0 then
  1185.  
  1186.                     end --if (reactorTemp < lastTempPoll) then
  1187.                     --if we're below temp but increasing, do nothing and let it continue to rise.
  1188.                 end --if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
  1189.  
  1190.                 if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) then
  1191.                     --engage cruise mode
  1192.                     _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
  1193.                 end -- if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) then
  1194.             end -- if reactorCruising then
  1195.             --always set this number
  1196.             _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
  1197.             config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1198.         end -- if reactor.getActive() then
  1199.     else
  1200.         printLog("Bypassed temperature control due to rodOverride being "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1201.     end -- if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
  1202. end -- function temperatureControl(reactorIndex)
  1203.  
  1204. -- Load saved reactor parameters if ReactorOptions file exists
  1205. local function loadReactorOptions()
  1206.     local reactorOptions = fs.open("ReactorOptions", "r") -- See http://computercraft.info/wiki/Fs.open
  1207.  
  1208.     if reactorOptions then
  1209.         -- The following values were added by Lolmer
  1210.         minStoredEnergyPercent = reactorOptions.readLine()
  1211.         maxStoredEnergyPercent = reactorOptions.readLine()
  1212.         --added by Mechaet
  1213.         -- If we succeeded in reading a string, convert it to a number
  1214.  
  1215.         if minStoredEnergyPercent ~= nil then
  1216.             minStoredEnergyPercent = tonumber(minStoredEnergyPercent)
  1217.         end
  1218.  
  1219.         if maxStoredEnergyPercent ~= nil then
  1220.             maxStoredEnergyPercent = tonumber(maxStoredEnergyPercent)
  1221.         end
  1222.  
  1223.         reactorOptions.close()
  1224.     end -- if reactorOptions then
  1225.  
  1226.     -- Set default values if we failed to read any of the above
  1227.     if minStoredEnergyPercent == nil then
  1228.         minStoredEnergyPercent = 15
  1229.     end
  1230.  
  1231.     if maxStoredEnergyPercent == nil then
  1232.         maxStoredEnergyPercent = 85
  1233.     end
  1234.  
  1235. end -- function loadReactorOptions()
  1236.  
  1237.  
  1238. -- Save our reactor parameters
  1239. local function saveReactorOptions()
  1240.     local reactorOptions = fs.open("ReactorOptions", "w") -- See http://computercraft.info/wiki/Fs.open
  1241.  
  1242.     -- If we can save the files, save them
  1243.     if reactorOptions then
  1244.         local reactorIndex = 1
  1245.         -- The following values were added by Lolmer
  1246.         reactorOptions.writeLine(minStoredEnergyPercent)
  1247.         reactorOptions.writeLine(maxStoredEnergyPercent)
  1248.         reactorOptions.close()
  1249.     else
  1250.         printLog("Failed to open file ReactorOptions for writing!")
  1251.     end -- if reactorOptions then
  1252. end -- function saveReactorOptions()
  1253.  
  1254.  
  1255. local function displayReactorBars(barParams)
  1256.     -- Default to first reactor and first monitor
  1257.     setmetatable(barParams,{__index={reactorIndex=1, monitorIndex=1}})
  1258.     local reactorIndex, monitorIndex =
  1259.         barParams[1] or barParams.reactorIndex,
  1260.         barParams[2] or barParams.monitorIndex
  1261.  
  1262.     printLog("Called as displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1263.  
  1264.     -- Grab current monitor
  1265.     local monitor = nil
  1266.     monitor = monitorList[monitorIndex]
  1267.     if not monitor then
  1268.         printLog("monitor["..monitorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  1269.         return -- Invalid monitorIndex
  1270.     end
  1271.  
  1272.     -- Grab current reactor
  1273.     local reactor = nil
  1274.     reactor = reactorList[reactorIndex]
  1275.     if not reactor then
  1276.         printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
  1277.         return -- Invalid reactorIndex
  1278.     else
  1279.         printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
  1280.         if reactor.getConnected() then
  1281.             printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
  1282.         else
  1283.             printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  1284.             return -- Disconnected reactor
  1285.         end -- if reactor.getConnected() then
  1286.     end -- if not reactor then
  1287.  
  1288.     -- Draw border lines
  1289.     local width, height = monitor.getSize()
  1290.     printLog("Size of monitor is "..width.."w x"..height.."h in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
  1291.  
  1292.     for i=3, 5 do
  1293.         monitor.setCursorPos(22, i)
  1294.         monitor.write("|")
  1295.     end
  1296.  
  1297.     drawLine(2, monitorIndex)
  1298.     drawLine(6, monitorIndex)
  1299.  
  1300.     -- Draw some text
  1301.     local fuelString = "Fuel: "
  1302.     local tempString = "Temp: "
  1303.     local energyBufferString = ""
  1304.  
  1305.     if reactor.isActivelyCooled() then
  1306.         energyBufferString = "Steam: "
  1307.     else
  1308.         energyBufferString = "Energy: "
  1309.     end
  1310.  
  1311.     local padding = math.max(string.len(fuelString), string.len(tempString), string.len(energyBufferString))
  1312.  
  1313.     local fuelPercentage = round(reactor.getFuelAmount()/reactor.getFuelAmountMax()*100,1)
  1314.     print{fuelString,2,3,monitorIndex}
  1315.     print{fuelPercentage.." %",padding+2,3,monitorIndex}
  1316.  
  1317.     local reactorTemp = math.ceil(reactor.getFuelTemperature())
  1318.     print{tempString,2,5,monitorIndex}
  1319.     print{reactorTemp.." C",padding+2,5,monitorIndex}
  1320.  
  1321.     local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
  1322.     printLog("Current Rod Percentage for reactor["..reactorIndex.."] is "..rodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1323.     -- Allow controlling Reactor Control Rod Level from GUI
  1324.     -- Decrease rod button: 23X, 4Y
  1325.     -- Increase rod button: 28X, 4Y
  1326.     if (xClick == 23) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1327.         printLog("Decreasing Rod Levels in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1328.         --Decrease rod level by amount
  1329.         newRodPercentage = rodPercentage - (5 * controlRodAdjustAmount)
  1330.         if newRodPercentage < 0 then
  1331.             newRodPercentage = 0
  1332.         end
  1333.         sideClick, xClick, yClick = 0, 0, 0
  1334.  
  1335.         printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1336.         reactor.setAllControlRodLevels(newRodPercentage)
  1337.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = newRodPercentage
  1338.  
  1339.         -- Save updated rod percentage
  1340.         config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1341.         rodPercentage = newRodPercentage
  1342.     elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1343.         printLog("Increasing Rod Levels in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1344.         --Increase rod level by amount
  1345.         newRodPercentage = rodPercentage + (5 * controlRodAdjustAmount)
  1346.         if newRodPercentage > 100 then
  1347.             newRodPercentage = 100
  1348.         end
  1349.         sideClick, xClick, yClick = 0, 0, 0
  1350.  
  1351.         printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1352.         reactor.setAllControlRodLevels(newRodPercentage)
  1353.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = newRodPercentage
  1354.        
  1355.         -- Save updated rod percentage
  1356.         config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1357.         rodPercentage = round(newRodPercentage,0)
  1358.     else
  1359.         printLog("No change to Rod Levels requested by "..progName.." GUI in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1360.     end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1361.  
  1362.     print{"Rod (%)",23,3,monitorIndex}
  1363.     print{"<     >",23,4,monitorIndex}
  1364.     print{stringTrim(rodPercentage),25,4,monitorIndex}
  1365.  
  1366.  
  1367.     -- getEnergyProducedLastTick() is used for both RF/t (passively cooled) and mB/t (actively cooled)
  1368.     local energyBuffer = reactor.getEnergyProducedLastTick()
  1369.     if reactor.isActivelyCooled() then
  1370.         printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." mB last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1371.     else
  1372.         printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." RF last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1373.     end
  1374.  
  1375.     print{energyBufferString,2,4,monitorIndex}
  1376.  
  1377.     -- Actively cooled reactors do not produce energy, only hot fluid mB/t to be used in a turbine
  1378.     -- still uses getEnergyProducedLastTick for mB/t of hot fluid generated
  1379.     if not reactor.isActivelyCooled() then
  1380.         printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT an actively cooled reactor.")
  1381.  
  1382.         -- Draw stored energy buffer bar
  1383.         drawBar(2,8,28,8,colors.gray,monitorIndex)
  1384.  
  1385.         local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
  1386.         if curStoredEnergyPercent > 4 then
  1387.             drawBar(2, 8, math.floor(26*curStoredEnergyPercent/100)+2, 8, colors.yellow, monitorIndex)
  1388.         elseif curStoredEnergyPercent > 0 then
  1389.             drawPixel(2, 8, colors.yellow, monitorIndex)
  1390.         end -- if curStoredEnergyPercent > 4 then
  1391.  
  1392.         print{"Energy Buffer",2,7,monitorIndex}
  1393.         print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),7,monitorIndex}
  1394.         print{"%",28,7,monitorIndex}
  1395.  
  1396.         print{math.ceil(energyBuffer).." RF/t",padding+2,4,monitorIndex}
  1397.     else
  1398.         printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is an actively cooled reactor.")
  1399.         print{math.ceil(energyBuffer).." mB/t",padding+2,4,monitorIndex}
  1400.     end -- if not reactor.isActivelyCooled() then
  1401.  
  1402.     -- Print rod override status
  1403.     local reactorRodOverrideStatus = ""
  1404.  
  1405.     print{"Rod Auto-adjust:",2,9,monitorIndex}
  1406.  
  1407.     if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
  1408.         printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1409.         reactorRodOverrideStatus = "Enabled"
  1410.         monitor.setTextColor(colors.green)
  1411.     else
  1412.         printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1413.         reactorRodOverrideStatus = "Disabled"
  1414.         monitor.setTextColor(colors.red)
  1415.     end -- if not reactorRodOverride then
  1416.     printLog("reactorRodOverrideStatus is \""..reactorRodOverrideStatus.."\" in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1417.  
  1418.     print{reactorRodOverrideStatus, width - string.len(reactorRodOverrideStatus) - 1, 9, monitorIndex}
  1419.     monitor.setTextColor(colors.white)
  1420.  
  1421.     print{"Reactivity: "..math.ceil(reactor.getFuelReactivity()).." %", 2, 10, monitorIndex}
  1422.     print{"Fuel: "..round(reactor.getFuelConsumedLastTick(),3).." mB/t", 2, 11, monitorIndex}
  1423.     print{"Waste: "..reactor.getWasteAmount().." mB", width-(string.len(reactor.getWasteAmount())+10), 11, monitorIndex}
  1424.  
  1425.     monitor.setTextColor(colors.blue)
  1426.     printCentered(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"],12,monitorIndex)
  1427.     monitor.setTextColor(colors.white)
  1428. end -- function displayReactorBars(barParams)
  1429.  
  1430.  
  1431. local function reactorStatus(statusParams)
  1432.     -- Default to first reactor and first monitor
  1433.     setmetatable(statusParams,{__index={reactorIndex=1, monitorIndex=1}})
  1434.     local reactorIndex, monitorIndex =
  1435.         statusParams[1] or statusParams.reactorIndex,
  1436.         statusParams[2] or statusParams.monitorIndex
  1437.     printLog("Called as reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
  1438.  
  1439.     -- Grab current monitor
  1440.     local monitor = nil
  1441.     monitor = monitorList[monitorIndex]
  1442.     if not monitor then
  1443.         printLog("monitor["..monitorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  1444.         return -- Invalid monitorIndex
  1445.     end
  1446.  
  1447.     -- Grab current reactor
  1448.     local reactor = nil
  1449.     reactor = reactorList[reactorIndex]
  1450.     if not reactor then
  1451.         printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
  1452.         return -- Invalid reactorIndex
  1453.     else
  1454.         printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
  1455.     end
  1456.  
  1457.     local width, height = monitor.getSize()
  1458.     local reactorStatus = ""
  1459.  
  1460.     if reactor.getConnected() then
  1461.         printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
  1462.  
  1463.         if reactor.getActive() then
  1464.             reactorStatus = "ONLINE"
  1465.  
  1466.             -- Set "ONLINE" to blue if the actively cooled reactor is both in cruise mode and online
  1467.             if _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] and reactor.isActivelyCooled() then
  1468.                 monitor.setTextColor(colors.blue)
  1469.             else
  1470.                 monitor.setTextColor(colors.green)
  1471.             end -- if reactorCruising and reactor.isActivelyCooled() then
  1472.         else
  1473.             reactorStatus = "OFFLINE"
  1474.             monitor.setTextColor(colors.red)
  1475.         end -- if reactor.getActive() then
  1476.  
  1477.         if xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1) and (sideClick == monitorNames[monitorIndex]) then
  1478.             if yClick == 1 then
  1479.                 reactor.setActive(not reactor.getActive()) -- Toggle reactor status
  1480.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = reactor.getActive()
  1481.                 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1482.                 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  1483.  
  1484.                 -- If someone offlines the reactor (offline after a status click was detected), then disable autoStart
  1485.                 if not reactor.getActive() then
  1486.                     _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
  1487.                 end
  1488.             end -- if yClick == 1 then
  1489.         end -- if (xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
  1490.  
  1491.         -- Allow disabling rod level auto-adjust and only manual rod level control
  1492.         if ((xClick > 23 and xClick < 28 and yClick == 4)
  1493.                 or (xClick > 20 and xClick < 27 and yClick == 9))
  1494.                 and (sideClick == monitorNames[monitorIndex]) then
  1495.             _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]
  1496.             config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1497.             sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  1498.         end -- if (xClick > 23) and (xClick < 28) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1499.  
  1500.     else
  1501.         printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  1502.         reactorStatus = "DISCONNECTED"
  1503.         monitor.setTextColor(colors.red)
  1504.     end -- if reactor.getConnected() then
  1505.  
  1506.     print{reactorStatus, width - string.len(reactorStatus) - 1, 1, monitorIndex}
  1507.     monitor.setTextColor(colors.white)
  1508. end -- function reactorStatus(statusParams)
  1509.  
  1510.  
  1511. -- Display all found reactors' status to monitor 1
  1512. -- This is only called if multiple reactors and/or a reactor plus at least one turbine are found
  1513. local function displayAllStatus()
  1514.     local reactor, turbine = nil, nil
  1515.     local onlineReactor, onlineTurbine = 0, 0
  1516.     local totalReactorRF, totalReactorSteam, totalTurbineRF = 0, 0, 0
  1517.     local totalReactorFuelConsumed = 0
  1518.     local capStored, capMax = getCapbanksTotalCharge()
  1519.     local totalCoolantStored, totalSteamStored, totalEnergy, totalMaxEnergyStored = 0, 0, capStored, capMax -- Total turbine and reactor energy buffer and overall capacity
  1520.     local maxSteamStored = (2000*#turbineList)+(5000*#reactorList)
  1521.     local maxCoolantStored = (2000*#turbineList)+(5000*#reactorList)
  1522.  
  1523.     local monitor, monitorIndex = nil, 1
  1524.     monitor = monitorList[monitorIndex]
  1525.     if not monitor then
  1526.         printLog("monitor["..monitorIndex.."] in displayAllStatus() is NOT a valid monitor.")
  1527.         return -- Invalid monitorIndex
  1528.     end
  1529.  
  1530.     for reactorIndex = 1, #reactorList do
  1531.         reactor = reactorList[reactorIndex]
  1532.         if not reactor then
  1533.             printLog("reactor["..reactorIndex.."] in displayAllStatus() is NOT a valid Big Reactor.")
  1534.             break -- Invalid reactorIndex
  1535.         else
  1536.             printLog("reactor["..reactorIndex.."] in displayAllStatus() is a valid Big Reactor.")
  1537.         end -- if not reactor then
  1538.  
  1539.         if reactor.getConnected() then
  1540.             printLog("reactor["..reactorIndex.."] in displayAllStatus() is connected.")
  1541.             if reactor.getActive() then
  1542.                 onlineReactor = onlineReactor + 1
  1543.                 totalReactorFuelConsumed = totalReactorFuelConsumed + reactor.getFuelConsumedLastTick()
  1544.             end -- reactor.getActive() then
  1545.  
  1546.             -- Actively cooled reactors do not produce or store energy
  1547.             if not reactor.isActivelyCooled() then
  1548.                 totalMaxEnergyStored = totalMaxEnergyStored + 10000000 -- Reactors store 10M RF
  1549.                 totalEnergy = totalEnergy + reactor.getEnergyStored()
  1550.                 totalReactorRF = totalReactorRF + reactor.getEnergyProducedLastTick()
  1551.             else
  1552.                 totalReactorSteam = totalReactorSteam + reactor.getEnergyProducedLastTick()
  1553.                 totalSteamStored = totalSteamStored + reactor.getHotFluidAmount()
  1554.                 totalCoolantStored = totalCoolantStored + reactor.getCoolantAmount()
  1555.             end -- if not reactor.isActivelyCooled() then
  1556.         else
  1557.             printLog("reactor["..reactorIndex.."] in displayAllStatus() is NOT connected.")
  1558.         end -- if reactor.getConnected() then
  1559.     end -- for reactorIndex = 1, #reactorList do
  1560.  
  1561.     for turbineIndex = 1, #turbineList do
  1562.         turbine = turbineList[turbineIndex]
  1563.         if not turbine then
  1564.             printLog("turbine["..turbineIndex.."] in displayAllStatus() is NOT a valid Turbine.")
  1565.             break -- Invalid turbineIndex
  1566.         else
  1567.             printLog("turbine["..turbineIndex.."] in displayAllStatus() is a valid Turbine.")
  1568.         end -- if not turbine then
  1569.  
  1570.         if turbine.getConnected() then
  1571.             printLog("turbine["..turbineIndex.."] in displayAllStatus() is connected.")
  1572.             if turbine.getActive() then
  1573.                 onlineTurbine = onlineTurbine + 1
  1574.             end
  1575.  
  1576.             totalMaxEnergyStored = totalMaxEnergyStored + 1000000 -- Turbines store 1M RF
  1577.             totalEnergy = totalEnergy + turbine.getEnergyStored()
  1578.             totalTurbineRF = totalTurbineRF + turbine.getEnergyProducedLastTick()
  1579.             totalSteamStored = totalSteamStored + turbine.getInputAmount()
  1580.             totalCoolantStored = totalCoolantStored + turbine.getOutputAmount()
  1581.         else
  1582.             printLog("turbine["..turbineIndex.."] in displayAllStatus() is NOT connected.")
  1583.         end -- if turbine.getConnected() then
  1584.     end -- for turbineIndex = 1, #turbineList do
  1585.  
  1586.     print{"Reactors online/found: "..onlineReactor.."/"..#reactorList, 2, 3, monitorIndex}
  1587.     print{"Turbines online/found: "..onlineTurbine.."/"..#turbineList, 2, 4, monitorIndex}
  1588.  
  1589.     if totalReactorRF ~= 0 then
  1590.         monitor.setTextColor(colors.blue)
  1591.         printRight("Reactor", 9, monitorIndex)
  1592.         monitor.setTextColor(colors.white)
  1593.         printRight(math.ceil(totalReactorRF).." (RF/t)", 10, monitorIndex)
  1594.     end
  1595.  
  1596.     if #turbineList then
  1597.         -- Display liquids
  1598.         monitor.setTextColor(colors.blue)
  1599.         printLeft("Steam (mB)", 6, monitorIndex)
  1600.         monitor.setTextColor(colors.white)
  1601.         printLeft(math.ceil(totalSteamStored).."/"..maxSteamStored, 7, monitorIndex)
  1602.         printLeft(math.ceil(totalReactorSteam).." mB/t", 8, monitorIndex)
  1603.         monitor.setTextColor(colors.blue)
  1604.         printRight("Coolant (mB)", 6, monitorIndex)
  1605.         monitor.setTextColor(colors.white)
  1606.         printRight(math.ceil(totalCoolantStored).."/"..maxCoolantStored, 7, monitorIndex)
  1607.  
  1608.         monitor.setTextColor(colors.blue)
  1609.         printLeft("Turbine", 9, monitorIndex)
  1610.         monitor.setTextColor(colors.white)
  1611.         printLeft(math.ceil(totalTurbineRF).." RF/t", 10, monitorIndex)
  1612.     end -- if #turbineList then
  1613.  
  1614.     printCentered("Fuel: "..round(totalReactorFuelConsumed,3).." mB/t", 11, monitorIndex)
  1615.     --print{"Buffer: "..math.ceil(totalEnergy,3).."/"..totalMaxEnergyStored.." RF", 2, 12, monitorIndex}
  1616.     print{"Buffer: "..round(totalEnergy/1000000,3).."/"..round(totalMaxEnergyStored/1000000,3).." MRF", 2, 12, monitorIndex}
  1617. end -- function displayAllStatus()
  1618.  
  1619.  
  1620. -- Get turbine status
  1621. local function displayTurbineBars(turbineIndex, monitorIndex)
  1622.     printLog("Called as displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1623.  
  1624.     -- Grab current monitor
  1625.     local monitor = nil
  1626.     monitor = monitorList[monitorIndex]
  1627.     if not monitor then
  1628.         printLog("monitor["..monitorIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  1629.         return -- Invalid monitorIndex
  1630.     end
  1631.  
  1632.     -- Grab current turbine
  1633.     local turbine = nil
  1634.     turbine = turbineList[turbineIndex]
  1635.     if not turbine then
  1636.         printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
  1637.         return -- Invalid turbineIndex
  1638.     else
  1639.         printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
  1640.         if turbine.getConnected() then
  1641.             printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
  1642.         else
  1643.             printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  1644.             return -- Disconnected turbine
  1645.         end -- if turbine.getConnected() then
  1646.     end -- if not turbine then
  1647.  
  1648.     --local variable to match the view on the monitor
  1649.     local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
  1650.  
  1651.     -- Draw border lines
  1652.     local width, height = monitor.getSize()
  1653.  
  1654.     for i=3, 6 do
  1655.         monitor.setCursorPos(21, i)
  1656.         monitor.write("|")
  1657.     end
  1658.  
  1659.     drawLine(2,monitorIndex)
  1660.     drawLine(7,monitorIndex)
  1661.  
  1662.     -- Allow controlling Turbine Flow Rate from GUI
  1663.     -- Decrease flow rate button: 22X, 4Y
  1664.     -- Increase flow rate button: 28X, 4Y
  1665.     local turbineFlowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
  1666.     if (xClick == 22) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1667.         printLog("Decrease to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1668.         --Decrease rod level by amount
  1669.         newTurbineFlowRate = turbineFlowRate - flowRateAdjustAmount
  1670.         if newTurbineFlowRate < 0 then
  1671.             newTurbineFlowRate = 0
  1672.         end
  1673.         sideClick, xClick, yClick = 0, 0, 0
  1674.  
  1675.         -- Check bounds [0,2000]
  1676.         if newTurbineFlowRate > 2000 then
  1677.             newTurbineFlowRate = 2000
  1678.         elseif newTurbineFlowRate < 0 then
  1679.             newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
  1680.         end
  1681.  
  1682.         turbine.setFluidFlowRateMax(newTurbineFlowRate)
  1683.         _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newTurbineFlowRate
  1684.         -- Save updated Turbine Flow Rate
  1685.         turbineFlowRate = newTurbineFlowRate
  1686.         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1687.     elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1688.         printLog("Increase to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1689.         --Increase rod level by amount
  1690.         newTurbineFlowRate = turbineFlowRate + flowRateAdjustAmount
  1691.         if newTurbineFlowRate > 2000 then
  1692.             newTurbineFlowRate = 2000
  1693.         end
  1694.         sideClick, xClick, yClick = 0, 0, 0
  1695.  
  1696.         -- Check bounds [0,2000]
  1697.         if newTurbineFlowRate > 2000 then
  1698.             newTurbineFlowRate = 2000
  1699.         elseif newTurbineFlowRate < 0 then
  1700.             newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
  1701.         end
  1702.  
  1703.         turbine.setFluidFlowRateMax(newTurbineFlowRate)
  1704.        
  1705.         -- Save updated Turbine Flow Rate
  1706.         turbineFlowRate = math.ceil(newTurbineFlowRate)
  1707.         _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = turbineFlowRate
  1708.         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1709.     else
  1710.         printLog("No change to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1711.     end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1712.  
  1713.     if (xClick == 22) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
  1714.         printLog("Decrease to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1715.         rpmRateAdjustment = 909
  1716.         newTurbineBaseSpeed = turbineBaseSpeed - rpmRateAdjustment
  1717.         if newTurbineBaseSpeed < 908 then
  1718.             newTurbineBaseSpeed = 908
  1719.         end
  1720.         sideClick, xClick, yClick = 0, 0, 0
  1721.         _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
  1722.         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1723.     elseif (xClick == 29) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
  1724.         printLog("Increase to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1725.         rpmRateAdjustment = 909
  1726.         newTurbineBaseSpeed = turbineBaseSpeed + rpmRateAdjustment
  1727.         if newTurbineBaseSpeed > 2726 then
  1728.             newTurbineBaseSpeed = 2726
  1729.         end
  1730.         sideClick, xClick, yClick = 0, 0, 0
  1731.         _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
  1732.         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1733.     else
  1734.         printLog("No change to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1735.     end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1736.     print{"  mB/t",22,3,monitorIndex}
  1737.     print{"<      >",22,4,monitorIndex}
  1738.     print{stringTrim(turbineFlowRate),24,4,monitorIndex}
  1739.     print{"  RPM",22,5,monitorIndex}
  1740.     print{"<      >",22,6,monitorIndex}
  1741.     print{stringTrim(tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])),24,6,monitorIndex}
  1742.     local rotorSpeedString = "Speed: "
  1743.     local energyBufferString = "Energy: "
  1744.     local padding = math.max(string.len(rotorSpeedString), string.len(energyBufferString))
  1745.  
  1746.     local energyBuffer = turbine.getEnergyProducedLastTick()
  1747.     print{energyBufferString,1,4,monitorIndex}
  1748.     print{math.ceil(energyBuffer).." RF/t",padding+1,4,monitorIndex}
  1749.  
  1750.     local rotorSpeed = math.ceil(turbine.getRotorSpeed())
  1751.     print{rotorSpeedString,1,5,monitorIndex}
  1752.     print{rotorSpeed.." RPM",padding+1,5,monitorIndex}
  1753.  
  1754.     -- PaintUtils only outputs to term., not monitor.
  1755.     -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
  1756.  
  1757.     -- Draw stored energy buffer bar
  1758.     drawBar(1,9,28,9,colors.gray,monitorIndex)
  1759.  
  1760.     local curStoredEnergyPercent = getTurbineStoredEnergyBufferPercent(turbine)
  1761.     if curStoredEnergyPercent > 4 then
  1762.         drawBar(1, 9, math.floor(26*curStoredEnergyPercent/100)+2, 9, colors.yellow,monitorIndex)
  1763.     elseif curStoredEnergyPercent > 0 then
  1764.         drawPixel(1, 9, colors.yellow, monitorIndex)
  1765.     end -- if curStoredEnergyPercent > 4 then
  1766.  
  1767.     print{"Energy Buffer",1,8,monitorIndex}
  1768.     print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),8,monitorIndex}
  1769.     print{"%",28,8,monitorIndex}
  1770.  
  1771.     -- Print rod override status
  1772.     local turbineFlowRateOverrideStatus = ""
  1773.  
  1774.     print{"Flow Auto-adjust:",2,10,monitorIndex}
  1775.  
  1776.     if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
  1777.         turbineFlowRateOverrideStatus = "Enabled"
  1778.         monitor.setTextColor(colors.green)
  1779.     else
  1780.         turbineFlowRateOverrideStatus = "Disabled"
  1781.         monitor.setTextColor(colors.red)
  1782.     end -- if not reactorRodOverride then
  1783.  
  1784.     print{turbineFlowRateOverrideStatus, width - string.len(turbineFlowRateOverrideStatus) - 1, 10, monitorIndex}
  1785.     monitor.setTextColor(colors.white)
  1786.  
  1787.     monitor.setTextColor(colors.blue)
  1788.     printCentered(_G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"],12,monitorIndex)
  1789.     monitor.setTextColor(colors.white)
  1790.  
  1791.     -- Need equation to figure out rotor efficiency and display
  1792. end -- function displayTurbineBars(statusParams)
  1793.  
  1794.  
  1795. -- Display turbine status
  1796. local function turbineStatus(turbineIndex, monitorIndex)
  1797.     -- Grab current monitor
  1798.     local monitor = nil
  1799.  
  1800.     printLog("Called as turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1801.  
  1802.     monitor = monitorList[monitorIndex]
  1803.     if not monitor then
  1804.         printLog("monitor["..monitorIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  1805.         return -- Invalid monitorIndex
  1806.     end
  1807.  
  1808.     -- Grab current turbine
  1809.     local turbine = nil
  1810.     turbine = turbineList[turbineIndex]
  1811.     if not turbine then
  1812.         printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
  1813.         return -- Invalid turbineIndex
  1814.     else
  1815.         printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
  1816.     end
  1817.  
  1818.     local width, height = monitor.getSize()
  1819.     local turbineStatus = ""
  1820.  
  1821.     if turbine.getConnected() then
  1822.         printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
  1823.         if turbine.getActive() then
  1824.             turbineStatus = "ONLINE"
  1825.             monitor.setTextColor(colors.green)
  1826.         else
  1827.             turbineStatus = "OFFLINE"
  1828.             monitor.setTextColor(colors.red)
  1829.         end -- if turbine.getActive() then
  1830.  
  1831.         if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
  1832.             if yClick == 1 then
  1833.                 turbine.setActive(not turbine.getActive()) -- Toggle turbine status
  1834.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = turbine.getActive()
  1835.                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1836.                 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  1837.             end -- if yClick == 1 then
  1838.         end -- if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
  1839.  
  1840.         -- Allow disabling/enabling flow rate auto-adjust
  1841.         if (xClick > 23 and xClick < 28 and yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  1842.             _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
  1843.             sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  1844.         elseif (xClick > 20 and xClick < 27 and yClick == 10) and (sideClick == monitorNames[monitorIndex]) then
  1845.            
  1846.             if ((_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "true")) then
  1847.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
  1848.             else
  1849.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
  1850.             end
  1851.             sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  1852.         end
  1853.         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1854.  
  1855.     else
  1856.         printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  1857.         turbineStatus = "DISCONNECTED"
  1858.         monitor.setTextColor(colors.red)
  1859.     end -- if turbine.getConnected() then
  1860.  
  1861.     print{turbineStatus, width - string.len(turbineStatus) - 1, 1, monitorIndex}
  1862.     monitor.setTextColor(colors.white)
  1863. end -- function function turbineStatus(turbineIndex, monitorIndex)
  1864.  
  1865.  
  1866. -- Maintain Turbine flow rate at 900 or 1,800 RPM
  1867. local function flowRateControl(turbineIndex)
  1868.     if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
  1869.        
  1870.         printLog("Called as flowRateControl(turbineIndex="..turbineIndex..").")
  1871.  
  1872.         -- Grab current turbine
  1873.         local turbine = nil
  1874.         turbine = turbineList[turbineIndex]
  1875.  
  1876.         -- assign for the duration of this run
  1877.         local lastTurbineSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"])
  1878.         local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
  1879.  
  1880.         if not turbine then
  1881.             printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT a valid Big Turbine.")
  1882.             return -- Invalid turbineIndex
  1883.         else
  1884.             printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is a valid Big Turbine.")
  1885.  
  1886.             if turbine.getConnected() then
  1887.                 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is connected.")
  1888.             else
  1889.                 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT connected.")
  1890.             end -- if turbine.getConnected() then
  1891.         end -- if not turbine then
  1892.  
  1893.         -- No point modifying control rod levels for temperature if the turbine is offline
  1894.         if turbine.getActive() then
  1895.             printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is active.")
  1896.  
  1897.             local flowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
  1898.             local flowRateUserMax = math.ceil(turbine.getFluidFlowRateMax())
  1899.             local rotorSpeed = math.ceil(turbine.getRotorSpeed())
  1900.             local newFlowRate = 0
  1901.  
  1902.             -- Going to control the turbine based on target RPM since changing the target flow rate bypasses this function
  1903.             if (rotorSpeed < turbineBaseSpeed) then
  1904.                 printLog("BELOW COMMANDED SPEED")
  1905.                 if (rotorSpeed > lastTurbineSpeed) then
  1906.                     --we're still increasing, let's let it level off
  1907.                     --also lets the first control pass go by on startup
  1908.                 elseif (rotorSpeed < lastTurbineSpeed) then
  1909.                     --we're decreasing where we should be increasing, do something
  1910.                     if ((lastTurbineSpeed - rotorSpeed) > 100) then
  1911.                         --kick it harder
  1912.                         newFlowRate = 2000
  1913.                         printLog("HARD KICK")
  1914.                     else
  1915.                         --let's adjust based on proximity
  1916.                         flowAdjustment = (turbineBaseSpeed - rotorSpeed)/5
  1917.                         newFlowRate = flowRate + flowAdjustment
  1918.                         printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  1919.                     end
  1920.                 else
  1921.                     --we've stagnated, kick it.
  1922.                     flowAdjustment = (turbineBaseSpeed - lastTurbineSpeed)
  1923.                     newFlowRate = flowRate + flowAdjustment
  1924.                     printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  1925.                 end --if (rotorSpeed > lastTurbineSpeed) then
  1926.             else
  1927.                 --we're above commanded turbine speed
  1928.                 printLog("ABOVE COMMANDED SPEED")
  1929.                 if (rotorSpeed < lastTurbineSpeed) then
  1930.                 --we're decreasing, let it level off
  1931.                 --also bypasses first control pass on startup
  1932.                 elseif (rotorSpeed > lastTurbineSpeed) then
  1933.                     --we're above and ascending.
  1934.                     if ((rotorSpeed - lastTurbineSpeed) > 100) then
  1935.                         --halt
  1936.                         newFlowRate = 25
  1937.                     else
  1938.                         --let's adjust based on proximity
  1939.                         flowAdjustment = (rotorSpeed - turbineBaseSpeed)/5
  1940.                         newFlowRate = flowRate - flowAdjustment
  1941.                         printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  1942.                     end
  1943.                 else
  1944.                     --we've stagnated, kick it.
  1945.                     flowAdjustment = (lastTurbineSpeed - turbineBaseSpeed)
  1946.                     newFlowRate = flowRate - flowAdjustment
  1947.                     printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  1948.                 end --if (rotorSpeed < lastTurbineSpeed) then
  1949.             end --if (rotorSpeed < turbineBaseSpeed)
  1950.  
  1951.             --check to make sure an adjustment was made
  1952.             if (newFlowRate == 0) then
  1953.                 --do nothing, we didn't ask for anything this pass
  1954.             else
  1955.                 --boundary check
  1956.                 if newFlowRate > 2000 then
  1957.                     newFlowRate = 2000
  1958.                 elseif newFlowRate < 25 then
  1959.                     newFlowRate = 25 -- Don't go to zero, might as well power off
  1960.                 end -- if newFlowRate > 2000 then
  1961.                 --no sense running an adjustment if it's not necessary
  1962.                 if ((newFlowRate < flowRate) or (newFlowRate > flowRate)) then
  1963.                     printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is being commanded to "..newFlowRate.." mB/t flow")
  1964.                     newFlowRate = round(newFlowRate, 0)
  1965.                     turbine.setFluidFlowRateMax(newFlowRate)
  1966.                     _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newFlowRate
  1967.                     config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1968.                 end
  1969.             end
  1970.             --always set this
  1971.             _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = rotorSpeed
  1972.             config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1973.         else
  1974.             printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT active.")
  1975.         end -- if turbine.getActive() then
  1976.     else
  1977.         printLog("turbine["..turbineIndex.."] has flow override set to "..tostring(_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"])..", bypassing flow control.")
  1978.     end -- if not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] then
  1979. end -- function flowRateControl(turbineIndex)
  1980.  
  1981.  
  1982. function main()
  1983.     -- Load reactor parameters and initialize systems
  1984.     loadReactorOptions()
  1985.  
  1986.     -- Get our initial list of connected monitors and reactors
  1987.     -- and initialize every cycle in case the connected devices change
  1988.     findMonitors()
  1989.     findReactors()
  1990.     findTurbines()
  1991.     findCapBanks()
  1992.  
  1993.     while not finished do
  1994.         local reactor = nil
  1995.         local monitorIndex = 1
  1996.  
  1997.         -- For multiple reactors/monitors, monitor #1 is reserved for overall status
  1998.         -- or for multiple reactors/turbines and only one monitor
  1999.         if ( ( ((#reactorList + #turbineList) > 1) and (#monitorList >= 1) )   or
  2000.              ( ((#reactorList + #turbineList) >=1) and (#monitorList >  1) ) ) then
  2001.             local monitor = nil
  2002.             monitor = monitorList[monitorIndex]
  2003.             if not monitor then
  2004.                 printLog("monitor["..monitorIndex.."] in main() is NOT a valid monitor.")
  2005.                 return -- Invalid monitorIndex
  2006.             end
  2007.  
  2008.             clearMonitor(progName.." "..progVer, monitorIndex) -- Clear monitor and draw borders
  2009.             printCentered(progName.." "..progVer, 1, monitorIndex)
  2010.             displayAllStatus()
  2011.             monitorIndex = 2 -- Next monitor, #1 is reserved for overall status
  2012.         end
  2013.  
  2014.         -- Iterate through reactors, continue to run even if not enough monitors are connected
  2015.         for reactorIndex = 1, #reactorList do
  2016.             local monitor = nil
  2017.             local reactorMonitorIndex = monitorIndex + reactorIndex - 1 -- reactorIndex starts at 1
  2018.  
  2019.             printLog("Attempting to display reactor["..reactorIndex.."] on monitor["..reactorMonitorIndex.."]...")
  2020.  
  2021.             reactor = reactorList[reactorIndex]
  2022.             if not reactor then
  2023.                 printLog("reactor["..reactorIndex.."] in main() is NOT a valid Big Reactor.")
  2024.                 break -- Invalid reactorIndex
  2025.             else
  2026.                 printLog("reactor["..reactorIndex.."] in main() is a valid Big Reactor.")
  2027.             end --  if not reactor then
  2028.  
  2029.             -- Only attempt to assign a monitor if we have a monitor for this reactor
  2030.             if (reactorMonitorIndex <= #monitorList) then
  2031.                 printLog("Displaying reactor["..reactorIndex.."] on monitor["..reactorMonitorIndex.."].")
  2032.                 monitor = monitorList[reactorMonitorIndex]
  2033.  
  2034.                 if not monitor then
  2035.                     printLog("monitor["..reactorMonitorIndex.."] in main() is NOT a valid monitor.")
  2036.                 else
  2037.                     clearMonitor(progName, reactorMonitorIndex) -- Clear monitor and draw borders
  2038.                     printCentered(progName, 1, reactorMonitorIndex)
  2039.  
  2040.                     -- Display reactor status, includes "Disconnected" but found reactors
  2041.                     reactorStatus{reactorIndex, reactorMonitorIndex}
  2042.  
  2043.                     -- Draw the borders and bars for the current reactor on the current monitor
  2044.                     displayReactorBars{reactorIndex, reactorMonitorIndex}
  2045.                 end -- if not monitor
  2046.             else
  2047.                 printLog("You may want "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines.")
  2048.             end -- if (#monitorList ~= 1) and (reactorMonitorIndex < #monitorList) then
  2049.  
  2050.             if reactor.getConnected() then
  2051.                 printLog("reactor["..reactorIndex.."] is connected.")
  2052.                 local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
  2053.  
  2054.                 -- Shutdown reactor if current stored energy % is >= desired level, otherwise activate
  2055.                 -- First pass will have curStoredEnergyPercent=0 until displayBars() is run once
  2056.                 if curStoredEnergyPercent >= maxStoredEnergyPercent then
  2057.                     reactor.setActive(false)
  2058.                 -- Do not auto-start the reactor if it was manually powered off (autoStart=false)
  2059.                 elseif (curStoredEnergyPercent <= minStoredEnergyPercent) and (_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] == true) then
  2060.                     reactor.setActive(true)
  2061.                 end -- if curStoredEnergyPercent >= maxStoredEnergyPercent then
  2062.  
  2063.                 -- Don't try to auto-adjust control rods if manual control is requested
  2064.                 if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
  2065.                     temperatureControl(reactorIndex)
  2066.                 end -- if not reactorRodOverride then
  2067.             else
  2068.                 printLog("reactor["..reactorIndex.."] is NOT connected.")
  2069.             end -- if reactor.getConnected() then
  2070.         end -- for reactorIndex = 1, #reactorList do
  2071.  
  2072.         -- Monitors for turbines start after turbineMonitorOffset
  2073.         for turbineIndex = 1, #turbineList do
  2074.             local monitor = nil
  2075.             local turbineMonitorIndex = turbineIndex + turbineMonitorOffset
  2076.  
  2077.             printLog("Attempting to display turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."]...")
  2078.  
  2079.             -- Only attempt to assign a monitor if we found a monitor for this turbine
  2080.             if (turbineMonitorIndex <= #monitorList) then
  2081.                 printLog("Displaying turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."].")
  2082.                 monitor = monitorList[turbineMonitorIndex]
  2083.                 if not monitor then
  2084.                     printLog("monitor["..turbineMonitorIndex.."] in main() is NOT a valid monitor.")
  2085.                 else
  2086.                     clearMonitor(progName, turbineMonitorIndex) -- Clear monitor and draw borders
  2087.                     printCentered(progName, 1, turbineMonitorIndex)
  2088.  
  2089.                     -- Display turbine status, includes "Disconnected" but found turbines
  2090.                     turbineStatus(turbineIndex, turbineMonitorIndex)
  2091.  
  2092.                     -- Draw the borders and bars for the current turbine on the current monitor
  2093.                     displayTurbineBars(turbineIndex, turbineMonitorIndex)
  2094.                 end -- if not monitor
  2095.             else
  2096.                 printLog("You may want "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines.")
  2097.             end -- if (#monitorList ~= 1) and (turbineMonitorIndex < #monitorList) then
  2098.  
  2099.             turbine = turbineList[turbineIndex]
  2100.             if not turbine then
  2101.                 printLog("turbine["..turbineIndex.."] in main() is NOT a valid Big Turbine.")
  2102.                 break -- Invalid turbineIndex
  2103.             else
  2104.                 printLog("turbine["..turbineIndex.."] in main() is a valid Big Turbine.")
  2105.             end -- if not turbine then
  2106.  
  2107.             if turbine.getConnected() then
  2108.                 printLog("turbine["..turbineIndex.."] is connected.")
  2109.  
  2110.                 if not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] then
  2111.                     flowRateControl(turbineIndex)
  2112.                 end -- if not turbineFlowRateOverride[turbineIndex] then
  2113.             else
  2114.                 printLog("turbine["..turbineIndex.."] is NOT connected.")
  2115.             end -- if turbine.getConnected() then
  2116.         end -- for reactorIndex = 1, #reactorList do
  2117.  
  2118.         sleep(loopTime) -- Sleep
  2119.         -- saveReactorOptions()
  2120.     end -- while not finished do
  2121. end -- main()
  2122.  
  2123.  
  2124. local function eventHandler()
  2125.     while not finished do
  2126.         -- http://computercraft.info/wiki/Os.pullEvent
  2127.         -- http://www.computercraft.info/forums2/index.php?/topic/1516-ospullevent-what-is-it-and-how-is-it-useful/
  2128.         event, arg1, arg2, arg3 = os.pullEvent()
  2129.  
  2130.         if event == "monitor_touch" then
  2131.             sideClick, xClick, yClick = arg1, math.floor(arg2), math.floor(arg3)
  2132.             printLog("Side: "..arg1.." Monitor touch X: "..xClick.." Y: "..yClick)
  2133.         elseif event == "char" and not inManualMode then
  2134.             local ch = string.lower(arg1)
  2135.             if ch == "q" then
  2136.                 finished = true
  2137.             elseif ch == "r" then
  2138.                 finished = true
  2139.                 os.reboot()
  2140.             end -- if ch == "q" then
  2141.         end -- if event == "monitor_touch" then
  2142.     end -- while not finished do
  2143. end -- function eventHandler()
  2144.  
  2145. saveReactorOptions()
  2146. while not finished do
  2147.     main()
  2148.     sleep(loopTime)
  2149. end -- while not finished do
  2150.  
  2151.  
  2152. -- Clear up after an exit
  2153. term.clear()
  2154. term.setCursorPos(1,1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement