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