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