· 6 years ago · Oct 28, 2019, 03:40 PM
1--[[
2Program name: Lolmer's EZ-NUKE reactor control system
3Version: v0.3.7
4Programmer: Lolmer
5Last update: 2014-04-12
6Pastebin: http://pastebin.com/fguScPBQ
7
8Description:
9This 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.
10
11This 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
12
13To simplify the code and guesswork, I assume the following monitor layout:
141) One Advanced Monitor for overall status display plus
15 one or more Reactors plus
16 none or more Turbines.
172) One Advanced Monitor for overall status display plus (first found monitor)
18 one Advanced Monitor for each connected Reactor plus (subsequent found monitors)
19 one Advanced Monitor for each connected Turbine (last group of monitors found).
20If you enable debug mode, add one additional Advanced Monitor for #1 or #2.
21
22Notes:
23 Only one reactor and one, two, and three turbines have been tested with the above, but IN THEORY any number is supported.
24 Devices are found in the reverse order they are plugged in, so monitor_10 will be found before monitor_9.
25 Two 15x15x14 Turbines can output 260K RF/t by just one 7^3 (four rods) reactor putting out 4k mB steam
26
27When using actively cooled reactors with turbines, keep the following in mind:
28 - 1 mB steam carries up to 10RF of potential energy to extract in a turbine.
29 - Actively cooled reactors produce steam, not power.
30 - You will need about 10 mB of water for each 1 mB of steam that you want to create in a 7^3 reactor.
31
32Features:
33 Configurable min/max energy buffer and min/max temperature via ReactorOptions file.
34 ReactorOptions is read on start and then current values are saved every program cycle.
35 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.
36 Auto-adjusts control rods per reactor to maintain temperature.
37 Will display reactor data to all attached monitors of correct dimensions.
38 For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
39 For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
40
41GUI Usage:
42 The "<" and ">" buttons, when right-clicked with the mouse, will decrease and increase, respectively, the values assigned to the monitor:
43 "Rod (%)" will lower/raise the Reactor Control Rods for that Reactor
44 "Flow mB/t" will lower/raise the Turbine Flow Rate maximum for that Turbine
45 Right-clicking between the "<" and ">" (not on them) will disable auto-adjust of that value for attached device.
46 Right-clicking on the "Enabled" or "Disabled" text for auto-adjust will do the same.
47 Right-clicking on "ONLINE" or "OFFLINE" at the top-right will toggle the state of attached device.
48
49Default values:
50 Rod Control: 90% (Let's start off safe and then power up as we can)
51 Minimum Energy Buffer: 15% (will power on below this value)
52 Maximum Energy Buffer: 85% (will power off above this value)
53 Minimum Passive Cooling Temperature: 850^C (will raise control rods below this value)
54 Maximum Passive Temperature: 950^C (will lower control rods above this value)
55 Optimal Turbine RPM: 900 or 1,800
56
57Requirements:
58 Advanced Monitor size is X: 29, Y: 12 with a 3x2 size
59 Computer or Advanced Computer
60 Modems (not wireless) connecting each of the Computer to both the Advanced Monitor and Reactor Computer Port.
61 Big Reactors (http://www.big-reactors.com/) 0.3
62 Computercraft (http://computercraft.info/) 1.57+
63 Reset the computer any time number of connected devices change.
64
65Resources:
66This script is available from:
67 http://pastebin.com/fguScPBQ
68 https://github.com/sandalle/minecraft_bigreactor_control
69Start-up script is available from:
70 http://pastebin.com/ZTMzRLez
71 https://github.com/sandalle/minecraft_bigreactor_control
72Other reactor control program which I based my program on:
73 http://pastebin.com/aMAu4X5J (ScatmanJohn)
74 http://pastebin.com/HjUVNDau (version ScatmanJohn based his on)
75A simpler Big Reactor control program is available from:
76 http://pastebin.com/7S5xCvgL (IronClaymore only for passively cooled reactors)
77
78Reactor Computer Port API: http://wiki.technicpack.net/Reactor_Computer_Port
79Computercraft API: http://computercraft.info/wiki/Category:APIs
80Big Reactors Efficiency, Speculation and Questions! http://www.reddit.com/r/feedthebeast/comments/1vzds0/big_reactors_efficiency_speculation_and_questions/
81Big Reactors API code: https://github.com/erogenousbeef/BigReactors/blob/master/erogenousbeef/bigreactors/common/multiblock/tileentity/TileEntityReactorComputerPort.java
82Big Reactors API: http://big-reactors.com/cc_api.html
83
84ChangeLog:
850.3.7 - Fix typo when initializing TurbineNames array.
86 Fix Issue #1, turbine display is using the Reactor buffer size (10M RF) instead of the Turbine buffer size (1M RF).
870.3.6 - Fix multi-reactors displaying on the correct monitors (thanks HybridFusion).
88 Fix rod auto-adjust text position.
89 Reactors store 10M RF and Turbines store 1M RF in their buffer.
90 Add more colour to displayAllStatus().
91 Sleep for only two seconds instead of five.
92 Fix getDeviceStoredEnergyBufferPercent() for Reactors storing 10M RF in buffer.
93 Keep actively cooled reactors between 0-300^C (non-configurable for now).
940.3.5 - Do not discover connected devices every loop - nicer on servers. Reset computer anytime number of connected devices change.
95 Fix multi-reactor setups to display the additional reactors on monitors, rather than the last one found.
96 Fix passive reactor display having auto-adjust and energy buffer overwrite each other (removes rod count).
970.3.4 - Fix arithmetic for checking if we have enough monitors for the number of reactors.
98 Turbines are optimal at 900, 1800, *and* 2700 RPM
99 Increase loop timer from 1 to 5 to be nicer to servers
1000.3.3 - Add Big Reactor Turbine support
101 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)
102 Display monitor number on top left of each monitor as "M#" to help find which monitor is which.
103 Enabling debug will use the last monitor found, if more than one, to print out debug info (also written to file)
104 Add monitor layout requirements to simplify code
105 Only clear monitors when we're about to use them (e.g. turbine monitors no longer clear, then wait for all reactors to update)
106 Fix getDeviceStoredEnergyBufferPercent(), was off by a decimal place
107 Just use first Control Rod level for entire reactor, they are no longer treated individually in BR 0.3
108 Allow for one monitor for n number of reactors and m number of turbines
109 Auto-adjust turbine flow rate by 25 mB to keep rotor speed at 900 or 1,800 RPM.
110 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)
111 Print monitor name and device (reactor|turbine) name in blue to monitor associated for easier design by users.
112 Remove version number from monitors to free up space for monitor names.
113 Add option of right-clicking on "Enabled"/"Disabled" of auto-adjust to toggle it.
1140.3.2 - Allow for rod control to override (disable) auto-adjust via UI (Rhonyn)
1150.3.1 - Add fuel consumption per tick to display
1160.3.0 - Add multi-monitor support! Sends one reactor's data to all monitors.
117 print function now takes table to support optional specified monitor
118 Set "numRods" every cycle for some people (mechaet)
119 Don't redirect terminal output with multiple monitor support
120 Log troubleshooting data to reactorcontrol.log
121 FC_API no longer used (copied and modified what I needed)
122 Multi-reactor support is theoretically implemented, but it is UNTESTED!
123 Updated for Big Reactor 0.3 (no longer works with 0.2)
124 BR getFuelTemperature() now returns many significant digits, just use math.ceil()
125 BR 0.3 removed individual rod temperatures, now it's only reactor-level temperature
1260.2.4 - Simplify math, don't divide by a simple large number and then multiply by 100 (#/10000000*100)
127 Fix direct-connected (no modem) devices. getDeviceSide -> FC_API.getDeviceSide (simple as that :))
1280.2.3 - Check bounds on reactor.setRodControlLevel(#,#), Big Reactor doesn't check for us.
1290.2.2 - Do not auto-start the reactor if it was manually powered off (autoStart=false)
1300.2.1 - Lower/raise only the hottest/coldest Control Rod while trying to control the reactor temperature.
131 "<" Rod Control buttons was off by one (to the left)
1320.2.0 - Lolmer Edition :)
133 Add min/max stored energy percentage (default is 15%/85%), configurable via ReactorOptions file.
134 No reason to keep burning fuel if our power output is going nowhere. :)
135 Use variables variable for the title and version.
136 Try to keep the temperature between configured values (default is 850^C-950^C)
137 Add Waste and number of Control/Fuel Rods to displayBards()
138
139TODO:
140- Save parameters per reactor instead of one global set for all reactors
141- Add min/max RF/t output and have it override temperature concerns (maybe?)
142- Add support for wireless modems, see http://computercraft.info/wiki/Modem_%28API%29, will not be secure (anyone can send/listen to your channels)!
143- Add support for any sized monitor (minimum 3x3), dynamic allocation/alignment
144- Lookup using pcall for better error handling http://www.computercraft.info/forums2/index.php?/topic/10992-using-pcall/
145
146]]--
147
148
149-- Some global variables
150local progVer = "0.3.7"
151local progName = "EZ-NUKE "
152local sideClick, xClick, yClick = nil, 0, 0
153local loopTime = 2
154local controlRodAdjustAmount = 1 -- Default Reactor Rod Control % adjustment amount
155local flowRateAdjustAmount = 25 -- Default Turbine Flow Rate in mB adjustment amount
156local debugMode = false
157-- These need to be updated for multiple reactors
158local baseControlRodLevel = nil
159local reactorRodOverride = false -- Rod override for Reactors
160-- End multi-reactor cleanup section
161local minStoredEnergyPercent = nil -- Max energy % to store before activate
162local maxStoredEnergyPercent = nil -- Max energy % to store before shutdown
163local minReactorTemp = nil -- Minimum reactor temperature (^C) to maintain
164local maxReactorTemp = nil -- Maximum reactor temperature (^C) to maintain
165local autoStart = {} -- Array for automatically starting reactors
166local monitorList = {} -- Empty monitor array
167local monitorNames = {} -- Empty array of monitor names
168local reactorList = {} -- Empty reactor array
169local reactorNames = {} -- Empty array of reactor names
170local turbineList = {} -- Empty turbine array
171local turbineNames = {} -- Empty array of turbine names
172local turbineFlowRateOverride = {} -- Flow rate override for each Turbine
173local turbineMonitorOffset = 0 -- Turbines are assigned monitors after reactors
174
175term.clear()
176term.setCursorPos(2,1)
177term.write("Initializing program...")
178
179
180-- File needs to exist for append "a" later and zero it out if it already exists
181-- Always initalize this file to avoid confusion with old files and the latest run
182local logFile = fs.open("reactorcontrol.log", "w")
183if logFile then
184 logFile.writeLine("Minecraft time: Day "..os.day().." at "..textutils.formatTime(os.time(),true))
185 logFile.close()
186else
187 error("Could not open file reactorcontrol.log for writing")
188end
189
190
191-- Helper functions
192
193
194-- round() function from
195-- http://www.computercraft.info/forums2/index.php?/topic/4023-lua-printformat-with-floating-point-numbers/page__view__findpost__p__31037
196local function round(num, places)
197 num = tostring(num)
198 local inc = false
199
200 -- Make sure decimal is a valid integer for later arithmetic
201 local decimal = string.find(num, "%.") or 0
202
203 if (num:len() - decimal) <= places then
204 return tonumber(num)
205 end --already rounded, nothing to do.
206
207 local digit = tonumber(num:sub(decimal + places + 1))
208 num = num:sub(1, decimal + places)
209
210 if digit <= 4 then
211 return tonumber(num)
212 end --no incrementation needed, return truncated number
213
214 local newNum = ""
215 for i=num:len(), 1, -1 do
216 digit = tonumber(num:sub(i))
217 if digit == 9 then
218 if i > 1 then
219 newNum = "0"..newNum
220 else
221 newNum = "10"..newNum
222 end
223 elseif digit == nil then
224 newNum = "."..newNum
225 else
226 if i > 1 then
227 newNum = num:sub(1,i-1)..(digit + 1)..newNum
228 else
229 newNum = (digit + 1)..newNum
230 end
231 return tonumber(newNum) --No more 9s found, so we are done incrementing. Copy remaining digits, then return number.
232 end -- if digit == 9 then
233 end -- for i=num:len(), 1, -1 do
234 return tonumber(newNum)
235end -- function round(num, places
236
237
238local function printLog(printStr)
239 if debugMode then
240 -- If multiple monitors, use the last monitor for debugging if debug is enabled
241 if #monitorList > 1 then
242 term.redirect(monitorList[#monitorList]) -- Redirect to last monitor for debugging
243 monitorList[#monitorList].setTextScale(0.5) -- Fit more logs on screen
244 write(printStr.."\n") -- May need to use term.scroll(x) if we output too much, not sure
245 term.restore()
246 end -- if #monitorList > 1 then
247
248 local logFile = fs.open("reactorcontrol.log", "a") -- See http://computercraft.info/wiki/Fs.open
249 if logFile then
250 logFile.writeLine(printStr)
251 logFile.close()
252 else
253 error("Cannot open file reactorcontrol.log for appending!")
254 end -- if logFile then
255 end -- if debugMode then
256end -- function printLog(printStr)
257
258
259local function print(printParams)
260 -- Default to xPos=1, yPos=1, and first monitor
261 setmetatable(printParams,{__index={xPos=1, yPos=1, monitorIndex=1}})
262 local printString, xPos, yPos, monitorIndex =
263 printParams[1], -- Required parameter
264 printParams[2] or printParams.xPos,
265 printParams[3] or printParams.yPos,
266 printParams[4] or printParams.monitorIndex
267
268 local monitor = nil
269 monitor = monitorList[monitorIndex]
270
271 if not monitor then
272 printLog("monitorList["..monitorIndex.."] in print() was not a valid monitor")
273 return -- Invalid monitorIndex
274 end
275
276 monitor.setCursorPos(xPos, yPos)
277 monitor.write(printString)
278end -- function print(printParams)
279
280
281-- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
282local function printCentered(printString, yPos, monitorIndex)
283 local monitor = nil
284 monitor = monitorList[monitorIndex]
285
286 if not monitor then
287 printLog("monitorList["..monitorIndex.."] in printCentered() was not a valid monitor")
288 return -- Invalid monitorIndex
289 end
290
291 local width, height = monitor.getSize()
292 local monitorNameLength = 0
293
294 -- Special changes for title bar
295 if yPos == 1 then
296 -- Add monitor name to first line
297 monitorNameLength = monitorNames[monitorIndex]:len()
298
299 -- Leave room for "offline" and "online" on the right except for overall status display
300 if (#monitorList ~= 1) and (monitorIndex ~= 1) then
301 width = width - 7
302 end
303 end
304
305 monitor.setCursorPos(math.floor(width/2) - math.ceil(printString:len()/2) + monitorNameLength/2, yPos)
306 monitor.clearLine()
307 monitor.write(printString)
308
309 monitor.setTextColor(colors.blue)
310 print{monitorNames[monitorIndex], 1, 1, monitorIndex}
311 monitor.setTextColor(colors.white)
312end -- function printCentered(printString, yPos, monitorIndex)
313
314
315-- Print text padded from the left side
316-- Clear the left side of the screen
317local function printLeft(printString, yPos, monitorIndex)
318 local monitor = nil
319 monitor = monitorList[monitorIndex]
320
321 if not monitor then
322 printLog("monitorList["..monitorIndex.."] in printLeft() was not a valid monitor")
323 return -- Invalid monitorIndex
324 end
325
326 local gap = 1
327 local width = monitor.getSize()
328
329 -- Clear left-half of the monitor
330
331 for curXPos = 1, (width / 2) do
332 monitor.setCursorPos(curXPos, yPos)
333 monitor.write(" ")
334 end
335
336 -- Write our string left-aligned
337 monitor.setCursorPos(1+gap, yPos)
338 monitor.write(printString)
339end
340
341
342-- Print text padded from the right side
343-- Clear the right side of the screen
344local function printRight(printString, yPos, monitorIndex)
345 local monitor = nil
346 monitor = monitorList[monitorIndex]
347
348 if not monitor then
349 printLog("monitorList["..monitorIndex.."] in printRight() was not a valid monitor")
350 return -- Invalid monitorIndex
351 end
352
353 -- Make sure printString is a string
354 printString = tostring(printString)
355
356 local gap = 1
357 local width = monitor.getSize()
358
359 -- Clear right-half of the monitor
360 for curXPos = (width/2), width do
361 monitor.setCursorPos(curXPos, yPos)
362 monitor.write(" ")
363 end
364
365 -- Write our string right-aligned
366 monitor.setCursorPos(math.floor(width) - math.ceil(printString:len()+gap), yPos)
367 monitor.write(printString)
368end
369
370
371-- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
372local function clearMonitor(printString, monitorIndex)
373 local monitor = nil
374 monitor = monitorList[monitorIndex]
375
376 if not monitor then
377 printLog("monitorList["..monitorIndex.."] in clearMonitor() was not a valid monitor")
378 return -- Invalid monitorIndex
379 end
380
381 local gap = 2
382 monitor.clear()
383 local width, height = monitor.getSize()
384
385 printCentered(printString, 1, monitorIndex)
386 monitor.setTextColor(colors.blue)
387 print{monitorNames[monitorIndex], 1, 1, monitorIndex}
388 monitor.setTextColor(colors.white)
389
390 for i=1, width do
391 monitor.setCursorPos(i, gap)
392 monitor.write("-")
393 end
394
395 monitor.setCursorPos(1, gap+1)
396end -- function clearMonitor(printString, monitorIndex)
397
398
399-- Return a list of all connected (including via wired modems) devices of "deviceType"
400local function getDevices(deviceType)
401 local deviceName = nil
402 local deviceIndex = 1
403 local deviceList, deviceNames = {}, {} -- Empty array, which grows as we need
404 local peripheralList = peripheral.getNames() -- Get table of connected peripherals
405
406 deviceType = deviceType:lower() -- Make sure we're matching case here
407
408 for peripheralIndex = 1, #peripheralList do
409 -- Log every device found
410 -- printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] attached as \""..peripheralList[peripheralIndex].."\".")
411 if (string.lower(peripheral.getType(peripheralList[peripheralIndex])) == deviceType) then
412 -- Log devices found which match deviceType and which device index we give them
413 printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".")
414 deviceNames[deviceIndex] = peripheralList[peripheralIndex]
415 deviceList[deviceIndex] = peripheral.wrap(peripheralList[peripheralIndex])
416 deviceIndex = deviceIndex + 1
417 end
418 end -- for peripheralIndex = 1, #peripheralList do
419
420 return deviceList, deviceNames
421end -- function getDevices(deviceType)
422
423-- Draw a line across the entire x-axis
424local function drawLine(yPos, monitorIndex)
425 local monitor = nil
426 monitor = monitorList[monitorIndex]
427
428 if not monitor then
429 printLog("monitorList["..monitorIndex.."] in drawLine() was not a valid monitor")
430 return -- Invalid monitorIndex
431 end
432
433 local width, height = monitor.getSize()
434
435 for i=1, width do
436 monitor.setCursorPos(i, yPos)
437 monitor.write("-")
438 end
439end -- function drawLine(yPos,monitorIndex)
440
441
442-- Display a solid bar of specified color
443local function drawBar(startXPos, startYPos, endXPos, endYPos, color, monitorIndex)
444 local monitor = nil
445 monitor = monitorList[monitorIndex]
446
447 if not monitor then
448 printLog("monitorList["..monitorIndex.."] in drawBar() was not a valid monitor")
449 return -- Invalid monitorIndex
450 end
451
452 -- PaintUtils only outputs to term., not monitor.
453 -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
454 term.redirect(monitor)
455 paintutils.drawLine(startXPos, startYPos, endXPos, endYPos, color)
456 monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
457 term.restore()
458end -- function drawBar(startXPos, startYPos,endXPos,endYPos,color,monitorIndex)
459
460
461-- Display single pixel color
462local function drawPixel(xPos, yPos, color, monitorIndex)
463 local monitor = nil
464 monitor = monitorList[monitorIndex]
465
466 if not monitor then
467 printLog("monitorList["..monitorIndex.."] in drawPixel() was not a valid monitor")
468 return -- Invalid monitorIndex
469 end
470
471 -- PaintUtils only outputs to term., not monitor.
472 -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
473 term.redirect(monitor)
474 paintutils.drawPixel(xPos, yPos, color)
475 monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
476 term.restore()
477end -- function drawPixel(xPos, yPos, color, monitorIndex)
478
479
480-- End helper functions
481
482
483-- Then initialize the monitors
484local function findMonitors()
485 -- Empty out old list of monitors
486 monitorList = {}
487
488 printLog("Finding monitors...")
489 monitorList, monitorNames = getDevices("monitor")
490
491 if #monitorList == 0 then
492 printLog("No monitors found!")
493 error("Can't find any monitors!")
494 else
495 for monitorIndex = 1, #monitorList do
496 local monitor = nil
497 monitor = monitorList[monitorIndex]
498
499 if not monitor then
500 printLog("monitorList["..monitorIndex.."] in findMonitors() was not a valid monitor")
501 break -- Invalid monitorIndex
502 end
503
504 local monitorX, monitorY = monitor.getSize()
505 printLog("Verifying monitor["..monitorIndex.."] is of size x:"..monitorX.." by y:"..monitorY)
506
507 -- Check for minimum size to allow for monitor.setTextScale(0.5) to work for 3x2 debugging monitor, changes getSize()
508 if monitorX < 29 or monitorY < 12 then
509 term.redirect(monitor)
510 monitor.clear()
511 printLog("Removing monitor "..monitorIndex.." for incorrect size")
512 monitor.setCursorPos(1,2)
513 write("Monitor is the wrong size!\n")
514 write("Needs to be 3x2.")
515 term.restore()
516
517 table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
518 if monitorIndex == #monitorList then -- If we're at the end already, break from loop
519 break
520 else
521 monitorIndex = monitorIndex - 1 -- We just removed an element
522 end -- if monitorIndex == #monitorList then
523
524 end -- if monitorX ~= 29 or monitorY ~= 12 then
525 end -- for monitorIndex = 1, #monitorList do
526 end -- if #monitorList == 0 then
527end -- local function findMonitors()
528
529
530-- Initialize all Big Reactors - Reactors
531local function findReactors()
532 -- Empty out old list of reactors
533 newReactorList = {}
534
535 printLog("Finding reactors...")
536 newReactorList, reactorNames = getDevices("BigReactors-Reactor")
537
538 if #newReactorList == 0 then
539 printLog("No reactors found!")
540 error("Can't find any reactors!")
541 else -- Placeholder
542 for reactorIndex = 1, #newReactorList do
543 local reactor = nil
544 reactor = newReactorList[reactorIndex]
545
546 if not reactor then
547 printLog("reactorList["..reactorIndex.."] in findReactors() was not a valid Big Reactor")
548 return -- Invalid reactorIndex
549 end
550
551 -- If number of found reactors changed, re-initialize them all for now
552 -- For now, initialize reactors to the same baseControlRodLevel
553 if #newReactorList ~= #reactorList then
554 reactor.setAllControlRodLevels(baseControlRodLevel)
555
556 -- Auto-start reactor when needed (e.g. program startup) by default, or use existing value
557 autoStart[reactorIndex] = true
558 end -- if #newReactorList ~= #reactorList then
559 end -- for reactorIndex = 1, #newReactorList do
560 end -- if #newReactorList == 0 then
561
562 -- Overwrite old reactor list with the now updated list
563 reactorList = newReactorList
564
565 -- Check if we have enough monitors for the number of reactors
566 if (#reactorList ~= 1) and ((#turbineList + #reactorList) + 1 < #monitorList) then
567 printLog("You need "..(#reactorList + 1).." monitors for your "..#reactorList.." connected reactors")
568 end
569
570 -- Start turbine monitor offset after reactors get monitors
571 -- This assumes that there is a monitor for each turbine and reactor, plus the overall monitor display
572 turbineMonitorOffset = #reactorList + 1 -- #turbineList will start at "1" if turbines found and move us just beyond #reactorList and status monitor range
573end -- function findReactors()
574
575
576-- Initialize all Big Reactors - Turbines
577local function findTurbines()
578 -- Empty out old list of turbines
579 newTurbineList = {}
580
581 printLog("Finding turbines...")
582 newTurbineList, turbineNames = getDevices("BigReactors-Turbine")
583
584 if #newTurbineList == 0 then
585 printLog("No turbines found") -- Not an error
586 else
587 for turbineIndex = 1, #newTurbineList do
588 local turbine = nil
589 turbine = newTurbineList[turbineIndex]
590
591 if not turbine then
592 printLog("turbineList["..turbineIndex.."] is not a valid Big Reactors Turbine")
593 return -- Invalid turbineIndex
594 end
595
596 -- If number of found turbines changed, re-initialize them all for now
597 if #newTurbineList ~= #turbineList then
598 -- Default is to allow flow rate auto-adjust
599 turbineFlowRateOverride[turbineIndex] = false
600 end -- if #newTurbineList ~= #turbineList then
601 end -- for turbineIndex = 1, #newTurbineList do
602
603 -- Overwrite old turbine list with the now updated list
604 turbineList = newTurbineList
605
606 -- Check if we have enough monitors for the number of turbines
607 if #monitorList < (#reactorList + #turbineList + 1) then
608 printLog("You need "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines")
609 end
610 end -- if #newTurbineList == 0 then
611end -- function findTurbines()
612
613
614-- Return current energy buffer in a specific reactor by %
615local function getReactorStoredEnergyBufferPercent(reactor)
616 if not reactor then
617 printLog("getReactorStoredEnergyBufferPercent() did not receive a valid Big Reactor Reactor")
618 return -- Invalid reactorIndex
619 end
620
621 local energyBufferStorage = reactor.getEnergyStored()
622 return (math.floor(energyBufferStorage/100000)) -- (buffer/10000000 RF)*100%
623end -- function getReactorStoredEnergyBufferPercent(reactor)
624
625
626-- Return current energy buffer in a specific Turbine by %
627local function getTurbineStoredEnergyBufferPercent(turbine)
628 if not turbine then
629 printLog("getTurbineStoredEnergyBufferPercent() did not receive a valid Big Reactor Turbine")
630 return -- Invalid reactorIndex
631 end
632
633 local energyBufferStorage = turbine.getEnergyStored()
634 return (math.floor(energyBufferStorage/10000)) -- (buffer/1000000 RF)*100%
635end -- function getTurbineStoredEnergyBufferPercent(turbine)
636
637
638-- Modify reactor control rod levels to keep temperature with defined parameters, but
639-- wait an in-game half-hour for the temperature to stabalize before modifying again
640local function temperatureControl(reactorIndex)
641 local reactor = nil
642 reactor = reactorList[reactorIndex]
643 if not reactor then
644 printLog("reactorList["..reactorIndex.."] in temperatureControl() was not a valid Big Reactor")
645 return -- Invalid reactorIndex
646 end
647
648 local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
649 local reactorTemp = math.ceil(reactor.getFuelTemperature())
650 local localMinReactorTemp, localMaxReactorTemp = minReactorTemp, maxReactorTemp
651
652 -- No point modifying control rod levels for temperature if the reactor is offline
653 if reactor.getActive() then
654 -- Actively cooled reactors should range between 0^C-300^C
655 if reactor.isActivelyCooled() then
656 localMinReactorTemp = 0
657 localMaxReactorTemp = 300
658 end
659
660 -- Don't bring us to 100, that's effectively a shutdown
661 if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
662 -- If more than double our maximum temperature, increase rodPercentage faster
663 if reactorTemp > (2 * localMaxReactorTemp) then
664 -- Check bounds, Big Reactor doesn't do this for us. :)
665 if (rodPercentage + (10 * controlRodAdjustAmount)) > 99 then
666 reactor.setAllControlRodLevels(99)
667 else
668 reactor.setAllControlRodLevels(rodPercentage + (10 * controlRodAdjustAmount))
669 end
670 else
671 -- Check bounds, Big Reactor doesn't do this for us. :)
672 if (rodPercentage + controlRodAdjustAmount) > 99 then
673 reactor.setAllControlRodLevels(99)
674 else
675 reactor.setAllControlRodLevels(rodPercentage + controlRodAdjustAmount)
676 end
677 end -- if reactorTemp > (2 * localMaxReactorTemp) then
678 elseif (reactorTemp < localMinReactorTemp) and (rodPercentage ~= 0) then
679 -- If less than half our minimum temperature, decrease rodPercentage faster
680 if reactorTemp < (localMinReactorTemp / 2) then
681 -- Check bounds, Big Reactor doesn't do this for us. :)
682 if (rodPercentage - (10 * controlRodAdjustAmount)) < 0 then
683 reactor.setAllControlRodLevels(0)
684 else
685 reactor.setAllControlRodLevels(rodPercentage - (10 * controlRodAdjustAmount))
686 end
687 else
688 -- Check bounds, Big Reactor doesn't do this for us. :)
689 if (rodPercentage - controlRodAdjustAmount) < 0 then
690 reactor.setAllControlRodLevels(0)
691 else
692 reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustAmount)
693 end
694 end -- if reactorTemp < (localMinReactorTemp / 2) then
695
696 baseControlRodLevel = rodPercentage
697 end -- if (reactorTemp > localMaxReactorTemp) and (rodPercentage < 99) then
698 end -- if reactor.getActive() then
699end -- function temperatureControl(reactorIndex)
700
701
702-- Load saved reactor parameters if ReactorOptions file exists
703local function loadReactorOptions()
704 local reactorOptions = fs.open("ReactorOptions", "r") -- See http://computercraft.info/wiki/Fs.open
705
706 if reactorOptions then
707 baseControlRodLevel = reactorOptions.readLine()
708 -- The following values were added by Lolmer
709 minStoredEnergyPercent = reactorOptions.readLine()
710 maxStoredEnergyPercent = reactorOptions.readLine()
711 minReactorTemp = reactorOptions.readLine()
712 maxReactorTemp = reactorOptions.readLine()
713 reactorRodOverride = reactorOptions.readLine() -- Should be string "true" or "false"
714
715 -- If we succeeded in reading a string, convert it to a number
716 if baseControlRodLevel ~= nil then
717 baseControlRodLevel = tonumber(baseControlRodLevel)
718 end
719
720 if minStoredEnergyPercent ~= nil then
721 minStoredEnergyPercent = tonumber(minStoredEnergyPercent)
722 end
723
724 if maxStoredEnergyPercent ~= nil then
725 maxStoredEnergyPercent = tonumber(maxStoredEnergyPercent)
726 end
727
728 if minReactorTemp ~= nil then
729 minReactorTemp = tonumber(minReactorTemp)
730 end
731
732 if maxReactorTemp ~= nil then
733 maxReactorTemp = tonumber(maxReactorTemp)
734 end
735
736 if reactorRodOverride == "true" then
737 reactorRodOverride = true
738 else
739 reactorRodOverride = false
740 end
741
742 reactorOptions.close()
743 end -- if reactorOptions then
744
745 -- Set default values if we failed to read any of the above
746 if baseControlRodLevel == nil then
747 baseControlRodLevel = 90
748 end
749
750 if minStoredEnergyPercent == nil then
751 minStoredEnergyPercent = 15
752 end
753
754 if maxStoredEnergyPercent == nil then
755 maxStoredEnergyPercent = 85
756 end
757
758 if minReactorTemp == nil then
759 minReactorTemp = 850
760 end
761
762 if maxReactorTemp == nil then
763 maxReactorTemp = 950
764 end
765end -- function loadReactorOptions()
766
767
768-- Save our reactor parameters
769local function saveReactorOptions()
770 local reactorOptions = fs.open("ReactorOptions", "w") -- See http://computercraft.info/wiki/Fs.open
771
772 -- If we can save the files, save them
773 if reactorOptions then
774 local reactorIndex = 1
775 reactorOptions.writeLine(math.ceil(reactorList[1].getControlRodLevel(0))) -- Store just the first reactor for now
776 -- The following values were added by Lolmer
777 reactorOptions.writeLine(minStoredEnergyPercent)
778 reactorOptions.writeLine(maxStoredEnergyPercent)
779 reactorOptions.writeLine(minReactorTemp)
780 reactorOptions.writeLine(maxReactorTemp)
781 reactorOptions.writeLine(reactorRodOverride)
782 reactorOptions.close()
783 else
784 printLog("Failed to open file ReactorOptions for writing!")
785 end -- if reactorOptions then
786end -- function saveReactorOptions()
787
788
789local function displayReactorBars(barParams)
790 -- Default to first reactor and first monitor
791 setmetatable(barParams,{__index={reactorIndex=1, monitorIndex=1}})
792 local reactorIndex, monitorIndex =
793 barParams[1] or barParams.reactorIndex,
794 barParams[2] or barParams.monitorIndex
795
796 -- Grab current monitor
797 local monitor = nil
798 monitor = monitorList[monitorIndex]
799 if not monitor then
800 printLog("monitorList["..monitorIndex.."] in displayReactorBars() was not a valid monitor")
801 return -- Invalid monitorIndex
802 end
803
804 -- Grab current reactor
805 local reactor = nil
806 reactor = reactorList[reactorIndex]
807 if not reactor then
808 printLog("reactorList["..reactorIndex.."] in displayReactorBars() was not a valid Big Reactor")
809 return -- Invalid reactorIndex
810 end
811
812 -- Draw border lines
813 local width, height = monitor.getSize()
814
815 for i=3, 5 do
816 monitor.setCursorPos(22, i)
817 monitor.write("|")
818 end
819
820 drawLine(2, monitorIndex)
821 drawLine(6, monitorIndex)
822
823 -- Draw some text
824 local fuelString = "Fuel: "
825 local tempString = "Temp: "
826 local energyBufferString = "Producing: "
827
828 local padding = math.max(string.len(fuelString), string.len(tempString), string.len(energyBufferString))
829
830 local fuelPercentage = math.ceil(reactor.getFuelAmount()/reactor.getFuelAmountMax()*100)
831 print{fuelString,2,3,monitorIndex}
832 print{fuelPercentage.." %",padding+2,3,monitorIndex}
833
834 local reactorTemp = math.ceil(reactor.getFuelTemperature())
835 print{tempString,2,5,monitorIndex}
836 print{reactorTemp.." C",padding+2,5,monitorIndex}
837
838 local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
839 -- Allow controlling Reactor Control Rod Level from GUI
840 -- Decrease rod button: 23X, 4Y
841 -- Increase rod button: 28X, 4Y
842 if (xClick == 23) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
843 --Decrease rod level by amount
844 newRodPercentage = rodPercentage - (5 * controlRodAdjustAmount)
845 if newRodPercentage < 0 then
846 newRodPercentage = 0
847 end
848 sideClick, xClick, yClick = 0, 0, 0
849
850 reactor.setAllControlRodLevels(newRodPercentage)
851
852 -- Save updated rod percentage
853 baseControlRodLevel = newRodPercentage
854 rodPercentage = newRodPercentage
855 end -- if (xClick == 23) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
856
857 if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
858 --Increase rod level by amount
859 newRodPercentage = rodPercentage + (5 * controlRodAdjustAmount)
860 if newRodPercentage > 100 then
861 newRodPercentage = 100
862 end
863 sideClick, xClick, yClick = 0, 0, 0
864
865 reactor.setAllControlRodLevels(newRodPercentage)
866
867 -- Save updated rod percentage
868 baseControlRodLevel = newRodPercentage
869 rodPercentage = newRodPercentage
870 end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
871
872 print{"Rod (%)",23,3,monitorIndex}
873 print{"< >",23,4,monitorIndex}
874 print{rodPercentage,25,4,monitorIndex}
875
876 -- getEnergyProducedLastTick() is used for both RF/t (passively cooled) and mB/t (actively cooled)
877 local energyBuffer = reactor.getEnergyProducedLastTick()
878 print{energyBufferString,2,4,monitorIndex}
879
880 -- Actively cooled reactors do not produce energy, only hot fluid mB/t to be used in a turbine
881 -- still uses getEnergyProducedLastTick for mB/t of hot fluid generated
882 if not reactor.isActivelyCooled() then
883 -- Draw stored energy buffer bar
884 drawBar(2,8,28,8,colors.gray,monitorIndex)
885
886 local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
887 if curStoredEnergyPercent > 4 then
888 drawBar(2, 8, math.floor(26*curStoredEnergyPercent/100)+2, 8, colors.yellow, monitorIndex)
889 elseif curStoredEnergyPercent > 0 then
890 drawPixel(2, 8, colors.yellow, monitorIndex)
891 end -- if curStoredEnergyPercent > 4 then
892
893 print{"Energy Buffer",2,7,monitorIndex}
894 print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+3),7,monitorIndex}
895 print{"%",28,7,monitorIndex}
896
897 print{math.ceil(energyBuffer).." RF/t",padding+2,4,monitorIndex}
898 else
899 print{math.ceil(energyBuffer).." mB/t",padding+2,4,monitorIndex}
900 end -- if not reactor.isActivelyCooled() then
901
902 -- Print rod override status
903 local reactorRodOverrideStatus = ""
904
905 print{"Rod Auto-adjust:",2,9,monitorIndex}
906
907 if not reactorRodOverride then
908 reactorRodOverrideStatus = "Enabled"
909 monitor.setTextColor(colors.green)
910 else
911 reactorRodOverrideStatus = "Disabled"
912 monitor.setTextColor(colors.red)
913 end -- if not reactorRodOverride then
914
915 print{reactorRodOverrideStatus, width - string.len(reactorRodOverrideStatus) - 1, 9, monitorIndex}
916 monitor.setTextColor(colors.white)
917
918 local numRods = reactor.getNumberOfControlRods() - 1 -- Call every time as some people modify their reactor without rebooting the computer
919
920 print{"Reactivity: "..math.ceil(reactor.getFuelReactivity()).." %", 2, 10, monitorIndex}
921 print{"Fuel: "..round(reactor.getFuelConsumedLastTick(),3).." mB/t", 2, 11, monitorIndex}
922 print{"Waste: "..reactor.getWasteAmount().." mB", width-(string.len(reactor.getWasteAmount())+10), 11, monitorIndex}
923
924 monitor.setTextColor(colors.blue)
925 printCentered(reactorNames[reactorIndex],12,monitorIndex)
926 monitor.setTextColor(colors.white)
927end -- function displayReactorBars(barParams)
928
929
930local function reactorStatus(statusParams)
931 -- Default to first reactor and first monitor
932 setmetatable(statusParams,{__index={reactorIndex=1, monitorIndex=1}})
933 local reactorIndex, monitorIndex =
934 statusParams[1] or statusParams.reactorIndex,
935 statusParams[2] or statusParams.monitorIndex
936
937 -- Grab current monitor
938 local monitor = nil
939 monitor = monitorList[monitorIndex]
940 if not monitor then
941 printLog("monitorList["..monitorIndex.."] in reactorStatus() was not a valid monitor")
942 return -- Invalid monitorIndex
943 end
944
945 -- Grab current reactor
946 local reactor = nil
947 reactor = reactorList[reactorIndex]
948 if not reactor then
949 printLog("reactorList["..reactorIndex.."] in reactorStatus() was not a valid Big Reactor")
950 return -- Invalid reactorIndex
951 end
952
953 local width, height = monitor.getSize()
954 local reactorStatus = ""
955
956 if reactor.getConnected() then
957 if reactor.getActive() then
958 reactorStatus = "ONLINE"
959 monitor.setTextColor(colors.green)
960 else
961 reactorStatus = "OFFLINE"
962 monitor.setTextColor(colors.red)
963 end -- if reactor.getActive() then
964
965 if xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1) and (sideClick == monitorNames[monitorIndex]) then
966 if yClick == 1 then
967 reactor.setActive(not reactor.getActive()) -- Toggle reactor status
968 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
969
970 -- If someone offlines the reactor (offline after a status click was detected), then disable autoStart
971 if not reactor.getActive() then
972 autoStart[reactorIndex] = false
973 end
974 end -- if yClick == 1 then
975 end -- if (xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
976
977 -- Allow disabling rod level auto-adjust and only manual rod level control
978 if ((xClick > 23 and xClick < 28 and yClick == 4)
979 or (xClick > 20 and xClick < 27 and yClick == 9))
980 and (sideClick == monitorNames[monitorIndex]) then
981 reactorRodOverride = not reactorRodOverride -- Toggle reactor rod override status
982 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
983 end -- if (xClick > 23) and (xClick < 28) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
984
985 else
986 reactorStatus = "DISCONNECTED"
987 monitor.setTextColor(colors.red)
988 end -- if reactor.getConnected() then
989
990 print{reactorStatus, width - string.len(reactorStatus) - 1, 1, monitorIndex}
991 monitor.setTextColor(colors.white)
992end -- function reactorStatus(statusParams)
993
994
995-- Display all found reactors' status to monitor 1
996-- This is only called if multiple reactors and/or a reactor plus at least one turbine are found
997local function displayAllStatus()
998 local reactor, turbine = nil, nil
999 local onlineReactor, onlineTurbine = 0, 0
1000 local totalReactorRF, totalReactorSteam, totalTurbineRF = 0, 0, 0
1001 local totalReactorFuelConsumed = 0
1002 local totalCoolantStored, totalSteamStored, totalEnergy, totalMaxEnergyStored = 0, 0, 0, 0 -- Total turbine and reactor energy buffer and overall capacity
1003 local maxSteamStored = (2000*#turbineList)+(5000*#reactorList)
1004 local maxCoolantStored = (2000*#turbineList)+(5000*#reactorList)
1005
1006 local monitor, monitorIndex = nil, 1
1007 monitor = monitorList[monitorIndex]
1008 if not monitor then
1009 printLog("monitorList["..monitorIndex.."] in reactorStatus() was not a valid monitor")
1010 return -- Invalid monitorIndex
1011 end
1012
1013 for reactorIndex = 1, #reactorList do
1014 reactor = reactorList[reactorIndex]
1015 if not reactor then
1016 printLog("reactorList["..reactorIndex.."] in main() was not a valid Big Reactor")
1017 break -- Invalid reactorIndex
1018 end -- if not reactor then
1019
1020 if reactor.getConnected() then
1021 if reactor.getActive() then
1022 onlineReactor = onlineReactor + 1
1023 totalReactorFuelConsumed = totalReactorFuelConsumed + reactor.getFuelConsumedLastTick()
1024 end -- reactor.getActive() then
1025
1026 -- Actively cooled reactors do not produce or store energy
1027 if not reactor.isActivelyCooled() then
1028 totalMaxEnergyStored = totalMaxEnergyStored + 10000000 -- Reactors store 10M RF
1029 totalEnergy = totalEnergy + reactor.getEnergyStored()
1030 totalReactorRF = totalReactorRF + reactor.getEnergyProducedLastTick()
1031 else
1032 totalReactorSteam = totalReactorSteam + reactor.getEnergyProducedLastTick()
1033 totalSteamStored = totalSteamStored + reactor.getHotFluidAmount()
1034 totalCoolantStored = totalCoolantStored + reactor.getCoolantAmount()
1035 end -- if not reactor.isActivelyCooled() then
1036 end -- if reactor.getConnected() then
1037 end -- for reactorIndex = 1, #reactorList do
1038
1039 for turbineIndex = 1, #turbineList do
1040 turbine = turbineList[turbineIndex]
1041 if not turbine then
1042 printLog("turbineList["..turbineIndex.."] in main() was not a valid Big Reactor")
1043 break -- Invalid turbineIndex
1044 end -- if not turbine then
1045
1046 if turbine.getConnected() then
1047 if turbine.getActive() then
1048 onlineTurbine = onlineTurbine + 1
1049 end
1050
1051 totalMaxEnergyStored = totalMaxEnergyStored + 1000000 -- Turbines store 1M RF
1052 totalEnergy = totalEnergy + turbine.getEnergyStored()
1053 totalTurbineRF = totalTurbineRF + turbine.getEnergyProducedLastTick()
1054 totalSteamStored = totalSteamStored + turbine.getInputAmount()
1055 totalCoolantStored = totalCoolantStored + turbine.getOutputAmount()
1056 end -- if turbine.getConnected() then
1057 end -- for turbineIndex = 1, #turbineList do
1058
1059 print{"Reactors online/found: "..onlineReactor.."/"..#reactorList, 2, 3, monitorIndex}
1060 print{"Turbines online/found: "..onlineTurbine.."/"..#turbineList, 2, 4, monitorIndex}
1061
1062 if totalReactorRF ~= 0 then
1063 monitor.setTextColor(colors.blue)
1064 printRight("Reactor", 9, monitorIndex)
1065 monitor.setTextColor(colors.white)
1066 printRight(math.ceil(totalReactorRF).." (RF/t)", 10, monitorIndex)
1067 end
1068
1069 if #turbineList then
1070 -- Display liquids
1071 monitor.setTextColor(colors.blue)
1072 printLeft("Steam (mB)", 6, monitorIndex)
1073 monitor.setTextColor(colors.white)
1074 printLeft(math.ceil(totalSteamStored).."/"..maxSteamStored, 7, monitorIndex)
1075 printLeft(math.ceil(totalReactorSteam).." mB/t", 8, monitorIndex)
1076 monitor.setTextColor(colors.blue)
1077 printRight("Coolant (mB)", 6, monitorIndex)
1078 monitor.setTextColor(colors.white)
1079 printRight(math.ceil(totalCoolantStored).."/"..maxCoolantStored, 7, monitorIndex)
1080
1081 monitor.setTextColor(colors.blue)
1082 printLeft("Turbine", 9, monitorIndex)
1083 monitor.setTextColor(colors.white)
1084 printLeft(math.ceil(totalTurbineRF).." RF/t", 10, monitorIndex)
1085 end -- if #turbineList then
1086
1087 printRight("Fuel: "..round(totalReactorFuelConsumed,3).." mB/t", 11, monitorIndex)
1088 print{"Buffer: "..math.ceil(totalEnergy,3).."/"..totalMaxEnergyStored.." RF", 2, 12, monitorIndex}
1089end -- function displayAllStatus()
1090
1091
1092-- Get turbine status
1093local function displayTurbineBars(turbineIndex, monitorIndex)
1094 -- Grab current monitor
1095 local monitor = nil
1096 monitor = monitorList[monitorIndex]
1097 if not monitor then
1098 printLog("monitorList["..monitorIndex.."] in displayTurbineBars() was not a valid monitor")
1099 return -- Invalid monitorIndex
1100 end
1101
1102 -- Grab current turbine
1103 local turbine = nil
1104 turbine = turbineList[turbineIndex]
1105 if not turbine then
1106 printLog("turbineList["..turbineIndex.."] in displayTurbineBars() was not a valid Big Turbine")
1107 return -- Invalid turbineIndex
1108 end
1109
1110 -- Draw border lines
1111 local width, height = monitor.getSize()
1112
1113 for i=3, 5 do
1114 monitor.setCursorPos(21, i)
1115 monitor.write("|")
1116 end
1117
1118 drawLine(2,monitorIndex)
1119 drawLine(6,monitorIndex)
1120
1121 -- Allow controlling Turbine Flow Rate from GUI
1122 -- Decrease flow rate button: 22X, 4Y
1123 -- Increase flow rate button: 28X, 4Y
1124 local turbineFlowRate = math.ceil(turbine.getFluidFlowRateMax())
1125 if (xClick == 22) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1126 --Decrease rod level by amount
1127 newTurbineFlowRate = turbineFlowRate - flowRateAdjustAmount
1128 if newTurbineFlowRate < 0 then
1129 newTurbineFlowRate = 0
1130 end
1131 sideClick, xClick, yClick = 0, 0, 0
1132
1133 -- Check bounds [0,2000]
1134 if newTurbineFlowRate > 2000 then
1135 newTurbineFlowRate = 2000
1136 elseif newTurbineFlowRate < 0 then
1137 newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
1138 end
1139
1140 turbine.setFluidFlowRateMax(newTurbineFlowRate)
1141
1142 -- Save updated Turbine Flow Rate
1143 turbineFlowRate = newTurbineFlowRate
1144 end -- if (xClick == 22) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1145
1146 if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1147 --Increase rod level by amount
1148 newTurbineFlowRate = turbineFlowRate + flowRateAdjustAmount
1149 if newTurbineFlowRate > 2000 then
1150 newTurbineFlowRate = 2000
1151 end
1152 sideClick, xClick, yClick = 0, 0, 0
1153
1154 -- Check bounds [0,2000]
1155 if newTurbineFlowRate > 2000 then
1156 newTurbineFlowRate = 2000
1157 elseif newTurbineFlowRate < 0 then
1158 newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
1159 end
1160
1161 turbine.setFluidFlowRateMax(newTurbineFlowRate)
1162
1163 -- Save updated Turbine Flow Rate
1164 turbineFlowRate = newTurbineFlowRate
1165 end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1166
1167 print{" Flow",22,3,monitorIndex}
1168 print{"< >",22,4,monitorIndex}
1169 print{turbineFlowRate,23,4,monitorIndex}
1170 print{" mB/t",22,5,monitorIndex}
1171
1172 local rotorSpeedString = "Speed: "
1173 local energyBufferString = "Producing: "
1174
1175 local padding = math.max(string.len(rotorSpeedString), string.len(energyBufferString))
1176
1177 local energyBuffer = turbine.getEnergyProducedLastTick()
1178 print{energyBufferString,1,4,monitorIndex}
1179 print{math.ceil(energyBuffer).." RF/t",padding+1,4,monitorIndex}
1180
1181 local rotorSpeed = math.ceil(turbine.getRotorSpeed())
1182 print{rotorSpeedString,1,5,monitorIndex}
1183 print{rotorSpeed.." RPM",padding+1,5,monitorIndex}
1184
1185 -- PaintUtils only outputs to term., not monitor.
1186 -- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
1187
1188 -- Draw stored energy buffer bar
1189 drawBar(1,8,28,8,colors.gray,monitorIndex)
1190 --paintutils.drawLine(2, 8, 28, 8, colors.gray)
1191
1192 local curStoredEnergyPercent = getTurbineStoredEnergyBufferPercent(turbine)
1193 if curStoredEnergyPercent > 4 then
1194 drawBar(1, 8, math.floor(26*curStoredEnergyPercent/100)+2, 8, colors.yellow,monitorIndex)
1195 elseif curStoredEnergyPercent > 0 then
1196 drawPixel(1, 8, colors.yellow, monitorIndex)
1197 end -- if curStoredEnergyPercent > 4 then
1198
1199 print{"Energy Buffer",1,7,monitorIndex}
1200 print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+3),7,monitorIndex}
1201 print{"%",28,7,monitorIndex}
1202
1203 -- Print rod override status
1204 local turbineFlowRateOverrideStatus = ""
1205
1206 print{"Flow Auto-adjust:",2,10,monitorIndex}
1207
1208 if not turbineFlowRateOverride[turbineIndex] then
1209 turbineFlowRateOverrideStatus = "Enabled"
1210 monitor.setTextColor(colors.green)
1211 else
1212 turbineFlowRateOverrideStatus = "Disabled"
1213 monitor.setTextColor(colors.red)
1214 end -- if not reactorRodOverride then
1215
1216 print{turbineFlowRateOverrideStatus, width - string.len(turbineFlowRateOverrideStatus) - 1, 10, monitorIndex}
1217 monitor.setTextColor(colors.white)
1218
1219 monitor.setTextColor(colors.blue)
1220 printCentered(turbineNames[turbineIndex],12,monitorIndex)
1221 monitor.setTextColor(colors.white)
1222
1223 -- Need equation to figure out rotor efficiency and display
1224end -- function displayTurbineBars(statusParams)
1225
1226
1227-- Display turbine status
1228local function turbineStatus(turbineIndex, monitorIndex)
1229 -- Grab current monitor
1230 local monitor = nil
1231 monitor = monitorList[monitorIndex]
1232 if not monitor then
1233 printLog("monitorList["..monitorIndex.."] in turbineStatus() was not a valid monitor")
1234 return -- Invalid monitorIndex
1235 end
1236
1237 -- Grab current turbine
1238 local turbine = nil
1239 turbine = turbineList[turbineIndex]
1240 if not turbine then
1241 printLog("turbineList["..turbineIndex.."] in turbineStatus() was not a valid Big Turbine")
1242 return -- Invalid turbineIndex
1243 end
1244
1245 local width, height = monitor.getSize()
1246 local turbineStatus = ""
1247
1248 if turbine.getConnected() then
1249 if turbine.getActive() then
1250 turbineStatus = "ONLINE"
1251 monitor.setTextColor(colors.green)
1252 else
1253 turbineStatus = "OFFLINE"
1254 monitor.setTextColor(colors.red)
1255 end -- if turbine.getActive() then
1256
1257 if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1258 if yClick == 1 then
1259 turbine.setActive(not turbine.getActive()) -- Toggle turbine status
1260 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1261 end -- if yClick == 1 then
1262 end -- if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1263
1264 -- Allow disabling/enabling flow rate auto-adjust
1265 if ((xClick > 23 and xClick < 28 and yClick == 4)
1266 or (xClick > 20 and xClick < 27 and yClick == 10))
1267 and (sideClick == monitorNames[monitorIndex]) then
1268 turbineFlowRateOverride[turbineIndex] = not turbineFlowRateOverride[turbineIndex] -- Toggle turbine rod override status
1269 sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1270 end
1271
1272 else
1273 turbineStatus = "DISCONNECTED"
1274 monitor.setTextColor(colors.red)
1275 end -- if turbine.getConnected() then
1276
1277 print{turbineStatus, width - string.len(turbineStatus) - 1, 1, monitorIndex}
1278 monitor.setTextColor(colors.white)
1279end -- function function turbineStatus(turbineIndex, monitorIndex)
1280
1281
1282-- Maintain Turbine flow rate at 900 or 1,800 RPM
1283local function flowRateControl(turbineIndex)
1284 -- Grab current turbine
1285 local turbine = nil
1286 turbine = turbineList[turbineIndex]
1287 if not turbine then
1288 printLog("turbineList["..turbineIndex.."] in flowRateControl() was not a valid Big Turbine")
1289 return -- Invalid turbineIndex
1290 end
1291
1292 -- No point modifying control rod levels for temperature if the turbine is offline
1293 if turbine.getActive() then
1294 local flowRate = turbine.getFluidFlowRate()
1295 local flowRateUserMax = math.ceil(turbine.getFluidFlowRateMax())
1296 local rotorSpeed = math.ceil(turbine.getRotorSpeed())
1297 local newFlowRate = 0
1298
1299 -- If we're not at max flow-rate and an optimal RPM, let's do something
1300 -- also don't do anything if the current flow rate hasn't caught up to the user defined flow rate maximum
1301 if (((rotorSpeed % 900) ~= 0) and (flowRate ~= 2000) and (flowRate == flowRateUserMax))
1302 or (flowRate == 0) then
1303 -- Make sure we are not going too fast
1304 if rotorSpeed > 2700 then
1305 newFlowRate = flowRateUserMax - flowRateAdjustAmount
1306 -- Make sure we're not going too slow
1307 elseif rotorSpeed < 900 then
1308 newFlowRate = flowRateUserMax + flowRateAdjustAmount
1309 -- We're not at optimal RPM or flow-rate and we're not out-of-bounds
1310 else
1311 return
1312 end
1313
1314 -- Check bounds [0,2000]
1315 if newFlowRate > 2000 then
1316 newFlowRate = 2000
1317 elseif newFlowRate < 0 then
1318 newFlowRate = 25 -- Don't go to zero, might as well power off
1319 end
1320
1321 turbine.setFluidFlowRateMax(newFlowRate)
1322 end -- if ((rotorSpeed % 900) ~= 0) and (flowRate ~= 2000) and (flowRate == flowRateUserMax) then
1323 end -- if turbine.getActive() then
1324end -- function flowRateControl(turbineIndex)
1325
1326
1327function main()
1328 -- Load reactor parameters and initialize systems
1329 loadReactorOptions()
1330
1331 -- Get our initial list of connected monitors and reactors
1332 -- and initialize every cycle in case the connected devices change
1333 findMonitors()
1334 findReactors()
1335 findTurbines()
1336
1337 while not finished do
1338 local reactor = nil
1339 local monitorIndex = 1
1340
1341 -- For multiple reactors/monitors, monitor #1 is reserved for overall status
1342 -- or for multiple reactors/turbines and only one monitor
1343 if (((#reactorList + #turbineList) > 1) and (#monitorList > 1)) or
1344 (((#reactorList + #turbineList) > 1) and (#monitorList == 1)) then
1345 local monitor = nil
1346 monitor = monitorList[monitorIndex]
1347 if not monitor then
1348 printLog("monitorList["..monitorIndex.."] in turbineStatus() was not a valid monitor")
1349 return -- Invalid monitorIndex
1350 end
1351
1352 clearMonitor(progName.." "..progVer, monitorIndex) -- Clear monitor and draw borders
1353 printCentered(progName.." "..progVer, 1, monitorIndex)
1354 displayAllStatus()
1355 monitorIndex = 2
1356 end
1357
1358 -- Iterate through reactors
1359 for reactorIndex = 1, #reactorList do
1360 local monitor = nil
1361 monitor = monitorList[monitorIndex]
1362 if not monitor then
1363 printLog("monitorList["..monitorIndex.."] in turbineStatus() was not a valid monitor")
1364 return -- Invalid monitorIndex
1365 end
1366
1367 local reactorMonitorIndex = monitorIndex + reactorIndex - 1 -- reactorIndex starts at 1
1368
1369 clearMonitor(progName, reactorMonitorIndex) -- Clear monitor and draw borders
1370 printCentered(progName, 1, reactorMonitorIndex)
1371
1372 -- Display reactor status, includes "Disconnected" but found reactors
1373 reactorStatus{reactorIndex, reactorMonitorIndex}
1374
1375 reactor = reactorList[reactorIndex]
1376 if not reactor then
1377 printLog("reactorList["..reactorIndex.."] in main() was not a valid Big Reactor")
1378 break -- Invalid reactorIndex
1379 end
1380
1381 if reactor.getConnected() then
1382 local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
1383
1384 -- Shutdown reactor if current stored energy % is >= desired level, otherwise activate
1385 -- First pass will have curStoredEnergyPercent=0 until displayBars() is run once
1386 if curStoredEnergyPercent >= maxStoredEnergyPercent then
1387 reactor.setActive(false)
1388 -- Do not auto-start the reactor if it was manually powered off (autoStart=false)
1389 elseif (curStoredEnergyPercent <= minStoredEnergyPercent) and (autoStart[reactorIndex] == true) then
1390 reactor.setActive(true)
1391 end
1392
1393 -- Don't try to auto-adjust control rods if manual control is requested
1394 if not reactorRodOverride then
1395 temperatureControl(reactorIndex)
1396 end
1397
1398 displayReactorBars{reactorIndex,reactorMonitorIndex}
1399 end -- if reactor.getConnected() then
1400 end -- for reactorIndex = 1, #reactorList do
1401
1402 -- Monitors for turbines start after turbineMonitorOffset
1403 for turbineIndex = 1, #turbineList do
1404 local monitor = nil
1405 monitor = monitorList[monitorIndex]
1406 if not monitor then
1407 printLog("monitorList["..monitorIndex.."] in turbineStatus() was not a valid monitor")
1408 return -- Invalid monitorIndex
1409 end
1410
1411 local turbineMonitorIndex = turbineIndex+turbineMonitorOffset
1412 clearMonitor(progName, turbineMonitorIndex) -- Clear monitor and draw borders
1413 printCentered(progName, 1, turbineMonitorIndex)
1414
1415 -- Display turbine status, includes "Disconnected" but found turbines
1416 turbineStatus(turbineIndex, turbineMonitorIndex)
1417
1418 turbine = turbineList[turbineIndex]
1419 if not turbine then
1420 printLog("turbineList["..turbineIndex.."] in main() was not a valid Big Turbine")
1421 break -- Invalid turbineIndex
1422 end
1423
1424 if turbine.getConnected() then
1425 if not turbineFlowRateOverride[turbineIndex] then
1426 flowRateControl(turbineIndex)
1427 end
1428
1429 displayTurbineBars(turbineIndex,turbineMonitorIndex)
1430 end
1431 end -- for reactorIndex = 1, #reactorList do
1432
1433 sleep(loopTime) -- Sleep
1434 saveReactorOptions()
1435 end -- while not finished do
1436end -- main()
1437
1438
1439local function eventHandler()
1440 while not finished do
1441 -- http://computercraft.info/wiki/Os.pullEvent
1442 -- http://www.computercraft.info/forums2/index.php?/topic/1516-ospullevent-what-is-it-and-how-is-it-useful/
1443 event, arg1, arg2, arg3 = os.pullEvent()
1444
1445 if event == "monitor_touch" then
1446 sideClick, xClick, yClick = arg1, math.floor(arg2), math.floor(arg3)
1447 printLog("Side: "..arg1.." Monitor touch X: "..xClick.." Y: "..yClick)
1448 elseif event == "char" and not inManualMode then
1449 local ch = string.lower(arg1)
1450 if ch == "q" then
1451 finished = true
1452 elseif ch == "r" then
1453 finished = true
1454 os.reboot()
1455 end -- if ch == "q" then
1456 end -- if event == "monitor_touch" then
1457 end -- while not finished do
1458end -- function eventHandler()
1459
1460
1461while not finished do
1462 parallel.waitForAny(eventHandler, main)
1463 sleep(loopTime)
1464end -- while not finished do
1465
1466
1467-- Clear up after an exit
1468term.clear()
1469term.setCursorPos(1,1)