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