Stravides

Reactor Control 3 monitors

May 19th, 2015
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 140.66 KB | None | 0 0
  1. --[[
  2. Last update: 2015-04-08
  3.  
  4.  
  5. Description:
  6. 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.
  7.  
  8. 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:
  9. 1) One Advanced Monitor for overall status display plus
  10.         one or more Reactors plus
  11.         none or more Turbines.
  12. 2) One Advanced Monitor for overall status display plus (furthest monitor from computer by cable length)
  13.         one Advanced Monitor for each connected Reactor plus (subsequent found monitors)
  14.         one Advanced Monitor for each connected Turbine (last group of monitors found).
  15. If you enable debug mode, add one additional Advanced Monitor for #1 or #2.
  16.  
  17. Notes
  18. ----------------------------
  19. - Only one reactor and one, two, and three turbines have been tested with the above, but IN THEORY any number is supported.
  20. - Devices are found in the reverse order they are plugged in, so monitor_10 will be found before monitor_9.
  21.  
  22. When using actively cooled reactors with turbines, keep the following in mind:
  23. - 1 mB steam carries up to 10RF of potential energy to extract in a turbine.
  24. - Actively cooled reactors produce steam, not power.
  25. - You will need about 10 mB of water for each 1 mB of steam that you want to create in a 7^3 reactor.
  26. - Two 15x15x14 Turbines can output 260K RF/t by just one 7^3 (four rods) reactor putting out 4k mB steam.
  27.  
  28. Features
  29. ----------------------------
  30. - Configurable min/max energy buffer and min/max temperature via ReactorOptions file.
  31. - Disengages coils and minimizes flow for turbines over max energy buffer.
  32. - ReactorOptions is read on start and then current values are saved every program cycle.
  33. - 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.
  34. - Auto-adjusts control rods per reactor to maintain temperature.
  35. - Will display reactor data to all attached monitors of correct dimensions.
  36.         - For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
  37. - For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
  38. - A new cruise mode from mechaet, ONLINE will be "blue" when active, to keep your actively cooled reactors running smoothly.
  39.  
  40. GUI Usage
  41. ----------------------------
  42. - Right-clicking between "< * >" of the last row of a monitor alternates the device selection between Reactor, Turbine, and Status output.
  43.         - Right-clicking "<" and ">" switches between connected devices, starting with the currently selected type, but not limited to them.
  44. - The other "<" and ">" buttons, when right-clicked with the mouse, will decrease and increase, respectively, the values assigned to the monitor:
  45.         - "Rod (%)" will lower/raise the Reactor Control Rods for that Reactor
  46.         - "mB/t" will lower/raise the Turbine Flow Rate maximum for that Turbine
  47.         - "RPM" will lower/raise the target Turbine RPM for that Turbine
  48. - Right-clicking between the "<" and ">" (not on them) will disable auto-adjust of that value for attached device.
  49.         - Right-clicking on the "Enabled" or "Disabled" text for auto-adjust will do the same.
  50. - Right-clicking on "ONLINE" or "OFFLINE" at the top-right will toggle the state of attached device.
  51.  
  52. Default values
  53. ----------------------------
  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. ----------------------------
  66. - Advanced Monitor size is X: 29, Y: 12 with a 3x2 size
  67. - Computer or Advanced Computer
  68. - Modems (not wireless) connecting each of the Computer to both the Advanced Monitor and Reactor Computer Port.
  69. - Big Reactors (http://www.big-reactors.com/) 0.3.2A+
  70. - Computercraft (http://computercraft.info/) 1.58, 1.63+, or 1.73+
  71. - Reset the computer any time number of connected devices change.
  72.  
  73. Resources
  74. ----------------------------
  75.  
  76. ]]--
  77.  
  78.  
  79. -- Some global variables
  80. local progVer = "0.3.17"
  81. local progName = "Strav-NUKE"
  82. local sideClick, xClick, yClick = nil, 0, 0
  83. local loopTime = 2
  84. local controlRodAdjustAmount = 1 -- Default Reactor Rod Control % adjustment amount
  85. local flowRateAdjustAmount = 25 -- Default Turbine Flow Rate in mB adjustment amount
  86. local debugMode = false
  87. -- End multi-reactor cleanup section
  88. local minStoredEnergyPercent = nil -- Max energy % to store before activate
  89. local maxStoredEnergyPercent = nil -- Max energy % to store before shutdown
  90. local monitorList = {} -- Empty monitor array
  91. local monitorNames = {} -- Empty array of monitor names
  92. local reactorList = {} -- Empty reactor array
  93. local reactorNames = {} -- Empty array of reactor names
  94. local turbineList = {} -- Empty turbine array
  95. local turbineNames = {} -- Empty array of turbine names
  96. local monitorAssignments = {} -- Empty array of monitor - "what to display" assignments
  97. local monitorOptionFileName = "monitors.options" -- File for saving the monitor assignments
  98. local knowlinglyOverride = false -- Issue #39 Allow the user to override safe values, currently only enabled for actively cooled reactor min/max temperature
  99. local steamRequested = 0 -- Sum of Turbine Flow Rate in mB
  100. local steamDelivered = 0 -- Sum of Active Reactor steam output in mB
  101.  
  102. -- Log levels
  103. local FATAL = 16
  104. local ERROR = 8
  105. local WARN = 4
  106. local INFO = 2
  107. local DEBUG = 1
  108.  
  109. term.clear()
  110. term.setCursorPos(2,1)
  111. write("Initializing program...\n")
  112.  
  113.  
  114. -- File needs to exist for append "a" later and zero it out if it already exists
  115. -- Always initalize this file to avoid confusion with old files and the latest run
  116. local logFile = fs.open("reactorcontrol.log", "w")
  117. if logFile then
  118.         logFile.writeLine("Minecraft time: Day "..os.day().." at "..textutils.formatTime(os.time(),true))
  119.         logFile.close()
  120. else
  121.         error("Could not open file reactorcontrol.log for writing.")
  122. end
  123.  
  124.  
  125. -- Helper functions
  126.  
  127. local function termRestore()
  128.         local ccVersion = nil
  129.         ccVersion = os.version()
  130.  
  131.         if ccVersion == "CraftOS 1.6" or "CraftOS 1.7" then
  132.                 term.redirect(term.native())
  133.         elseif ccVersion == "CraftOS 1.5" then
  134.                 term.restore()
  135.         else -- Default to older term.restore
  136.                 printLog("Unsupported CraftOS found. Reported version is \""..ccVersion.."\".")
  137.                 term.restore()
  138.         end -- if ccVersion
  139. end -- function termRestore()
  140.  
  141. local function printLog(printStr, logLevel)
  142.         logLevel = logLevel or INFO
  143.         -- No, I'm not going to write full syslog style levels. But this makes it a little easier filtering and finding stuff in the logfile.
  144.         -- Since you're already looking at it, you can adjust your preferred log level right here.
  145.         if debugMode and (logLevel >= WARN) then
  146.                 -- If multiple monitors, print to all of them
  147.                 for monitorName, deviceData in pairs(monitorAssignments) do
  148.                         if deviceData.type == "Debug" then
  149.                                 debugMonitor = monitorList[deviceData.index]
  150.                                 if(not debugMonitor) or (not debugMonitor.getSize()) then
  151.                                         term.write("printLog(): debug monitor "..monitorName.." failed")
  152.                                 else
  153.                                         term.redirect(debugMonitor) -- Redirect to selected monitor
  154.                                         debugMonitor.setTextScale(0.5) -- Fit more logs on screen
  155.                                         local color = colors.lightGray
  156.                                         if (logLevel == WARN) then
  157.                                                 color = colors.white
  158.                                         elseif (logLevel == ERROR) then
  159.                                                 color = colors.red
  160.                                         elseif (logLevel == FATAL) then
  161.                                                 color = colors.black
  162.                                                 debugMonitor.setBackgroundColor(colors.red)
  163.                                         end
  164.                                         debugMonitor.setTextColor(color)
  165.                                         write(printStr.."\n")   -- May need to use term.scroll(x) if we output too much, not sure
  166.                                         debugMonitor.setBackgroundColor(colors.black)
  167.                                         termRestore()
  168.                                 end
  169.                         end
  170.                 end -- for
  171.  
  172.                 local logFile = fs.open("reactorcontrol.log", "a") -- See http://computercraft.info/wiki/Fs.open
  173.                 if logFile then
  174.                         logFile.writeLine(printStr)
  175.                         logFile.close()
  176.                 else
  177.                         error("Cannot open file reactorcontrol.log for appending!")
  178.                 end -- if logFile then
  179.         end -- if debugMode then
  180. end -- function printLog(printStr)
  181.  
  182. -- Trim a string
  183. function stringTrim(s)
  184.         assert(s ~= nil, "String can't be nil")
  185.         return(string.gsub(s, "^%s*(.-)%s*$", "%1"))
  186. end
  187.  
  188. -- Format number with [k,M,G,T,P,E] postfix or exponent, depending on how large it is
  189. local function formatReadableSIUnit(num)
  190.         printLog("formatReadableSIUnit("..num..")", DEBUG)
  191.         num = tonumber(num)
  192.         if(num < 1000) then return tostring(num) end
  193.         local sizes = {"", "k", "M", "G", "T", "P", "E"}
  194.         local exponent = math.floor(math.log10(num))
  195.         local group = math.floor(exponent / 3)
  196.         if group > #sizes then
  197.                 return string.format("%e", num)
  198.         else
  199.                 local divisor = math.pow(10, (group - 1) * 3)
  200.                 return string.format("%i%s", num / divisor, sizes[group])
  201.         end
  202. end -- local function formatReadableSIUnit(num)
  203.  
  204. -- pretty printLog() a table
  205. local function tprint (tbl, loglevel, indent)
  206.         if not loglevel then loglevel = DEBUG end
  207.         if not indent then indent = 0 end
  208.         for k, v in pairs(tbl) do
  209.                 formatting = string.rep("  ", indent) .. k .. ": "
  210.                 if type(v) == "table" then
  211.                         printLog(formatting, loglevel)
  212.                         tprint(v, loglevel, indent+1)
  213.                 elseif type(v) == 'boolean' or type(v) == "function" then
  214.                         printLog(formatting .. tostring(v), loglevel)      
  215.                 else
  216.                         printLog(formatting .. v, loglevel)
  217.                 end
  218.         end
  219. end -- function tprint()
  220.  
  221. config = {}
  222.  
  223. -- Save a table into a config file
  224. -- path: path of the file to write
  225. -- tab: table to save
  226. config.save = function(path, tab)
  227.         printLog("Save function called for config for "..path.." EOL")
  228.         assert(path ~= nil, "Path can't be nil")
  229.         assert(type(tab) == "table", "Second parameter must be a table")
  230.         local f = io.open(path, "w")
  231.         local i = 0
  232.         for key, value in pairs(tab) do
  233.                 if i ~= 0 then
  234.                         f:write("\n")
  235.                 end
  236.                 f:write("["..key.."]".."\n")
  237.                 for key2, value2 in pairs(tab[key]) do
  238.                         key2 = stringTrim(key2)
  239.                         --doesn't like boolean values
  240.                         if (type(value2) ~= "boolean") then
  241.                         value2 = stringTrim(value2)
  242.                         else
  243.                         value2 = tostring(value2)
  244.                         end
  245.                         key2 = key2:gsub(";", "\\;")
  246.                         key2 = key2:gsub("=", "\\=")
  247.                         value2 = value2:gsub(";", "\\;")
  248.                         value2 = value2:gsub("=", "\\=")      
  249.                         f:write(key2.."="..value2.."\n")
  250.                 end
  251.                 i = i + 1
  252.         end
  253.         f:close()
  254. end --config.save = function(path, tab)
  255.  
  256. -- Load a config file
  257. -- path: path of the file to read
  258. config.load = function(path)
  259.         printLog("Load function called for config for "..path.." EOL")
  260.         assert(path ~= nil, "Path can't be nil")
  261.         local f = fs.open(path, "r")
  262.         if f ~= nil then
  263.                 printLog("Successfully opened "..path.." for reading EOL")
  264.                 local tab = {}
  265.                 local line = ""
  266.                 local newLine
  267.                 local i
  268.                 local currentTag = nil
  269.                 local found = false
  270.                 local pos = 0
  271.                 while line ~= nil do
  272.                         found = false          
  273.                         line = line:gsub("\\;", "#_!36!_#") -- to keep \;
  274.                         line = line:gsub("\\=", "#_!71!_#") -- to keep \=
  275.                         if line ~= "" then
  276.                                 -- Delete comments
  277.                                 newLine = line
  278.                                 line = ""
  279.                                 for i=1, string.len(newLine) do                        
  280.                                         if string.sub(newLine, i, i) ~= ";" then
  281.                                                 line = line..newLine:sub(i, i)                                        
  282.                                         else                          
  283.                                                 break
  284.                                         end
  285.                                 end
  286.                                 line = stringTrim(line)
  287.                                 -- Find tag                    
  288.                                 if line:sub(1, 1) == "[" and line:sub(line:len(), line:len()) == "]" then
  289.                                         currentTag = stringTrim(line:sub(2, line:len()-1))
  290.                                         tab[currentTag] = {}
  291.                                         found = true                                                  
  292.                                 end
  293.                                 -- Find key and values
  294.                                 if not found and line ~= "" then                              
  295.                                         pos = line:find("=")                          
  296.                                         if pos == nil then
  297.                                                 error("Bad INI file structure")
  298.                                         end
  299.                                         line = line:gsub("#_!36!_#", ";")
  300.                                         line = line:gsub("#_!71!_#", "=")
  301.                                         tab[currentTag][stringTrim(line:sub(1, pos-1))] = stringTrim(line:sub(pos+1, line:len()))
  302.                                         found = true                  
  303.                                 end                    
  304.                         end
  305.                         line = f.readLine()
  306.                 end
  307.                
  308.                 f:close()
  309.                
  310.                 return tab
  311.         else
  312.                 printLog("Could NOT opened "..path.." for reading! EOL")
  313.                 return nil
  314.         end
  315. end --config.load = function(path)
  316.  
  317.  
  318.  
  319. -- round() function from mechaet
  320. local function round(num, places)
  321.         local mult = 10^places
  322.         local addon = nil
  323.         if ((num * mult) < 0) then
  324.                 addon = -.5
  325.         else
  326.                 addon = .5
  327.         end
  328.  
  329.         local integer, decimal = math.modf(num*mult+addon)
  330.         newNum = integer/mult
  331.         printLog("Called round(num="..num..",places="..places..") returns \""..newNum.."\".")
  332.         return newNum
  333. end -- function round(num, places)
  334.  
  335.  
  336. local function print(printParams)
  337.         -- Default to xPos=1, yPos=1, and first monitor
  338.         setmetatable(printParams,{__index={xPos=1, yPos=1, monitorIndex=1}})
  339.         local printString, xPos, yPos, monitorIndex =
  340.                 printParams[1], -- Required parameter
  341.                 printParams[2] or printParams.xPos,
  342.                 printParams[3] or printParams.yPos,
  343.                 printParams[4] or printParams.monitorIndex
  344.  
  345.         local monitor = nil
  346.         monitor = monitorList[monitorIndex]
  347.  
  348.         if not monitor then
  349.                 printLog("monitor["..monitorIndex.."] in print() is NOT a valid monitor.")
  350.                 return -- Invalid monitorIndex
  351.         end
  352.  
  353.         monitor.setCursorPos(xPos, yPos)
  354.         monitor.write(printString)
  355. end -- function print(printParams)
  356.  
  357.  
  358. -- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
  359. local function printCentered(printString, yPos, monitorIndex)
  360.         local monitor = nil
  361.         monitor = monitorList[monitorIndex]
  362.  
  363.         if not monitor then
  364.                 printLog("monitor["..monitorIndex.."] in printCentered() is NOT a valid monitor.", ERROR)
  365.                 return -- Invalid monitorIndex
  366.         end
  367.  
  368.         local width, height = monitor.getSize()
  369.         local monitorNameLength = 0
  370.  
  371.         -- Special changes for title bar
  372.         if yPos == 1 then
  373.                 -- Add monitor name to first line
  374.                 monitorNameLength = monitorNames[monitorIndex]:len()
  375.                 width = width - monitorNameLength -- add a space
  376.  
  377.                 -- Leave room for "offline" and "online" on the right except for overall status display
  378.                 if monitorAssignments[monitorNames[monitorIndex]].type ~= "Status" then
  379.                         width = width - 7
  380.                 end
  381.         end
  382.  
  383.         monitor.setCursorPos(monitorNameLength + math.ceil((1 + width - printString:len())/2), yPos)
  384.         monitor.write(printString)
  385. end -- function printCentered(printString, yPos, monitorIndex)
  386.  
  387.  
  388. -- Print text padded from the left side
  389. -- Clear the left side of the screen
  390. local function printLeft(printString, yPos, monitorIndex)
  391.         local monitor = nil
  392.         monitor = monitorList[monitorIndex]
  393.  
  394.         if not monitor then
  395.                 printLog("monitor["..monitorIndex.."] in printLeft() is NOT a valid monitor.", ERROR)
  396.                 return -- Invalid monitorIndex
  397.         end
  398.  
  399.         local gap = 1
  400.         local width = monitor.getSize()
  401.  
  402.         -- Clear left-half of the monitor
  403.  
  404.         for curXPos = 1, (width / 2) do
  405.                 monitor.setCursorPos(curXPos, yPos)
  406.                 monitor.write(" ")
  407.         end
  408.  
  409.         -- Write our string left-aligned
  410.         monitor.setCursorPos(1+gap, yPos)
  411.         monitor.write(printString)
  412. end
  413.  
  414.  
  415. -- Print text padded from the right side
  416. -- Clear the right side of the screen
  417. local function printRight(printString, yPos, monitorIndex)
  418.         local monitor = nil
  419.         monitor = monitorList[monitorIndex]
  420.  
  421.         if not monitor then
  422.                 printLog("monitor["..monitorIndex.."] in printRight() is NOT a valid monitor.", ERROR)
  423.                 return -- Invalid monitorIndex
  424.         end
  425.  
  426.         -- Make sure printString is a string
  427.         printString = tostring(printString)
  428.  
  429.         local gap = 1
  430.         local width = monitor.getSize()
  431.  
  432.         -- Clear right-half of the monitor
  433.         for curXPos = (width/2), width do
  434.                 monitor.setCursorPos(curXPos, yPos)
  435.                 monitor.write(" ")
  436.         end
  437.  
  438.         -- Write our string right-aligned
  439.         monitor.setCursorPos(math.floor(width) - math.ceil(printString:len()+gap), yPos)
  440.         monitor.write(printString)
  441. end
  442.  
  443.  
  444. -- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
  445. local function clearMonitor(printString, monitorIndex)
  446.         local monitor = nil
  447.         monitor = monitorList[monitorIndex]
  448.  
  449.         printLog("Called as clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..").")
  450.  
  451.         if not monitor then
  452.                 printLog("monitor["..monitorIndex.."] in clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..") is NOT a valid monitor.", ERROR)
  453.                 return -- Invalid monitorIndex
  454.         end
  455.  
  456.         local gap = 2
  457.         monitor.clear()
  458.         local width, height = monitor.getSize()
  459.  
  460.         printCentered(printString, 1, monitorIndex)
  461.         monitor.setTextColor(colors.blue)
  462.         print{monitorNames[monitorIndex], 1, 1, monitorIndex}
  463.         monitor.setTextColor(colors.white)
  464.  
  465.         for i=1, width do
  466.                 monitor.setCursorPos(i, gap)
  467.                 monitor.write("-")
  468.         end
  469.  
  470.         monitor.setCursorPos(1, gap+1)
  471. end -- function clearMonitor(printString, monitorIndex)
  472.  
  473.  
  474. -- Return a list of all connected (including via wired modems) devices of "deviceType"
  475. local function getDevices(deviceType)
  476.         printLog("Called as getDevices(deviceType="..deviceType..")")
  477.  
  478.         local deviceName = nil
  479.         local deviceIndex = 1
  480.         local deviceList, deviceNames = {}, {} -- Empty array, which grows as we need
  481.         local peripheralList = peripheral.getNames() -- Get table of connected peripherals
  482.  
  483.         deviceType = deviceType:lower() -- Make sure we're matching case here
  484.  
  485.         for peripheralIndex = 1, #peripheralList do
  486.                 -- Log every device found
  487.                 -- printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] attached as \""..peripheralList[peripheralIndex].."\".")
  488.                 if (string.lower(peripheral.getType(peripheralList[peripheralIndex])) == deviceType) then
  489.                         -- Log devices found which match deviceType and which device index we give them
  490.                         printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".")
  491.                         write("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".\n")
  492.                         deviceNames[deviceIndex] = peripheralList[peripheralIndex]
  493.                         deviceList[deviceIndex] = peripheral.wrap(peripheralList[peripheralIndex])
  494.                         deviceIndex = deviceIndex + 1
  495.                 end
  496.         end -- for peripheralIndex = 1, #peripheralList do
  497.  
  498.         return deviceList, deviceNames
  499. end -- function getDevices(deviceType)
  500.  
  501. -- Draw a line across the entire x-axis
  502. local function drawLine(yPos, monitorIndex)
  503.         local monitor = nil
  504.         monitor = monitorList[monitorIndex]
  505.  
  506.         if not monitor then
  507.                 printLog("monitor["..monitorIndex.."] in drawLine() is NOT a valid monitor.")
  508.                 return -- Invalid monitorIndex
  509.         end
  510.  
  511.         local width, height = monitor.getSize()
  512.  
  513.         for i=1, width do
  514.                 monitor.setCursorPos(i, yPos)
  515.                 monitor.write("-")
  516.         end
  517. end -- function drawLine(yPos,monitorIndex)
  518.  
  519.  
  520. -- Display a solid bar of specified color
  521. local function drawBar(startXPos, startYPos, endXPos, endYPos, color, monitorIndex)
  522.         local monitor = nil
  523.         monitor = monitorList[monitorIndex]
  524.  
  525.         if not monitor then
  526.                 printLog("monitor["..monitorIndex.."] in drawBar() is NOT a valid monitor.")
  527.                 return -- Invalid monitorIndex
  528.         end
  529.  
  530.         -- PaintUtils only outputs to term., not monitor.
  531.         -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
  532.         term.redirect(monitor)
  533.         paintutils.drawLine(startXPos, startYPos, endXPos, endYPos, color)
  534.         monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
  535.         termRestore()
  536. end -- function drawBar(startXPos, startYPos,endXPos,endYPos,color,monitorIndex)
  537.  
  538.  
  539. -- Display single pixel color
  540. local function drawPixel(xPos, yPos, color, monitorIndex)
  541.         local monitor = nil
  542.         monitor = monitorList[monitorIndex]
  543.  
  544.         if not monitor then
  545.                 printLog("monitor["..monitorIndex.."] in drawPixel() is NOT a valid monitor.")
  546.                 return -- Invalid monitorIndex
  547.         end
  548.  
  549.         -- PaintUtils only outputs to term., not monitor.
  550.         -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
  551.         term.redirect(monitor)
  552.         paintutils.drawPixel(xPos, yPos, color)
  553.         monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
  554.         termRestore()
  555. end -- function drawPixel(xPos, yPos, color, monitorIndex)
  556.  
  557. local function saveMonitorAssignments()
  558.         local assignments = {}
  559.         for monitor, data in pairs(monitorAssignments) do
  560.                 local name = nil
  561.                 if (data.type == "Reactor") then
  562.                         name = data.reactorName
  563.                 elseif (data.type == "Turbine") then
  564.                         name = data.turbineName
  565.                 else
  566.                         name = data.type
  567.                 end
  568.                 assignments[monitor] = name
  569.         end
  570.         config.save(monitorOptionFileName, {Monitors = assignments})
  571. end
  572.  
  573. UI = {
  574.         monitorIndex = 1,
  575.         reactorIndex = 1,
  576.         turbineIndex = 1
  577. }
  578.  
  579. UI.handlePossibleClick = function(self)
  580.         local monitorData = monitorAssignments[sideClick]
  581.         if monitorData == nil then
  582.                 printLog("UI.handlePossibleClick(): "..sideClick.." is unassigned, can't handle click", WARN)
  583.                 return
  584.         end
  585.  
  586.         self.monitorIndex = monitorData.index
  587.         local width, height = monitorList[self.monitorIndex].getSize()
  588.         -- All the last line are belong to us
  589.         if (yClick == height) then
  590.                 if (monitorData.type == "Reactor") then
  591.                         if (xClick == 1) then
  592.                                 self:selectPrevReactor()
  593.                         elseif (xClick == width) then
  594.                                 self:selectNextReactor()
  595.                         elseif (3 <= xClick and xClick <= width - 2) then
  596.                                 self:selectTurbine()
  597.                         end
  598.                 elseif (monitorData.type == "Turbine") then
  599.                         if (xClick == 1) then
  600.                                 self:selectPrevTurbine()
  601.                         elseif (xClick == width) then
  602.                                 self:selectNextTurbine()
  603.                         elseif (3 <= xClick and xClick <= width - 2) then
  604.                                 self:selectStatus()
  605.                         end
  606.                 elseif (monitorData.type == "Status") then
  607.                         if (xClick == 1) then
  608.                                 self.turbineIndex = #turbineList
  609.                                 self:selectTurbine()
  610.                         elseif (xClick == width) then
  611.                                 self.reactorIndex = 1
  612.                                 self:selectReactor()
  613.                         elseif (3 <= xClick and xClick <= width - 2) then
  614.                                 self:selectReactor()
  615.                         end
  616.                 else
  617.                         self:selectStatus()
  618.                 end
  619.                 -- Yes, that means we're skipping Debug. I figure everyone who wants that is
  620.                 -- bound to use the console key commands anyway, and that way we don't have
  621.                 -- it interfere with regular use.
  622.  
  623.                 sideClick, xClick, yClick = 0, 0, 0
  624.         else
  625.                 if (monitorData.type == "Turbine") then
  626.                         self:handleTurbineMonitorClick(monitorData.turbineIndex, monitorData.index)
  627.                 elseif (monitorData.type == "Reactor") then
  628.                         self:handleReactorMonitorClick(monitorData.reactorIndex, monitorData.index)
  629.                 end
  630.         end
  631. end -- UI.handlePossibleClick()
  632.  
  633. UI.logChange = function(self, messageText)
  634.         printLog("UI: "..messageText)
  635.         termRestore()
  636.         write(messageText.."\n")
  637. end
  638.  
  639. UI.selectNextMonitor = function(self)
  640.         self.monitorIndex = self.monitorIndex + 1
  641.         if self.monitorIndex > #monitorList then
  642.                 self.monitorIndex = 1
  643.         end
  644.         local messageText = "Selected monitor "..monitorNames[self.monitorIndex]
  645.         self:logChange(messageText)
  646. end -- UI.selectNextMonitor()
  647.  
  648.        
  649. UI.selectReactor = function(self)
  650.         monitorAssignments[monitorNames[self.monitorIndex]] = {type="Reactor", index=self.monitorIndex, reactorName=reactorNames[self.reactorIndex], reactorIndex=self.reactorIndex}
  651.         saveMonitorAssignments()
  652.         local messageText = "Selected reactor "..reactorNames[self.reactorIndex].." for display on "..monitorNames[self.monitorIndex]
  653.         self:logChange(messageText)
  654. end -- UI.selectReactor()
  655.        
  656. UI.selectPrevReactor = function(self)
  657.         if self.reactorIndex <= 1 then
  658.                 self.reactorIndex = #reactorList
  659.                 self:selectStatus()
  660.         else
  661.                 self.reactorIndex = self.reactorIndex - 1
  662.                 self:selectReactor()
  663.         end
  664. end -- UI.selectPrevReactor()
  665.  
  666. UI.selectNextReactor = function(self)
  667.         if self.reactorIndex >= #reactorList then
  668.                 self.reactorIndex = 1
  669.                 self.turbineIndex = 1
  670.                 self:selectTurbine()
  671.         else
  672.                 self.reactorIndex = self.reactorIndex + 1
  673.                 self:selectReactor()
  674.         end
  675. end -- UI.selectNextReactor()
  676.  
  677.  
  678. UI.selectTurbine = function(self)
  679.         monitorAssignments[monitorNames[self.monitorIndex]] = {type="Turbine", index=self.monitorIndex, turbineName=turbineNames[self.turbineIndex], turbineIndex=self.turbineIndex}
  680.         saveMonitorAssignments()
  681.         local messageText = "Selected turbine "..turbineNames[self.turbineIndex].." for display on "..monitorNames[self.monitorIndex]
  682.         self:logChange(messageText)
  683. end -- UI.selectTurbine()
  684.        
  685. UI.selectPrevTurbine = function(self)
  686.         if self.turbineIndex <= 1 then
  687.                 self.turbineIndex = #turbineList
  688.                 self.reactorIndex = #reactorList
  689.                 self:selectReactor()
  690.         else
  691.                 self.turbineIndex = self.turbineIndex - 1
  692.                 self:selectTurbine()
  693.         end
  694. end -- UI.selectPrevTurbine()
  695.        
  696. UI.selectNextTurbine = function(self)
  697.         if self.turbineIndex >= #turbineList then
  698.                 self.turbineIndex = 1
  699.                 self:selectStatus()
  700.         else
  701.                 self.turbineIndex = self.turbineIndex + 1
  702.                 self:selectTurbine()
  703.         end
  704. end -- UI.selectNextTurbine()
  705.        
  706.  
  707. UI.selectStatus = function(self)
  708.         monitorAssignments[monitorNames[self.monitorIndex]] = {type="Status", index=self.monitorIndex}
  709.         saveMonitorAssignments()
  710.         local messageText = "Selected status summary for display on "..monitorNames[self.monitorIndex]
  711.         self:logChange(messageText)
  712. end -- UI.selectStatus()
  713.        
  714. UI.selectDebug = function(self)
  715.         monitorAssignments[monitorNames[self.monitorIndex]] = {type="Debug", index=self.monitorIndex}
  716.         saveMonitorAssignments()
  717.         monitorList[self.monitorIndex].clear()
  718.         local messageText = "Selected debug output for display on "..monitorNames[self.monitorIndex]
  719.         self:logChange(messageText)
  720. end -- UI.selectDebug()
  721.        
  722. -- Allow controlling Reactor Control Rod Level from GUI
  723. UI.handleReactorMonitorClick = function(self, reactorIndex, monitorIndex)
  724.  
  725.         -- Decrease rod button: 23X, 4Y
  726.         -- Increase rod button: 28X, 4Y
  727.  
  728.         -- Grab current monitor
  729.         local monitor = nil
  730.         monitor = monitorList[monitorIndex]
  731.         if not monitor then
  732.                 printLog("monitor["..monitorIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  733.                 return -- Invalid monitorIndex
  734.         end
  735.  
  736.         -- Grab current reactor
  737.         local reactor = nil
  738.         reactor = reactorList[reactorIndex]
  739.         if not reactor then
  740.                 printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
  741.                 return -- Invalid reactorIndex
  742.         else
  743.                 printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
  744.                 if reactor.getConnected() then
  745.                         printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
  746.                 else
  747.                         printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  748.                         return -- Disconnected reactor
  749.                 end -- if reactor.getConnected() then
  750.         end -- if not reactor then
  751.  
  752.         local reactorStatus = _G[reactorNames[reactorIndex]]["ReactorOptions"]["Status"]
  753.  
  754.         local width, height = monitor.getSize()
  755.         if xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1) and (sideClick == monitorNames[monitorIndex]) then
  756.                 if yClick == 1 then
  757.                         reactor.setActive(not reactor.getActive()) -- Toggle reactor status
  758.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = reactor.getActive()
  759.                         config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  760.                         sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  761.  
  762.                         -- If someone offlines the reactor (offline after a status click was detected), then disable autoStart
  763.                         if not reactor.getActive() then
  764.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
  765.                         end
  766.                 end -- if yClick == 1 then
  767.         end -- if (xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
  768.  
  769.         -- Allow disabling rod level auto-adjust and only manual rod level control
  770.         if ((xClick > 23 and xClick < 28 and yClick == 4)
  771.                         or (xClick > 20 and xClick < 27 and yClick == 9))
  772.                         and (sideClick == monitorNames[monitorIndex]) then
  773.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]
  774.                 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  775.                 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  776.         end -- if (xClick > 23) and (xClick < 28) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  777.  
  778.         local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
  779.         local newRodPercentage = rodPercentage
  780.         if (xClick == 23) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  781.                 printLog("Decreasing Rod Levels in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  782.                 --Decrease rod level by amount
  783.                 newRodPercentage = rodPercentage - (5 * controlRodAdjustAmount)
  784.                 if newRodPercentage < 0 then
  785.                         newRodPercentage = 0
  786.                 end
  787.                 sideClick, xClick, yClick = 0, 0, 0
  788.  
  789.                 printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  790.                 reactor.setAllControlRodLevels(newRodPercentage)
  791.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = newRodPercentage
  792.  
  793.                 -- Save updated rod percentage
  794.                 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  795.                 rodPercentage = newRodPercentage
  796.         elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  797.                 printLog("Increasing Rod Levels in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  798.                 --Increase rod level by amount
  799.                 newRodPercentage = rodPercentage + (5 * controlRodAdjustAmount)
  800.                 if newRodPercentage > 100 then
  801.                         newRodPercentage = 100
  802.                 end
  803.                 sideClick, xClick, yClick = 0, 0, 0
  804.  
  805.                 printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  806.                 reactor.setAllControlRodLevels(newRodPercentage)
  807.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = newRodPercentage
  808.                
  809.                 -- Save updated rod percentage
  810.                 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  811.                 rodPercentage = round(newRodPercentage,0)
  812.         else
  813.                 printLog("No change to Rod Levels requested by "..progName.." GUI in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  814.         end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  815. end -- UI.handleReactorMonitorClick = function(self, reactorIndex, monitorIndex)
  816.  
  817. -- Allow controlling Turbine Flow Rate from GUI
  818. UI.handleTurbineMonitorClick = function(self, turbineIndex, monitorIndex)
  819.  
  820.         -- Decrease flow rate button: 22X, 4Y
  821.         -- Increase flow rate button: 28X, 4Y
  822.  
  823.         -- Grab current monitor
  824.         local monitor = nil
  825.         monitor = monitorList[monitorIndex]
  826.         if not monitor then
  827.                 printLog("monitor["..monitorIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  828.                 return -- Invalid monitorIndex
  829.         end
  830.  
  831.         -- Grab current turbine
  832.         local turbine = nil
  833.         turbine = turbineList[turbineIndex]
  834.         if not turbine then
  835.                 printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
  836.                 return -- Invalid turbineIndex
  837.         else
  838.                 printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
  839.                 if turbine.getConnected() then
  840.                         printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
  841.                 else
  842.                         printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  843.                         return -- Disconnected turbine
  844.                 end -- if turbine.getConnected() then
  845.         end
  846.  
  847.         local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
  848.         local turbineFlowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
  849.         local turbineStatus = _G[turbineNames[turbineIndex]]["TurbineOptions"]["Status"]
  850.         local width, height = monitor.getSize()
  851.  
  852.         if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
  853.                 if yClick == 1 then
  854.                         turbine.setActive(not turbine.getActive()) -- Toggle turbine status
  855.                         _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = turbine.getActive()
  856.                         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  857.                         sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  858.                         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  859.                 end -- if yClick == 1 then
  860.         end -- if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
  861.  
  862.         -- Allow disabling/enabling flow rate auto-adjust
  863.         if (xClick > 23 and xClick < 28 and yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  864.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
  865.                 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  866.         elseif (xClick > 20 and xClick < 27 and yClick == 10) and (sideClick == monitorNames[monitorIndex]) then
  867.                
  868.                 if ((_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "true")) then
  869.                         _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
  870.                 else
  871.                         _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
  872.                 end
  873.                 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
  874.                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  875.         end
  876.  
  877.         if (xClick == 22) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  878.                 printLog("Decrease to Flow Rate requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  879.                 --Decrease rod level by amount
  880.                 newTurbineFlowRate = turbineFlowRate - flowRateAdjustAmount
  881.                 if newTurbineFlowRate < 0 then
  882.                         newTurbineFlowRate = 0
  883.                 end
  884.                 sideClick, xClick, yClick = 0, 0, 0
  885.  
  886.                 -- Check bounds [0,2000]
  887.                 if newTurbineFlowRate > 2000 then
  888.                         newTurbineFlowRate = 2000
  889.                 elseif newTurbineFlowRate < 0 then
  890.                         newTurbineFlowRate = 0
  891.                 end
  892.  
  893.                 turbine.setFluidFlowRateMax(newTurbineFlowRate)
  894.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newTurbineFlowRate
  895.                 -- Save updated Turbine Flow Rate
  896.                 turbineFlowRate = newTurbineFlowRate
  897.                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  898.         elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  899.                 printLog("Increase to Flow Rate requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  900.                 --Increase rod level by amount
  901.                 newTurbineFlowRate = turbineFlowRate + flowRateAdjustAmount
  902.                 if newTurbineFlowRate > 2000 then
  903.                         newTurbineFlowRate = 2000
  904.                 end
  905.                 sideClick, xClick, yClick = 0, 0, 0
  906.  
  907.                 -- Check bounds [0,2000]
  908.                 if newTurbineFlowRate > 2000 then
  909.                         newTurbineFlowRate = 2000
  910.                 elseif newTurbineFlowRate < 0 then
  911.                         newTurbineFlowRate = 0
  912.                 end
  913.  
  914.                 turbine.setFluidFlowRateMax(newTurbineFlowRate)
  915.                
  916.                 -- Save updated Turbine Flow Rate
  917.                 turbineFlowRate = math.ceil(newTurbineFlowRate)
  918.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = turbineFlowRate
  919.                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  920.         else
  921.                 printLog("No change to Flow Rate requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  922.         end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  923.  
  924.         if (xClick == 22) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
  925.                 printLog("Decrease to Turbine RPM requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  926.                 rpmRateAdjustment = 909
  927.                 newTurbineBaseSpeed = turbineBaseSpeed - rpmRateAdjustment
  928.                 if newTurbineBaseSpeed < 908 then
  929.                         newTurbineBaseSpeed = 908
  930.                 end
  931.                 sideClick, xClick, yClick = 0, 0, 0
  932.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
  933.                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  934.         elseif (xClick == 29) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
  935.                 printLog("Increase to Turbine RPM requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  936.                 rpmRateAdjustment = 909
  937.                 newTurbineBaseSpeed = turbineBaseSpeed + rpmRateAdjustment
  938.                 if newTurbineBaseSpeed > 2726 then
  939.                         newTurbineBaseSpeed = 2726
  940.                 end
  941.                 sideClick, xClick, yClick = 0, 0, 0
  942.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
  943.                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  944.         else
  945.                 printLog("No change to Turbine RPM requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  946.         end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
  947. end -- function handleTurbineMonitorClick(turbineIndex, monitorIndex)
  948.  
  949.  
  950. -- End helper functions
  951.  
  952.  
  953. -- Then initialize the monitors
  954. local function findMonitors()
  955.         -- Empty out old list of monitors
  956.         monitorList = {}
  957.  
  958.         printLog("Finding monitors...")
  959.         monitorList, monitorNames = getDevices("monitor")
  960.  
  961.         if #monitorList == 0 then
  962.                 printLog("No monitors found, continuing headless")
  963.         else
  964.                 for monitorIndex = 1, #monitorList do
  965.                         local monitor, monitorX, monitorY = nil, nil, nil
  966.                         monitor = monitorList[monitorIndex]
  967.  
  968.                         if not monitor then
  969.                                 printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid monitor.")
  970.  
  971.                                 table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
  972.                                 if monitorIndex ~= #monitorList then    -- If we're not at the end, clean up
  973.                                         monitorIndex = monitorIndex - 1 -- We just removed an element
  974.                                 end -- if monitorIndex == #monitorList then
  975.                                 break -- Invalid monitorIndex
  976.                         else -- valid monitor
  977.                                 monitor.setTextScale(1.0) -- Make sure scale is correct
  978.                                 monitorX, monitorY = monitor.getSize()
  979.  
  980.                                 if (monitorX == nil) or (monitorY == nil) then -- somehow a valid monitor, but non-existent sizes? Maybe fixes Issue #3
  981.                                         printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid sized monitor.")
  982.  
  983.                                         table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
  984.                                         if monitorIndex ~= #monitorList then    -- If we're not at the end, clean up
  985.                                                 monitorIndex = monitorIndex - 1 -- We just removed an element
  986.                                         end -- if monitorIndex == #monitorList then
  987.                                         break -- Invalid monitorIndex
  988.  
  989.                                 -- Check for minimum size to allow for monitor.setTextScale(0.5) to work for 3x2 debugging monitor, changes getSize()
  990.                                 elseif monitorX < 29 or monitorY < 12 then
  991.                                         term.redirect(monitor)
  992.                                         monitor.clear()
  993.                                         printLog("Removing monitor "..monitorIndex.." for being too small.")
  994.                                         monitor.setCursorPos(1,2)
  995.                                         write("Monitor is the wrong size!\n")
  996.                                         write("Needs to be at least 3x2.")
  997.                                         termRestore()
  998.  
  999.                                         table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
  1000.                                         if monitorIndex == #monitorList then    -- If we're at the end already, break from loop
  1001.                                                 break
  1002.                                         else
  1003.                                                 monitorIndex = monitorIndex - 1 -- We just removed an element
  1004.                                         end -- if monitorIndex == #monitorList then
  1005.  
  1006.                                 end -- if monitorX < 29 or monitorY < 12 then
  1007.                         end -- if not monitor then
  1008.  
  1009.                         printLog("Monitor["..monitorIndex.."] named \""..monitorNames[monitorIndex].."\" is a valid monitor of size x:"..monitorX.." by y:"..monitorY..".")
  1010.                 end -- for monitorIndex = 1, #monitorList do
  1011.         end -- if #monitorList == 0 then
  1012.  
  1013.         printLog("Found "..#monitorList.." monitor(s) in findMonitors().")
  1014. end -- local function findMonitors()
  1015.  
  1016.  
  1017. -- Initialize all Big Reactors - Reactors
  1018. local function findReactors()
  1019.         -- Empty out old list of reactors
  1020.         newReactorList = {}
  1021.         printLog("Finding reactors...")
  1022.         newReactorList, reactorNames = getDevices("BigReactors-Reactor")
  1023.  
  1024.         if #newReactorList == 0 then
  1025.                 printLog("No reactors found!")
  1026.                 error("Can't find any reactors!")
  1027.         else  -- Placeholder
  1028.                 for reactorIndex = 1, #newReactorList do
  1029.                         local reactor = nil
  1030.                         reactor = newReactorList[reactorIndex]
  1031.  
  1032.                         if not reactor then
  1033.                                 printLog("reactorList["..reactorIndex.."] in findReactors() is NOT a valid Big Reactor.")
  1034.  
  1035.                                 table.remove(newReactorList, reactorIndex) -- Remove invalid reactor from list
  1036.                                 if reactorIndex ~= #newReactorList then    -- If we're not at the end, clean up
  1037.                                         reactorIndex = reactorIndex - 1 -- We just removed an element
  1038.                                 end -- reactorIndex ~= #newReactorList then
  1039.                                 return -- Invalid reactorIndex
  1040.                         else
  1041.                                 printLog("reactor["..reactorIndex.."] in findReactors() is a valid Big Reactor.")
  1042.                                 --initialize the default table
  1043.                                 _G[reactorNames[reactorIndex]] = {}
  1044.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"] = {}
  1045.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = 80
  1046.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = 0
  1047.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
  1048.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
  1049.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = 1400 --set for passive-cooled, the active-cooled subroutine will correct it
  1050.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = 1000
  1051.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = false
  1052.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = controlRodAdjustAmount
  1053.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = reactorNames[reactorIndex]
  1054.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  1055.                                 if reactor.getConnected() then
  1056.                                         printLog("reactor["..reactorIndex.."] in findReactors() is connected.")
  1057.                                 else
  1058.                                         printLog("reactor["..reactorIndex.."] in findReactors() is NOT connected.")
  1059.                                         return -- Disconnected reactor
  1060.                                 end
  1061.                         end
  1062.                        
  1063.                         --failsafe
  1064.                         local tempTable = _G[reactorNames[reactorIndex]]
  1065.                        
  1066.                         --check to make sure we get a valid config
  1067.                         if (config.load(reactorNames[reactorIndex]..".options")) ~= nil then
  1068.                                 tempTable = config.load(reactorNames[reactorIndex]..".options")
  1069.                         else
  1070.                                 --if we don't have a valid config from disk, make a valid config
  1071.                                 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1072.                         end
  1073.                        
  1074.                         --load values from tempTable, checking for nil values along the way
  1075.                         if tempTable["ReactorOptions"]["baseControlRodLevel"] ~= nil then
  1076.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tempTable["ReactorOptions"]["baseControlRodLevel"]
  1077.                         end
  1078.                        
  1079.                         if tempTable["ReactorOptions"]["lastTempPoll"] ~= nil then
  1080.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tempTable["ReactorOptions"]["lastTempPoll"]
  1081.                         end
  1082.                        
  1083.                         if tempTable["ReactorOptions"]["autoStart"] ~= nil then
  1084.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = tempTable["ReactorOptions"]["autoStart"]
  1085.                         end
  1086.                        
  1087.                         if tempTable["ReactorOptions"]["activeCooled"] ~= nil then
  1088.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = tempTable["ReactorOptions"]["activeCooled"]
  1089.                         end
  1090.                        
  1091.                         if tempTable["ReactorOptions"]["reactorMaxTemp"] ~= nil then
  1092.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tempTable["ReactorOptions"]["reactorMaxTemp"]
  1093.                         end
  1094.                        
  1095.                         if tempTable["ReactorOptions"]["reactorMinTemp"] ~= nil then
  1096.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tempTable["ReactorOptions"]["reactorMinTemp"]
  1097.                         end
  1098.                        
  1099.                         if tempTable["ReactorOptions"]["rodOverride"] ~= nil then
  1100.                                 printLog("Got value from config file for Rod Override, the value is: "..tostring(tempTable["ReactorOptions"]["rodOverride"]).." EOL")
  1101.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = tempTable["ReactorOptions"]["rodOverride"]
  1102.                         end
  1103.                        
  1104.                         if tempTable["ReactorOptions"]["controlRodAdjustAmount"] ~= nil then
  1105.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = tempTable["ReactorOptions"]["controlRodAdjustAmount"]
  1106.                         end
  1107.                        
  1108.                         if tempTable["ReactorOptions"]["reactorName"] ~= nil then
  1109.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = tempTable["ReactorOptions"]["reactorName"]
  1110.                         end
  1111.                        
  1112.                         if tempTable["ReactorOptions"]["reactorCruising"] ~= nil then
  1113.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = tempTable["ReactorOptions"]["reactorCruising"]
  1114.                         end
  1115.                        
  1116.                         --stricter typing, let's set these puppies up with the right type of value.
  1117.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"])
  1118.                        
  1119.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"])
  1120.                        
  1121.                         if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"]) == "true") then
  1122.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
  1123.                         else
  1124.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
  1125.                         end
  1126.                        
  1127.                         if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"]) == "true") then
  1128.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
  1129.                         else
  1130.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = false
  1131.                         end
  1132.                        
  1133.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"])
  1134.                        
  1135.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"])
  1136.                        
  1137.                         if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]) == "true") then
  1138.                                 printLog("Setting Rod Override for  "..reactorNames[reactorIndex].." to true because value was "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1139.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = true
  1140.                         else
  1141.                                 printLog("Setting Rod Override for  "..reactorNames[reactorIndex].." to false because value was "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1142.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = false
  1143.                         end
  1144.                        
  1145.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"])
  1146.  
  1147.                         if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"]) == "true") then
  1148.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
  1149.                         else
  1150.                                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  1151.                         end
  1152.                                                
  1153.                         --save one more time, in case we didn't have a complete config file before
  1154.                         config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1155.                 end -- for reactorIndex = 1, #newReactorList do
  1156.         end -- if #newReactorList == 0 then
  1157.  
  1158.         -- Overwrite old reactor list with the now updated list
  1159.         reactorList = newReactorList
  1160.  
  1161.         printLog("Found "..#reactorList.." reactor(s) in findReactors().")
  1162. end -- function findReactors()
  1163.  
  1164.  
  1165. -- Initialize all Big Reactors - Turbines
  1166. local function findTurbines()
  1167.         -- Empty out old list of turbines
  1168.         newTurbineList = {}
  1169.  
  1170.         printLog("Finding turbines...")
  1171.         newTurbineList, turbineNames = getDevices("BigReactors-Turbine")
  1172.  
  1173.         if #newTurbineList == 0 then
  1174.                 printLog("No turbines found") -- Not an error
  1175.         else
  1176.                 for turbineIndex = 1, #newTurbineList do
  1177.                         local turbine = nil
  1178.                         turbine = newTurbineList[turbineIndex]
  1179.  
  1180.                         if not turbine then
  1181.                                 printLog("turbineList["..turbineIndex.."] in findTurbines() is NOT a valid Big Reactors Turbine.")
  1182.  
  1183.                                 table.remove(newTurbineList, turbineIndex) -- Remove invalid turbine from list
  1184.                                 if turbineIndex ~= #newTurbineList then    -- If we're not at the end, clean up
  1185.                                         turbineIndex = turbineIndex - 1 -- We just removed an element
  1186.                                 end -- turbineIndex ~= #newTurbineList then
  1187.  
  1188.                                 return -- Invalid turbineIndex
  1189.                         else
  1190.                        
  1191.                                 _G[turbineNames[turbineIndex]] = {}
  1192.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"] = {}
  1193.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = 0
  1194.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = 2726
  1195.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = true
  1196.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = 2000 --open up with all the steam wide open
  1197.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
  1198.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = turbineNames[turbineIndex]
  1199.                                 printLog("turbineList["..turbineIndex.."] in findTurbines() is a valid Big Reactors Turbine.")
  1200.                                 if turbine.getConnected() then
  1201.                                         printLog("turbine["..turbineIndex.."] in findTurbines() is connected.")
  1202.                                 else
  1203.                                         printLog("turbine["..turbineIndex.."] in findTurbines() is NOT connected.")
  1204.                                         return -- Disconnected turbine
  1205.                                 end
  1206.                         end
  1207.                        
  1208.                         --failsafe
  1209.                         local tempTable = _G[turbineNames[turbineIndex]]
  1210.                        
  1211.                         --check to make sure we get a valid config
  1212.                         if (config.load(turbineNames[turbineIndex]..".options")) ~= nil then
  1213.                                 tempTable = config.load(turbineNames[turbineIndex]..".options")
  1214.                         else
  1215.                                 --if we don't have a valid config from disk, make a valid config
  1216.                                 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1217.                         end
  1218.                        
  1219.                         --load values from tempTable, checking for nil values along the way
  1220.                         if tempTable["TurbineOptions"]["LastSpeed"] ~= nil then
  1221.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = tempTable["TurbineOptions"]["LastSpeed"]
  1222.                         end
  1223.                        
  1224.                         if tempTable["TurbineOptions"]["BaseSpeed"] ~= nil then
  1225.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = tempTable["TurbineOptions"]["BaseSpeed"]
  1226.                         end
  1227.                        
  1228.                         if tempTable["TurbineOptions"]["autoStart"] ~= nil then
  1229.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = tempTable["TurbineOptions"]["autoStart"]
  1230.                         end
  1231.                        
  1232.                         if tempTable["TurbineOptions"]["LastFlow"] ~= nil then
  1233.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = tempTable["TurbineOptions"]["LastFlow"]
  1234.                         end
  1235.                        
  1236.                         if tempTable["TurbineOptions"]["flowOverride"] ~= nil then
  1237.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = tempTable["TurbineOptions"]["flowOverride"]
  1238.                         end
  1239.                        
  1240.                         if tempTable["TurbineOptions"]["turbineName"] ~= nil then
  1241.                                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = tempTable["TurbineOptions"]["turbineName"]
  1242.                         end
  1243.                        
  1244.                         --save once more just to make sure we got it
  1245.                         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  1246.                 end -- for turbineIndex = 1, #newTurbineList do
  1247.  
  1248.                 -- Overwrite old turbine list with the now updated list
  1249.                 turbineList = newTurbineList
  1250.         end -- if #newTurbineList == 0 then
  1251.  
  1252.         printLog("Found "..#turbineList.." turbine(s) in findTurbines().")
  1253. end -- function findTurbines()
  1254.  
  1255. -- Assign status, reactors, turbines and debug output to the monitors that shall display them
  1256. -- Depends on the [monitor,reactor,turbine]Lists being populated already
  1257. local function assignMonitors()
  1258.  
  1259.         local monitors = {}
  1260.         monitorAssignments = {}
  1261.  
  1262.         printLog("Assigning monitors...")
  1263.  
  1264.         local m = config.load(monitorOptionFileName)
  1265.         if (m ~= nil) then
  1266.                 -- first, merge the detected and the configured monitor lists
  1267.                 -- this is to ensure we pick up new additions to the network
  1268.                 for monitorIndex, monitorName in ipairs(monitorNames) do
  1269.                         monitors[monitorName] = m.Monitors[monitorName] or ""
  1270.                 end
  1271.                 -- then, go through all of it again to build our runtime data structure
  1272.                 for monitorName, assignedName in pairs(monitors) do
  1273.                         printLog("Looking for monitor and device named "..monitorName.." and "..assignedName)
  1274.                         for monitorIndex = 1, #monitorNames do
  1275.                                 printLog("if "..monitorName.." == "..monitorNames[monitorIndex].." then", DEBUG)
  1276.                                
  1277.                                 if monitorName == monitorNames[monitorIndex] then
  1278.                                         printLog("Found "..monitorName.." at index "..monitorIndex, DEBUG)
  1279.                                         if assignedName == "Status" then
  1280.                                                 monitorAssignments[monitorName] = {type="Status", index=monitorIndex}
  1281.                                         elseif assignedName == "Debug" then
  1282.                                                 monitorAssignments[monitorName] = {type="Debug", index=monitorIndex}
  1283.                                         else
  1284.                                                 local maxListLen = math.max(#reactorNames, #turbineNames)
  1285.                                                 for i = 1, maxListLen do
  1286.                                                         if assignedName == reactorNames[i] then
  1287.                                                                 monitorAssignments[monitorName] = {type="Reactor", index=monitorIndex, reactorName=reactorNames[i], reactorIndex=i}
  1288.                                                                 break
  1289.                                                         elseif assignedName == turbineNames[i] then
  1290.                                                                 monitorAssignments[monitorName] = {type="Turbine", index=monitorIndex, turbineName=turbineNames[i], turbineIndex=i}
  1291.                                                                 break
  1292.                                                         elseif i == maxListLen then
  1293.                                                                 printLog("assignMonitors(): Monitor "..monitorName.." was configured to display nonexistant device "..assignedName..". Setting inactive.", WARN)
  1294.                                                                 monitorAssignments[monitorName] = {type="Inactive", index=monitorIndex}
  1295.                                                         end
  1296.                                                 end
  1297.                                         end
  1298.                                         break
  1299.                                 elseif monitorIndex == #monitorNames then
  1300.                                         printLog("assignMonitors(): Monitor "..monitorName.." not found. It was configured to display device "..assignedName..". Discarding.", WARN)
  1301.                                 end
  1302.                         end
  1303.                 end
  1304.         else
  1305.                 printLog("No valid monitor configuration found, generating...")
  1306.  
  1307.                 -- create assignments that reflect the setup before 0.3.17
  1308.                 local monitorIndex = 1
  1309.                 monitorAssignments[monitorNames[1]] = {type="Status", index=1}
  1310.                 monitorIndex = monitorIndex + 1
  1311.                 for reactorIndex = 1, #reactorList do
  1312.                         if monitorIndex > #monitorList then
  1313.                                 break
  1314.                         end
  1315.                         monitorAssignments[monitorNames[monitorIndex]] = {type="Reactor", index=monitorIndex, reactorName=reactorNames[reactorIndex], reactorIndex=reactorIndex}
  1316.                         printLog(monitorNames[monitorIndex].." -> "..reactorNames[reactorIndex])
  1317.  
  1318.                         monitorIndex = monitorIndex + 1
  1319.                 end
  1320.                 for turbineIndex = 1, #turbineList do
  1321.                         if monitorIndex > #monitorList then
  1322.                                 break
  1323.                         end
  1324.                         monitorAssignments[monitorNames[monitorIndex]] = {type="Turbine", index=monitorIndex, turbineName=turbineNames[turbineIndex], turbineIndex=turbineIndex}
  1325.                         printLog(monitorNames[monitorIndex].." -> "..turbineNames[turbineIndex])
  1326.  
  1327.                         monitorIndex = monitorIndex + 1
  1328.                 end
  1329.                 if monitorIndex <= #monitorList then
  1330.                         monitorAssignments[monitorNames[#monitorList]] = {type="Debug", index=#monitorList}
  1331.                 end
  1332.         end
  1333.  
  1334.         tprint(monitorAssignments)
  1335.  
  1336.         saveMonitorAssignments()
  1337.  
  1338. end -- function assignMonitors()
  1339.  
  1340. local eventHandler
  1341. -- Replacement for sleep, which passes on events instead of dropping themo
  1342. -- Straight from http://computercraft.info/wiki/Os.sleep
  1343. local function wait(time)
  1344.         local timer = os.startTimer(time)
  1345.  
  1346.         while true do
  1347.                 local event = {os.pullEvent()}
  1348.  
  1349.                 if (event[1] == "timer" and event[2] == timer) then
  1350.                         break
  1351.                 else
  1352.                         eventHandler(event[1], event[2], event[3], event[4])
  1353.                 end
  1354.         end
  1355. end
  1356.  
  1357.  
  1358. -- Return current energy buffer in a specific reactor by %
  1359. local function getReactorStoredEnergyBufferPercent(reactor)
  1360.         printLog("Called as getReactorStoredEnergyBufferPercent(reactor).")
  1361.  
  1362.         if not reactor then
  1363.                 printLog("getReactorStoredEnergyBufferPercent() did NOT receive a valid Big Reactor Reactor.")
  1364.                 return -- Invalid reactorIndex
  1365.         else
  1366.                 printLog("getReactorStoredEnergyBufferPercent() did receive a valid Big Reactor Reactor.")
  1367.         end
  1368.  
  1369.         local energyBufferStorage = reactor.getEnergyStored()
  1370.         return round(energyBufferStorage/100000, 1) -- (buffer/10000000 RF)*100%
  1371. end -- function getReactorStoredEnergyBufferPercent(reactor)
  1372.  
  1373.  
  1374. -- Return current energy buffer in a specific Turbine by %
  1375. local function getTurbineStoredEnergyBufferPercent(turbine)
  1376.         printLog("Called as getTurbineStoredEnergyBufferPercent(turbine)")
  1377.  
  1378.         if not turbine then
  1379.                 printLog("getTurbineStoredEnergyBufferPercent() did NOT receive a valid Big Reactor Turbine.")
  1380.                 return -- Invalid reactorIndex
  1381.         else
  1382.                 printLog("getTurbineStoredEnergyBufferPercent() did receive a valid Big Reactor Turbine.")
  1383.         end
  1384.  
  1385.         local energyBufferStorage = turbine.getEnergyStored()
  1386.         return round(energyBufferStorage/10000, 1) -- (buffer/1000000 RF)*100%
  1387. end -- function getTurbineStoredEnergyBufferPercent(turbine)
  1388.  
  1389. local function reactorCruise(cruiseMaxTemp, cruiseMinTemp, reactorIndex)
  1390.         printLog("Called as reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp=".._G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"]..",reactorIndex="..reactorIndex..").")
  1391.        
  1392.         --sanitization
  1393.         local lastPolledTemp = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"])
  1394.         cruiseMaxTemp = tonumber(cruiseMaxTemp)
  1395.         cruiseMinTemp = tonumber(cruiseMinTemp)
  1396.        
  1397.         if ((lastPolledTemp < cruiseMaxTemp) and (lastPolledTemp > cruiseMinTemp)) then
  1398.                 local reactor = nil
  1399.                 reactor = reactorList[reactorIndex]
  1400.                 if not reactor then
  1401.                         printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is NOT a valid Big Reactor.")
  1402.                         return -- Invalid reactorIndex
  1403.                 else
  1404.                         printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is a valid Big Reactor.")
  1405.                         if reactor.getConnected() then
  1406.                                 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is connected.")
  1407.                         else
  1408.                                 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is NOT connected.")
  1409.                                 return -- Disconnected reactor
  1410.                         end -- if reactor.getConnected() then
  1411.                 end -- if not reactor then
  1412.  
  1413.                 local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
  1414.                 local reactorTemp = math.ceil(reactor.getFuelTemperature())
  1415.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = rodPercentage
  1416.                
  1417.                 if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
  1418.                         if (reactorTemp < lastPolledTemp) then
  1419.                                 rodPercentage = (rodPercentage - 1)
  1420.                                 --Boundary check
  1421.                                 if rodPercentage < 0 then
  1422.                                         reactor.setAllControlRodLevels(0)
  1423.                                 else
  1424.                                         reactor.setAllControlRodLevels(rodPercentage)
  1425.                                 end
  1426.                         else
  1427.                                 rodPercentage = (rodPercentage + 1)
  1428.                                 --Boundary check
  1429.                                 if rodPercentage > 99 then
  1430.                                         reactor.setAllControlRodLevels(99)
  1431.                                 else
  1432.                                         reactor.setAllControlRodLevels(rodPercentage)
  1433.                                 end
  1434.                         end -- if (reactorTemp > lastPolledTemp) then
  1435.                 else
  1436.                         --disengage cruise, we've fallen out of the ideal temperature range
  1437.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  1438.                 end -- if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
  1439.         else
  1440.                 --I don't know how we'd get here, but let's turn the cruise mode off
  1441.                 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
  1442.         end -- if ((lastPolledTemp < cruiseMaxTemp) and (lastPolledTemp > cruiseMinTemp)) then
  1443.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
  1444.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
  1445.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = cruiseMaxTemp
  1446.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = cruiseMinTemp
  1447.         config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1448. end -- function reactorCruise(cruiseMaxTemp, cruiseMinTemp, lastPolledTemp, reactorIndex)
  1449.  
  1450. -- Modify reactor control rod levels to keep temperature with defined parameters, but
  1451. -- wait an in-game half-hour for the temperature to stabalize before modifying again
  1452. local function temperatureControl(reactorIndex)
  1453.         printLog("Called as temperatureControl(reactorIndex="..reactorIndex..")")
  1454.  
  1455.         local reactor = nil
  1456.         reactor = reactorList[reactorIndex]
  1457.         if not reactor then
  1458.                 printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT a valid Big Reactor.")
  1459.                 return -- Invalid reactorIndex
  1460.         else
  1461.                 printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is a valid Big Reactor.")
  1462.  
  1463.                 if reactor.getConnected() then
  1464.                         printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is connected.")
  1465.                 else
  1466.                         printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT connected.")
  1467.                         return -- Disconnected reactor
  1468.                 end -- if reactor.getConnected() then
  1469.         end
  1470.  
  1471.         local reactorNum = reactorIndex
  1472.         local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
  1473.         local reactorTemp = math.ceil(reactor.getFuelTemperature())
  1474.         local localMinReactorTemp, localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"], _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
  1475.  
  1476.         --bypass if the reactor itself is set to not be auto-controlled
  1477.         if ((not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]) or (_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] == "false")) then
  1478.                 -- No point modifying control rod levels for temperature if the reactor is offline
  1479.                 if reactor.getActive() then
  1480.                         -- Actively cooled reactors should range between 0^C-300^C
  1481.                         -- Actually, active-cooled reactors should range between 300 and 420C (Mechaet)
  1482.                         -- Accordingly I changed the below lines
  1483.                         if reactor.isActivelyCooled() and not knowlinglyOverride then
  1484.                                 -- below was 0
  1485.                                 localMinReactorTemp = 300
  1486.                                 -- below was 300
  1487.                                 localMaxReactorTemp = 420
  1488.                         else
  1489.                                 localMinReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"]
  1490.                                 localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
  1491.                         end
  1492.                         local lastTempPoll = _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"]
  1493.                         if _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] then
  1494.                                 --let's bypass all this math and hit the much-more-subtle cruise feature
  1495.                                 --printLog("min: "..localMinReactorTemp..", max: "..localMaxReactorTemp..", lasttemp: "..lastTempPoll..", ri: "..reactorIndex.."  EOL")
  1496.                                 reactorCruise(localMaxReactorTemp, localMinReactorTemp, reactorIndex)
  1497.                         else
  1498.                                 local localControlRodAdjustAmount = _G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"]
  1499.                                 -- Don't bring us to 100, that's effectively a shutdown
  1500.                                 if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
  1501.                                         --increase the rods, but by how much?
  1502.                                         if (reactorTemp > lastTempPoll) then
  1503.                                                 --we're climbing, we need to get this to decrease
  1504.                                                 if ((reactorTemp - lastTempPoll) > 100) then
  1505.                                                         --we're climbing really fast, arrest it
  1506.                                                         if (rodPercentage + (10 * localControlRodAdjustAmount)) > 99 then
  1507.                                                                 reactor.setAllControlRodLevels(99)
  1508.                                                         else
  1509.                                                                 reactor.setAllControlRodLevels(rodPercentage + (10 * localControlRodAdjustAmount))
  1510.                                                         end
  1511.                                                 else
  1512.                                                         --we're not climbing by leaps and bounds, let's give it a rod adjustment based on temperature increase
  1513.                                                         local diffAmount = reactorTemp - lastTempPoll
  1514.                                                         diffAmount = (round(diffAmount/10, 0))/5
  1515.                                                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = diffAmount
  1516.                                                         if (rodPercentage + diffAmount) > 99 then
  1517.                                                                 reactor.setAllControlRodLevels(99)
  1518.                                                         else
  1519.                                                                 reactor.setAllControlRodLevels(rodPercentage + diffAmount)
  1520.                                                         end
  1521.                                                 end --if ((reactorTemp - lastTempPoll) > 100) then
  1522.                                         elseif ((lastTempPoll - reactorTemp) < (reactorTemp * 0.005)) then
  1523.                                                 --temperature has stagnated, kick it very lightly
  1524.                                                 local controlRodAdjustment = 1
  1525.                                                 if (rodPercentage + controlRodAdjustment) > 99 then
  1526.                                                         reactor.setAllControlRodLevels(99)
  1527.                                                 else
  1528.                                                         reactor.setAllControlRodLevels(rodPercentage + controlRodAdjustment)
  1529.                                                 end
  1530.                                         end --if (reactorTemp > lastTempPoll) then
  1531.                                                 --worth noting that if we're above temp but decreasing, we do nothing. let it continue decreasing.
  1532.  
  1533.                                 elseif ((reactorTemp < localMinReactorTemp) and (rodPercentage ~=0)) or (steamRequested - steamDelivered > 0) then
  1534.                                         --we're too cold. time to warm up, but by how much?
  1535.                                         if (steamRequested > (steamDelivered*2)) then
  1536.                                                 -- Bridge to machine room: Full steam ahead!
  1537.                                                 reactor.setAllControlRodLevels(0)
  1538.                                         elseif (reactorTemp < lastTempPoll) then
  1539.                                                 --we're descending, let's stop that.
  1540.                                                 if ((lastTempPoll - reactorTemp) > 100) then
  1541.                                                         --we're headed for a new ice age, bring the heat
  1542.                                                         if (rodPercentage - (10 * localControlRodAdjustAmount)) < 0 then
  1543.                                                                 reactor.setAllControlRodLevels(0)
  1544.                                                         else
  1545.                                                                 reactor.setAllControlRodLevels(rodPercentage - (10 * localControlRodAdjustAmount))
  1546.                                                         end
  1547.                                                 else
  1548.                                                         --we're not descending quickly, let's bump it based on descent rate
  1549.                                                         local diffAmount = lastTempPoll - reactorTemp
  1550.                                                         diffAmount = (round(diffAmount/10, 0))/5
  1551.                                                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = diffAmount
  1552.                                                         if (rodPercentage - diffAmount) < 0 then
  1553.                                                                 reactor.setAllControlRodLevels(0)
  1554.                                                         else
  1555.                                                                 reactor.setAllControlRodLevels(rodPercentage - diffAmount)
  1556.                                                         end
  1557.                                                 end --if ((lastTempPoll - reactorTemp) > 100) then
  1558.                                         elseif (reactorTemp == lastTempPoll) then
  1559.                                                 --temperature has stagnated, kick it very lightly
  1560.                                                 local controlRodAdjustment = 1
  1561.                                                 if (rodPercentage - controlRodAdjustment) < 0 then
  1562.                                                         reactor.setAllControlRodLevels(0)
  1563.                                                 else
  1564.                                                         reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustment)
  1565.                                                 end --if (rodPercentage - controlRodAdjustment) < 0 then
  1566.  
  1567.                                         end --if (reactorTemp < lastTempPoll) then
  1568.                                         --if we're below temp but increasing, do nothing and let it continue to rise.
  1569.                                 end --if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
  1570.  
  1571.                                 if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) and not (steamRequested - steamDelivered > 0) then
  1572.                                         --engage cruise mode
  1573.                                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
  1574.                                 end
  1575.                         end -- if reactorCruising then
  1576.                         --always set this number
  1577.                         _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
  1578.                         config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
  1579.                 end -- if reactor.getActive() then
  1580.         else
  1581.                 printLog("Bypassed temperature control due to rodOverride being "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1582.         end -- if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
  1583. end -- function temperatureControl(reactorIndex)
  1584.  
  1585. -- Load saved reactor parameters if ReactorOptions file exists
  1586. local function loadReactorOptions()
  1587.         local reactorOptions = fs.open("ReactorOptions", "r") -- See http://computercraft.info/wiki/Fs.open
  1588.  
  1589.         if reactorOptions then
  1590.                 -- The following values were added by Lolmer
  1591.                 minStoredEnergyPercent = reactorOptions.readLine()
  1592.                 maxStoredEnergyPercent = reactorOptions.readLine()
  1593.                 --added by Mechaet
  1594.                 -- If we succeeded in reading a string, convert it to a number
  1595.  
  1596.                 if minStoredEnergyPercent ~= nil then
  1597.                         minStoredEnergyPercent = tonumber(minStoredEnergyPercent)
  1598.                 end
  1599.  
  1600.                 if maxStoredEnergyPercent ~= nil then
  1601.                         maxStoredEnergyPercent = tonumber(maxStoredEnergyPercent)
  1602.                 end
  1603.  
  1604.                 reactorOptions.close()
  1605.         end -- if reactorOptions then
  1606.  
  1607.         -- Set default values if we failed to read any of the above
  1608.         if minStoredEnergyPercent == nil then
  1609.                 minStoredEnergyPercent = 15
  1610.         end
  1611.  
  1612.         if maxStoredEnergyPercent == nil then
  1613.                 maxStoredEnergyPercent = 85
  1614.         end
  1615.  
  1616. end -- function loadReactorOptions()
  1617.  
  1618.  
  1619. -- Save our reactor parameters
  1620. local function saveReactorOptions()
  1621.         local reactorOptions = fs.open("ReactorOptions", "w") -- See http://computercraft.info/wiki/Fs.open
  1622.  
  1623.         -- If we can save the files, save them
  1624.         if reactorOptions then
  1625.                 local reactorIndex = 1
  1626.                 -- The following values were added by Lolmer
  1627.                 reactorOptions.writeLine(minStoredEnergyPercent)
  1628.                 reactorOptions.writeLine(maxStoredEnergyPercent)
  1629.                 reactorOptions.close()
  1630.         else
  1631.                 printLog("Failed to open file ReactorOptions for writing!")
  1632.         end -- if reactorOptions then
  1633. end -- function saveReactorOptions()
  1634.  
  1635.  
  1636. local function displayReactorBars(barParams)
  1637.         -- Default to first reactor and first monitor
  1638.         setmetatable(barParams,{__index={reactorIndex=1, monitorIndex=1}})
  1639.         local reactorIndex, monitorIndex =
  1640.                 barParams[1] or barParams.reactorIndex,
  1641.                 barParams[2] or barParams.monitorIndex
  1642.  
  1643.         printLog("Called as displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1644.  
  1645.         -- Grab current monitor
  1646.         local monitor = nil
  1647.         monitor = monitorList[monitorIndex]
  1648.         if not monitor then
  1649.                 printLog("monitor["..monitorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  1650.                 return -- Invalid monitorIndex
  1651.         end
  1652.  
  1653.         -- Grab current reactor
  1654.         local reactor = nil
  1655.         reactor = reactorList[reactorIndex]
  1656.         if not reactor then
  1657.                 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
  1658.                 return -- Invalid reactorIndex
  1659.         else
  1660.                 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
  1661.                 if reactor.getConnected() then
  1662.                         printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
  1663.                 else
  1664.                         printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  1665.                         return -- Disconnected reactor
  1666.                 end -- if reactor.getConnected() then
  1667.         end -- if not reactor then
  1668.  
  1669.         -- Draw border lines
  1670.         local width, height = monitor.getSize()
  1671.         printLog("Size of monitor is "..width.."w x"..height.."h in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
  1672.  
  1673.         for i=3, 5 do
  1674.                 monitor.setCursorPos(22, i)
  1675.                 monitor.write("|")
  1676.         end
  1677.  
  1678.         drawLine(6, monitorIndex)
  1679.         monitor.setCursorPos(1, height)
  1680.         monitor.write("< ")
  1681.         monitor.setCursorPos(width-1, height)
  1682.         monitor.write(" >")
  1683.  
  1684.         -- Draw some text
  1685.         local fuelString = "Fuel: "
  1686.         local tempString = "Temp: "
  1687.         local energyBufferString = ""
  1688.  
  1689.         if reactor.isActivelyCooled() then
  1690.                 energyBufferString = "Steam: "
  1691.         else
  1692.                 energyBufferString = "Energy: "
  1693.         end
  1694.  
  1695.         local padding = math.max(string.len(fuelString), string.len(tempString), string.len(energyBufferString))
  1696.  
  1697.         local fuelPercentage = round(reactor.getFuelAmount()/reactor.getFuelAmountMax()*100,1)
  1698.         print{fuelString,2,3,monitorIndex}
  1699.         print{fuelPercentage.." %",padding+2,3,monitorIndex}
  1700.  
  1701.         local reactorTemp = math.ceil(reactor.getFuelTemperature())
  1702.         print{tempString,2,5,monitorIndex}
  1703.         print{reactorTemp.." C",padding+2,5,monitorIndex}
  1704.  
  1705.         local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
  1706.         printLog("Current Rod Percentage for reactor["..reactorIndex.."] is "..rodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1707.         print{"Rod (%)",23,3,monitorIndex}
  1708.         print{"<     >",23,4,monitorIndex}
  1709.         print{stringTrim(rodPercentage),25,4,monitorIndex}
  1710.  
  1711.  
  1712.         -- getEnergyProducedLastTick() is used for both RF/t (passively cooled) and mB/t (actively cooled)
  1713.         local energyBuffer = reactor.getEnergyProducedLastTick()
  1714.         if reactor.isActivelyCooled() then
  1715.                 printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." mB last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1716.         else
  1717.                 printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." RF last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1718.         end
  1719.  
  1720.         print{energyBufferString,2,4,monitorIndex}
  1721.  
  1722.         -- Actively cooled reactors do not produce energy, only hot fluid mB/t to be used in a turbine
  1723.         -- still uses getEnergyProducedLastTick for mB/t of hot fluid generated
  1724.         if not reactor.isActivelyCooled() then
  1725.                 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT an actively cooled reactor.")
  1726.  
  1727.                 -- Draw stored energy buffer bar
  1728.                 drawBar(2,8,28,8,colors.gray,monitorIndex)
  1729.  
  1730.                 local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
  1731.                 if curStoredEnergyPercent > 4 then
  1732.                         drawBar(2, 8, math.floor(26*curStoredEnergyPercent/100)+2, 8, colors.yellow, monitorIndex)
  1733.                 elseif curStoredEnergyPercent > 0 then
  1734.                         drawPixel(2, 8, colors.yellow, monitorIndex)
  1735.                 end -- if curStoredEnergyPercent > 4 then
  1736.  
  1737.                 print{"Energy Buffer",2,7,monitorIndex}
  1738.                 print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),7,monitorIndex}
  1739.                 print{"%",28,7,monitorIndex}
  1740.  
  1741.                 print{math.ceil(energyBuffer).." RF/t",padding+2,4,monitorIndex}
  1742.         else
  1743.                 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is an actively cooled reactor.")
  1744.                 print{math.ceil(energyBuffer).." mB/t",padding+2,4,monitorIndex}
  1745.         end -- if not reactor.isActivelyCooled() then
  1746.  
  1747.         -- Print rod override status
  1748.         local reactorRodOverrideStatus = ""
  1749.  
  1750.         print{"Rod Auto-adjust:",2,9,monitorIndex}
  1751.  
  1752.         if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
  1753.                 printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1754.                 reactorRodOverrideStatus = "Enabled"
  1755.                 monitor.setTextColor(colors.green)
  1756.         else
  1757.                 printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
  1758.                 reactorRodOverrideStatus = "Disabled"
  1759.                 monitor.setTextColor(colors.red)
  1760.         end -- if not reactorRodOverride then
  1761.         printLog("reactorRodOverrideStatus is \""..reactorRodOverrideStatus.."\" in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
  1762.  
  1763.         print{reactorRodOverrideStatus, width - string.len(reactorRodOverrideStatus) - 1, 9, monitorIndex}
  1764.         monitor.setTextColor(colors.white)
  1765.  
  1766.         print{"Reactivity: "..math.ceil(reactor.getFuelReactivity()).." %", 2, 10, monitorIndex}
  1767.         print{"Fuel: "..round(reactor.getFuelConsumedLastTick(),3).." mB/t", 2, 11, monitorIndex}
  1768.         print{"Waste: "..reactor.getWasteAmount().." mB", width-(string.len(reactor.getWasteAmount())+10), 11, monitorIndex}
  1769.  
  1770.         monitor.setTextColor(colors.blue)
  1771.         printCentered(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"],12,monitorIndex)
  1772.         monitor.setTextColor(colors.white)
  1773.  
  1774.         -- monitor switch controls
  1775.         monitor.setCursorPos(1, height)
  1776.         monitor.write("<")
  1777.         monitor.setCursorPos(width, height)
  1778.         monitor.write(">")
  1779.  
  1780. end -- function displayReactorBars(barParams)
  1781.  
  1782.  
  1783. local function reactorStatus(statusParams)
  1784.         -- Default to first reactor and first monitor
  1785.         setmetatable(statusParams,{__index={reactorIndex=1, monitorIndex=1}})
  1786.         local reactorIndex, monitorIndex =
  1787.                 statusParams[1] or statusParams.reactorIndex,
  1788.                 statusParams[2] or statusParams.monitorIndex
  1789.         printLog("Called as reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
  1790.  
  1791.         -- Grab current monitor
  1792.         local monitor = nil
  1793.         monitor = monitorList[monitorIndex]
  1794.         if not monitor then
  1795.                 printLog("monitor["..monitorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  1796.                 return -- Invalid monitorIndex
  1797.         end
  1798.  
  1799.         -- Grab current reactor
  1800.         local reactor = nil
  1801.         reactor = reactorList[reactorIndex]
  1802.         if not reactor then
  1803.                 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
  1804.                 return -- Invalid reactorIndex
  1805.         else
  1806.                 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
  1807.         end
  1808.  
  1809.         local width, height = monitor.getSize()
  1810.         local reactorStatus = ""
  1811.  
  1812.         if reactor.getConnected() then
  1813.                 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
  1814.  
  1815.                 if reactor.getActive() then
  1816.                         reactorStatus = "ONLINE"
  1817.  
  1818.                         -- Set "ONLINE" to blue if the actively cooled reactor is both in cruise mode and online
  1819.                         if _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] and reactor.isActivelyCooled() then
  1820.                                 monitor.setTextColor(colors.blue)
  1821.                         else
  1822.                                 monitor.setTextColor(colors.green)
  1823.                         end -- if reactorCruising and reactor.isActivelyCooled() then
  1824.                 else
  1825.                         reactorStatus = "OFFLINE"
  1826.                         monitor.setTextColor(colors.red)
  1827.                 end -- if reactor.getActive() then
  1828.  
  1829.         else
  1830.                 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  1831.                 reactorStatus = "DISCONNECTED"
  1832.                 monitor.setTextColor(colors.red)
  1833.         end -- if reactor.getConnected() then
  1834.         _G[reactorNames[reactorIndex]]["ReactorOptions"]["Status"] = reactorStatus
  1835.  
  1836.         print{reactorStatus, width - string.len(reactorStatus) - 1, 1, monitorIndex}
  1837.         monitor.setTextColor(colors.white)
  1838. end -- function reactorStatus(statusParams)
  1839.  
  1840.  
  1841. -- Display all found reactors' status to selected monitor
  1842. -- This is only called if multiple reactors and/or a reactor plus at least one turbine are found
  1843. local function displayAllStatus(monitorIndex)
  1844.         local reactor, turbine = nil, nil
  1845.         local onlineReactor, onlineTurbine = 0, 0
  1846.         local totalReactorRF, totalReactorSteam, totalTurbineRF = 0, 0, 0
  1847.         local totalReactorFuelConsumed = 0
  1848.         local totalCoolantStored, totalSteamStored, totalEnergy, totalMaxEnergyStored = 0, 0, 0, 0 -- Total turbine and reactor energy buffer and overall capacity
  1849.         local maxSteamStored = (2000*#turbineList)+(5000*#reactorList)
  1850.         local maxCoolantStored = (2000*#turbineList)+(5000*#reactorList)
  1851.  
  1852.         local monitor = monitorList[monitorIndex]
  1853.         if not monitor then
  1854.                 printLog("monitor["..monitorIndex.."] in displayAllStatus() is NOT a valid monitor.")
  1855.                 return -- Invalid monitorIndex
  1856.         end
  1857.  
  1858.         for reactorIndex = 1, #reactorList do
  1859.                 reactor = reactorList[reactorIndex]
  1860.                 if not reactor then
  1861.                         printLog("reactor["..reactorIndex.."] in displayAllStatus() is NOT a valid Big Reactor.")
  1862.                         break -- Invalid reactorIndex
  1863.                 else
  1864.                         printLog("reactor["..reactorIndex.."] in displayAllStatus() is a valid Big Reactor.")
  1865.                 end -- if not reactor then
  1866.  
  1867.                 if reactor.getConnected() then
  1868.                         printLog("reactor["..reactorIndex.."] in displayAllStatus() is connected.")
  1869.                         if reactor.getActive() then
  1870.                                 onlineReactor = onlineReactor + 1
  1871.                                 totalReactorFuelConsumed = totalReactorFuelConsumed + reactor.getFuelConsumedLastTick()
  1872.                         end -- reactor.getActive() then
  1873.  
  1874.                         -- Actively cooled reactors do not produce or store energy
  1875.                         if not reactor.isActivelyCooled() then
  1876.                                 totalMaxEnergyStored = totalMaxEnergyStored + 10000000 -- Reactors store 10M RF
  1877.                                 totalEnergy = totalEnergy + reactor.getEnergyStored()
  1878.                                 totalReactorRF = totalReactorRF + reactor.getEnergyProducedLastTick()
  1879.                         else
  1880.                                 totalReactorSteam = totalReactorSteam + reactor.getEnergyProducedLastTick()
  1881.                                 totalSteamStored = totalSteamStored + reactor.getHotFluidAmount()
  1882.                                 totalCoolantStored = totalCoolantStored + reactor.getCoolantAmount()
  1883.                         end -- if not reactor.isActivelyCooled() then
  1884.                 else
  1885.                         printLog("reactor["..reactorIndex.."] in displayAllStatus() is NOT connected.")
  1886.                 end -- if reactor.getConnected() then
  1887.         end -- for reactorIndex = 1, #reactorList do
  1888.  
  1889.         for turbineIndex = 1, #turbineList do
  1890.                 turbine = turbineList[turbineIndex]
  1891.                 if not turbine then
  1892.                         printLog("turbine["..turbineIndex.."] in displayAllStatus() is NOT a valid Turbine.")
  1893.                         break -- Invalid turbineIndex
  1894.                 else
  1895.                         printLog("turbine["..turbineIndex.."] in displayAllStatus() is a valid Turbine.")
  1896.                 end -- if not turbine then
  1897.  
  1898.                 if turbine.getConnected() then
  1899.                         printLog("turbine["..turbineIndex.."] in displayAllStatus() is connected.")
  1900.                         if turbine.getActive() then
  1901.                                 onlineTurbine = onlineTurbine + 1
  1902.                         end
  1903.  
  1904.                         totalMaxEnergyStored = totalMaxEnergyStored + 1000000 -- Turbines store 1M RF
  1905.                         totalEnergy = totalEnergy + turbine.getEnergyStored()
  1906.                         totalTurbineRF = totalTurbineRF + turbine.getEnergyProducedLastTick()
  1907.                         totalSteamStored = totalSteamStored + turbine.getInputAmount()
  1908.                         totalCoolantStored = totalCoolantStored + turbine.getOutputAmount()
  1909.                 else
  1910.                         printLog("turbine["..turbineIndex.."] in displayAllStatus() is NOT connected.")
  1911.                 end -- if turbine.getConnected() then
  1912.         end -- for turbineIndex = 1, #turbineList do
  1913.  
  1914.         print{"Reactors online/found: "..onlineReactor.."/"..#reactorList, 2, 3, monitorIndex}
  1915.         print{"Turbines online/found: "..onlineTurbine.."/"..#turbineList, 2, 4, monitorIndex}
  1916.  
  1917.         if totalReactorRF ~= 0 then
  1918.                 monitor.setTextColor(colors.blue)
  1919.                 printRight("Reactor", 9, monitorIndex)
  1920.                 monitor.setTextColor(colors.white)
  1921.                 printRight(math.ceil(totalReactorRF).." (RF/t)", 10, monitorIndex)
  1922.         end
  1923.  
  1924.         if #turbineList then
  1925.                 -- Display liquids
  1926.                 monitor.setTextColor(colors.blue)
  1927.                 printLeft("Steam (mB)", 6, monitorIndex)
  1928.                 monitor.setTextColor(colors.white)
  1929.                 printLeft(math.ceil(totalSteamStored).."/"..maxSteamStored, 7, monitorIndex)
  1930.                 printLeft(math.ceil(totalReactorSteam).." mB/t", 8, monitorIndex)
  1931.                 monitor.setTextColor(colors.blue)
  1932.                 printRight("Coolant (mB)", 6, monitorIndex)
  1933.                 monitor.setTextColor(colors.white)
  1934.                 printRight(math.ceil(totalCoolantStored).."/"..maxCoolantStored, 7, monitorIndex)
  1935.  
  1936.                 monitor.setTextColor(colors.blue)
  1937.                 printLeft("Turbine", 9, monitorIndex)
  1938.                 monitor.setTextColor(colors.white)
  1939.                 printLeft(math.ceil(totalTurbineRF).." RF/t", 10, monitorIndex)
  1940.         end -- if #turbineList then
  1941.  
  1942.         printCentered("Fuel: "..round(totalReactorFuelConsumed,3).." mB/t", 11, monitorIndex)
  1943.         printCentered("Buffer: "..formatReadableSIUnit(math.ceil(totalEnergy)).."/"..formatReadableSIUnit(totalMaxEnergyStored).." RF", 12, monitorIndex)
  1944.  
  1945.         -- monitor switch controls
  1946.         local width, height = monitor.getSize()
  1947.         monitor.setCursorPos(1, height)
  1948.         monitor.write("<")
  1949.         monitor.setCursorPos(width, height)
  1950.         monitor.write(">")
  1951.  
  1952. end -- function displayAllStatus()
  1953.  
  1954.  
  1955. -- Get turbine status
  1956. local function displayTurbineBars(turbineIndex, monitorIndex)
  1957.         printLog("Called as displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  1958.  
  1959.         -- Grab current monitor
  1960.         local monitor = nil
  1961.         monitor = monitorList[monitorIndex]
  1962.         if not monitor then
  1963.                 printLog("monitor["..monitorIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  1964.                 return -- Invalid monitorIndex
  1965.         end
  1966.  
  1967.         -- Grab current turbine
  1968.         local turbine = nil
  1969.         turbine = turbineList[turbineIndex]
  1970.         if not turbine then
  1971.                 printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
  1972.                 return -- Invalid turbineIndex
  1973.         else
  1974.                 printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
  1975.                 if turbine.getConnected() then
  1976.                         printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
  1977.                 else
  1978.                         printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  1979.                         return -- Disconnected turbine
  1980.                 end -- if turbine.getConnected() then
  1981.         end -- if not turbine then
  1982.  
  1983.         --local variable to match the view on the monitor
  1984.         local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
  1985.  
  1986.         -- Draw border lines
  1987.         local width, height = monitor.getSize()
  1988.  
  1989.         for i=3, 6 do
  1990.                 monitor.setCursorPos(21, i)
  1991.                 monitor.write("|")
  1992.         end
  1993.  
  1994.         drawLine(7,monitorIndex)
  1995.         monitor.setCursorPos(1, height)
  1996.         monitor.write("< ")
  1997.         monitor.setCursorPos(width-1, height)
  1998.         monitor.write(" >")
  1999.  
  2000.         local turbineFlowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
  2001.         print{"  mB/t",22,3,monitorIndex}
  2002.         print{"<      >",22,4,monitorIndex}
  2003.         print{stringTrim(turbineFlowRate),24,4,monitorIndex}
  2004.         print{"  RPM",22,5,monitorIndex}
  2005.         print{"<      >",22,6,monitorIndex}
  2006.         print{stringTrim(tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])),24,6,monitorIndex}
  2007.         local rotorSpeedString = "Speed: "
  2008.         local energyBufferString = "Energy: "
  2009.         local steamBufferString = "Steam: "
  2010.         local padding = math.max(string.len(rotorSpeedString), string.len(energyBufferString), string.len(steamBufferString))
  2011.  
  2012.         local energyBuffer = turbine.getEnergyProducedLastTick()
  2013.         print{energyBufferString,1,4,monitorIndex}
  2014.         print{math.ceil(energyBuffer).." RF/t",padding+1,4,monitorIndex}
  2015.  
  2016.         local rotorSpeed = math.ceil(turbine.getRotorSpeed())
  2017.         print{rotorSpeedString,1,5,monitorIndex}
  2018.         print{rotorSpeed.." RPM",padding+1,5,monitorIndex}
  2019.  
  2020.         local steamBuffer = turbine.getFluidFlowRate()
  2021.         print{steamBufferString,1,6,monitorIndex}
  2022.         print{steamBuffer.." mB/t",padding+1,6,monitorIndex}
  2023.  
  2024.         -- PaintUtils only outputs to term., not monitor.
  2025.         -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
  2026.  
  2027.         -- Draw stored energy buffer bar
  2028.         drawBar(1,9,28,9,colors.gray,monitorIndex)
  2029.  
  2030.         local curStoredEnergyPercent = getTurbineStoredEnergyBufferPercent(turbine)
  2031.         if curStoredEnergyPercent > 4 then
  2032.                 drawBar(1, 9, math.floor(26*curStoredEnergyPercent/100)+2, 9, colors.yellow,monitorIndex)
  2033.         elseif curStoredEnergyPercent > 0 then
  2034.                 drawPixel(1, 9, colors.yellow, monitorIndex)
  2035.         end -- if curStoredEnergyPercent > 4 then
  2036.  
  2037.         print{"Energy Buffer",1,8,monitorIndex}
  2038.         print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),8,monitorIndex}
  2039.         print{"%",28,8,monitorIndex}
  2040.  
  2041.         -- Print rod override status
  2042.         local turbineFlowRateOverrideStatus = ""
  2043.  
  2044.         print{"Flow Auto-adjust:",2,10,monitorIndex}
  2045.  
  2046.         if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
  2047.                 turbineFlowRateOverrideStatus = "Enabled"
  2048.                 monitor.setTextColor(colors.green)
  2049.         else
  2050.                 turbineFlowRateOverrideStatus = "Disabled"
  2051.                 monitor.setTextColor(colors.red)
  2052.         end -- if not reactorRodOverride then
  2053.  
  2054.         print{turbineFlowRateOverrideStatus, width - string.len(turbineFlowRateOverrideStatus) - 1, 10, monitorIndex}
  2055.         monitor.setTextColor(colors.white)
  2056.  
  2057.         -- Print coil status
  2058.         local turbineCoilStatus = ""
  2059.  
  2060.         print{"Turbine coils:",2,11,monitorIndex}
  2061.  
  2062.         if ((_G[turbineNames[turbineIndex]]["TurbineOptions"]["CoilsEngaged"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["CoilsEngaged"] == "true")) then
  2063.                 turbineCoilStatus = "Engaged"
  2064.                 monitor.setTextColor(colors.green)
  2065.         else
  2066.                 turbineCoilStatus = "Disengaged"
  2067.                 monitor.setTextColor(colors.red)
  2068.         end
  2069.  
  2070.         print{turbineCoilStatus, width - string.len(turbineCoilStatus) - 1, 11, monitorIndex}
  2071.         monitor.setTextColor(colors.white)
  2072.  
  2073.         monitor.setTextColor(colors.blue)
  2074.         printCentered(_G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"],12,monitorIndex)
  2075.         monitor.setTextColor(colors.white)
  2076.  
  2077.         -- monitor switch controls
  2078.         monitor.setCursorPos(1, height)
  2079.         monitor.write("<")
  2080.         monitor.setCursorPos(width, height)
  2081.         monitor.write(">")
  2082.  
  2083.         -- Need equation to figure out rotor efficiency and display
  2084. end -- function displayTurbineBars(statusParams)
  2085.  
  2086.  
  2087. -- Display turbine status
  2088. local function turbineStatus(turbineIndex, monitorIndex)
  2089.         printLog("Called as turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
  2090.  
  2091.         -- Grab current monitor
  2092.         local monitor = nil
  2093.         monitor = monitorList[monitorIndex]
  2094.         if not monitor then
  2095.                 printLog("monitor["..monitorIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
  2096.                 return -- Invalid monitorIndex
  2097.         end
  2098.  
  2099.         -- Grab current turbine
  2100.         local turbine = nil
  2101.         turbine = turbineList[turbineIndex]
  2102.         if not turbine then
  2103.                 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
  2104.                 return -- Invalid turbineIndex
  2105.         else
  2106.                 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
  2107.         end
  2108.  
  2109.         local width, height = monitor.getSize()
  2110.         local turbineStatus = ""
  2111.  
  2112.         if turbine.getConnected() then
  2113.                 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
  2114.                 if turbine.getActive() then
  2115.                         turbineStatus = "ONLINE"
  2116.                         monitor.setTextColor(colors.green)
  2117.                 else
  2118.                         turbineStatus = "OFFLINE"
  2119.                         monitor.setTextColor(colors.red)
  2120.                 end -- if turbine.getActive() then
  2121.                 _G[turbineNames[turbineIndex]]["TurbineOptions"]["Status"] = turbineStatus
  2122.         else
  2123.                 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
  2124.                 turbineStatus = "DISCONNECTED"
  2125.                 monitor.setTextColor(colors.red)
  2126.         end -- if turbine.getConnected() then
  2127.  
  2128.         print{turbineStatus, width - string.len(turbineStatus) - 1, 1, monitorIndex}
  2129.         monitor.setTextColor(colors.white)
  2130. end -- function function turbineStatus(turbineIndex, monitorIndex)
  2131.  
  2132.  
  2133. -- Adjust Turbine flow rate to maintain 900 or 1,800 RPM, and disengage coils when buffer full
  2134. local function flowRateControl(turbineIndex)
  2135.         if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
  2136.                
  2137.                 printLog("Called as flowRateControl(turbineIndex="..turbineIndex..").")
  2138.  
  2139.                 -- Grab current turbine
  2140.                 local turbine = nil
  2141.                 turbine = turbineList[turbineIndex]
  2142.  
  2143.                 -- assign for the duration of this run
  2144.                 local lastTurbineSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"])
  2145.                 local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
  2146.                 local coilsEngaged = _G[turbineNames[turbineIndex]]["TurbineOptions"]["CoilsEngaged"] or _G[turbineNames[turbineIndex]]["TurbineOptions"]["CoilsEngaged"] == "true"
  2147.  
  2148.                 if not turbine then
  2149.                         printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT a valid Big Turbine.")
  2150.                         return -- Invalid turbineIndex
  2151.                 else
  2152.                         printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is a valid Big Turbine.")
  2153.  
  2154.                         if turbine.getConnected() then
  2155.                                 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is connected.")
  2156.                         else
  2157.                                 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT connected.")
  2158.                         end -- if turbine.getConnected() then
  2159.                 end -- if not turbine then
  2160.  
  2161.                 -- No point modifying control rod levels for temperature if the turbine is offline
  2162.                 if turbine.getActive() then
  2163.                         printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is active.")
  2164.  
  2165.                         local flowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
  2166.                         local flowRateUserMax = math.ceil(turbine.getFluidFlowRateMax())
  2167.                         local rotorSpeed = math.ceil(turbine.getRotorSpeed())
  2168.                         local newFlowRate = -1
  2169.  
  2170.                         local currentStoredEnergyPercent = getTurbineStoredEnergyBufferPercent(turbine)
  2171.                         if (currentStoredEnergyPercent >= maxStoredEnergyPercent) then
  2172.                                 if (coilsEngaged) then
  2173.                                         printLog("turbine["..turbineIndex.."]: Disengaging coils, energy buffer at "..currentStoredEnergyPercent.." (>="..maxStoredEnergyPercent..").")
  2174.                                         newFlowRate = 0
  2175.                                         coilsEngaged = false
  2176.                                 end
  2177.                         elseif (currentStoredEnergyPercent < minStoredEnergyPercent) then
  2178.                                 if (not coilsEngaged) then
  2179.                                         printLog("turbine["..turbineIndex.."]: Engaging coils, energy buffer at "..currentStoredEnergyPercent.." (<"..minStoredEnergyPercent..").")
  2180.                                         -- set flow rate to what's probably the max load flow for the desired RPM
  2181.                                         newFlowRate = 2000 / (1817 / turbineBaseSpeed)
  2182.                                         coilsEngaged = true
  2183.                                 end
  2184.                         end
  2185.  
  2186.                         -- Going to control the turbine based on target RPM since changing the target flow rate bypasses this function
  2187.                         if (rotorSpeed < turbineBaseSpeed) then
  2188.                                 printLog("BELOW COMMANDED SPEED")
  2189.  
  2190.                                 local diffSpeed = rotorSpeed - lastTurbineSpeed
  2191.                                 local diffBaseSpeed = turbineBaseSpeed - rotorSpeed
  2192.                                 if (diffSpeed > 0) then
  2193.                                         if (diffBaseSpeed > turbineBaseSpeed * 0.05) then
  2194.                                                 -- let's speed this up. DOUBLE TIME!
  2195.                                                 coilsEngaged = false
  2196.                                                 printLog("COILS DISENGAGED")
  2197.                                         elseif (diffSpeed > diffBaseSpeed * 0.05) then
  2198.                                                 --we're still increasing, let's let it level off
  2199.                                                 --also lets the first control pass go by on startup
  2200.                                                 printLog("Leveling off...")
  2201.                                         end
  2202.                                 elseif (rotorSpeed < lastTurbineSpeed) then
  2203.                                         --we're decreasing where we should be increasing, do something
  2204.                                         if ((lastTurbineSpeed - rotorSpeed) > 100) then
  2205.                                                 --kick it harder
  2206.                                                 newFlowRate = 2000
  2207.                                                 printLog("HARD KICK")
  2208.                                         else
  2209.                                                 --let's adjust based on proximity
  2210.                                                 flowAdjustment = (turbineBaseSpeed - rotorSpeed)/5
  2211.                                                 newFlowRate = flowRate + flowAdjustment
  2212.                                                 printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  2213.                                         end
  2214.                                 else
  2215.                                         --we've stagnated, kick it.
  2216.                                         flowAdjustment = (turbineBaseSpeed - lastTurbineSpeed)
  2217.                                         newFlowRate = flowRate + flowAdjustment
  2218.                                         printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  2219.                                 end --if (rotorSpeed > lastTurbineSpeed) then
  2220.                         else
  2221.                                 --we're above commanded turbine speed
  2222.                                 printLog("ABOVE COMMANDED SPEED")
  2223.                                 if (rotorSpeed < lastTurbineSpeed) then
  2224.                                 --we're decreasing, let it level off
  2225.                                 --also bypasses first control pass on startup
  2226.                                 elseif (rotorSpeed > lastTurbineSpeed) then
  2227.                                         --we're above and ascending.
  2228.                                         if ((rotorSpeed - lastTurbineSpeed) > 100) then
  2229.                                                 --halt
  2230.                                                 newFlowRate = 0
  2231.                                         else
  2232.                                                 --let's adjust based on proximity
  2233.                                                 flowAdjustment = (rotorSpeed - turbineBaseSpeed)/5
  2234.                                                 newFlowRate = flowRate - flowAdjustment
  2235.                                                 printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  2236.                                         end
  2237.                                         -- With coils disengaged, we have no chance of slowing. More importantly, this stops DOUBLE TIME.
  2238.                                         coilsEngaged = true
  2239.                                 else
  2240.                                         --we've stagnated, kick it.
  2241.                                         flowAdjustment = (lastTurbineSpeed - turbineBaseSpeed)
  2242.                                         newFlowRate = flowRate - flowAdjustment
  2243.                                         printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
  2244.                                 end --if (rotorSpeed < lastTurbineSpeed) then
  2245.                         end --if (rotorSpeed < turbineBaseSpeed)
  2246.  
  2247.                         --check to make sure an adjustment was made
  2248.                         if (newFlowRate == -1) then
  2249.                                 --do nothing, we didn't ask for anything this pass
  2250.                         else
  2251.                                 --boundary check
  2252.                                 if newFlowRate > 2000 then
  2253.                                         newFlowRate = 2000
  2254.                                 elseif newFlowRate < 0 then
  2255.                                         newFlowRate = 0
  2256.                                 end -- if newFlowRate > 2000 then
  2257.                                 --no sense running an adjustment if it's not necessary
  2258.                                 if ((newFlowRate < flowRate) or (newFlowRate > flowRate)) then
  2259.                                         printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is being commanded to "..newFlowRate.." mB/t flow")
  2260.                                         newFlowRate = round(newFlowRate, 0)
  2261.                                         turbine.setFluidFlowRateMax(newFlowRate)
  2262.                                         _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newFlowRate
  2263.                                         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  2264.                                 end
  2265.                         end
  2266.  
  2267.                         turbine.setInductorEngaged(coilsEngaged)
  2268.  
  2269.                         --always set this
  2270.                         _G[turbineNames[turbineIndex]]["TurbineOptions"]["CoilsEngaged"] = coilsEngaged
  2271.                         _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = rotorSpeed
  2272.                         config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
  2273.                 else
  2274.                         printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT active.")
  2275.                 end -- if turbine.getActive() then
  2276.         else
  2277.                 printLog("turbine["..turbineIndex.."] has flow override set to "..tostring(_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"])..", bypassing flow control.")
  2278.         end -- if not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] then
  2279. end -- function flowRateControl(turbineIndex)
  2280.  
  2281.  
  2282. local function helpText()
  2283.  
  2284.         -- these keys are actually defined in eventHandler(), check there
  2285.         return [[Keyboard commands:
  2286.                         m       Select next monitor
  2287.                         s       Make selected monitor display global status
  2288.                         x       Make selected monitor display debug information
  2289.  
  2290.                         d       Toggle debug mode
  2291.  
  2292.                         q       Quit
  2293.                         r       Quit and reboot
  2294.                         h       Print this help
  2295. ]]
  2296.  
  2297. end -- function helpText()
  2298.  
  2299. local function initializePeripherals()
  2300.         monitorAssignments = {}
  2301.         -- Get our list of connected monitors and reactors
  2302.         findMonitors()
  2303.         findReactors()
  2304.         findTurbines()
  2305.         assignMonitors()
  2306. end
  2307.  
  2308.  
  2309. local function updateMonitors()
  2310.  
  2311.         -- Display overall status on selected monitors
  2312.         for monitorName, deviceData in pairs(monitorAssignments) do
  2313.                 local monitor = nil
  2314.                 local monitorIndex = deviceData.index
  2315.                 local monitorType =  deviceData.type
  2316.                 monitor = monitorList[monitorIndex]
  2317.  
  2318.                 printLog("main(): Trying to display "..monitorType.." on "..monitorNames[monitorIndex].."["..monitorIndex.."]", DEBUG)
  2319.  
  2320.                 if #monitorList < (#reactorList + #turbineList + 1) then
  2321.                         printLog("You may want "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines.")
  2322.                 end
  2323.  
  2324.                 if (not monitor) or (not monitor.getSize()) then
  2325.  
  2326.                         printLog("monitor["..monitorIndex.."] in main() is NOT a valid monitor, discarding", ERROR)
  2327.                         monitorAssignments[monitorName] = nil
  2328.                         -- we need to get out of the for loop now, or it will dereference x.next (where x is the element we just killed) and crash
  2329.                         break
  2330.  
  2331.                 elseif monitorType == "Status" then
  2332.  
  2333.                         -- General status display
  2334.                         clearMonitor(progName.." "..progVer, monitorIndex) -- Clear monitor and draw borders
  2335.                         printCentered(progName.." "..progVer, 1, monitorIndex)
  2336.                         displayAllStatus(monitorIndex)
  2337.  
  2338.                 elseif monitorType == "Reactor" then
  2339.  
  2340.                         -- Reactor display
  2341.                         local reactorMonitorIndex = monitorIndex
  2342.                         for reactorIndex = 1, #reactorList do
  2343.  
  2344.                                 if deviceData.reactorName == reactorNames[reactorIndex] then
  2345.  
  2346.                                         printLog("Attempting to display reactor["..reactorIndex.."] on monitor["..monitorIndex.."]...", DEBUG)
  2347.                                         -- Only attempt to assign a monitor if we have a monitor for this reactor
  2348.                                         if (reactorMonitorIndex <= #monitorList) then
  2349.                                                 printLog("Displaying reactor["..reactorIndex.."] on monitor["..reactorMonitorIndex.."].")
  2350.  
  2351.                                                 clearMonitor(progName, reactorMonitorIndex) -- Clear monitor and draw borders
  2352.                                                 printCentered(progName, 1, reactorMonitorIndex)
  2353.  
  2354.                                                 -- Display reactor status, includes "Disconnected" but found reactors
  2355.                                                 reactorStatus{reactorIndex, reactorMonitorIndex}
  2356.  
  2357.                                                 -- Draw the borders and bars for the current reactor on the current monitor
  2358.                                                 displayReactorBars{reactorIndex, reactorMonitorIndex}
  2359.                                         end
  2360.  
  2361.                                 end -- if deviceData.reactorName == reactorNames[reactorIndex] then
  2362.  
  2363.                         end -- for reactorIndex = 1, #reactorList do
  2364.  
  2365.                 elseif monitorType == "Turbine" then
  2366.  
  2367.                         -- Turbine display
  2368.                         local turbineMonitorIndex = monitorIndex
  2369.                         for turbineIndex = 1, #turbineList do
  2370.  
  2371.                                 if deviceData.turbineName == turbineNames[turbineIndex] then
  2372.                                         printLog("Attempting to display turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."]...", DEBUG)
  2373.                                         -- Only attempt to assign a monitor if we have a monitor for this turbine
  2374.                                         if (turbineMonitorIndex <= #monitorList) then
  2375.                                                 printLog("Displaying turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."].")
  2376.                                                 clearMonitor(progName, turbineMonitorIndex) -- Clear monitor and draw borders
  2377.                                                 printCentered(progName, 1, turbineMonitorIndex)
  2378.  
  2379.                                                 -- Display turbine status, includes "Disconnected" but found turbines
  2380.                                                 turbineStatus(turbineIndex, turbineMonitorIndex)
  2381.  
  2382.                                                 -- Draw the borders and bars for the current turbine on the current monitor
  2383.                                                 displayTurbineBars(turbineIndex, turbineMonitorIndex)
  2384.                                         end
  2385.                                 end
  2386.                         end
  2387.  
  2388.                 elseif monitorType == "Debug" then
  2389.  
  2390.                         -- do nothing, printLog() outputs to here
  2391.  
  2392.                 else
  2393.  
  2394.                         clearMonitor(progName, monitorIndex)
  2395.                         print{"Monitor  inactive", 7, 7, monitorIndex}
  2396.  
  2397.                 end -- if monitorType == [...]
  2398.         end
  2399. end
  2400.  
  2401. function main()
  2402.         -- Load reactor parameters and initialize systems
  2403.         loadReactorOptions()
  2404.         initializePeripherals()
  2405.  
  2406.         write(helpText())
  2407.  
  2408.         while not finished do
  2409.  
  2410.                 updateMonitors()
  2411.  
  2412.                 local reactor = nil
  2413.                 local sd = 0
  2414.  
  2415.                 -- Iterate through reactors
  2416.                 for reactorIndex = 1, #reactorList do
  2417.                         local monitor = nil
  2418.  
  2419.                         reactor = reactorList[reactorIndex]
  2420.                         if not reactor then
  2421.                                 printLog("reactor["..reactorIndex.."] in main() is NOT a valid Big Reactor.")
  2422.                                 break -- Invalid reactorIndex
  2423.                         else
  2424.                                 printLog("reactor["..reactorIndex.."] in main() is a valid Big Reactor.")
  2425.                         end --  if not reactor then
  2426.  
  2427.                         if reactor.getConnected() then
  2428.                                 printLog("reactor["..reactorIndex.."] is connected.")
  2429.                                 local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
  2430.  
  2431.                                 -- Shutdown reactor if current stored energy % is >= desired level, otherwise activate
  2432.                                 -- First pass will have curStoredEnergyPercent=0 until displayBars() is run once
  2433.                                 if curStoredEnergyPercent >= maxStoredEnergyPercent then
  2434.                                         reactor.setActive(false)
  2435.                                 -- Do not auto-start the reactor if it was manually powered off (autoStart=false)
  2436.                                 elseif (curStoredEnergyPercent <= minStoredEnergyPercent) and (_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] == true) then
  2437.                                         reactor.setActive(true)
  2438.                                 end -- if curStoredEnergyPercent >= maxStoredEnergyPercent then
  2439.  
  2440.                                 -- Don't try to auto-adjust control rods if manual control is requested
  2441.                                 if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
  2442.                                         temperatureControl(reactorIndex)
  2443.                                 end -- if not reactorRodOverride then
  2444.  
  2445.                                 -- Collect steam production data
  2446.                                 if reactor.isActivelyCooled() then
  2447.                                         sd = sd + reactor.getHotFluidProducedLastTick()
  2448.                                 end
  2449.                         else
  2450.                                 printLog("reactor["..reactorIndex.."] is NOT connected.")
  2451.                         end -- if reactor.getConnected() then
  2452.                 end -- for reactorIndex = 1, #reactorList do
  2453.  
  2454.                 -- Now that temperatureControl() had a chance to use it, reset/calculate steam data for next iteration
  2455.                 printLog("Steam requested: "..steamRequested.." mB")
  2456.                 printLog("Steam delivered: "..steamDelivered.." mB")
  2457.                 steamDelivered = sd
  2458.                 steamRequested = 0
  2459.  
  2460.                 -- Turbine control
  2461.                 for turbineIndex = 1, #turbineList do
  2462.  
  2463.                         turbine = turbineList[turbineIndex]
  2464.                         if not turbine then
  2465.                                 printLog("turbine["..turbineIndex.."] in main() is NOT a valid Big Turbine.")
  2466.                                 break -- Invalid turbineIndex
  2467.                         else
  2468.                                 printLog("turbine["..turbineIndex.."] in main() is a valid Big Turbine.")
  2469.                         end -- if not turbine then
  2470.  
  2471.                         if turbine.getConnected() then
  2472.                                 printLog("turbine["..turbineIndex.."] is connected.")
  2473.  
  2474.                                 if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
  2475.                                         flowRateControl(turbineIndex)
  2476.                                 end -- if not turbineFlowRateOverride[turbineIndex] then
  2477.  
  2478.                                 -- Collect steam consumption data
  2479.                                 if turbine.getActive() then
  2480.                                         steamRequested = steamRequested + turbine.getFluidFlowRateMax()
  2481.                                 end
  2482.                         else
  2483.                                 printLog("turbine["..turbineIndex.."] is NOT connected.")
  2484.                         end -- if turbine.getConnected() then
  2485.                 end -- for reactorIndex = 1, #reactorList do
  2486.  
  2487.                 wait(loopTime) -- Sleep. No, wait...
  2488.                 saveReactorOptions()
  2489.         end -- while not finished do
  2490. end -- main()
  2491.  
  2492. -- handle all the user interaction events
  2493. eventHandler = function(event, arg1, arg2, arg3)
  2494.  
  2495.                 printLog(string.format("handleEvent(%s, %s, %s, %s)", tostring(event), tostring(arg1), tostring(arg2), tostring(arg3)), DEBUG)
  2496.  
  2497.                 if event == "monitor_touch" then
  2498.                         sideClick, xClick, yClick = arg1, math.floor(arg2), math.floor(arg3)
  2499.                         UI:handlePossibleClick()
  2500.                 elseif (event == "peripheral") or (event == "peripheral_detach") then
  2501.                         printLog("Change in network detected. Reinitializing peripherals. We will be back shortly.", WARN)
  2502.                         initializePeripherals()
  2503.                 elseif event == "char" and not inManualMode then
  2504.                         local ch = string.lower(arg1)
  2505.                         -- remember to update helpText() when you edit these
  2506.                         if ch == "q" then
  2507.                                 finished = true
  2508.                         elseif ch == "d" then
  2509.                                 debugMode = not debugMode
  2510.                                 local modeText
  2511.                                 if debugMode then
  2512.                                         modeText = "on"
  2513.                                 else
  2514.                                         modeText = "off"
  2515.                                 end
  2516.                                 termRestore()
  2517.                                 write("debugMode "..modeText.."\n")
  2518.                         elseif ch == "m" then
  2519.                                 UI:selectNextMonitor()
  2520.                         elseif ch == "s" then
  2521.                                 UI:selectStatus()
  2522.                         elseif ch == "x" then
  2523.                                 UI:selectDebug()
  2524.                         elseif ch == "r" then
  2525.                                 finished = true
  2526.                                 os.reboot()
  2527.                         elseif ch == "h" then
  2528.                                 write(helpText())
  2529.                         end -- if ch == "q" then
  2530.                 end -- if event == "monitor_touch" then
  2531.  
  2532.                 updateMonitors()
  2533.  
  2534. end -- function eventHandler()
  2535.  
  2536. main()
  2537.  
  2538. -- Clear up after an exit
  2539. term.clear()
  2540. term.setCursorPos(1,1)
Advertisement
Add Comment
Please, Sign In to add comment