Advertisement
Evdev

lolmer_bigreactor_monitor_prog.lua

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