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