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