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