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