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