· 6 years ago · Jul 15, 2019, 07:36 PM
1--[[
2Program name: Lolmer's EZ-NUKE reactor control system
3Version: v0.3.16
4Programmer: Lolmer
5Great assistance by Mechaet
6Last update: 2015-03-25
7Pastebin: http://pastebin.com/fguScPBQ
8GitHub: https://github.com/sandalle/minecraft_bigreactor_control
9
10Description:
11This program controls a Big Reactors nuclear reactor in Minecraft with a Computercraft computer, using Computercraft's own wired modem connected to the reactors computer control port.
12
13This program was designed to work with the mods and versions installed on Never Stop Toasting (NST) Diet http://www.technicpack.net/modpack/details/never-stop-toasting-diet.254882 Endeavour: Never Stop Toasting: Diet official Minecraft server http://forums.somethingawful.com/showthread.php?threadid=3603757
14
15To simplify the code and guesswork, I assume the following monitor layout, where each "monitor" listed below is a collection of three wide by two high Advanced Monitors:
161) One Advanced Monitor for overall status display plus
17 one or more Reactors plus
18 none or more Turbines.
192) One Advanced Monitor for overall status display plus (furthest monitor from computer by cable length)
20 one Advanced Monitor for each connected Reactor plus (subsequent found monitors)
21 one Advanced Monitor for each connected Turbine (last group of monitors found).
22If you enable debug mode, add one additional Advanced Monitor for #1 or #2.
23
24Notes:
25 Only one reactor and one, two, and three turbines have been tested with the above, but IN THEORY any number is supported.
26 Devices are found in the reverse order they are plugged in, so monitor_10 will be found before monitor_9.
27 Two 15x15x14 Turbines can output 260K RF/t by just one 7^3 (four rods) reactor putting out 4k mB steam.
28
29When using actively cooled reactors with turbines, keep the following in mind:
30 - 1 mB steam carries up to 10RF of potential energy to extract in a turbine.
31 - Actively cooled reactors produce steam, not power.
32 - You will need about 10 mB of water for each 1 mB of steam that you want to create in a 7^3 reactor.
33
34Features:
35 Configurable min/max energy buffer and min/max temperature via ReactorOptions file.
36 ReactorOptions is read on start and then current values are saved every program cycle.
37 Rod Control value in ReactorOptions is only useful for initial start, after that the program saves the current Rod Control average over all Fuel Rods for next boot.
38 Auto-adjusts control rods per reactor to maintain temperature.
39 Will display reactor data to all attached monitors of correct dimensions.
40 For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
41 For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
42 A new cruise mode from mechaet, ONLINE will be "blue" when active, to keep your actively cooled reactors running smoothly.
43
44GUI Usage:
45 The "<" and ">" buttons, when right-clicked with the mouse, will decrease and increase, respectively, the values assigned to the monitor:
46 "Rod (%)" will lower/raise the Reactor Control Rods for that Reactor
47 "mB/t" will lower/raise the Turbine Flow Rate maximum for that Turbine
48 "RPM" will lower/raise the target Turbine RPM for that Turbine
49 Right-clicking between the "<" and ">" (not on them) will disable auto-adjust of that value for attached device.
50 Right-clicking on the "Enabled" or "Disabled" text for auto-adjust will do the same.
51 Right-clicking on "ONLINE" or "OFFLINE" at the top-right will toggle the state of attached device.
52
53Default values:
54 Rod Control: 90% (Let's start off safe and then power up as we can)
55 Minimum Energy Buffer: 15% (will power on below this value)
56 Maximum Energy Buffer: 85% (will power off above this value)
57 Minimum Passive Cooling Temperature: 950^C (will raise control rods below this value)
58 Maximum Passive Cooling Temperature: 1,400^C (will lower control rods above this value)
59 Minimum Active Cooling Temperature: 300^C (will raise the control rods below this value)
60 Maximum Active Cooling Temperature: 420^C (will lower control rods above this value)
61 Optimal Turbine RPM: 900, 1,800, or 2,700 (divisible by 900)
62 New user-controlled option for target speed of turbines, defaults to 2726RPM, which is high-optimal.
63
64Requirements:
65 Advanced Monitor size is X: 29, Y: 12 with a 3x2 size
66 Computer or Advanced Computer
67 Modems (not wireless) connecting each of the Computer to both the Advanced Monitor and Reactor Computer Port.
68 Big Reactors (http://www.big-reactors.com/) 0.3.2A+
69 Computercraft (http://computercraft.info/) 1.58 or 1.63+
70 Reset the computer any time number of connected devices change.
71
72Resources:
73This script is available from:
74 http://pastebin.com/fguScPBQ
75 https://github.com/sandalle/minecraft_bigreactor_control
76Start-up script is available from:
77 http://pastebin.com/ZTMzRLez
78 https://github.com/sandalle/minecraft_bigreactor_control
79Other reactor control program which I based my program on:
80 http://pastebin.com/aMAu4X5J (ScatmanJohn)
81 http://pastebin.com/HjUVNDau (version ScatmanJohn based his on)
82A simpler Big Reactor control program is available from:
83 http://pastebin.com/7S5xCvgL (IronClaymore only for passively cooled reactors)
84
85 Reactor Computer Port API: http://wiki.technicpack.net/Reactor_Computer_Port
86 Computercraft API: http://computercraft.info/wiki/Category:APIs
87 Big Reactors Efficiency, Speculation and Questions! http://www.reddit.com/r/feedthebeast/comments/1vzds0/big_reactors_efficiency_speculation_and_questions/
88 Big Reactors API code: https://github.com/erogenousbeef/BigReactors/blob/master/erogenousbeef/bigreactors/common/multiblock/tileentity/TileEntityReactorComputerPort.java
89 Big Reactors API: http://big-reactors.com/cc_api.html
90 Big Reactor Simulator from http://reddit.com/r/feedthebeast : http://br.sidoh.org/
91
92ChangeLog:
93- 0.3.16
94 - Add support for ComputerCraft 1.7 (thanks dkowis and jnyl42).
95 - Fix typo for unsupported OS (found from above)
96
97- 0.3.15
98 - Add ability to override safe values for Issue #39.
99
100- 0.3.14
101 - Fix Issue #5. EZ-Nuke should now work with ComputerCraft 1.58 or 1.63.
102
103- 0.3.13
104 - Fix one reactor and one monitor incorrectly using status display instead of control display (Issue #35)
105 - Fix concatenating a string and boolean, see http://stackoverflow.com/questions/6615572/how-to-format-a-lua-string-with-a-boolean-variable
106 - Hopefully fix concatenating string and nul in printLog (Issue #3)
107
108- 0.3.12
109 - Mechaet's changes:
110 - Redid some typing to correct a bug where the reactors always started with rod control disabled.
111
112- 0.3.11
113 - Mechaet's changes:
114 - Cleaned up global variables list
115 - Added in per-device naming (displays a friendly name on the bottom of the monitor if configured in the device options file)
116 - Bigger bypasses of control routines when the control has been overridden
117 - Individual config files for turbines and reactors. Persistent between reboots, remembers your last saved settings.
118 - Cruise mode override bypass
119 - Changing flow rate no longer toggles flow rate override on and off. Changing the flow rate clearly indicates intent, so we put the override flag on and leave it there.
120 - Changed the rate at which the regular algorithm adjusts reactor rod control rates. Instead of being 1:1 we now move at 1:5 speed because there is a wide loophole where big adjustments can cause a swinging pendulum effect continually missing the target.
121
122- 0.3.10
123 - Turbine algorithm pass by Mechaet.
124 - Updated turbine GUI.
125 - Fix single monitor (again) for Issue #22.
126
127- 0.3.9
128 - Reactor algorithm pass by Mechaet.
129 - Additional user config options by Mechaet.
130 - Fix multiple reactors and none or more turbines with only one status monitor.
131 - Fix monitor scaling after one was used as debug (or in case of other modifications).
132 - Cruise mode implemented, defaults off but is saved between boots.
133 - Fix energy/% displays to match Big Reactors' GUI (Issue #9).
134 - Always write out found devices on computer terminal.
135 - Much improved round() function from mechaet (Issue #14).
136 - Refactoring pass/algorithm change on the reactor temperature control. Should now adjust in increments to achieve the desired temperature range quicker and more accurately.
137 - Optimal passive-cooled reactor temperature range changed from 850-900 to 950-1400.
138 - Fix display Issue #15.
139
140- 0.3.8
141 - Update to ComputerCraft 1.6 API (only term.restore() -> term.native() required :)).
142
143- 0.3.7
144 - Fix typo when initializing TurbineNames array.
145 - Fix Issue #1, turbine display is using the Reactor buffer size (10M RF) instead of the Turbine buffer size (1M RF).
146
147- 0.3.6
148 - Fix multi-reactors displaying on the correct monitors (thanks HybridFusion).
149 - Fix rod auto-adjust text position.
150 - Reactors store 10M RF and Turbines store 1M RF in their buffer.
151 - Add more colour to displayAllStatus().
152 - Sleep for only two seconds instead of five.
153 - Fix getDeviceStoredEnergyBufferPercent() for Reactors storing 10M RF in buffer.
154 - Keep actively cooled reactors between 0-300^C (non-configurable for now).
155
156- 0.3.5
157 - Do not discover connected devices every loop - nicer on servers. Reset computer anytime number of connected devices change.
158 - Fix multi-reactor setups to display the additional reactors on monitors, rather than the last one found.
159 - Fix passive reactor display having auto-adjust and energy buffer overwrite each other (removes rod count).
160
161- 0.3.4
162 - Fix arithmetic for checking if we have enough monitors for the number of reactors.
163 - Turbines are optimal at 900, 1800, *and* 2700 RPM
164 - Increase loop timer from 1 to 5 to be nicer to servers
165
166- 0.3.3
167 - Add Big Reactors Turbine support.
168 - First found monitor (appears to be last connected monitor) is used to display status of all found devices (if more than one valid monitor is found)
169 - Display monitor number on top left of each monitor as "M#" to help find which monitor is which.
170 - Enabling debug will use the last monitor found, if more than one, to print out debug info (also written to file)
171 - Only clear monitors when we're about to use them (e.g. turbine monitors no longer clear, then wait for all reactors to update)
172 - Fix getDeviceStoredEnergyBufferPercent(), was off by a decimal place
173 - Just use first Control Rod level for entire reactor, they are no longer treated individually in BR 0.3
174 - Allow for one monitor for n number of reactors and m number of turbines
175 - Auto-adjust turbine flow rate by 25 mB to keep rotor speed at 900 or 1,800 RPM.
176 - Clicks on monitors relate to what the monitor is showing (e.g. clicking on reactor 1's display won't modify turbine 1's nor reactor 2's values)
177 - Print monitor name and device (reactor|turbine) name in blue to monitor associated for easier design by users.
178 - Remove version number from monitors to free up space for monitor names.
179 - Add option of right-clicking on "Enabled"/"Disabled" of auto-adjust to toggle it.
180
181- 0.3.2
182 - Allow for rod control to override (disable) auto-adjust via UI (Rhonyn)
183
184- 0.3.1
185 - Add fuel consumption per tick to display
186
187- 0.3.0
188 - Add multi-monitor support! Sends one reactor's data to all monitors.
189 - print function now takes table to support optional specified monitor
190 - Set "numRods" every cycle for some people (mechaet)
191 - Don't redirect terminal output with multiple monitor support
192 - Log troubleshooting data to reactorcontrol.log
193 - FC_API no longer used (copied and modified what I needed)
194 - Multi-reactor support is theoretically implemented, but it is UNTESTED!
195 - Updated for Big Reactor 0.3 (no longer works with 0.2)
196 - BR getFuelTemperature() now returns many significant digits, just use math.ceil()
197 - BR 0.3 removed individual rod temperatures, now it's only reactor-level temperature
198
199- 0.2.4
200 - Simplify math, don't divide by a simple large number and then multiply by 100 (#/10000000*100)
201 - Fix direct-connected (no modem) devices. getDeviceSide -> FC_API.getDeviceSide (simple as that :))
202
203- 0.2.3
204 - Check bounds on reactor.setRodControlLevel(#,#), Big Reactor doesn't check for us.
205
206- 0.2.2
207 - Do not auto-start the reactor if it was manually powered off (autoStart=false)
208
209- 0.2.1
210 - Lower/raise only the hottest/coldest Control Rod while trying to control the reactor temperature.
211 - "<" Rod Control buttons was off by one (to the left)
212
213- 0.2.0 - Lolmer Edition :)
214 - Add min/max stored energy percentage (default is 15%/85%), configurable via ReactorOptions file.
215 - No reason to keep burning fuel if our power output is going nowhere. :)
216 - Use variables variable for the title and version.
217 - Try to keep the temperature between configured values (default is 850^C-950^C)
218 - Add Waste and number of Control/Fuel Rods to displayBards()
219
220TODO:
221- Save parameters per reactor instead of one global set for all reactors.
222- Add min/max RF/t output and have it override temperature concerns (maybe?).
223- Add support for wireless modems, see http://computercraft.info/wiki/Modem_%28API%29, will not be secure (anyone can send/listen to your channels)!
224- Add support for any sized monitor (minimum 3x3), dynamic allocation/alignment.
225- Lookup using pcall for better error handling http://www.computercraft.info/forums2/index.php?/topic/10992-using-pcall/ .
226- Update cruise mode to work independently for each actively-cooled reactor.
227
228]]--
229
230
231-- Some global variables
232local progVer = "0.3.16"
233local progName = "EZ-NUKE"
234local sideClick, xClick, yClick = nil, 0, 0
235local loopTime = 2
236local controlRodAdjustAmount = 1 -- Default Reactor Rod Control % adjustment amount
237local flowRateAdjustAmount = 25 -- Default Turbine Flow Rate in mB adjustment amount
238local debugMode = false
239-- End multi-reactor cleanup section
240local minStoredEnergyPercent = nil -- Max energy % to store before activate
241local maxStoredEnergyPercent = nil -- Max energy % to store before shutdown
242local monitorList = {} -- Empty monitor array
243local monitorNames = {} -- Empty array of monitor names
244local reactorList = {} -- Empty reactor array
245local reactorNames = {} -- Empty array of reactor names
246local turbineList = {} -- Empty turbine array
247local turbineNames = {} -- Empty array of turbine names
248local turbineMonitorOffset = 0 -- Turbines are assigned monitors after reactors
249local knowlinglyOverride = false -- Issue #39 Allow the user to override safe values, currently only enabled for actively cooled reactor min/max temperature
250
251term.clear()
252term.setCursorPos(2,1)
253write("Initializing program...\n")
254
255
256-- File needs to exist for append "a" later and zero it out if it already exists
257-- Always initalize this file to avoid confusion with old files and the latest run
258local logFile = fs.open("reactorcontrol.log", "w")
259if logFile then
260 logFile.writeLine("Minecraft time: Day "..os.day().." at "..textutils.formatTime(os.time(),true))
261 logFile.close()
262else
263 error("Could not open file reactorcontrol.log for writing.")
264end
265
266
267-- Helper functions
268
269local function termRestore()
270 local ccVersion = nil
271 ccVersion = os.version()
272
273 if ccVersion == "CraftOS 1.6" or "CraftOS 1.7" then
274 term.native()
275 elseif ccVersion == "CraftOS 1.5" then
276 term.restore()
277 else -- Default to older term.restore
278 printLog("Unsupported CraftOS found. Reported version is \""..ccVersion.."\".")
279 term.restore()
280 end -- if ccVersion
281end -- function termRestore()
282
283local function printLog(printStr)
284 if debugMode then
285 -- If multiple monitors, use the last monitor for debugging if debug is enabled
286 if #monitorList > 1 then
287 term.redirect(monitorList[#monitorList]) -- Redirect to last monitor for debugging
288 monitorList[#monitorList].setTextScale(0.5) -- Fit more logs on screen
289 write(printStr.."\n") -- May need to use term.scroll(x) if we output too much, not sure
290 termRestore()
291 end -- if #monitorList > 1 then
292
293 local logFile = fs.open("reactorcontrol.log", "a") -- See http://computercraft.info/wiki/Fs.open
294 if logFile then
295 logFile.writeLine(printStr)
296 logFile.close()
297 else
298 error("Cannot open file reactorcontrol.log for appending!")
299 end -- if logFile then
300 end -- if debugMode then
301end -- function printLog(printStr)
302
303-- Trim a string
304function stringTrim(s)
305 assert(s ~= nil, "String can't be nil")
306 return(string.gsub(s, "^%s*(.-)%s*$", "%1"))
307end
308
309config = {}
310
311-- Save a table into a config file
312-- path: path of the file to write
313-- tab: table to save
314config.save = function(path, tab)
315 printLog("Save function called for config for "..path.." EOL")
316 assert(path ~= nil, "Path can't be nil")
317 assert(type(tab) == "table", "Second parameter must be a table")
318 local f = io.open(path, "w")
319 local i = 0
320 for key, value in pairs(tab) do
321 if i ~= 0 then
322 f:write("\n")
323 end
324 f:write("["..key.."]".."\n")
325 for key2, value2 in pairs(tab[key]) do
326 key2 = stringTrim(key2)
327 --doesn't like boolean values
328 if (type(value2) ~= "boolean") then
329 value2 = stringTrim(value2)
330 else
331 value2 = tostring(value2)
332 end
333 key2 = key2:gsub(";", "\\;")
334 key2 = key2:gsub("=", "\\=")
335 value2 = value2:gsub(";", "\\;")
336 value2 = value2:gsub("=", "\\=")
337 f:write(key2.."="..value2.."\n")
338 end
339 i = i + 1
340 end
341 f:close()
342end --config.save = function(path, tab)
343
344-- Load a config file
345-- path: path of the file to read
346config.load = function(path)
347 printLog("Load function called for config for "..path.." EOL")
348 assert(path ~= nil, "Path can't be nil")
349 local f = fs.open(path, "r")
350 if f ~= nil then
351 local tab = {}
352 local line = ""
353 local newLine
354 local i
355 local currentTag = nil
356 local found = false
357 local pos = 0
358 while line ~= nil do
359 found = false
360 line = line:gsub("\\;", "#_!36!_#") -- to keep \;
361 line = line:gsub("\\=", "#_!71!_#") -- to keep \=
362 if line ~= "" then
363 -- Delete comments
364 newLine = line
365 line = ""
366 for i=1, string.len(newLine) do
367 if string.sub(newLine, i, i) ~= ";" then
368 line = line..newLine:sub(i, i)
369 else
370 break
371 end
372 end
373 line = stringTrim(line)
374 -- Find tag
375 if line:sub(1, 1) == "[" and line:sub(line:len(), line:len()) == "]" then
376 currentTag = stringTrim(line:sub(2, line:len()-1))
377 tab[currentTag] = {}
378 found = true
379 end
380 -- Find key and values
381 if not found and line ~= "" then
382 pos = line:find("=")
383 if pos == nil then
384 error("Bad INI file structure")
385 end
386 line = line:gsub("#_!36!_#", ";")
387 line = line:gsub("#_!71!_#", "=")
388 tab[currentTag][stringTrim(line:sub(1, pos-1))] = stringTrim(line:sub(pos+1, line:len()))
389 found = true
390 end
391 end
392 line = f.readLine()
393 end
394
395 f:close()
396
397 return tab
398 else
399 return nil
400 end
401end --config.load = function(path)
402
403
404
405-- round() function from mechaet
406local function round(num, places)
407 local mult = 10^places
408 local addon = nil
409 if ((num * mult) < 0) then
410 addon = -.5
411 else
412 addon = .5
413 end
414
415 local integer, decimal = math.modf(num*mult+addon)
416 newNum = integer/mult
417 printLog("Called round(num="..num..",places="..places..") returns \""..newNum.."\".")
418 return newNum
419end -- function round(num, places)
420
421
422local function print(printParams)
423 -- Default to xPos=1, yPos=1, and first monitor
424 setmetatable(printParams,{__index={xPos=1, yPos=1, monitorIndex=1}})
425 local printString, xPos, yPos, monitorIndex =
426 printParams[1], -- Required parameter
427 printParams[2] or printParams.xPos,
428 printParams[3] or printParams.yPos,
429 printParams[4] or printParams.monitorIndex
430
431 local monitor = nil
432 monitor = monitorList[monitorIndex]
433
434 if not monitor then
435 printLog("monitor["..monitorIndex.."] in print() is NOT a valid monitor.")
436 return -- Invalid monitorIndex
437 end
438
439 monitor.setCursorPos(xPos, yPos)
440 monitor.write(printString)
441end -- function print(printParams)
442
443
444-- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
445local function printCentered(printString, yPos, monitorIndex)
446 local monitor = nil
447 monitor = monitorList[monitorIndex]
448
449 if not monitor then
450 printLog("monitor["..monitorIndex.."] in printCentered() is NOT a valid monitor.")
451 return -- Invalid monitorIndex
452 end
453
454 local width, height = monitor.getSize()
455 local monitorNameLength = 0
456
457 -- Special changes for title bar
458 if yPos == 1 then
459 -- Add monitor name to first line
460 monitorNameLength = monitorNames[monitorIndex]:len()
461
462 -- Leave room for "offline" and "online" on the right except for overall status display
463 if (#monitorList ~= 1) and (monitorIndex ~= 1) then
464 width = width - 7
465 end
466 end
467
468 monitor.setCursorPos(math.floor(width/2) - math.ceil(printString:len()/2) + monitorNameLength/2, yPos)
469 monitor.clearLine()
470 monitor.write(printString)
471
472 monitor.setTextColor(colors.blue)
473 print{monitorNames[monitorIndex], 1, 1, monitorIndex}
474 monitor.setTextColor(colors.white)
475end -- function printCentered(printString, yPos, monitorIndex)
476
477
478-- Print text padded from the left side
479-- Clear the left side of the screen
480local function printLeft(printString, yPos, monitorIndex)
481 local monitor = nil
482 monitor = monitorList[monitorIndex]
483
484 if not monitor then
485 printLog("monitor["..monitorIndex.."] in printLeft() is NOT a valid monitor.")
486 return -- Invalid monitorIndex
487 end
488
489 local gap = 1
490 local width = monitor.getSize()
491
492 -- Clear left-half of the monitor
493
494 for curXPos = 1, (width / 2) do
495 monitor.setCursorPos(curXPos, yPos)
496 monitor.write(" ")
497 end
498
499 -- Write our string left-aligned
500 monitor.setCursorPos(1+gap, yPos)
501 monitor.write(printString)
502end
503
504
505-- Print text padded from the right side
506-- Clear the right side of the screen
507local function printRight(printString, yPos, monitorIndex)
508 local monitor = nil
509 monitor = monitorList[monitorIndex]
510
511 if not monitor then
512 printLog("monitor["..monitorIndex.."] in printRight() is NOT a valid monitor.")
513 return -- Invalid monitorIndex
514 end
515
516 -- Make sure printString is a string
517 printString = tostring(printString)
518
519 local gap = 1
520 local width = monitor.getSize()
521
522 -- Clear right-half of the monitor
523 for curXPos = (width/2), width do
524 monitor.setCursorPos(curXPos, yPos)
525 monitor.write(" ")
526 end
527
528 -- Write our string right-aligned
529 monitor.setCursorPos(math.floor(width) - math.ceil(printString:len()+gap), yPos)
530 monitor.write(printString)
531end
532
533
534-- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
535local function clearMonitor(printString, monitorIndex)
536 local monitor = nil
537 monitor = monitorList[monitorIndex]
538
539 printLog("Called as clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..").")
540
541 if not monitor then
542 printLog("monitor["..monitorIndex.."] in clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
543 return -- Invalid monitorIndex
544 end
545
546 local gap = 2
547 monitor.clear()
548 local width, height = monitor.getSize()
549 monitor.setTextScale(1.0) -- Make sure scale is correct
550
551 printCentered(printString, 1, monitorIndex)
552 monitor.setTextColor(colors.blue)
553 print{monitorNames[monitorIndex], 1, 1, monitorIndex}
554 monitor.setTextColor(colors.white)
555
556 for i=1, width do
557 monitor.setCursorPos(i, gap)
558 monitor.write("-")
559 end
560
561 monitor.setCursorPos(1, gap+1)
562end -- function clearMonitor(printString, monitorIndex)
563
564
565-- Return a list of all connected (including via wired modems) devices of "deviceType"
566local function getDevices(deviceType)
567 printLog("Called as getDevices(deviceType="..deviceType..")")
568
569 local deviceName = nil
570 local deviceIndex = 1
571 local deviceList, deviceNames = {}, {} -- Empty array, which grows as we need
572 local peripheralList = peripheral.getNames() -- Get table of connected peripherals
573
574 deviceType = deviceType:lower() -- Make sure we're matching case here
575
576 for peripheralIndex = 1, #peripheralList do
577 -- Log every device found
578 -- printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] attached as \""..peripheralList[peripheralIndex].."\".")
579 if (string.lower(peripheral.getType(peripheralList[peripheralIndex])) == deviceType) then
580 -- Log devices found which match deviceType and which device index we give them
581 printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".")
582 write("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".\n")
583 deviceNames[deviceIndex] = peripheralList[peripheralIndex]
584 deviceList[deviceIndex] = peripheral.wrap(peripheralList[peripheralIndex])
585 deviceIndex = deviceIndex + 1
586 end
587 end -- for peripheralIndex = 1, #peripheralList do
588
589 return deviceList, deviceNames
590end -- function getDevices(deviceType)
591
592-- Draw a line across the entire x-axis
593local function drawLine(yPos, monitorIndex)
594 local monitor = nil
595 monitor = monitorList[monitorIndex]
596
597 if not monitor then
598 printLog("monitor["..monitorIndex.."] in drawLine() is NOT a valid monitor.")
599 return -- Invalid monitorIndex
600 end
601
602 local width, height = monitor.getSize()
603
604 for i=1, width do
605 monitor.setCursorPos(i, yPos)
606 monitor.write("-")
607 end
608end -- function drawLine(yPos,monitorIndex)
609
610
611-- Display a solid bar of specified color
612local function drawBar(startXPos, startYPos, endXPos, endYPos, color, monitorIndex)
613 local monitor = nil
614 monitor = monitorList[monitorIndex]
615
616 if not monitor then
617 printLog("monitor["..monitorIndex.."] in drawBar() is NOT a valid monitor.")
618 return -- Invalid monitorIndex
619 end
620
621 -- PaintUtils only outputs to term., not monitor.
622 -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
623 term.redirect(monitor)
624 paintutils.drawLine(startXPos, startYPos, endXPos, endYPos, color)
625 monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
626 termRestore()
627end -- function drawBar(startXPos, startYPos,endXPos,endYPos,color,monitorIndex)
628
629
630-- Display single pixel color
631local function drawPixel(xPos, yPos, color, monitorIndex)
632 local monitor = nil
633 monitor = monitorList[monitorIndex]
634
635 if not monitor then
636 printLog("monitor["..monitorIndex.."] in drawPixel() is NOT a valid monitor.")
637 return -- Invalid monitorIndex
638 end
639
640 -- PaintUtils only outputs to term., not monitor.
641 -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
642 term.redirect(monitor)
643 paintutils.drawPixel(xPos, yPos, color)
644 monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
645 termRestore()
646end -- function drawPixel(xPos, yPos, color, monitorIndex)
647
648
649-- End helper functions
650
651
652-- Then initialize the monitors
653local function findMonitors()
654 -- Empty out old list of monitors
655 monitorList = {}
656
657 printLog("Finding monitors...")
658 monitorList, monitorNames = getDevices("monitor")
659
660 if #monitorList == 0 then
661 printLog("No monitors found!")
662 error("Can't find any monitors!")
663 else
664 for monitorIndex = 1, #monitorList do
665 local monitor, monitorX, monitorY = nil, nil, nil
666 monitor = monitorList[monitorIndex]
667
668 if not monitor then
669 printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid monitor.")
670
671 table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
672 if monitorIndex ~= #monitorList then -- If we're not at the end, clean up
673 monitorIndex = monitorIndex - 1 -- We just removed an element
674 end -- if monitorIndex == #monitorList then
675 break -- Invalid monitorIndex
676 else -- valid monitor
677 monitorX, monitorY = monitor.getSize()
678 if (monitorX == nil) or (monitorY == nil) then -- somehow a valid monitor, but non-existent sizes? Maybe fixes Issue #3
679 printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid sized monitor.")
680
681 table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
682 if monitorIndex ~= #monitorList then -- If we're not at the end, clean up
683 monitorIndex = monitorIndex - 1 -- We just removed an element
684 end -- if monitorIndex == #monitorList then
685 break -- Invalid monitorIndex
686
687 -- Check for minimum size to allow for monitor.setTextScale(0.5) to work for 3x2 debugging monitor, changes getSize()
688 elseif monitorX < 29 or monitorY < 12 then
689 term.redirect(monitor)
690 monitor.clear()
691 printLog("Removing monitor "..monitorIndex.." for being too small.")
692 monitor.setCursorPos(1,2)
693 write("Monitor is the wrong size!\n")
694 write("Needs to be at least 3x2.")
695 termRestore()
696
697 table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
698 if monitorIndex == #monitorList then -- If we're at the end already, break from loop
699 break
700 else
701 monitorIndex = monitorIndex - 1 -- We just removed an element
702 end -- if monitorIndex == #monitorList then
703
704 end -- if monitorX < 29 or monitorY < 12 then
705 end -- if not monitor then
706
707 printLog("Monitor["..monitorIndex.."] named \""..monitorNames[monitorIndex].."\" is a valid monitor of size x:"..monitorX.." by y:"..monitorY..".")
708 end -- for monitorIndex = 1, #monitorList do
709 end -- if #monitorList == 0 then
710
711 printLog("Found "..#monitorList.." monitor(s) in findMonitors().")
712end -- local function findMonitors()
713
714
715-- Initialize all Big Reactors - Reactors
716local function findReactors()
717 -- Empty out old list of reactors
718 newReactorList = {}
719 printLog("Finding reactors...")
720 newReactorList, reactorNames = getDevices("BigReactors-Reactor")
721
722 if #newReactorList == 0 then
723 printLog("No reactors found!")
724 error("Can't find any reactors!")
725 else -- Placeholder
726 for reactorIndex = 1, #newReactorList do
727 local reactor = nil
728 reactor = newReactorList[reactorIndex]
729
730 if not reactor then
731 printLog("reactorList["..reactorIndex.."] in findReactors() is NOT a valid Big Reactor.")
732
733 table.remove(newReactorList, reactorIndex) -- Remove invalid reactor from list
734 if reactorIndex ~= #newReactorList then -- If we're not at the end, clean up
735 reactorIndex = reactorIndex - 1 -- We just removed an element
736 end -- reactorIndex ~= #newReactorList then
737 return -- Invalid reactorIndex
738 else
739 printLog("reactor["..reactorIndex.."] in findReactors() is a valid Big Reactor.")
740 --initialize the default table
741 _G[reactorNames[reactorIndex]] = {}
742 _G[reactorNames[reactorIndex]]["ReactorOptions"] = {}
743 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = 80
744 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = 0
745 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
746 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
747 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = 1400 --set for passive-cooled, the active-cooled subroutine will correct it
748 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = 1000
749 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = false
750 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = reactorNames[reactorIndex]
751 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
752 if reactor.getConnected() then
753 printLog("reactor["..reactorIndex.."] in findReactors() is connected.")
754 else
755 printLog("reactor["..reactorIndex.."] in findReactors() is NOT connected.")
756 return -- Disconnected reactor
757 end
758 end
759
760 --failsafe
761 local tempTable = _G[reactorNames[reactorIndex]]
762
763 --check to make sure we get a valid config
764 if (config.load(reactorNames[reactorIndex]..".options")) ~= nil then
765 tempTable = config.load(reactorNames[reactorIndex]..".options")
766 else
767 --if we don't have a valid config from disk, make a valid config
768 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
769 end
770
771 --load values from tempTable, checking for nil values along the way
772 if tempTable["ReactorOptions"]["baseControlRodLevel"] ~= nil then
773 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tempTable["ReactorOptions"]["baseControlRodLevel"]
774 end
775
776 if tempTable["ReactorOptions"]["lastTempPoll"] ~= nil then
777 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tempTable["ReactorOptions"]["lastTempPoll"]
778 end
779
780 if tempTable["ReactorOptions"]["autoStart"] ~= nil then
781 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = tempTable["ReactorOptions"]["autoStart"]
782 end
783
784 if tempTable["ReactorOptions"]["activeCooled"] ~= nil then
785 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = tempTable["ReactorOptions"]["activeCooled"]
786 end
787
788 if tempTable["ReactorOptions"]["reactorMaxTemp"] ~= nil then
789 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tempTable["ReactorOptions"]["reactorMaxTemp"]
790 end
791
792 if tempTable["ReactorOptions"]["reactorMinTemp"] ~= nil then
793 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tempTable["ReactorOptions"]["reactorMinTemp"]
794 end
795
796 if tempTable["ReactorOptions"]["rodOverride"] ~= nil then
797 printLog("Got value from config file for Rod Override, the value is: "..tostring(tempTable["ReactorOptions"]["rodOverride"]).." EOL")
798 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = tempTable["ReactorOptions"]["rodOverride"]
799 end
800
801 if tempTable["ReactorOptions"]["reactorName"] ~= nil then
802 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = tempTable["ReactorOptions"]["reactorName"]
803 end
804
805 if tempTable["ReactorOptions"]["reactorCruising"] ~= nil then
806 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = tempTable["ReactorOptions"]["reactorCruising"]
807 end
808
809 --stricter typing, let's set these puppies up with the right type of value.
810 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"])
811
812 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"])
813
814 if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"]) == "true") then
815 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
816 else
817 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
818 end
819
820 if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"]) == "true") then
821 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
822 else
823 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = false
824 end
825
826 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"])
827
828 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"])
829
830 if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]) == "true") then
831 printLog("Setting Rod Override for "..reactorNames[reactorIndex].." to true because value was "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
832 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = true
833 else
834 printLog("Setting Rod Override for "..reactorNames[reactorIndex].." to false because value was "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
835 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = false
836 end
837
838 if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"]) == "true") then
839 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
840 else
841 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
842 end
843
844 --save one more time, in case we didn't have a complete config file before
845 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
846 end -- for reactorIndex = 1, #newReactorList do
847 end -- if #newReactorList == 0 then
848
849 -- Overwrite old reactor list with the now updated list
850 reactorList = newReactorList
851
852 -- Start turbine monitor offset after reactors get monitors
853 -- This assumes that there is a monitor for each turbine and reactor, plus the overall monitor display
854 turbineMonitorOffset = #reactorList + 1 -- #turbineList will start at "1" if turbines found and move us just beyond #reactorList and status monitor range
855
856 printLog("Found "..#reactorList.." reactor(s) in findReactors().")
857 printLog("Set turbineMonitorOffset to "..turbineMonitorOffset.." in findReactors().")
858end -- function findReactors()
859
860
861-- Initialize all Big Reactors - Turbines
862local function findTurbines()
863 -- Empty out old list of turbines
864 newTurbineList = {}
865
866 printLog("Finding turbines...")
867 newTurbineList, turbineNames = getDevices("BigReactors-Turbine")
868
869 if #newTurbineList == 0 then
870 printLog("No turbines found") -- Not an error
871 else
872 for turbineIndex = 1, #newTurbineList do
873 local turbine = nil
874 turbine = newTurbineList[turbineIndex]
875
876 if not turbine then
877 printLog("turbineList["..turbineIndex.."] in findTurbines() is NOT a valid Big Reactors Turbine.")
878
879 table.remove(newTurbineList, turbineIndex) -- Remove invalid turbine from list
880 if turbineIndex ~= #newTurbineList then -- If we're not at the end, clean up
881 turbineIndex = turbineIndex - 1 -- We just removed an element
882 end -- turbineIndex ~= #newTurbineList then
883
884 return -- Invalid turbineIndex
885 else
886
887 _G[turbineNames[turbineIndex]] = {}
888 _G[turbineNames[turbineIndex]]["TurbineOptions"] = {}
889 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = 0
890 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = 2726
891 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = true
892 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = 2000 --open up with all the steam wide open
893 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
894 _G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = turbineNames[turbineIndex]
895 printLog("turbineList["..turbineIndex.."] in findTurbines() is a valid Big Reactors Turbine.")
896 if turbine.getConnected() then
897 printLog("turbine["..turbineIndex.."] in findTurbines() is connected.")
898 else
899 printLog("turbine["..turbineIndex.."] in findTurbines() is NOT connected.")
900 return -- Disconnected turbine
901 end
902 end
903
904 --failsafe
905 local tempTable = _G[turbineNames[turbineIndex]]
906
907 --check to make sure we get a valid config
908 if (config.load(turbineNames[turbineIndex]..".options")) ~= nil then
909 tempTable = config.load(turbineNames[turbineIndex]..".options")
910 else
911 --if we don't have a valid config from disk, make a valid config
912 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
913 end
914
915 --load values from tempTable, checking for nil values along the way
916 if tempTable["TurbineOptions"]["LastSpeed"] ~= nil then
917 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = tempTable["TurbineOptions"]["LastSpeed"]
918 end
919
920 if tempTable["TurbineOptions"]["BaseSpeed"] ~= nil then
921 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = tempTable["TurbineOptions"]["BaseSpeed"]
922 end
923
924 if tempTable["TurbineOptions"]["autoStart"] ~= nil then
925 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = tempTable["TurbineOptions"]["autoStart"]
926 end
927
928 if tempTable["TurbineOptions"]["LastFlow"] ~= nil then
929 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = tempTable["TurbineOptions"]["LastFlow"]
930 end
931
932 if tempTable["TurbineOptions"]["flowOverride"] ~= nil then
933 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = tempTable["TurbineOptions"]["flowOverride"]
934 end
935
936 if tempTable["TurbineOptions"]["turbineName"] ~= nil then
937 _G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = tempTable["TurbineOptions"]["turbineName"]
938 end
939
940 --save once more just to make sure we got it
941 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
942 end -- for turbineIndex = 1, #newTurbineList do
943
944 -- Overwrite old turbine list with the now updated list
945 turbineList = newTurbineList
946 end -- if #newTurbineList == 0 then
947
948 printLog("Found "..#turbineList.." turbine(s) in findTurbines().")
949end -- function findTurbines()
950
951
952-- Return current energy buffer in a specific reactor by %
953local function getReactorStoredEnergyBufferPercent(reactor)
954 printLog("Called as getReactorStoredEnergyBufferPercent(reactor).")
955
956 if not reactor then
957 printLog("getReactorStoredEnergyBufferPercent() did NOT receive a valid Big Reactor Reactor.")
958 return -- Invalid reactorIndex
959 else
960 printLog("getReactorStoredEnergyBufferPercent() did receive a valid Big Reactor Reactor.")
961 end
962
963 local energyBufferStorage = reactor.getEnergyStored()
964 return round(energyBufferStorage/100000, 1) -- (buffer/10000000 RF)*100%
965end -- function getReactorStoredEnergyBufferPercent(reactor)
966
967
968-- Return current energy buffer in a specific Turbine by %
969local function getTurbineStoredEnergyBufferPercent(turbine)
970 printLog("Called as getTurbineStoredEnergyBufferPercent(turbine)")
971
972 if not turbine then
973 printLog("getTurbineStoredEnergyBufferPercent() did NOT receive a valid Big Reactor Turbine.")
974 return -- Invalid reactorIndex
975 else
976 printLog("getTurbineStoredEnergyBufferPercent() did receive a valid Big Reactor Turbine.")
977 end
978
979 local energyBufferStorage = turbine.getEnergyStored()
980 return round(energyBufferStorage/10000, 1) -- (buffer/1000000 RF)*100%
981end -- function getTurbineStoredEnergyBufferPercent(turbine)
982
983local function reactorCruise(cruiseMaxTemp, cruiseMinTemp, reactorIndex)
984 printLog("Called as reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp=".._G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"]..",reactorIndex="..reactorIndex..").")
985
986 --sanitization
987 local lastPolledTemp = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"])
988 cruiseMaxTemp = tonumber(cruiseMaxTemp)
989 cruiseMinTemp = tonumber(cruiseMinTemp)
990
991 if ((lastPolledTemp < cruiseMaxTemp) and (lastPolledTemp > cruiseMinTemp)) then
992 local reactor = nil
993 reactor = reactorList[reactorIndex]
994 if not reactor then
995 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is NOT a valid Big Reactor.")
996 return -- Invalid reactorIndex
997 else
998 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is a valid Big Reactor.")
999 if reactor.getConnected() then
1000 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is connected.")
1001 else
1002 printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is NOT connected.")
1003 return -- Disconnected reactor
1004 end -- if reactor.getConnected() then
1005 end -- if not reactor then
1006
1007 local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
1008 local reactorTemp = math.ceil(reactor.getFuelTemperature())
1009 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = rodPercentage
1010
1011 if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
1012 if (reactorTemp < lastPolledTemp) then
1013 rodPercentage = (rodPercentage - 1)
1014 --Boundary check
1015 if rodPercentage < 0 then
1016 reactor.setAllControlRodLevels(0)
1017 else
1018 reactor.setAllControlRodLevels(rodPercentage)
1019 end
1020 else
1021 rodPercentage = (rodPercentage + 1)
1022 --Boundary check
1023 if rodPercentage > 99 then
1024 reactor.setAllControlRodLevels(99)
1025 else
1026 reactor.setAllControlRodLevels(rodPercentage)
1027 end
1028 end -- if (reactorTemp > lastPolledTemp) then
1029 else
1030 --disengage cruise, we've fallen out of the ideal temperature range
1031 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
1032 end -- if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
1033 else
1034 --I don't know how we'd get here, but let's turn the cruise mode off
1035 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
1036 end -- if ((lastPolledTemp < cruiseMaxTemp) and (lastPolledTemp > cruiseMinTemp)) then
1037 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
1038 _G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
1039 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = cruiseMaxTemp
1040 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = cruiseMinTemp
1041 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1042end -- function reactorCruise(cruiseMaxTemp, cruiseMinTemp, lastPolledTemp, reactorIndex)
1043
1044-- Modify reactor control rod levels to keep temperature with defined parameters, but
1045-- wait an in-game half-hour for the temperature to stabalize before modifying again
1046local function temperatureControl(reactorIndex)
1047 printLog("Called as temperatureControl(reactorIndex="..reactorIndex..")")
1048
1049 local reactor = nil
1050 reactor = reactorList[reactorIndex]
1051 if not reactor then
1052 printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT a valid Big Reactor.")
1053 return -- Invalid reactorIndex
1054 else
1055 printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is a valid Big Reactor.")
1056
1057 if reactor.getConnected() then
1058 printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is connected.")
1059 else
1060 printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT connected.")
1061 return -- Disconnected reactor
1062 end -- if reactor.getConnected() then
1063 end
1064
1065 local reactorNum = reactorIndex
1066 local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
1067 local reactorTemp = math.ceil(reactor.getFuelTemperature())
1068 local localMinReactorTemp, localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"], _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
1069
1070 --bypass if the reactor itself is set to not be auto-controlled
1071 if ((not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]) or (_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] == "false")) then
1072 -- No point modifying control rod levels for temperature if the reactor is offline
1073 if reactor.getActive() then
1074 -- Actively cooled reactors should range between 0^C-300^C
1075 -- Actually, active-cooled reactors should range between 300 and 420C (Mechaet)
1076 -- Accordingly I changed the below lines
1077 if reactor.isActivelyCooled() and not knowlinglyOverride then
1078 -- below was 0
1079 localMinReactorTemp = 300
1080 -- below was 300
1081 localMaxReactorTemp = 420
1082 else
1083 localMinReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"]
1084 localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
1085 end
1086 local lastTempPoll = _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"]
1087 if _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] then
1088 --let's bypass all this math and hit the much-more-subtle cruise feature
1089 --printLog("min: "..localMinReactorTemp..", max: "..localMaxReactorTemp..", lasttemp: "..lastTempPoll..", ri: "..reactorIndex.." EOL")
1090 reactorCruise(localMaxReactorTemp, localMinReactorTemp, reactorIndex)
1091 else
1092 -- Don't bring us to 100, that's effectively a shutdown
1093 if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
1094 --increase the rods, but by how much?
1095 if (reactorTemp > lastTempPoll) then
1096 --we're climbing, we need to get this to decrease
1097 if ((reactorTemp - lastTempPoll) > 100) then
1098 --we're climbing really fast, arrest it
1099 if (rodPercentage + (10 * controlRodAdjustAmount)) > 99 then
1100 reactor.setAllControlRodLevels(99)
1101 else
1102 reactor.setAllControlRodLevels(rodPercentage + (10 * controlRodAdjustAmount))
1103 end
1104 else
1105 --we're not climbing by leaps and bounds, let's give it a rod adjustment based on temperature increase
1106 local diffAmount = reactorTemp - lastTempPoll
1107 diffAmount = (round(diffAmount/10, 0))/5
1108 controlRodAdjustAmount = diffAmount
1109 if (rodPercentage + controlRodAdjustAmount) > 99 then
1110 reactor.setAllControlRodLevels(99)
1111 else
1112 reactor.setAllControlRodLevels(rodPercentage + controlRodAdjustAmount)
1113 end
1114 end --if ((reactorTemp - lastTempPoll) > 100) then
1115 elseif (reactorTemp == lastTempPoll) then
1116 --temperature has stagnated, kick it very lightly
1117 local controlRodAdjustment = 1
1118 if (rodPercentage + controlRodAdjustment) > 99 then
1119 reactor.setAllControlRodLevels(99)
1120 else
1121 reactor.setAllControlRodLevels(rodPercentage + controlRodAdjustment)
1122 end
1123 end --if (reactorTemp > lastTempPoll) then
1124 --worth noting that if we're above temp but decreasing, we do nothing. let it continue decreasing.
1125
1126 elseif (reactorTemp < localMinReactorTemp) and (rodPercentage ~=0) then
1127 --we're too cold. time to warm up, but by how much?
1128 if (reactorTemp < lastTempPoll) then
1129 --we're descending, let's stop that.
1130 if ((lastTempPoll - reactorTemp) > 100) then
1131 --we're headed for a new ice age, bring the heat
1132 if (rodPercentage - (10 * controlRodAdjustAmount)) < 0 then
1133 reactor.setAllControlRodLevels(0)
1134 else
1135 reactor.setAllControlRodLevels(rodPercentage - (10 * controlRodAdjustAmount))
1136 end
1137 else
1138 --we're not descending quickly, let's bump it based on descent rate
1139 local diffAmount = lastTempPoll - reactorTemp
1140 diffAmount = (round(diffAmount/10, 0))/5
1141 controlRodAdjustAmount = diffAmount
1142 if (rodPercentage - controlRodAdjustAmount) < 0 then
1143 reactor.setAllControlRodLevels(0)
1144 else
1145 reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustAmount)
1146 end
1147 end --if ((lastTempPoll - reactorTemp) > 100) then
1148 elseif (reactorTemp == lastTempPoll) then
1149 --temperature has stagnated, kick it very lightly
1150 local controlRodAdjustment = 1
1151 if (rodPercentage - controlRodAdjustment) < 0 then
1152 reactor.setAllControlRodLevels(0)
1153 else
1154 reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustment)
1155 end --if (rodPercentage - controlRodAdjustment) < 0 then
1156
1157 end --if (reactorTemp < lastTempPoll) then
1158 --if we're below temp but increasing, do nothing and let it continue to rise.
1159 end --if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
1160
1161 if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) then
1162 --engage cruise mode
1163 _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
1164 end -- if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) then
1165 end -- if reactorCruising then
1166 --always set this number
1167 _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
1168 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1169 end -- if reactor.getActive() then
1170 else
1171 printLog("Bypassed temperature control due to rodOverride being "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
1172 end -- if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
1173end -- function temperatureControl(reactorIndex)
1174
1175-- Load saved reactor parameters if ReactorOptions file exists
1176local function loadReactorOptions()
1177 local reactorOptions = fs.open("ReactorOptions", "r") -- See http://computercraft.info/wiki/Fs.open
1178
1179 if reactorOptions then
1180 -- The following values were added by Lolmer
1181 minStoredEnergyPercent = reactorOptions.readLine()
1182 maxStoredEnergyPercent = reactorOptions.readLine()
1183 --added by Mechaet
1184 -- If we succeeded in reading a string, convert it to a number
1185
1186 if minStoredEnergyPercent ~= nil then
1187 minStoredEnergyPercent = tonumber(minStoredEnergyPercent)
1188 end
1189
1190 if maxStoredEnergyPercent ~= nil then
1191 maxStoredEnergyPercent = tonumber(maxStoredEnergyPercent)
1192 end
1193
1194 reactorOptions.close()
1195 end -- if reactorOptions then
1196
1197 -- Set default values if we failed to read any of the above
1198 if minStoredEnergyPercent == nil then
1199 minStoredEnergyPercent = 15
1200 end
1201
1202 if maxStoredEnergyPercent == nil then
1203 maxStoredEnergyPercent = 85
1204 end
1205
1206end -- function loadReactorOptions()
1207
1208
1209-- Save our reactor parameters
1210local function saveReactorOptions()
1211 local reactorOptions = fs.open("ReactorOptions", "w") -- See http://computercraft.info/wiki/Fs.open
1212
1213 -- If we can save the files, save them
1214 if reactorOptions then
1215 local reactorIndex = 1
1216 -- The following values were added by Lolmer
1217 reactorOptions.writeLine(minStoredEnergyPercent)
1218 reactorOptions.writeLine(maxStoredEnergyPercent)
1219 reactorOptions.close()
1220 else
1221 printLog("Failed to open file ReactorOptions for writing!")
1222 end -- if reactorOptions then
1223end -- function saveReactorOptions()
1224
1225
1226local function displayReactorBars(barParams)
1227 -- Default to first reactor and first monitor
1228 setmetatable(barParams,{__index={reactorIndex=1, monitorIndex=1}})
1229 local reactorIndex, monitorIndex =
1230 barParams[1] or barParams.reactorIndex,
1231 barParams[2] or barParams.monitorIndex
1232
1233 printLog("Called as displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1234
1235 -- Grab current monitor
1236 local monitor = nil
1237 monitor = monitorList[monitorIndex]
1238 if not monitor then
1239 printLog("monitor["..monitorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
1240 return -- Invalid monitorIndex
1241 end
1242
1243 -- Grab current reactor
1244 local reactor = nil
1245 reactor = reactorList[reactorIndex]
1246 if not reactor then
1247 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
1248 return -- Invalid reactorIndex
1249 else
1250 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
1251 if reactor.getConnected() then
1252 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
1253 else
1254 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
1255 return -- Disconnected reactor
1256 end -- if reactor.getConnected() then
1257 end -- if not reactor then
1258
1259 -- Draw border lines
1260 local width, height = monitor.getSize()
1261 printLog("Size of monitor is "..width.."w x"..height.."h in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
1262
1263 for i=3, 5 do
1264 monitor.setCursorPos(22, i)
1265 monitor.write("|")
1266 end
1267
1268 drawLine(2, monitorIndex)
1269 drawLine(6, monitorIndex)
1270
1271 -- Draw some text
1272 local fuelString = "Fuel: "
1273 local tempString = "Temp: "
1274 local energyBufferString = ""
1275
1276 if reactor.isActivelyCooled() then
1277 energyBufferString = "Steam: "
1278 else
1279 energyBufferString = "Energy: "
1280 end
1281
1282 local padding = math.max(string.len(fuelString), string.len(tempString), string.len(energyBufferString))
1283
1284 local fuelPercentage = round(reactor.getFuelAmount()/reactor.getFuelAmountMax()*100,1)
1285 print{fuelString,2,3,monitorIndex}
1286 print{fuelPercentage.." %",padding+2,3,monitorIndex}
1287
1288 local reactorTemp = math.ceil(reactor.getFuelTemperature())
1289 print{tempString,2,5,monitorIndex}
1290 print{reactorTemp.." C",padding+2,5,monitorIndex}
1291
1292 local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
1293 printLog("Current Rod Percentage for reactor["..reactorIndex.."] is "..rodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1294 -- Allow controlling Reactor Control Rod Level from GUI
1295 -- Decrease rod button: 23X, 4Y
1296 -- Increase rod button: 28X, 4Y
1297 if (xClick == 23) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1298 printLog("Decreasing Rod Levels in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1299 --Decrease rod level by amount
1300 newRodPercentage = rodPercentage - (5 * controlRodAdjustAmount)
1301 if newRodPercentage < 0 then
1302 newRodPercentage = 0
1303 end
1304 sideClick, xClick, yClick = 0, 0, 0
1305
1306 printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1307 reactor.setAllControlRodLevels(newRodPercentage)
1308 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = newRodPercentage
1309
1310 -- Save updated rod percentage
1311 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1312 rodPercentage = newRodPercentage
1313 elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1314 printLog("Increasing Rod Levels in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1315 --Increase rod level by amount
1316 newRodPercentage = rodPercentage + (5 * controlRodAdjustAmount)
1317 if newRodPercentage > 100 then
1318 newRodPercentage = 100
1319 end
1320 sideClick, xClick, yClick = 0, 0, 0
1321
1322 printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1323 reactor.setAllControlRodLevels(newRodPercentage)
1324 _G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = newRodPercentage
1325
1326 -- Save updated rod percentage
1327 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1328 rodPercentage = round(newRodPercentage,0)
1329 else
1330 printLog("No change to Rod Levels requested by "..progName.." GUI in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1331 end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1332
1333 print{"Rod (%)",23,3,monitorIndex}
1334 print{"< >",23,4,monitorIndex}
1335 print{stringTrim(rodPercentage),25,4,monitorIndex}
1336
1337
1338 -- getEnergyProducedLastTick() is used for both RF/t (passively cooled) and mB/t (actively cooled)
1339 local energyBuffer = reactor.getEnergyProducedLastTick()
1340 if reactor.isActivelyCooled() then
1341 printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." mB last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1342 else
1343 printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." RF last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1344 end
1345
1346 print{energyBufferString,2,4,monitorIndex}
1347
1348 -- Actively cooled reactors do not produce energy, only hot fluid mB/t to be used in a turbine
1349 -- still uses getEnergyProducedLastTick for mB/t of hot fluid generated
1350 if not reactor.isActivelyCooled() then
1351 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT an actively cooled reactor.")
1352
1353 -- Draw stored energy buffer bar
1354 drawBar(2,8,28,8,colors.gray,monitorIndex)
1355
1356 local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
1357 if curStoredEnergyPercent > 4 then
1358 drawBar(2, 8, math.floor(26*curStoredEnergyPercent/100)+2, 8, colors.yellow, monitorIndex)
1359 elseif curStoredEnergyPercent > 0 then
1360 drawPixel(2, 8, colors.yellow, monitorIndex)
1361 end -- if curStoredEnergyPercent > 4 then
1362
1363 print{"Energy Buffer",2,7,monitorIndex}
1364 print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),7,monitorIndex}
1365 print{"%",28,7,monitorIndex}
1366
1367 print{math.ceil(energyBuffer).." RF/t",padding+2,4,monitorIndex}
1368 else
1369 printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is an actively cooled reactor.")
1370 print{math.ceil(energyBuffer).." mB/t",padding+2,4,monitorIndex}
1371 end -- if not reactor.isActivelyCooled() then
1372
1373 -- Print rod override status
1374 local reactorRodOverrideStatus = ""
1375
1376 print{"Rod Auto-adjust:",2,9,monitorIndex}
1377
1378 if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
1379 printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
1380 reactorRodOverrideStatus = "Enabled"
1381 monitor.setTextColor(colors.green)
1382 else
1383 printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
1384 reactorRodOverrideStatus = "Disabled"
1385 monitor.setTextColor(colors.red)
1386 end -- if not reactorRodOverride then
1387 printLog("reactorRodOverrideStatus is \""..reactorRodOverrideStatus.."\" in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1388
1389 print{reactorRodOverrideStatus, width - string.len(reactorRodOverrideStatus) - 1, 9, monitorIndex}
1390 monitor.setTextColor(colors.white)
1391
1392 print{"Reactivity: "..math.ceil(reactor.getFuelReactivity()).." %", 2, 10, monitorIndex}
1393 print{"Fuel: "..round(reactor.getFuelConsumedLastTick(),3).." mB/t", 2, 11, monitorIndex}
1394 print{"Waste: "..reactor.getWasteAmount().." mB", width-(string.len(reactor.getWasteAmount())+10), 11, monitorIndex}
1395
1396 monitor.setTextColor(colors.blue)
1397 printCentered(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"],12,monitorIndex)
1398 monitor.setTextColor(colors.white)
1399end -- function displayReactorBars(barParams)
1400
1401
1402local function reactorStatus(statusParams)
1403 -- Default to first reactor and first monitor
1404 setmetatable(statusParams,{__index={reactorIndex=1, monitorIndex=1}})
1405 local reactorIndex, monitorIndex =
1406 statusParams[1] or statusParams.reactorIndex,
1407 statusParams[2] or statusParams.monitorIndex
1408 printLog("Called as reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
1409
1410 -- Grab current monitor
1411 local monitor = nil
1412 monitor = monitorList[monitorIndex]
1413 if not monitor then
1414 printLog("monitor["..monitorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
1415 return -- Invalid monitorIndex
1416 end
1417
1418 -- Grab current reactor
1419 local reactor = nil
1420 reactor = reactorList[reactorIndex]
1421 if not reactor then
1422 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
1423 return -- Invalid reactorIndex
1424 else
1425 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
1426 end
1427
1428 local width, height = monitor.getSize()
1429 local reactorStatus = ""
1430
1431 if reactor.getConnected() then
1432 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
1433
1434 if reactor.getActive() then
1435 reactorStatus = "ONLINE"
1436
1437 -- Set "ONLINE" to blue if the actively cooled reactor is both in cruise mode and online
1438 if _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] and reactor.isActivelyCooled() then
1439 monitor.setTextColor(colors.blue)
1440 else
1441 monitor.setTextColor(colors.green)
1442 end -- if reactorCruising and reactor.isActivelyCooled() then
1443 else
1444 reactorStatus = "OFFLINE"
1445 monitor.setTextColor(colors.red)
1446 end -- if reactor.getActive() then
1447
1448 if xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1) and (sideClick == monitorNames[monitorIndex]) then
1449 if yClick == 1 then
1450 reactor.setActive(not reactor.getActive()) -- Toggle reactor status
1451 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = reactor.getActive()
1452 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1453 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1454
1455 -- If someone offlines the reactor (offline after a status click was detected), then disable autoStart
1456 if not reactor.getActive() then
1457 _G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
1458 end
1459 end -- if yClick == 1 then
1460 end -- if (xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1461
1462 -- Allow disabling rod level auto-adjust and only manual rod level control
1463 if ((xClick > 23 and xClick < 28 and yClick == 4)
1464 or (xClick > 20 and xClick < 27 and yClick == 9))
1465 and (sideClick == monitorNames[monitorIndex]) then
1466 _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]
1467 config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1468 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1469 end -- if (xClick > 23) and (xClick < 28) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1470
1471 else
1472 printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
1473 reactorStatus = "DISCONNECTED"
1474 monitor.setTextColor(colors.red)
1475 end -- if reactor.getConnected() then
1476
1477 print{reactorStatus, width - string.len(reactorStatus) - 1, 1, monitorIndex}
1478 monitor.setTextColor(colors.white)
1479end -- function reactorStatus(statusParams)
1480
1481
1482-- Display all found reactors' status to monitor 1
1483-- This is only called if multiple reactors and/or a reactor plus at least one turbine are found
1484local function displayAllStatus()
1485 local reactor, turbine = nil, nil
1486 local onlineReactor, onlineTurbine = 0, 0
1487 local totalReactorRF, totalReactorSteam, totalTurbineRF = 0, 0, 0
1488 local totalReactorFuelConsumed = 0
1489 local totalCoolantStored, totalSteamStored, totalEnergy, totalMaxEnergyStored = 0, 0, 0, 0 -- Total turbine and reactor energy buffer and overall capacity
1490 local maxSteamStored = (2000*#turbineList)+(5000*#reactorList)
1491 local maxCoolantStored = (2000*#turbineList)+(5000*#reactorList)
1492
1493 local monitor, monitorIndex = nil, 1
1494 monitor = monitorList[monitorIndex]
1495 if not monitor then
1496 printLog("monitor["..monitorIndex.."] in displayAllStatus() is NOT a valid monitor.")
1497 return -- Invalid monitorIndex
1498 end
1499
1500 for reactorIndex = 1, #reactorList do
1501 reactor = reactorList[reactorIndex]
1502 if not reactor then
1503 printLog("reactor["..reactorIndex.."] in displayAllStatus() is NOT a valid Big Reactor.")
1504 break -- Invalid reactorIndex
1505 else
1506 printLog("reactor["..reactorIndex.."] in displayAllStatus() is a valid Big Reactor.")
1507 end -- if not reactor then
1508
1509 if reactor.getConnected() then
1510 printLog("reactor["..reactorIndex.."] in displayAllStatus() is connected.")
1511 if reactor.getActive() then
1512 onlineReactor = onlineReactor + 1
1513 totalReactorFuelConsumed = totalReactorFuelConsumed + reactor.getFuelConsumedLastTick()
1514 end -- reactor.getActive() then
1515
1516 -- Actively cooled reactors do not produce or store energy
1517 if not reactor.isActivelyCooled() then
1518 totalMaxEnergyStored = totalMaxEnergyStored + 10000000 -- Reactors store 10M RF
1519 totalEnergy = totalEnergy + reactor.getEnergyStored()
1520 totalReactorRF = totalReactorRF + reactor.getEnergyProducedLastTick()
1521 else
1522 totalReactorSteam = totalReactorSteam + reactor.getEnergyProducedLastTick()
1523 totalSteamStored = totalSteamStored + reactor.getHotFluidAmount()
1524 totalCoolantStored = totalCoolantStored + reactor.getCoolantAmount()
1525 end -- if not reactor.isActivelyCooled() then
1526 else
1527 printLog("reactor["..reactorIndex.."] in displayAllStatus() is NOT connected.")
1528 end -- if reactor.getConnected() then
1529 end -- for reactorIndex = 1, #reactorList do
1530
1531 for turbineIndex = 1, #turbineList do
1532 turbine = turbineList[turbineIndex]
1533 if not turbine then
1534 printLog("turbine["..turbineIndex.."] in displayAllStatus() is NOT a valid Turbine.")
1535 break -- Invalid turbineIndex
1536 else
1537 printLog("turbine["..turbineIndex.."] in displayAllStatus() is a valid Turbine.")
1538 end -- if not turbine then
1539
1540 if turbine.getConnected() then
1541 printLog("turbine["..turbineIndex.."] in displayAllStatus() is connected.")
1542 if turbine.getActive() then
1543 onlineTurbine = onlineTurbine + 1
1544 end
1545
1546 totalMaxEnergyStored = totalMaxEnergyStored + 1000000 -- Turbines store 1M RF
1547 totalEnergy = totalEnergy + turbine.getEnergyStored()
1548 totalTurbineRF = totalTurbineRF + turbine.getEnergyProducedLastTick()
1549 totalSteamStored = totalSteamStored + turbine.getInputAmount()
1550 totalCoolantStored = totalCoolantStored + turbine.getOutputAmount()
1551 else
1552 printLog("turbine["..turbineIndex.."] in displayAllStatus() is NOT connected.")
1553 end -- if turbine.getConnected() then
1554 end -- for turbineIndex = 1, #turbineList do
1555
1556 print{"Reactors online/found: "..onlineReactor.."/"..#reactorList, 2, 3, monitorIndex}
1557 print{"Turbines online/found: "..onlineTurbine.."/"..#turbineList, 2, 4, monitorIndex}
1558
1559 if totalReactorRF ~= 0 then
1560 monitor.setTextColor(colors.blue)
1561 printRight("Reactor", 9, monitorIndex)
1562 monitor.setTextColor(colors.white)
1563 printRight(math.ceil(totalReactorRF).." (RF/t)", 10, monitorIndex)
1564 end
1565
1566 if #turbineList then
1567 -- Display liquids
1568 monitor.setTextColor(colors.blue)
1569 printLeft("Steam (mB)", 6, monitorIndex)
1570 monitor.setTextColor(colors.white)
1571 printLeft(math.ceil(totalSteamStored).."/"..maxSteamStored, 7, monitorIndex)
1572 printLeft(math.ceil(totalReactorSteam).." mB/t", 8, monitorIndex)
1573 monitor.setTextColor(colors.blue)
1574 printRight("Coolant (mB)", 6, monitorIndex)
1575 monitor.setTextColor(colors.white)
1576 printRight(math.ceil(totalCoolantStored).."/"..maxCoolantStored, 7, monitorIndex)
1577
1578 monitor.setTextColor(colors.blue)
1579 printLeft("Turbine", 9, monitorIndex)
1580 monitor.setTextColor(colors.white)
1581 printLeft(math.ceil(totalTurbineRF).." RF/t", 10, monitorIndex)
1582 end -- if #turbineList then
1583
1584 printCentered("Fuel: "..round(totalReactorFuelConsumed,3).." mB/t", 11, monitorIndex)
1585 print{"Buffer: "..math.ceil(totalEnergy,3).."/"..totalMaxEnergyStored.." RF", 2, 12, monitorIndex}
1586end -- function displayAllStatus()
1587
1588
1589-- Get turbine status
1590local function displayTurbineBars(turbineIndex, monitorIndex)
1591 printLog("Called as displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1592
1593 -- Grab current monitor
1594 local monitor = nil
1595 monitor = monitorList[monitorIndex]
1596 if not monitor then
1597 printLog("monitor["..monitorIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
1598 return -- Invalid monitorIndex
1599 end
1600
1601 -- Grab current turbine
1602 local turbine = nil
1603 turbine = turbineList[turbineIndex]
1604 if not turbine then
1605 printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
1606 return -- Invalid turbineIndex
1607 else
1608 printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
1609 if turbine.getConnected() then
1610 printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
1611 else
1612 printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
1613 return -- Disconnected turbine
1614 end -- if turbine.getConnected() then
1615 end -- if not turbine then
1616
1617 --local variable to match the view on the monitor
1618 local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
1619
1620 -- Draw border lines
1621 local width, height = monitor.getSize()
1622
1623 for i=3, 6 do
1624 monitor.setCursorPos(21, i)
1625 monitor.write("|")
1626 end
1627
1628 drawLine(2,monitorIndex)
1629 drawLine(7,monitorIndex)
1630
1631 -- Allow controlling Turbine Flow Rate from GUI
1632 -- Decrease flow rate button: 22X, 4Y
1633 -- Increase flow rate button: 28X, 4Y
1634 local turbineFlowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
1635 if (xClick == 22) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1636 printLog("Decrease to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1637 --Decrease rod level by amount
1638 newTurbineFlowRate = turbineFlowRate - flowRateAdjustAmount
1639 if newTurbineFlowRate < 0 then
1640 newTurbineFlowRate = 0
1641 end
1642 sideClick, xClick, yClick = 0, 0, 0
1643
1644 -- Check bounds [0,2000]
1645 if newTurbineFlowRate > 2000 then
1646 newTurbineFlowRate = 2000
1647 elseif newTurbineFlowRate < 0 then
1648 newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
1649 end
1650
1651 turbine.setFluidFlowRateMax(newTurbineFlowRate)
1652 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newTurbineFlowRate
1653 -- Save updated Turbine Flow Rate
1654 turbineFlowRate = newTurbineFlowRate
1655 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1656 elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1657 printLog("Increase to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1658 --Increase rod level by amount
1659 newTurbineFlowRate = turbineFlowRate + flowRateAdjustAmount
1660 if newTurbineFlowRate > 2000 then
1661 newTurbineFlowRate = 2000
1662 end
1663 sideClick, xClick, yClick = 0, 0, 0
1664
1665 -- Check bounds [0,2000]
1666 if newTurbineFlowRate > 2000 then
1667 newTurbineFlowRate = 2000
1668 elseif newTurbineFlowRate < 0 then
1669 newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
1670 end
1671
1672 turbine.setFluidFlowRateMax(newTurbineFlowRate)
1673
1674 -- Save updated Turbine Flow Rate
1675 turbineFlowRate = math.ceil(newTurbineFlowRate)
1676 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = turbineFlowRate
1677 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1678 else
1679 printLog("No change to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1680 end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1681
1682 if (xClick == 22) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
1683 printLog("Decrease to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1684 rpmRateAdjustment = 909
1685 newTurbineBaseSpeed = turbineBaseSpeed - rpmRateAdjustment
1686 if newTurbineBaseSpeed < 908 then
1687 newTurbineBaseSpeed = 908
1688 end
1689 sideClick, xClick, yClick = 0, 0, 0
1690 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
1691 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1692 elseif (xClick == 29) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
1693 printLog("Increase to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1694 rpmRateAdjustment = 909
1695 newTurbineBaseSpeed = turbineBaseSpeed + rpmRateAdjustment
1696 if newTurbineBaseSpeed > 2726 then
1697 newTurbineBaseSpeed = 2726
1698 end
1699 sideClick, xClick, yClick = 0, 0, 0
1700 _G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
1701 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1702 else
1703 printLog("No change to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1704 end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1705 print{" mB/t",22,3,monitorIndex}
1706 print{"< >",22,4,monitorIndex}
1707 print{stringTrim(turbineFlowRate),24,4,monitorIndex}
1708 print{" RPM",22,5,monitorIndex}
1709 print{"< >",22,6,monitorIndex}
1710 print{stringTrim(tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])),24,6,monitorIndex}
1711 local rotorSpeedString = "Speed: "
1712 local energyBufferString = "Energy: "
1713 local padding = math.max(string.len(rotorSpeedString), string.len(energyBufferString))
1714
1715 local energyBuffer = turbine.getEnergyProducedLastTick()
1716 print{energyBufferString,1,4,monitorIndex}
1717 print{math.ceil(energyBuffer).." RF/t",padding+1,4,monitorIndex}
1718
1719 local rotorSpeed = math.ceil(turbine.getRotorSpeed())
1720 print{rotorSpeedString,1,5,monitorIndex}
1721 print{rotorSpeed.." RPM",padding+1,5,monitorIndex}
1722
1723 -- PaintUtils only outputs to term., not monitor.
1724 -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
1725
1726 -- Draw stored energy buffer bar
1727 drawBar(1,9,28,9,colors.gray,monitorIndex)
1728
1729 local curStoredEnergyPercent = getTurbineStoredEnergyBufferPercent(turbine)
1730 if curStoredEnergyPercent > 4 then
1731 drawBar(1, 9, math.floor(26*curStoredEnergyPercent/100)+2, 9, colors.yellow,monitorIndex)
1732 elseif curStoredEnergyPercent > 0 then
1733 drawPixel(1, 9, colors.yellow, monitorIndex)
1734 end -- if curStoredEnergyPercent > 4 then
1735
1736 print{"Energy Buffer",1,8,monitorIndex}
1737 print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),8,monitorIndex}
1738 print{"%",28,8,monitorIndex}
1739
1740 -- Print rod override status
1741 local turbineFlowRateOverrideStatus = ""
1742
1743 print{"Flow Auto-adjust:",2,10,monitorIndex}
1744
1745 if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
1746 turbineFlowRateOverrideStatus = "Enabled"
1747 monitor.setTextColor(colors.green)
1748 else
1749 turbineFlowRateOverrideStatus = "Disabled"
1750 monitor.setTextColor(colors.red)
1751 end -- if not reactorRodOverride then
1752
1753 print{turbineFlowRateOverrideStatus, width - string.len(turbineFlowRateOverrideStatus) - 1, 10, monitorIndex}
1754 monitor.setTextColor(colors.white)
1755
1756 monitor.setTextColor(colors.blue)
1757 printCentered(_G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"],12,monitorIndex)
1758 monitor.setTextColor(colors.white)
1759
1760 -- Need equation to figure out rotor efficiency and display
1761end -- function displayTurbineBars(statusParams)
1762
1763
1764-- Display turbine status
1765local function turbineStatus(turbineIndex, monitorIndex)
1766 -- Grab current monitor
1767 local monitor = nil
1768
1769 printLog("Called as turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1770
1771 monitor = monitorList[monitorIndex]
1772 if not monitor then
1773 printLog("monitor["..monitorIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
1774 return -- Invalid monitorIndex
1775 end
1776
1777 -- Grab current turbine
1778 local turbine = nil
1779 turbine = turbineList[turbineIndex]
1780 if not turbine then
1781 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
1782 return -- Invalid turbineIndex
1783 else
1784 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
1785 end
1786
1787 local width, height = monitor.getSize()
1788 local turbineStatus = ""
1789
1790 if turbine.getConnected() then
1791 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
1792 if turbine.getActive() then
1793 turbineStatus = "ONLINE"
1794 monitor.setTextColor(colors.green)
1795 else
1796 turbineStatus = "OFFLINE"
1797 monitor.setTextColor(colors.red)
1798 end -- if turbine.getActive() then
1799
1800 if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1801 if yClick == 1 then
1802 turbine.setActive(not turbine.getActive()) -- Toggle turbine status
1803 _G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = turbine.getActive()
1804 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1805 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1806 end -- if yClick == 1 then
1807 end -- if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1808
1809 -- Allow disabling/enabling flow rate auto-adjust
1810 if (xClick > 23 and xClick < 28 and yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1811 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
1812 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1813 elseif (xClick > 20 and xClick < 27 and yClick == 10) and (sideClick == monitorNames[monitorIndex]) then
1814
1815 if ((_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "true")) then
1816 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
1817 else
1818 _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
1819 end
1820 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1821 end
1822 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1823
1824 else
1825 printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
1826 turbineStatus = "DISCONNECTED"
1827 monitor.setTextColor(colors.red)
1828 end -- if turbine.getConnected() then
1829
1830 print{turbineStatus, width - string.len(turbineStatus) - 1, 1, monitorIndex}
1831 monitor.setTextColor(colors.white)
1832end -- function function turbineStatus(turbineIndex, monitorIndex)
1833
1834
1835-- Maintain Turbine flow rate at 900 or 1,800 RPM
1836local function flowRateControl(turbineIndex)
1837 if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
1838
1839 printLog("Called as flowRateControl(turbineIndex="..turbineIndex..").")
1840
1841 -- Grab current turbine
1842 local turbine = nil
1843 turbine = turbineList[turbineIndex]
1844
1845 -- assign for the duration of this run
1846 local lastTurbineSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"])
1847 local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
1848
1849 if not turbine then
1850 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT a valid Big Turbine.")
1851 return -- Invalid turbineIndex
1852 else
1853 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is a valid Big Turbine.")
1854
1855 if turbine.getConnected() then
1856 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is connected.")
1857 else
1858 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT connected.")
1859 end -- if turbine.getConnected() then
1860 end -- if not turbine then
1861
1862 -- No point modifying control rod levels for temperature if the turbine is offline
1863 if turbine.getActive() then
1864 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is active.")
1865
1866 local flowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
1867 local flowRateUserMax = math.ceil(turbine.getFluidFlowRateMax())
1868 local rotorSpeed = math.ceil(turbine.getRotorSpeed())
1869 local newFlowRate = 0
1870
1871 -- Going to control the turbine based on target RPM since changing the target flow rate bypasses this function
1872 if (rotorSpeed < turbineBaseSpeed) then
1873 printLog("BELOW COMMANDED SPEED")
1874 if (rotorSpeed > lastTurbineSpeed) then
1875 --we're still increasing, let's let it level off
1876 --also lets the first control pass go by on startup
1877 elseif (rotorSpeed < lastTurbineSpeed) then
1878 --we're decreasing where we should be increasing, do something
1879 if ((lastTurbineSpeed - rotorSpeed) > 100) then
1880 --kick it harder
1881 newFlowRate = 2000
1882 printLog("HARD KICK")
1883 else
1884 --let's adjust based on proximity
1885 flowAdjustment = (turbineBaseSpeed - rotorSpeed)/5
1886 newFlowRate = flowRate + flowAdjustment
1887 printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1888 end
1889 else
1890 --we've stagnated, kick it.
1891 flowAdjustment = (turbineBaseSpeed - lastTurbineSpeed)
1892 newFlowRate = flowRate + flowAdjustment
1893 printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1894 end --if (rotorSpeed > lastTurbineSpeed) then
1895 else
1896 --we're above commanded turbine speed
1897 printLog("ABOVE COMMANDED SPEED")
1898 if (rotorSpeed < lastTurbineSpeed) then
1899 --we're decreasing, let it level off
1900 --also bypasses first control pass on startup
1901 elseif (rotorSpeed > lastTurbineSpeed) then
1902 --we're above and ascending.
1903 if ((rotorSpeed - lastTurbineSpeed) > 100) then
1904 --halt
1905 newFlowRate = 25
1906 else
1907 --let's adjust based on proximity
1908 flowAdjustment = (rotorSpeed - turbineBaseSpeed)/5
1909 newFlowRate = flowRate - flowAdjustment
1910 printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1911 end
1912 else
1913 --we've stagnated, kick it.
1914 flowAdjustment = (lastTurbineSpeed - turbineBaseSpeed)
1915 newFlowRate = flowRate - flowAdjustment
1916 printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1917 end --if (rotorSpeed < lastTurbineSpeed) then
1918 end --if (rotorSpeed < turbineBaseSpeed)
1919
1920 --check to make sure an adjustment was made
1921 if (newFlowRate == 0) then
1922 --do nothing, we didn't ask for anything this pass
1923 else
1924 --boundary check
1925 if newFlowRate > 2000 then
1926 newFlowRate = 2000
1927 elseif newFlowRate < 25 then
1928 newFlowRate = 25 -- Don't go to zero, might as well power off
1929 end -- if newFlowRate > 2000 then
1930 --no sense running an adjustment if it's not necessary
1931 if ((newFlowRate < flowRate) or (newFlowRate > flowRate)) then
1932 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is being commanded to "..newFlowRate.." mB/t flow")
1933 newFlowRate = round(newFlowRate, 0)
1934 turbine.setFluidFlowRateMax(newFlowRate)
1935 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newFlowRate
1936 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1937 end
1938 end
1939 --always set this
1940 _G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = rotorSpeed
1941 config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1942 else
1943 printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT active.")
1944 end -- if turbine.getActive() then
1945 else
1946 printLog("turbine["..turbineIndex.."] has flow override set to "..tostring(_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"])..", bypassing flow control.")
1947 end -- if not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] then
1948end -- function flowRateControl(turbineIndex)
1949
1950
1951function main()
1952 -- Load reactor parameters and initialize systems
1953 loadReactorOptions()
1954
1955 -- Get our initial list of connected monitors and reactors
1956 -- and initialize every cycle in case the connected devices change
1957 findMonitors()
1958 findReactors()
1959 findTurbines()
1960
1961 while not finished do
1962 local reactor = nil
1963 local monitorIndex = 1
1964
1965 -- For multiple reactors/monitors, monitor #1 is reserved for overall status
1966 -- or for multiple reactors/turbines and only one monitor
1967 if ( ( ((#reactorList + #turbineList) > 1) and (#monitorList >= 1) ) or
1968 ( ((#reactorList + #turbineList) >=1) and (#monitorList > 1) ) ) then
1969 local monitor = nil
1970 monitor = monitorList[monitorIndex]
1971 if not monitor then
1972 printLog("monitor["..monitorIndex.."] in main() is NOT a valid monitor.")
1973 return -- Invalid monitorIndex
1974 end
1975
1976 clearMonitor(progName.." "..progVer, monitorIndex) -- Clear monitor and draw borders
1977 printCentered(progName.." "..progVer, 1, monitorIndex)
1978 displayAllStatus()
1979 monitorIndex = 2 -- Next monitor, #1 is reserved for overall status
1980 end
1981
1982 -- Iterate through reactors, continue to run even if not enough monitors are connected
1983 for reactorIndex = 1, #reactorList do
1984 local monitor = nil
1985 local reactorMonitorIndex = monitorIndex + reactorIndex - 1 -- reactorIndex starts at 1
1986
1987 printLog("Attempting to display reactor["..reactorIndex.."] on monitor["..reactorMonitorIndex.."]...")
1988
1989 reactor = reactorList[reactorIndex]
1990 if not reactor then
1991 printLog("reactor["..reactorIndex.."] in main() is NOT a valid Big Reactor.")
1992 break -- Invalid reactorIndex
1993 else
1994 printLog("reactor["..reactorIndex.."] in main() is a valid Big Reactor.")
1995 end -- if not reactor then
1996
1997 -- Only attempt to assign a monitor if we have a monitor for this reactor
1998 if (reactorMonitorIndex <= #monitorList) then
1999 printLog("Displaying reactor["..reactorIndex.."] on monitor["..reactorMonitorIndex.."].")
2000 monitor = monitorList[reactorMonitorIndex]
2001
2002 if not monitor then
2003 printLog("monitor["..reactorMonitorIndex.."] in main() is NOT a valid monitor.")
2004 else
2005 clearMonitor(progName, reactorMonitorIndex) -- Clear monitor and draw borders
2006 printCentered(progName, 1, reactorMonitorIndex)
2007
2008 -- Display reactor status, includes "Disconnected" but found reactors
2009 reactorStatus{reactorIndex, reactorMonitorIndex}
2010
2011 -- Draw the borders and bars for the current reactor on the current monitor
2012 displayReactorBars{reactorIndex, reactorMonitorIndex}
2013 end -- if not monitor
2014 else
2015 printLog("You may want "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines.")
2016 end -- if (#monitorList ~= 1) and (reactorMonitorIndex < #monitorList) then
2017
2018 if reactor.getConnected() then
2019 printLog("reactor["..reactorIndex.."] is connected.")
2020 local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
2021
2022 -- Shutdown reactor if current stored energy % is >= desired level, otherwise activate
2023 -- First pass will have curStoredEnergyPercent=0 until displayBars() is run once
2024 if curStoredEnergyPercent >= maxStoredEnergyPercent then
2025 reactor.setActive(false)
2026 -- Do not auto-start the reactor if it was manually powered off (autoStart=false)
2027 elseif (curStoredEnergyPercent <= minStoredEnergyPercent) and (_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] == true) then
2028 reactor.setActive(true)
2029 end -- if curStoredEnergyPercent >= maxStoredEnergyPercent then
2030
2031 -- Don't try to auto-adjust control rods if manual control is requested
2032 if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
2033 temperatureControl(reactorIndex)
2034 end -- if not reactorRodOverride then
2035 else
2036 printLog("reactor["..reactorIndex.."] is NOT connected.")
2037 end -- if reactor.getConnected() then
2038 end -- for reactorIndex = 1, #reactorList do
2039
2040 -- Monitors for turbines start after turbineMonitorOffset
2041 for turbineIndex = 1, #turbineList do
2042 local monitor = nil
2043 local turbineMonitorIndex = turbineIndex + turbineMonitorOffset
2044
2045 printLog("Attempting to display turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."]...")
2046
2047 -- Only attempt to assign a monitor if we found a monitor for this turbine
2048 if (turbineMonitorIndex <= #monitorList) then
2049 printLog("Displaying turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."].")
2050 monitor = monitorList[turbineMonitorIndex]
2051 if not monitor then
2052 printLog("monitor["..turbineMonitorIndex.."] in main() is NOT a valid monitor.")
2053 else
2054 clearMonitor(progName, turbineMonitorIndex) -- Clear monitor and draw borders
2055 printCentered(progName, 1, turbineMonitorIndex)
2056
2057 -- Display turbine status, includes "Disconnected" but found turbines
2058 turbineStatus(turbineIndex, turbineMonitorIndex)
2059
2060 -- Draw the borders and bars for the current turbine on the current monitor
2061 displayTurbineBars(turbineIndex, turbineMonitorIndex)
2062 end -- if not monitor
2063 else
2064 printLog("You may want "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines.")
2065 end -- if (#monitorList ~= 1) and (turbineMonitorIndex < #monitorList) then
2066
2067 turbine = turbineList[turbineIndex]
2068 if not turbine then
2069 printLog("turbine["..turbineIndex.."] in main() is NOT a valid Big Turbine.")
2070 break -- Invalid turbineIndex
2071 else
2072 printLog("turbine["..turbineIndex.."] in main() is a valid Big Turbine.")
2073 end -- if not turbine then
2074
2075 if turbine.getConnected() then
2076 printLog("turbine["..turbineIndex.."] is connected.")
2077
2078 if not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] then
2079 flowRateControl(turbineIndex)
2080 end -- if not turbineFlowRateOverride[turbineIndex] then
2081 else
2082 printLog("turbine["..turbineIndex.."] is NOT connected.")
2083 end -- if turbine.getConnected() then
2084 end -- for reactorIndex = 1, #reactorList do
2085
2086 sleep(loopTime) -- Sleep
2087 saveReactorOptions()
2088 end -- while not finished do
2089end -- main()
2090
2091
2092local function eventHandler()
2093 while not finished do
2094 -- http://computercraft.info/wiki/Os.pullEvent
2095 -- http://www.computercraft.info/forums2/index.php?/topic/1516-ospullevent-what-is-it-and-how-is-it-useful/
2096 event, arg1, arg2, arg3 = os.pullEvent()
2097
2098 if event == "monitor_touch" then
2099 sideClick, xClick, yClick = arg1, math.floor(arg2), math.floor(arg3)
2100 printLog("Side: "..arg1.." Monitor touch X: "..xClick.." Y: "..yClick)
2101 elseif event == "char" and not inManualMode then
2102 local ch = string.lower(arg1)
2103 if ch == "q" then
2104 finished = true
2105 elseif ch == "r" then
2106 finished = true
2107 os.reboot()
2108 end -- if ch == "q" then
2109 end -- if event == "monitor_touch" then
2110 end -- while not finished do
2111end -- function eventHandler()
2112
2113
2114while not finished do
2115 parallel.waitForAny(eventHandler, main)
2116 sleep(loopTime)
2117end -- while not finished do
2118
2119
2120-- Clear up after an exit
2121term.clear()
2122term.setCursorPos(1,1)