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