Advertisement
peptide

Reactor Control

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