lpenap

Bigreactor Monitor

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