View difference between Paste ID: x3FVV8P5 and fguScPBQ
SHOW: | | - or go back to the newest paste.
1-
--[[
1+
2-
Program name: Lolmer's EZ-NUKE reactor control system
2+
local progVer = ""
3-
Version: v0.3.17
3+
local progName = "Mt. Weather"
4-
Programmer: Lolmer
4+
5-
With reat assistance from @echaet and @thetaphi
5+
6-
Last update: 2015-04-08
6+
7-
Pastebin: http://pastebin.com/fguScPBQ
7+
8-
GitHub: https://github.com/sandalle/minecraft_bigreactor_control
8+
9
-- End multi-reactor cleanup section
10-
Description:
10+
11-
This 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.
11+
12
local monitorList = {} -- Empty monitor array
13-
This 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
13+
14
local reactorList = {} -- Empty reactor array
15-
To 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:
15+
16-
1) One Advanced Monitor for overall status display plus
16+
17-
	one or more Reactors plus
17+
18-
	none or more Turbines.
18+
local turbineMonitorOffset = 0 -- Turbines are assigned monitors after reactors
19-
2) One Advanced Monitor for overall status display plus (furthest monitor from computer by cable length)
19+
20-
	one Advanced Monitor for each connected Reactor plus (subsequent found monitors)
20+
21-
	one Advanced Monitor for each connected Turbine (last group of monitors found).
21+
22-
If you enable debug mode, add one additional Advanced Monitor for #1 or #2.
22+
23
write("Initializing program...\n")
24-
Notes
24+
25-
----------------------------
25+
26-
- Only one reactor and one, two, and three turbines have been tested with the above, but IN THEORY any number is supported.
26+
27-
- Devices are found in the reverse order they are plugged in, so monitor_10 will be found before monitor_9.
27+
28
local logFile = fs.open("reactorcontrol.log", "w")
29-
When using actively cooled reactors with turbines, keep the following in mind:
29+
30-
- 1 mB steam carries up to 10RF of potential energy to extract in a turbine.
30+
31-
- Actively cooled reactors produce steam, not power.
31+
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.
32+
33-
- Two 15x15x14 Turbines can output 260K RF/t by just one 7^3 (four rods) reactor putting out 4k mB steam.
33+
34
end
35-
Features
35+
36-
----------------------------
36+
37-
- Configurable min/max energy buffer and min/max temperature via ReactorOptions file.
37+
38-
- Disengages coils and minimizes flow for turbines over max energy buffer.
38+
39-
- ReactorOptions is read on start and then current values are saved every program cycle.
39+
40-
- 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.
40+
  local ccVersion = nil
41-
- Auto-adjusts control rods per reactor to maintain temperature.
41+
  ccVersion = os.version()
42-
- Will display reactor data to all attached monitors of correct dimensions.
42+
43-
	- For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
43+
	if ccVersion == "CraftOS 1.6" then
44-
- For multiple monitors, the first monitor (often last plugged in) is the overall status monitor.
44+
		term.native()
45-
- A new cruise mode from mechaet, ONLINE will be "blue" when active, to keep your actively cooled reactors running smoothly.
45+
46
		term.restore()
47-
GUI Usage
47+
48-
----------------------------
48+
49-
- Right-clicking between "< * >" of the last row of a monitor alternates the device selection between Reactor, Turbine, and Status output.
49+
50-
	- Right-clicking "<" and ">" switches between connected devices, starting with the currently selected type, but not limited to them.
50+
51-
- The other "<" and ">" buttons, when right-clicked with the mouse, will decrease and increase, respectively, the values assigned to the monitor:
51+
52-
	- "Rod (%)" will lower/raise the Reactor Control Rods for that Reactor
52+
53-
	- "mB/t" will lower/raise the Turbine Flow Rate maximum for that Turbine
53+
local function printLog(printStr)
54-
	- "RPM" will lower/raise the target Turbine RPM for that Turbine
54+
	if debugMode then
55-
- Right-clicking between the "<" and ">" (not on them) will disable auto-adjust of that value for attached device.
55+
		-- If multiple monitors, use the last monitor for debugging if debug is enabled
56-
	- Right-clicking on the "Enabled" or "Disabled" text for auto-adjust will do the same.
56+
		if #monitorList > 1 then
57-
- Right-clicking on "ONLINE" or "OFFLINE" at the top-right will toggle the state of attached device.
57+
			term.redirect(monitorList[#monitorList]) -- Redirect to last monitor for debugging
58
			monitorList[#monitorList].setTextScale(0.5) -- Fit more logs on screen
59-
Default values
59+
			write(printStr.."\n")   -- May need to use term.scroll(x) if we output too much, not sure
60-
----------------------------
60+
			termRestore()
61-
- Rod Control: 90% (Let's start off safe and then power up as we can)
61+
		end -- if #monitorList > 1 then
62-
- Minimum Energy Buffer: 15% (will power on below this value)
62+
63-
- Maximum Energy Buffer: 85% (will power off above this value)
63+
64-
- Minimum Passive Cooling Temperature: 950^C (will raise control rods below this value)
64+
65-
- Maximum Passive Cooling Temperature: 1,400^C (will lower control rods above this value)
65+
66-
- Minimum Active Cooling Temperature: 300^C (will raise the control rods below this value)
66+
67-
- Maximum Active Cooling Temperature: 420^C (will lower control rods above this value)
67+
68-
- Optimal Turbine RPM:  900, 1,800, or 2,700 (divisible by 900)
68+
69-
	- New user-controlled option for target speed of turbines, defaults to 2726RPM, which is high-optimal.
69+
70
	end -- if debugMode then
71-
Requirements
71+
72-
----------------------------
72+
73-
- Advanced Monitor size is X: 29, Y: 12 with a 3x2 size
73+
74-
- Computer or Advanced Computer
74+
75-
- Modems (not wireless) connecting each of the Computer to both the Advanced Monitor and Reactor Computer Port.
75+
76-
- Big Reactors (http://www.big-reactors.com/) 0.3.2A+
76+
77-
- Computercraft (http://computercraft.info/) 1.58, 1.63+, or 1.73+
77+
78-
- Reset the computer any time number of connected devices change.
78+
79
config = {}
80-
Resources
80+
81-
----------------------------
81+
82-
- This script is available from:
82+
83-
	- http://pastebin.com/fguScPBQ
83+
84-
	- https://github.com/sandalle/minecraft_bigreactor_control
84+
85
	printLog("Save function called for config for "..path.." EOL")
86-
- Start-up script is available from:
86+
87-
	- http://pastebin.com/ZTMzRLez
87+
88-
	- https://github.com/sandalle/minecraft_bigreactor_control
88+
89-
- Other reactor control program which I based my program on:
89+
90-
	- http://pastebin.com/aMAu4X5J (ScatmanJohn)
90+
91-
	- http://pastebin.com/HjUVNDau (version ScatmanJohn based his on)
91+
92-
- A simpler Big Reactor control program is available from:
92+
93-
	- http://pastebin.com/7S5xCvgL (IronClaymore only for passively cooled reactors)
93+
94-
- Reactor Computer Port API: http://wiki.technicpack.net/Reactor_Computer_Port
94+
95-
- Computercraft API: http://computercraft.info/wiki/Category:APIs
95+
96-
- Big Reactors Efficiency, Speculation and Questions! http://www.reddit.com/r/feedthebeast/comments/1vzds0/big_reactors_efficiency_speculation_and_questions/
96+
97-
- Big Reactors API code: https://github.com/erogenousbeef/BigReactors/blob/master/erogenousbeef/bigreactors/common/multiblock/tileentity/TileEntityReactorComputerPort.java
97+
98-
- Big Reactors API: http://big-reactors.com/cc_api.html
98+
99-
- Big Reactor Simulator from http://reddit.com/r/feedthebeast : http://br.sidoh.org/
99+
100-
- A tutorial from FTB's rhn : http://forum.feed-the-beast.com/threads/rhns-continued-adventures-a-build-journal-guide-collection-etc.42664/page-10#post-657819
100+
101
			value2 = tostring(value2)
102-
ChangeLog
102+
103-
============================
103+
104-
- 0.3.17
104+
105-
	- Display how much steam (in mB/t) a Turbine is receiving on that Turbine's monitor.
105+
106-
	- Set monitor scale before checking size fixing Issue #50.
106+
107-
	- Having a monitor is now optional, closing Issue #46.
107+
108-
	- Incorporate steam supply and demand in reactor control thanks to @thetaphi (Nicolas Kratz).
108+
109-
	- Added turbine coil auto dis-/engage (Issue #51 and #55) thanks to @thetaphi (Nicolas Kratz).
109+
110-
	- Stop overzealous controlRodAdjustAmount adjustment amount adjustment Issue #56 thanks to @thetaphi (Nicolas Kratz).
110+
111-
	- Monitors can be reconfigured via GUI fixes Issue #34 thanks to @thetaphi (Nicolas Kratz).
111+
112
end --config.save = function(path, tab)
113-
Prior ChangeLogs are posted at https://github.com/sandalle/minecraft_bigreactor_control/releases
113+
114
-- Load a config file
115-
TODO
115+
116-
============================
116+
117-
See https://github.com/sandalle/minecraft_bigreactor_control/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement :)
117+
118
	assert(path ~= nil, "Path can't be nil")
119-
]]--
119+
120
	if f ~= nil then
121
		local tab = {}
122
		local line = ""
123-
local progVer = "0.3.17"
123+
124-
local progName = "EZ-NUKE"
124+
125
		local currentTag = nil
126
		local found = false
127
		local pos = 0
128
		while line ~= nil do
129
			found = false		
130
			line = line:gsub("\\;", "#_!36!_#") -- to keep \;
131
			line = line:gsub("\\=", "#_!71!_#") -- to keep \=
132
			if line ~= "" then
133
				-- Delete comments
134
				newLine = line
135
				line = ""
136
				for i=1, string.len(newLine) do				
137
					if string.sub(newLine, i, i) ~= ";" then
138
						line = line..newLine:sub(i, i)						
139-
local monitorAssignments = {} -- Empty array of monitor - "what to display" assignments
139+
140-
local monitorOptionFileName = "monitors.options" -- File for saving the monitor assignments
140+
141
					end
142-
local steamRequested = 0 -- Sum of Turbine Flow Rate in mB
142+
143-
local steamDelivered = 0 -- Sum of Active Reactor steam output in mB
143+
144
				-- Find tag			
145-
-- Log levels
145+
146-
local FATAL = 16
146+
147-
local ERROR = 8
147+
148-
local WARN = 4
148+
149-
local INFO = 2
149+
150-
local DEBUG = 1
150+
151
				if not found and line ~= "" then				
152
					pos = line:find("=")				
153
					if pos == nil then
154
						error("Bad INI file structure")
155
					end
156
					line = line:gsub("#_!36!_#", ";")
157
					line = line:gsub("#_!71!_#", "=")
158
					tab[currentTag][stringTrim(line:sub(1, pos-1))] = stringTrim(line:sub(pos+1, line:len()))
159
					found = true			
160
				end			
161
			end
162
			line = f.readLine()
163
		end
164
		
165
		f:close()
166
		
167
		return tab
168
	else
169
		return nil
170
	end
171-
	local ccVersion = nil
171+
172-
	ccVersion = os.version()
172+
173
174-
	if ccVersion == "CraftOS 1.6" or "CraftOS 1.7" then
174+
175-
		term.redirect(term.native())
175+
176
local function round(num, places)
177
	local mult = 10^places
178
	local addon = nil
179
	if ((num * mult) < 0) then
180
		addon = -.5
181
	else
182
		addon = .5
183
	end
184-
local function printLog(printStr, logLevel)
184+
185-
	logLevel = logLevel or INFO
185+
186-
	-- No, I'm not going to write full syslog style levels. But this makes it a little easier filtering and finding stuff in the logfile.
186+
187-
	-- Since you're already looking at it, you can adjust your preferred log level right here.
187+
188-
	if debugMode and (logLevel >= WARN) then
188+
189-
		-- If multiple monitors, print to all of them
189+
190-
		for monitorName, deviceData in pairs(monitorAssignments) do
190+
191-
			if deviceData.type == "Debug" then
191+
192-
				debugMonitor = monitorList[deviceData.index]
192+
193-
				if(not debugMonitor) or (not debugMonitor.getSize()) then
193+
194-
					term.write("printLog(): debug monitor "..monitorName.." failed")
194+
195
	local printString, xPos, yPos, monitorIndex =
196-
					term.redirect(debugMonitor) -- Redirect to selected monitor
196+
197-
					debugMonitor.setTextScale(0.5) -- Fit more logs on screen
197+
198-
					local color = colors.lightGray
198+
199-
					if (logLevel == WARN) then
199+
200-
						color = colors.white
200+
201-
					elseif (logLevel == ERROR) then
201+
202-
						color = colors.red
202+
203-
					elseif (logLevel == FATAL) then
203+
204-
						color = colors.black
204+
205-
						debugMonitor.setBackgroundColor(colors.red)
205+
206
		return -- Invalid monitorIndex
207-
					debugMonitor.setTextColor(color)
207+
208-
					write(printStr.."\n")   -- May need to use term.scroll(x) if we output too much, not sure
208+
209-
					debugMonitor.setBackgroundColor(colors.black)
209+
210
	monitor.write(printString)
211
end -- function print(printParams)
212
213-
		end -- for 
213+
214
-- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
215
local function printCentered(printString, yPos, monitorIndex)
216
	local monitor = nil
217
	monitor = monitorList[monitorIndex]
218
219
	if not monitor then
220
		printLog("monitor["..monitorIndex.."] in printCentered() is NOT a valid monitor.")
221
		return -- Invalid monitorIndex
222
	end
223
224
	local width, height = monitor.getSize()
225
	local monitorNameLength = 0
226
227
	-- Special changes for title bar
228
	if yPos == 1 then
229
		-- Add monitor name to first line
230
		monitorNameLength = monitorNames[monitorIndex]:len()
231-
-- Format number with [k,M,G,T,P,E] postfix or exponent, depending on how large it is
231+
232-
local function formatReadableSIUnit(num)
232+
233-
	printLog("formatReadableSIUnit("..num..")", DEBUG)
233+
		if (#monitorList ~= 1) and (monitorIndex ~= 1) then
234-
	num = tonumber(num)
234+
235-
	if(num < 1000) then return tostring(num) end
235+
236-
	local sizes = {"", "k", "M", "G", "T", "P", "E"}
236+
237-
	local exponent = math.floor(math.log10(num))
237+
238-
	local group = math.floor(exponent / 3)
238+
	monitor.setCursorPos(math.floor(width/2) - math.ceil(printString:len()/2) +  monitorNameLength/2, yPos)
239-
	if group > #sizes then
239+
	monitor.clearLine()
240-
		return string.format("%e", num)
240+
241
242-
		local divisor = math.pow(10, (group - 1) * 3)
242+
243-
		return string.format("%i%s", num / divisor, sizes[group])
243+
	-- print{monitorNames[monitorIndex], 1, 1, monitorIndex} -- Print monitor name
244
	
245-
end -- local function formatReadableSIUnit(num)
245+
246
end -- function printCentered(printString, yPos, monitorIndex)
247-
-- pretty printLog() a table
247+
248-
local function tprint (tbl, loglevel, indent)
248+
249-
	if not loglevel then loglevel = DEBUG end
249+
250-
	if not indent then indent = 0 end
250+
251-
	for k, v in pairs(tbl) do
251+
252-
		formatting = string.rep("  ", indent) .. k .. ": "
252+
253-
		if type(v) == "table" then
253+
254-
			printLog(formatting, loglevel)
254+
255-
			tprint(v, loglevel, indent+1)
255+
256-
		elseif type(v) == 'boolean' or type(v) == "function" then
256+
		printLog("monitor["..monitorIndex.."] in printLeft() is NOT a valid monitor.")
257-
			printLog(formatting .. tostring(v), loglevel)      
257+
258
	end
259-
			printLog(formatting .. v, loglevel)
259+
260
	local gap = 1
261
	local width = monitor.getSize()
262-
end -- function tprint()
262+
263
	-- Clear left-half of the monitor
264
265
	for curXPos = 1, (width / 2) do
266
		monitor.setCursorPos(curXPos, yPos)
267
		monitor.write(" ")
268
	end
269
270
	-- Write our string left-aligned
271
	monitor.setCursorPos(1+gap, yPos)
272
	monitor.write(printString)
273
end
274
275
276
-- Print text padded from the right side
277
-- Clear the right side of the screen
278
local function printRight(printString, yPos, monitorIndex)
279
	local monitor = nil
280
	monitor = monitorList[monitorIndex]
281
282
	if not monitor then
283
		printLog("monitor["..monitorIndex.."] in printRight() is NOT a valid monitor.")
284
		return -- Invalid monitorIndex
285
	end
286
287
	-- Make sure printString is a string
288
	printString = tostring(printString)
289
290
	local gap = 1
291
	local width = monitor.getSize()
292
293
	-- Clear right-half of the monitor
294
	for curXPos = (width/2), width do
295
		monitor.setCursorPos(curXPos, yPos)
296
		monitor.write(" ")
297
	end
298
299
	-- Write our string right-aligned
300
	monitor.setCursorPos(math.floor(width) - math.ceil(printString:len()+gap), yPos)
301
	monitor.write(printString)
302
end
303
304
305
-- Replaces the one from FC_API (http://pastebin.com/A9hcbZWe) and adding multi-monitor support
306-
		printLog("Successfully opened "..path.." for reading EOL")
306+
307
	local monitor = nil
308
	monitor = monitorList[monitorIndex]
309
310
	printLog("Called as clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..").")
311
312
	if not monitor then
313
		printLog("monitor["..monitorIndex.."] in clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
314
		return -- Invalid monitorIndex
315
	end
316
317
	local gap = 2
318
	monitor.clear()
319
	local width, height = monitor.getSize()
320
	monitor.setTextScale(1.0) -- Make sure scale is correct
321
322
	printCentered(printString, 1, monitorIndex)
323
	monitor.setTextColor(colors.blue)
324
	print{monitorNames[monitorIndex], 1, 1, monitorIndex}
325
	monitor.setTextColor(colors.white)
326
327
	for i=1, width do
328
		monitor.setCursorPos(i, gap)
329
		monitor.write("-")
330
	end
331
332
	monitor.setCursorPos(1, gap+1)
333
end -- function clearMonitor(printString, monitorIndex)
334
335
336
-- Return a list of all connected (including via wired modems) devices of "deviceType"
337
local function getDevices(deviceType)
338
	printLog("Called as getDevices(deviceType="..deviceType..")")
339
340
	local deviceName = nil
341
	local deviceIndex = 1
342
	local deviceList, deviceNames = {}, {} -- Empty array, which grows as we need
343
	local peripheralList = peripheral.getNames() -- Get table of connected peripherals
344
345
	deviceType = deviceType:lower() -- Make sure we're matching case here
346
347
	for peripheralIndex = 1, #peripheralList do
348
		-- Log every device found
349
		-- printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] attached as \""..peripheralList[peripheralIndex].."\".")
350
		if (string.lower(peripheral.getType(peripheralList[peripheralIndex])) == deviceType) then
351
			-- Log devices found which match deviceType and which device index we give them
352
			printLog("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".")
353
			write("Found "..peripheral.getType(peripheralList[peripheralIndex]).."["..peripheralIndex.."] as index \"["..deviceIndex.."]\" attached as \""..peripheralList[peripheralIndex].."\".\n")
354
			deviceNames[deviceIndex] = peripheralList[peripheralIndex]
355-
		printLog("Could NOT opened "..path.." for reading! EOL")
355+
356
			deviceIndex = deviceIndex + 1
357
		end
358
	end -- for peripheralIndex = 1, #peripheralList do
359
360
	return deviceList, deviceNames
361
end -- function getDevices(deviceType)
362
363
-- Draw a line across the entire x-axis
364
local function drawLine(yPos, monitorIndex)
365
	local monitor = nil
366
	monitor = monitorList[monitorIndex]
367
368
	if not monitor then
369
		printLog("monitor["..monitorIndex.."] in drawLine() is NOT a valid monitor.")
370
		return -- Invalid monitorIndex
371
	end
372
373
	local width, height = monitor.getSize()
374
375
	for i=1, width do
376
		monitor.setCursorPos(i, yPos)
377
		monitor.write("-")
378
	end
379
end -- function drawLine(yPos,monitorIndex)
380
381
382
-- Display a solid bar of specified color
383
local function drawBar(startXPos, startYPos, endXPos, endYPos, color, monitorIndex)
384
	local monitor = nil
385
	monitor = monitorList[monitorIndex]
386
387
	if not monitor then
388
		printLog("monitor["..monitorIndex.."] in drawBar() is NOT a valid monitor.")
389
		return -- Invalid monitorIndex
390
	end
391
392
	-- PaintUtils only outputs to term., not monitor.
393
	-- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
394
	term.redirect(monitor)
395
	paintutils.drawLine(startXPos, startYPos, endXPos, endYPos, color)
396
	monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
397
	termRestore()
398
end -- function drawBar(startXPos, startYPos,endXPos,endYPos,color,monitorIndex)
399
400
401
-- Display single pixel color
402
local function drawPixel(xPos, yPos, color, monitorIndex)
403
	local monitor = nil
404
	monitor = monitorList[monitorIndex]
405
406
	if not monitor then
407-
		printLog("monitor["..monitorIndex.."] in printCentered() is NOT a valid monitor.", ERROR)
407+
408
		return -- Invalid monitorIndex
409
	end
410
411
	-- PaintUtils only outputs to term., not monitor.
412
	-- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
413
	term.redirect(monitor)
414
	paintutils.drawPixel(xPos, yPos, color)
415
	monitor.setBackgroundColor(colors.black) -- PaintUtils doesn't restore the color
416
	termRestore()
417
end -- function drawPixel(xPos, yPos, color, monitorIndex)
418-
		width = width - monitorNameLength -- add a space
418+
419
420
-- End helper functions
421-
		if monitorAssignments[monitorNames[monitorIndex]].type ~= "Status" then
421+
422
423
-- Then initialize the monitors
424
local function findMonitors()
425
	-- Empty out old list of monitors
426-
	monitor.setCursorPos(monitorNameLength + math.ceil((1 + width - printString:len())/2), yPos)
426+
427
428
	printLog("Finding monitors...")
429
	monitorList, monitorNames = getDevices("monitor")
430
431
	if #monitorList == 0 then
432
		printLog("No monitors found!")
433
		error("Can't find any monitors!")
434
	else
435
		for monitorIndex = 1, #monitorList do
436
			local monitor, monitorX, monitorY = nil, nil, nil
437
			monitor = monitorList[monitorIndex]
438-
		printLog("monitor["..monitorIndex.."] in printLeft() is NOT a valid monitor.", ERROR)
438+
439
			if not monitor then
440
				printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid monitor.")
441
442
				table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
443
				if monitorIndex ~= #monitorList then    -- If we're not at the end, clean up
444
					monitorIndex = monitorIndex - 1 -- We just removed an element
445
				end -- if monitorIndex == #monitorList then
446
				break -- Invalid monitorIndex
447
			else -- valid monitor
448
				monitorX, monitorY = monitor.getSize()
449
				if (monitorX == nil) or (monitorY == nil) then -- somehow a valid monitor, but non-existent sizes? Maybe fixes Issue #3
450
					printLog("monitorList["..monitorIndex.."] in findMonitors() is NOT a valid sized monitor.")
451
452
					table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
453
					if monitorIndex ~= #monitorList then    -- If we're not at the end, clean up
454
						monitorIndex = monitorIndex - 1 -- We just removed an element
455
					end -- if monitorIndex == #monitorList then
456
					break -- Invalid monitorIndex
457
458
				-- Check for minimum size to allow for monitor.setTextScale(0.5) to work for 3x2 debugging monitor, changes getSize()
459
				elseif monitorX < 29 or monitorY < 12 then
460
					term.redirect(monitor)
461
					monitor.clear()
462
					printLog("Removing monitor "..monitorIndex.." for being too small.")
463
					monitor.setCursorPos(1,2)
464
					write("Monitor is the wrong size!\n")
465-
		printLog("monitor["..monitorIndex.."] in printRight() is NOT a valid monitor.", ERROR)
465+
466
					termRestore()
467
468
					table.remove(monitorList, monitorIndex) -- Remove invalid monitor from list
469
					if monitorIndex == #monitorList then    -- If we're at the end already, break from loop
470
						break
471
					else
472
						monitorIndex = monitorIndex - 1 -- We just removed an element
473
					end -- if monitorIndex == #monitorList then
474
475
				end -- if monitorX < 29 or monitorY < 12 then
476
			end -- if not monitor then
477
478
			printLog("Monitor["..monitorIndex.."] named \""..monitorNames[monitorIndex].."\" is a valid monitor of size x:"..monitorX.." by y:"..monitorY..".")
479
		end -- for monitorIndex = 1, #monitorList do
480
	end -- if #monitorList == 0 then
481
482
	printLog("Found "..#monitorList.." monitor(s) in findMonitors().")
483
end -- local function findMonitors()
484
485
486
-- Initialize all Big Reactors - Reactors
487
local function findReactors()
488
	-- Empty out old list of reactors
489
	newReactorList = {}
490
	printLog("Finding reactors...")
491
	newReactorList, reactorNames = getDevices("BigReactors-Reactor")
492
493
	if #newReactorList == 0 then
494
		printLog("No reactors found!")
495-
		printLog("monitor["..monitorIndex.."] in clearMonitor(printString="..printString..",monitorIndex="..monitorIndex..") is NOT a valid monitor.", ERROR)
495+
496
	else  -- Placeholder
497
		for reactorIndex = 1, #newReactorList do
498
			local reactor = nil
499
			reactor = newReactorList[reactorIndex]
500
501
			if not reactor then
502
				printLog("reactorList["..reactorIndex.."] in findReactors() is NOT a valid Big Reactor.")
503
504
				table.remove(newReactorList, reactorIndex) -- Remove invalid reactor from list
505
				if reactorIndex ~= #newReactorList then    -- If we're not at the end, clean up
506
					reactorIndex = reactorIndex - 1 -- We just removed an element
507
				end -- reactorIndex ~= #newReactorList then
508
				return -- Invalid reactorIndex
509
			else
510
				printLog("reactor["..reactorIndex.."] in findReactors() is a valid Big Reactor.")
511
				--initialize the default table
512
				_G[reactorNames[reactorIndex]] = {}
513
				_G[reactorNames[reactorIndex]]["ReactorOptions"] = {}
514
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = 80
515
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = 0
516
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
517
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
518
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = 1400 --set for passive-cooled, the active-cooled subroutine will correct it
519
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = 1000
520
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = false
521
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = reactorNames[reactorIndex]
522
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
523
				if reactor.getConnected() then
524
					printLog("reactor["..reactorIndex.."] in findReactors() is connected.")
525
				else
526
					printLog("reactor["..reactorIndex.."] in findReactors() is NOT connected.")
527
					return -- Disconnected reactor
528
				end
529
			end
530
			
531
			--failsafe
532
			local tempTable = _G[reactorNames[reactorIndex]]
533
			
534
			--check to make sure we get a valid config
535
			if (config.load(reactorNames[reactorIndex]..".options")) ~= nil then
536
				tempTable = config.load(reactorNames[reactorIndex]..".options")
537
			else
538
				--if we don't have a valid config from disk, make a valid config
539
				config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
540
			end
541
			
542
			--load values from tempTable, checking for nil values along the way
543
			if tempTable["ReactorOptions"]["baseControlRodLevel"] ~= nil then
544
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tempTable["ReactorOptions"]["baseControlRodLevel"]
545
			end
546
			
547
			if tempTable["ReactorOptions"]["lastTempPoll"] ~= nil then
548
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tempTable["ReactorOptions"]["lastTempPoll"]
549
			end
550
			
551
			if tempTable["ReactorOptions"]["autoStart"] ~= nil then
552
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = tempTable["ReactorOptions"]["autoStart"]
553
			end
554
			
555
			if tempTable["ReactorOptions"]["activeCooled"] ~= nil then
556
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = tempTable["ReactorOptions"]["activeCooled"]
557
			end
558
			
559
			if tempTable["ReactorOptions"]["reactorMaxTemp"] ~= nil then
560
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tempTable["ReactorOptions"]["reactorMaxTemp"]
561
			end
562
			
563
			if tempTable["ReactorOptions"]["reactorMinTemp"] ~= nil then
564
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tempTable["ReactorOptions"]["reactorMinTemp"]
565
			end
566
			
567
			if tempTable["ReactorOptions"]["rodOverride"] ~= nil then
568
				printLog("Got value from config file for Rod Override, the value is: "..tostring(tempTable["ReactorOptions"]["rodOverride"]).." EOL")
569
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = tempTable["ReactorOptions"]["rodOverride"]
570
			end
571
			
572
			if tempTable["ReactorOptions"]["reactorName"] ~= nil then
573
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"] = tempTable["ReactorOptions"]["reactorName"]
574
			end
575
			
576
			if tempTable["ReactorOptions"]["reactorCruising"] ~= nil then
577
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = tempTable["ReactorOptions"]["reactorCruising"]
578
			end
579
			
580
			--stricter typing, let's set these puppies up with the right type of value.
581
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"])
582
			
583
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"])
584
			
585
			if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"]) == "true") then
586
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = true
587
			else
588
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
589
			end
590
			
591
			if (tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"]) == "true") then
592
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = true
593
			else
594
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["activeCooled"] = false
595
			end
596
			
597
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"])
598
			
599
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"])
600-
local function saveMonitorAssignments()
600+
601-
	local assignments = {}
601+
602-
	for monitor, data in pairs(monitorAssignments) do
602+
603-
		local name = nil
603+
604-
		if (data.type == "Reactor") then
604+
605-
			name = data.reactorName
605+
606-
		elseif (data.type == "Turbine") then
606+
607-
			name = data.turbineName
607+
608
			
609-
			name = data.type
609+
610
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
611-
		assignments[monitor] = name
611+
612
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
613-
	config.save(monitorOptionFileName, {Monitors = assignments})
613+
614
						
615
			--save one more time, in case we didn't have a complete config file before
616-
UI = {
616+
617-
	monitorIndex = 1,
617+
618-
	reactorIndex = 1,
618+
619-
	turbineIndex = 1
619+
620-
}
620+
621
	reactorList = newReactorList
622-
UI.handlePossibleClick = function(self)
622+
623-
	local monitorData = monitorAssignments[sideClick]
623+
	-- Start turbine monitor offset after reactors get monitors
624-
	if monitorData == nil then
624+
	-- This assumes that there is a monitor for each turbine and reactor, plus the overall monitor display
625-
		printLog("UI.handlePossibleClick(): "..sideClick.." is unassigned, can't handle click", WARN)
625+
	turbineMonitorOffset = #reactorList + 1 -- #turbineList will start at "1" if turbines found and move us just beyond #reactorList and status monitor range
626-
		return
626+
627
	printLog("Found "..#reactorList.." reactor(s) in findReactors().")
628
	printLog("Set turbineMonitorOffset to "..turbineMonitorOffset.." in findReactors().")
629-
	self.monitorIndex = monitorData.index
629+
630-
	local width, height = monitorList[self.monitorIndex].getSize()
630+
631-
	-- All the last line are belong to us
631+
632-
	if (yClick == height) then
632+
633-
		if (monitorData.type == "Reactor") then
633+
634-
			if (xClick == 1) then
634+
635-
				self:selectPrevReactor()
635+
636-
			elseif (xClick == width) then
636+
637-
				self:selectNextReactor()
637+
638-
			elseif (3 <= xClick and xClick <= width - 2) then
638+
639-
				self:selectTurbine()
639+
640
	if #newTurbineList == 0 then
641-
		elseif (monitorData.type == "Turbine") then
641+
642-
			if (xClick == 1) then
642+
643-
				self:selectPrevTurbine()
643+
644-
			elseif (xClick == width) then
644+
645-
				self:selectNextTurbine()
645+
646-
			elseif (3 <= xClick and xClick <= width - 2) then
646+
647-
				self:selectStatus()
647+
648
				printLog("turbineList["..turbineIndex.."] in findTurbines() is NOT a valid Big Reactors Turbine.")
649-
		elseif (monitorData.type == "Status") then
649+
650-
			if (xClick == 1) then
650+
651-
				self.turbineIndex = #turbineList
651+
652-
				self:selectTurbine()
652+
653-
			elseif (xClick == width) then
653+
654-
				self.reactorIndex = 1
654+
655-
				self:selectReactor()
655+
656-
			elseif (3 <= xClick and xClick <= width - 2) then
656+
657-
				self:selectReactor()
657+
658
				_G[turbineNames[turbineIndex]] = {}
659
				_G[turbineNames[turbineIndex]]["TurbineOptions"] = {}
660-
			self:selectStatus()
660+
661
				_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = 2726
662-
		-- Yes, that means we're skipping Debug. I figure everyone who wants that is
662+
663-
		-- bound to use the console key commands anyway, and that way we don't have
663+
664-
		-- it interfere with regular use.
664+
665
				_G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = turbineNames[turbineIndex]
666
				printLog("turbineList["..turbineIndex.."] in findTurbines() is a valid Big Reactors Turbine.")
667
				if turbine.getConnected() then
668-
		if (monitorData.type == "Turbine") then
668+
669-
			self:handleTurbineMonitorClick(monitorData.turbineIndex, monitorData.index)
669+
670-
		elseif (monitorData.type == "Reactor") then
670+
671-
			self:handleReactorMonitorClick(monitorData.reactorIndex, monitorData.index)
671+
672
				end
673
			end
674-
end -- UI.handlePossibleClick()
674+
675
			--failsafe
676-
UI.logChange = function(self, messageText)
676+
677-
	printLog("UI: "..messageText)
677+
678
			--check to make sure we get a valid config
679-
	write(messageText.."\n")
679+
680
				tempTable = config.load(turbineNames[turbineIndex]..".options")
681
			else
682-
UI.selectNextMonitor = function(self)
682+
683-
	self.monitorIndex = self.monitorIndex + 1
683+
684-
	if self.monitorIndex > #monitorList then
684+
685-
		self.monitorIndex = 1
685+
686
			--load values from tempTable, checking for nil values along the way
687-
	local messageText = "Selected monitor "..monitorNames[self.monitorIndex]
687+
688-
	self:logChange(messageText)
688+
689-
end -- UI.selectNextMonitor()
689+
690
			
691
			if tempTable["TurbineOptions"]["BaseSpeed"] ~= nil then
692-
UI.selectReactor = function(self)
692+
693-
	monitorAssignments[monitorNames[self.monitorIndex]] = {type="Reactor", index=self.monitorIndex, reactorName=reactorNames[self.reactorIndex], reactorIndex=self.reactorIndex}
693+
694-
	saveMonitorAssignments()
694+
695-
	local messageText = "Selected reactor "..reactorNames[self.reactorIndex].." for display on "..monitorNames[self.monitorIndex]
695+
696-
	self:logChange(messageText)
696+
697-
end -- UI.selectReactor()
697+
698
			
699-
UI.selectPrevReactor = function(self)
699+
700-
	if self.reactorIndex <= 1 then
700+
701-
		self.reactorIndex = #reactorList
701+
702-
		self:selectStatus()
702+
703
			if tempTable["TurbineOptions"]["flowOverride"] ~= nil then
704-
		self.reactorIndex = self.reactorIndex - 1
704+
705-
		self:selectReactor()
705+
706
			
707-
end -- UI.selectPrevReactor()
707+
708
				_G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"] = tempTable["TurbineOptions"]["turbineName"]
709-
UI.selectNextReactor = function(self)
709+
710-
	if self.reactorIndex >= #reactorList then
710+
711-
		self.reactorIndex = 1
711+
712-
		self.turbineIndex = 1
712+
713-
		self:selectTurbine()
713+
714
715-
		self.reactorIndex = self.reactorIndex + 1
715+
716-
		self:selectReactor()
716+
717
	end -- if #newTurbineList == 0 then
718-
end -- UI.selectNextReactor()
718+
719
	printLog("Found "..#turbineList.." turbine(s) in findTurbines().")
720
end -- function findTurbines()
721-
UI.selectTurbine = function(self)
721+
722-
	monitorAssignments[monitorNames[self.monitorIndex]] = {type="Turbine", index=self.monitorIndex, turbineName=turbineNames[self.turbineIndex], turbineIndex=self.turbineIndex}
722+
723-
	saveMonitorAssignments()
723+
724-
	local messageText = "Selected turbine "..turbineNames[self.turbineIndex].." for display on "..monitorNames[self.monitorIndex]
724+
725-
	self:logChange(messageText)
725+
726-
end -- UI.selectTurbine()
726+
727
	if not reactor then
728-
UI.selectPrevTurbine = function(self)
728+
729-
	if self.turbineIndex <= 1 then
729+
730-
		self.turbineIndex = #turbineList
730+
731-
		self.reactorIndex = #reactorList
731+
732-
		self:selectReactor()
732+
733
734-
		self.turbineIndex = self.turbineIndex - 1
734+
735-
		self:selectTurbine()
735+
736
end -- function getReactorStoredEnergyBufferPercent(reactor)
737-
end -- UI.selectPrevTurbine()
737+
738
739-
UI.selectNextTurbine = function(self)
739+
740-
	if self.turbineIndex >= #turbineList then
740+
741-
		self.turbineIndex = 1
741+
742-
		self:selectStatus()
742+
743
	if not turbine then
744-
		self.turbineIndex = self.turbineIndex + 1
744+
745-
		self:selectTurbine()
745+
746
	else
747-
end -- UI.selectNextTurbine()
747+
748
	end
749
750-
UI.selectStatus = function(self)
750+
751-
	monitorAssignments[monitorNames[self.monitorIndex]] = {type="Status", index=self.monitorIndex}
751+
752-
	saveMonitorAssignments()
752+
753-
	local messageText = "Selected status summary for display on "..monitorNames[self.monitorIndex]
753+
754-
	self:logChange(messageText)
754+
755-
end -- UI.selectStatus()
755+
756
	
757-
UI.selectDebug = function(self)
757+
758-
	monitorAssignments[monitorNames[self.monitorIndex]] = {type="Debug", index=self.monitorIndex}
758+
759-
	saveMonitorAssignments()
759+
760-
	monitorList[self.monitorIndex].clear()
760+
761-
	local messageText = "Selected debug output for display on "..monitorNames[self.monitorIndex]
761+
762-
	self:logChange(messageText)
762+
763-
end -- UI.selectDebug()
763+
764
		reactor = reactorList[reactorIndex]
765-
-- Allow controlling Reactor Control Rod Level from GUI
765+
766-
UI.handleReactorMonitorClick = function(self, reactorIndex, monitorIndex)
766+
767
			return -- Invalid reactorIndex
768
		else
769
			printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is a valid Big Reactor.")
770
			if reactor.getConnected() then
771
				printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is connected.")
772
			else
773
				printLog("reactor["..reactorIndex.."] in reactorCruise(cruiseMaxTemp="..cruiseMaxTemp..",cruiseMinTemp="..cruiseMinTemp..",lastPolledTemp="..lastPolledTemp..",reactorIndex="..reactorIndex..") is NOT connected.")
774
				return -- Disconnected reactor
775
			end -- if reactor.getConnected() then
776
		end -- if not reactor then
777
778
		local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
779
		local reactorTemp = math.ceil(reactor.getFuelTemperature())
780
		_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = rodPercentage
781
		
782
		if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
783-
		printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
783+
784
				rodPercentage = (rodPercentage - 1)
785
				--Boundary check
786-
		printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
786+
787
					reactor.setAllControlRodLevels(0)
788-
			printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
788+
789
					reactor.setAllControlRodLevels(rodPercentage)
790-
			printLog("reactor["..reactorIndex.."] in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
790+
791
			else
792
				rodPercentage = (rodPercentage + 1)
793
				--Boundary check
794
				if rodPercentage > 99 then
795-
	local reactorStatus = _G[reactorNames[reactorIndex]]["ReactorOptions"]["Status"]
795+
796
				else
797
					reactor.setAllControlRodLevels(rodPercentage)
798-
	if xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1) and (sideClick == monitorNames[monitorIndex]) then
798+
799-
		if yClick == 1 then
799+
800-
			reactor.setActive(not reactor.getActive()) -- Toggle reactor status
800+
801-
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = reactor.getActive()
801+
802
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = false
803
		end -- if ((reactorTemp < cruiseMaxTemp) and (reactorTemp > cruiseMinTemp)) then
804
	else
805-
			-- If someone offlines the reactor (offline after a status click was detected), then disable autoStart
805+
806-
			if not reactor.getActive() then
806+
807
	end -- if ((lastPolledTemp < cruiseMaxTemp) and (lastPolledTemp > cruiseMinTemp)) then
808
	_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
809-
		end -- if yClick == 1 then
809+
810-
	end -- if (xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
810+
811
	_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"] = cruiseMinTemp
812-
	-- Allow disabling rod level auto-adjust and only manual rod level control
812+
813-
	if ((xClick > 23 and xClick < 28 and yClick == 4)
813+
814-
			or (xClick > 20 and xClick < 27 and yClick == 9))
814+
815-
			and (sideClick == monitorNames[monitorIndex]) then
815+
816-
		_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]
816+
817
local function temperatureControl(reactorIndex)
818-
		sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
818+
819-
	end -- if (xClick > 23) and (xClick < 28) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
819+
820
	local reactor = nil
821
	reactor = reactorList[reactorIndex]
822-
	local newRodPercentage = rodPercentage
822+
823
		printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT a valid Big Reactor.")
824-
		printLog("Decreasing Rod Levels in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
824+
825
	else
826
		printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is a valid Big Reactor.")
827
828
		if reactor.getConnected() then
829
			printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is connected.")
830
		else
831
			printLog("reactor["..reactorIndex.."] in temperatureControl(reactorIndex="..reactorIndex..") is NOT connected.")
832-
		printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
832+
833
		end -- if reactor.getConnected() then
834
	end
835
836
	local reactorNum = reactorIndex
837
	local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
838
	local reactorTemp = math.ceil(reactor.getFuelTemperature())
839
	local localMinReactorTemp, localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"], _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
840-
		printLog("Increasing Rod Levels in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
840+
841
	--bypass if the reactor itself is set to not be auto-controlled
842
	if ((not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]) or (_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] == "false")) then
843
		-- No point modifying control rod levels for temperature if the reactor is offline
844
		if reactor.getActive() then
845
			-- Actively cooled reactors should range between 0^C-300^C
846
			-- Actually, active-cooled reactors should range between 300 and 420C (Mechaet)
847
			-- Accordingly I changed the below lines
848-
		printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
848+
849
				-- below was 0
850
				localMinReactorTemp = 300
851
				-- below was 300
852
				localMaxReactorTemp = 420
853
			else
854
				localMinReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMinTemp"]
855
				localMaxReactorTemp = _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorMaxTemp"]
856-
		printLog("No change to Rod Levels requested by "..progName.." GUI in handleReactorMonitorClick(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
856+
857
			local lastTempPoll = _G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"]
858-
end -- UI.handleReactorMonitorClick = function(self, reactorIndex, monitorIndex)
858+
859
				--let's bypass all this math and hit the much-more-subtle cruise feature
860-
-- Allow controlling Turbine Flow Rate from GUI
860+
861-
UI.handleTurbineMonitorClick = function(self, turbineIndex, monitorIndex)
861+
862
			else
863
				-- Don't bring us to 100, that's effectively a shutdown
864
				if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
865
					--increase the rods, but by how much?
866
					if (reactorTemp > lastTempPoll) then
867
						--we're climbing, we need to get this to decrease
868
						if ((reactorTemp - lastTempPoll) > 100) then
869
							--we're climbing really fast, arrest it
870
							if (rodPercentage + (10 * controlRodAdjustAmount)) > 99 then
871
								reactor.setAllControlRodLevels(99)
872
							else
873
								reactor.setAllControlRodLevels(rodPercentage + (10 * controlRodAdjustAmount))
874
							end
875
						else
876
							--we're not climbing by leaps and bounds, let's give it a rod adjustment based on temperature increase
877
							local diffAmount = reactorTemp - lastTempPoll
878-
		printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
878+
879
							controlRodAdjustAmount = diffAmount
880
							if (rodPercentage + controlRodAdjustAmount) > 99 then
881-
		printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
881+
882
							else
883-
			printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is connected.")
883+
								reactor.setAllControlRodLevels(rodPercentage + controlRodAdjustAmount)
884
							end
885-
			printLog("turbine["..turbineIndex.."] in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
885+
886
					elseif (reactorTemp == lastTempPoll) then
887
						--temperature has stagnated, kick it very lightly
888
						local controlRodAdjustment = 1
889
						if (rodPercentage + controlRodAdjustment) > 99 then
890
							reactor.setAllControlRodLevels(99)
891
						else
892-
	local turbineStatus = _G[turbineNames[turbineIndex]]["TurbineOptions"]["Status"]
892+
893
						end
894
					end --if (reactorTemp > lastTempPoll) then
895-
	if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
895+
896-
		if yClick == 1 then
896+
897-
			turbine.setActive(not turbine.getActive()) -- Toggle turbine status
897+
				elseif (reactorTemp < localMinReactorTemp) and (rodPercentage ~=0) then
898-
			_G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = turbine.getActive()
898+
899
					if (reactorTemp < lastTempPoll) then
900
						--we're descending, let's stop that.
901
						if ((lastTempPoll - reactorTemp) > 100) then
902-
		end -- if yClick == 1 then
902+
903-
	end -- if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
903+
							if (rodPercentage - (10 * controlRodAdjustAmount)) < 0 then
904
								reactor.setAllControlRodLevels(0)
905-
	-- Allow disabling/enabling flow rate auto-adjust
905+
906-
	if (xClick > 23 and xClick < 28 and yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
906+
								reactor.setAllControlRodLevels(rodPercentage - (10 * controlRodAdjustAmount))
907-
		_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
907+
908-
		sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
908+
909-
	elseif (xClick > 20 and xClick < 27 and yClick == 10) and (sideClick == monitorNames[monitorIndex]) then
909+
910
							local diffAmount = lastTempPoll - reactorTemp
911-
		if ((_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "true")) then
911+
912-
			_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
912+
							controlRodAdjustAmount = diffAmount
913
							if (rodPercentage - controlRodAdjustAmount) < 0 then
914
								reactor.setAllControlRodLevels(0)
915
							else
916-
		sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
916+
								reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustAmount)
917
							end
918
						end --if ((lastTempPoll - reactorTemp) > 100) then
919
					elseif (reactorTemp == lastTempPoll) then
920
						--temperature has stagnated, kick it very lightly
921-
		printLog("Decrease to Flow Rate requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
921+
922
						if (rodPercentage - controlRodAdjustment) < 0 then
923
							reactor.setAllControlRodLevels(0)
924
						else
925
							reactor.setAllControlRodLevels(rodPercentage - controlRodAdjustment)
926
						end --if (rodPercentage - controlRodAdjustment) < 0 then
927
928
					end --if (reactorTemp < lastTempPoll) then
929
					--if we're below temp but increasing, do nothing and let it continue to rise.
930
				end --if (reactorTemp > localMaxReactorTemp) and (rodPercentage ~= 99) then
931
932
				if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) then
933
					--engage cruise mode
934
					_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] = true
935
				end -- if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) then
936
			end -- if reactorCruising then
937
			--always set this number
938
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["lastTempPoll"] = reactorTemp
939
			config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
940
		end -- if reactor.getActive() then
941
	else
942-
		printLog("Increase to Flow Rate requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
942+
943
	end -- if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
944
end -- function temperatureControl(reactorIndex)
945
946
-- Load saved reactor parameters if ReactorOptions file exists
947
local function loadReactorOptions()
948
	local reactorOptions = fs.open("ReactorOptions", "r") -- See http://computercraft.info/wiki/Fs.open
949
950
	if reactorOptions then
951
		-- The following values were added by Lolmer
952
		minStoredEnergyPercent = reactorOptions.readLine()
953
		maxStoredEnergyPercent = reactorOptions.readLine()
954
		--added by Mechaet
955
		-- If we succeeded in reading a string, convert it to a number
956
957
		if minStoredEnergyPercent ~= nil then
958
			minStoredEnergyPercent = tonumber(minStoredEnergyPercent)
959
		end
960
961
		if maxStoredEnergyPercent ~= nil then
962
			maxStoredEnergyPercent = tonumber(maxStoredEnergyPercent)
963
		end
964-
		printLog("No change to Flow Rate requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
964+
965
		reactorOptions.close()
966
	end -- if reactorOptions then
967
968-
		printLog("Decrease to Turbine RPM requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
968+
969
	if minStoredEnergyPercent == nil then
970
		minStoredEnergyPercent = 15
971
	end
972
973
	if maxStoredEnergyPercent == nil then
974
		maxStoredEnergyPercent = 85
975
	end
976
977
end -- function loadReactorOptions()
978-
		printLog("Increase to Turbine RPM requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
978+
979
980
-- Save our reactor parameters
981
local function saveReactorOptions()
982
	local reactorOptions = fs.open("ReactorOptions", "w") -- See http://computercraft.info/wiki/Fs.open
983
984
	-- If we can save the files, save them
985
	if reactorOptions then
986
		local reactorIndex = 1
987
		-- The following values were added by Lolmer
988-
		printLog("No change to Turbine RPM requested by "..progName.." GUI in handleTurbineMonitorClick(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
988+
989
		reactorOptions.writeLine(maxStoredEnergyPercent)
990-
end -- function handleTurbineMonitorClick(turbineIndex, monitorIndex)
990+
991
	else
992
		printLog("Failed to open file ReactorOptions for writing!")
993
	end -- if reactorOptions then
994
end -- function saveReactorOptions()
995
996
997
local function displayReactorBars(barParams)
998
	-- Default to first reactor and first monitor
999
	setmetatable(barParams,{__index={reactorIndex=1, monitorIndex=1}})
1000
	local reactorIndex, monitorIndex =
1001
		barParams[1] or barParams.reactorIndex,
1002
		barParams[2] or barParams.monitorIndex
1003
1004
	printLog("Called as displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1005-
		printLog("No monitors found, continuing headless")
1005+
1006
	-- Grab current monitor
1007
	local monitor = nil
1008
	monitor = monitorList[monitorIndex]
1009
	if not monitor then
1010
		printLog("monitor["..monitorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
1011
		return -- Invalid monitorIndex
1012
	end
1013
1014
	-- Grab current reactor
1015
	local reactor = nil
1016
	reactor = reactorList[reactorIndex]
1017
	if not reactor then
1018
		printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
1019
		return -- Invalid reactorIndex
1020-
				monitor.setTextScale(1.0) -- Make sure scale is correct
1020+
1021
		printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
1022
		if reactor.getConnected() then
1023
			printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
1024
		else
1025
			printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
1026
			return -- Disconnected reactor
1027
		end -- if reactor.getConnected() then
1028
	end -- if not reactor then
1029
1030
	-- Draw border lines
1031
	local width, height = monitor.getSize()
1032
	printLog("Size of monitor is "..width.."w x"..height.."h in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
1033
1034
	for i=3, 5 do
1035
		monitor.setCursorPos(22, i)
1036
		monitor.write("|")
1037
	end
1038
1039
	drawLine(2, monitorIndex)
1040
	drawLine(6, monitorIndex)
1041
1042
	-- Draw some text
1043
	local fuelString = "Fuel: "
1044
	local tempString = "Temp: "
1045
	local energyBufferString = ""
1046
1047
	if reactor.isActivelyCooled() then
1048
		energyBufferString = "Steam: "
1049
	else
1050
		energyBufferString = "Energy: "
1051
	end
1052
1053
	local padding = math.max(string.len(fuelString), string.len(tempString), string.len(energyBufferString))
1054
1055
	local fuelPercentage = round(reactor.getFuelAmount()/reactor.getFuelAmountMax()*100,1)
1056
	print{fuelString,2,3,monitorIndex}
1057
	print{fuelPercentage.." %",padding+2,3,monitorIndex}
1058
1059
	local reactorTemp = math.ceil(reactor.getFuelTemperature())
1060
	print{tempString,2,5,monitorIndex}
1061
	print{reactorTemp.." C",padding+2,5,monitorIndex}
1062
1063
	local rodPercentage = math.ceil(reactor.getControlRodLevel(0))
1064
	printLog("Current Rod Percentage for reactor["..reactorIndex.."] is "..rodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1065
	-- Allow controlling Reactor Control Rod Level from GUI
1066
	-- Decrease rod button: 23X, 4Y
1067
	-- Increase rod button: 28X, 4Y
1068
	if (xClick == 23) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1069
		printLog("Decreasing Rod Levels in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1070
		--Decrease rod level by amount
1071
		newRodPercentage = rodPercentage - (5 * controlRodAdjustAmount)
1072
		if newRodPercentage < 0 then
1073
			newRodPercentage = 0
1074
		end
1075
		sideClick, xClick, yClick = 0, 0, 0
1076
1077
		printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1078
		reactor.setAllControlRodLevels(newRodPercentage)
1079
		_G[reactorNames[reactorIndex]]["ReactorOptions"]["baseControlRodLevel"] = newRodPercentage
1080
1081
		-- Save updated rod percentage
1082
		config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1083
		rodPercentage = newRodPercentage
1084
	elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1085
		printLog("Increasing Rod Levels in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1086
		--Increase rod level by amount
1087
		newRodPercentage = rodPercentage + (5 * controlRodAdjustAmount)
1088
		if newRodPercentage > 100 then
1089
			newRodPercentage = 100
1090
		end
1091
		sideClick, xClick, yClick = 0, 0, 0
1092
1093
		printLog("Setting reactor["..reactorIndex.."] Rod Levels to "..newRodPercentage.."% in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1094
		reactor.setAllControlRodLevels(newRodPercentage)
1095-
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = controlRodAdjustAmount
1095+
1096
		
1097
		-- Save updated rod percentage
1098
		config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1099
		rodPercentage = round(newRodPercentage,0)
1100
	else
1101
		printLog("No change to Rod Levels requested by "..progName.." GUI in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1102
	end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1103
1104
	print{"Rod (%)",23,3,monitorIndex}
1105
	print{"<     >",23,4,monitorIndex}
1106
	print{stringTrim(rodPercentage),25,4,monitorIndex}
1107
1108
1109
	-- getEnergyProducedLastTick() is used for both RF/t (passively cooled) and mB/t (actively cooled)
1110
	local energyBuffer = reactor.getEnergyProducedLastTick()
1111
	if reactor.isActivelyCooled() then
1112
		printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." mB last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1113
	else
1114
		printLog("reactor["..reactorIndex.."] produced "..energyBuffer.." RF last tick in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1115
	end
1116
1117
	print{energyBufferString,2,4,monitorIndex}
1118
1119
	-- Actively cooled reactors do not produce energy, only hot fluid mB/t to be used in a turbine
1120
	-- still uses getEnergyProducedLastTick for mB/t of hot fluid generated
1121
	if not reactor.isActivelyCooled() then
1122
		printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT an actively cooled reactor.")
1123
1124
		-- Draw stored energy buffer bar
1125
		drawBar(2,8,28,8,colors.gray,monitorIndex)
1126
1127
		local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
1128
		if curStoredEnergyPercent > 4 then
1129
			drawBar(2, 8, math.floor(26*curStoredEnergyPercent/100)+2, 8, colors.yellow, monitorIndex)
1130
		elseif curStoredEnergyPercent > 0 then
1131
			drawPixel(2, 8, colors.yellow, monitorIndex)
1132
		end -- if curStoredEnergyPercent > 4 then
1133
1134
		print{"Energy Buffer",2,7,monitorIndex}
1135
		print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),7,monitorIndex}
1136
		print{"%",28,7,monitorIndex}
1137
1138
		print{math.ceil(energyBuffer).." RF/t",padding+2,4,monitorIndex}
1139
	else
1140
		printLog("reactor["..reactorIndex.."] in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is an actively cooled reactor.")
1141
		print{math.ceil(energyBuffer).." mB/t",padding+2,4,monitorIndex}
1142
	end -- if not reactor.isActivelyCooled() then
1143
1144
	-- Print rod override status
1145
	local reactorRodOverrideStatus = ""
1146
1147-
			if tempTable["ReactorOptions"]["controlRodAdjustAmount"] ~= nil then
1147+
1148-
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = tempTable["ReactorOptions"]["controlRodAdjustAmount"]
1148+
1149
	if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
1150
		printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
1151
		reactorRodOverrideStatus = "Enabled"
1152
		monitor.setTextColor(colors.green)
1153
	else
1154
		printLog("Reactor Rod Override status is: "..tostring(_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]).." EOL")
1155
		reactorRodOverrideStatus = "Disabled"
1156
		monitor.setTextColor(colors.red)
1157
	end -- if not reactorRodOverride then
1158
	printLog("reactorRodOverrideStatus is \""..reactorRodOverrideStatus.."\" in displayReactorBars(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..").")
1159
1160
	print{reactorRodOverrideStatus, width - string.len(reactorRodOverrideStatus) - 1, 9, monitorIndex}
1161
	monitor.setTextColor(colors.white)
1162
1163
	print{"Reactivity: "..math.ceil(reactor.getFuelReactivity()).." %", 2, 10, monitorIndex}
1164
	print{"Fuel: "..round(reactor.getFuelConsumedLastTick(),3).." mB/t", 2, 11, monitorIndex}
1165
	print{"Waste: "..reactor.getWasteAmount().." mB", width-(string.len(reactor.getWasteAmount())+10), 11, monitorIndex}
1166
1167
	monitor.setTextColor(colors.blue)
1168
	printCentered(_G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorName"],12,monitorIndex)
1169
	monitor.setTextColor(colors.white)
1170
end -- function displayReactorBars(barParams)
1171
1172
1173
local function reactorStatus(statusParams)
1174
	-- Default to first reactor and first monitor
1175
	setmetatable(statusParams,{__index={reactorIndex=1, monitorIndex=1}})
1176
	local reactorIndex, monitorIndex =
1177
		statusParams[1] or statusParams.reactorIndex,
1178
		statusParams[2] or statusParams.monitorIndex
1179
	printLog("Called as reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..")")
1180
1181
	-- Grab current monitor
1182
	local monitor = nil
1183
	monitor = monitorList[monitorIndex]
1184
	if not monitor then
1185
		printLog("monitor["..monitorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
1186
		return -- Invalid monitorIndex
1187
	end
1188-
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = tonumber(_G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"])
1188+
1189
	-- Grab current reactor
1190
	local reactor = nil
1191
	reactor = reactorList[reactorIndex]
1192
	if not reactor then
1193
		printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Reactor.")
1194
		return -- Invalid reactorIndex
1195
	else
1196
		printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is a valid Big Reactor.")
1197
	end
1198
1199
	local width, height = monitor.getSize()
1200
	local reactorStatus = ""
1201
1202
	if reactor.getConnected() then
1203
		printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is connected.")
1204
1205
		if reactor.getActive() then
1206
			reactorStatus = "ONLINE"
1207
1208
			-- Set "ONLINE" to blue if the actively cooled reactor is both in cruise mode and online
1209
			if _G[reactorNames[reactorIndex]]["ReactorOptions"]["reactorCruising"] and reactor.isActivelyCooled() then
1210
				monitor.setTextColor(colors.blue)
1211
			else
1212
				monitor.setTextColor(colors.green)
1213
			end -- if reactorCruising and reactor.isActivelyCooled() then
1214
		else
1215
			reactorStatus = "OFFLINE"
1216
			monitor.setTextColor(colors.red)
1217
		end -- if reactor.getActive() then
1218
1219
		if xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1) and (sideClick == monitorNames[monitorIndex]) then
1220
			if yClick == 1 then
1221
				reactor.setActive(not reactor.getActive()) -- Toggle reactor status
1222
				_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = reactor.getActive()
1223
				config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1224
				sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1225
1226
				-- If someone offlines the reactor (offline after a status click was detected), then disable autoStart
1227
				if not reactor.getActive() then
1228
					_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] = false
1229
				end
1230
			end -- if yClick == 1 then
1231
		end -- if (xClick >= (width - string.len(reactorStatus) - 1) and xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1232
1233
		-- Allow disabling rod level auto-adjust and only manual rod level control
1234
		if ((xClick > 23 and xClick < 28 and yClick == 4)
1235
				or (xClick > 20 and xClick < 27 and yClick == 9))
1236
				and (sideClick == monitorNames[monitorIndex]) then
1237
			_G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] = not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"]
1238
			config.save(reactorNames[reactorIndex]..".options", _G[reactorNames[reactorIndex]])
1239
			sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1240
		end -- if (xClick > 23) and (xClick < 28) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1241
1242
	else
1243
		printLog("reactor["..reactorIndex.."] in reactorStatus(reactorIndex="..reactorIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
1244
		reactorStatus = "DISCONNECTED"
1245
		monitor.setTextColor(colors.red)
1246
	end -- if reactor.getConnected() then
1247
1248
	print{reactorStatus, width - string.len(reactorStatus) - 1, 1, monitorIndex}
1249
	monitor.setTextColor(colors.white)
1250
end -- function reactorStatus(statusParams)
1251
1252
1253
-- Display all found reactors' status to monitor 1
1254
-- This is only called if multiple reactors and/or a reactor plus at least one turbine are found
1255
local function displayAllStatus()
1256
	local reactor, turbine = nil, nil
1257
	local onlineReactor, onlineTurbine = 0, 0
1258
	local totalReactorRF, totalReactorSteam, totalTurbineRF = 0, 0, 0
1259
	local totalReactorFuelConsumed = 0
1260
	local totalCoolantStored, totalSteamStored, totalEnergy, totalMaxEnergyStored = 0, 0, 0, 0 -- Total turbine and reactor energy buffer and overall capacity
1261
	local maxSteamStored = (2000*#turbineList)+(5000*#reactorList)
1262
	local maxCoolantStored = (2000*#turbineList)+(5000*#reactorList)
1263
1264
	local monitor, monitorIndex = nil, 1
1265
	monitor = monitorList[monitorIndex]
1266
	if not monitor then
1267
		printLog("monitor["..monitorIndex.."] in displayAllStatus() is NOT a valid monitor.")
1268
		return -- Invalid monitorIndex
1269
	end
1270
1271
	for reactorIndex = 1, #reactorList do
1272
		reactor = reactorList[reactorIndex]
1273
		if not reactor then
1274
			printLog("reactor["..reactorIndex.."] in displayAllStatus() is NOT a valid Big Reactor.")
1275
			break -- Invalid reactorIndex
1276
		else
1277
			printLog("reactor["..reactorIndex.."] in displayAllStatus() is a valid Big Reactor.")
1278
		end -- if not reactor then
1279
1280
		if reactor.getConnected() then
1281
			printLog("reactor["..reactorIndex.."] in displayAllStatus() is connected.")
1282
			if reactor.getActive() then
1283
				onlineReactor = onlineReactor + 1
1284
				totalReactorFuelConsumed = totalReactorFuelConsumed + reactor.getFuelConsumedLastTick()
1285
			end -- reactor.getActive() then
1286
1287
			-- Actively cooled reactors do not produce or store energy
1288
			if not reactor.isActivelyCooled() then
1289
				totalMaxEnergyStored = totalMaxEnergyStored + 10000000 -- Reactors store 10M RF
1290
				totalEnergy = totalEnergy + reactor.getEnergyStored()
1291
				totalReactorRF = totalReactorRF + reactor.getEnergyProducedLastTick()
1292
			else
1293
				totalReactorSteam = totalReactorSteam + reactor.getEnergyProducedLastTick()
1294
				totalSteamStored = totalSteamStored + reactor.getHotFluidAmount()
1295
				totalCoolantStored = totalCoolantStored + reactor.getCoolantAmount()
1296
			end -- if not reactor.isActivelyCooled() then
1297
		else
1298-
-- Assign status, reactors, turbines and debug output to the monitors that shall display them
1298+
1299-
-- Depends on the [monitor,reactor,turbine]Lists being populated already
1299+
1300-
local function assignMonitors()
1300+
1301
1302-
	local monitors = {}
1302+
1303-
	monitorAssignments = {}
1303+
1304
		if not turbine then
1305-
	printLog("Assigning monitors...")
1305+
1306
			break -- Invalid turbineIndex
1307-
	local m = config.load(monitorOptionFileName) 
1307+
1308-
	if (m ~= nil) then
1308+
1309-
		-- first, merge the detected and the configured monitor lists
1309+
1310-
		-- this is to ensure we pick up new additions to the network
1310+
1311-
		for monitorIndex, monitorName in ipairs(monitorNames) do
1311+
1312-
			monitors[monitorName] = m.Monitors[monitorName] or ""
1312+
1313
			if turbine.getActive() then
1314-
		-- then, go through all of it again to build our runtime data structure
1314+
1315-
		for monitorName, assignedName in pairs(monitors) do
1315+
1316-
			printLog("Looking for monitor and device named "..monitorName.." and "..assignedName)
1316+
1317-
			for monitorIndex = 1, #monitorNames do
1317+
1318-
				printLog("if "..monitorName.." == "..monitorNames[monitorIndex].." then", DEBUG)
1318+
1319-
				
1319+
1320-
				if monitorName == monitorNames[monitorIndex] then
1320+
1321-
					printLog("Found "..monitorName.." at index "..monitorIndex, DEBUG)
1321+
1322-
					if assignedName == "Status" then
1322+
1323-
						monitorAssignments[monitorName] = {type="Status", index=monitorIndex}
1323+
1324-
					elseif assignedName == "Debug" then
1324+
1325-
						monitorAssignments[monitorName] = {type="Debug", index=monitorIndex}
1325+
1326
	
1327-
						local maxListLen = math.max(#reactorNames, #turbineNames)
1327+
1328-
						for i = 1, maxListLen do
1328+
1329-
							if assignedName == reactorNames[i] then
1329+
1330-
								monitorAssignments[monitorName] = {type="Reactor", index=monitorIndex, reactorName=reactorNames[i], reactorIndex=i}
1330+
1331-
								break
1331+
1332-
							elseif assignedName == turbineNames[i] then
1332+
1333-
								monitorAssignments[monitorName] = {type="Turbine", index=monitorIndex, turbineName=turbineNames[i], turbineIndex=i}
1333+
1334-
								break
1334+
1335-
							elseif i == maxListLen then
1335+
1336-
								printLog("assignMonitors(): Monitor "..monitorName.." was configured to display nonexistant device "..assignedName..". Setting inactive.", WARN)
1336+
1337-
								monitorAssignments[monitorName] = {type="Inactive", index=monitorIndex}
1337+
1338
		-- Display liquids
1339
		monitor.setTextColor(colors.blue)
1340
		printLeft("Steam (mB)", 6, monitorIndex)
1341-
					break
1341+
1342-
				elseif monitorIndex == #monitorNames then
1342+
1343-
					printLog("assignMonitors(): Monitor "..monitorName.." not found. It was configured to display device "..assignedName..". Discarding.", WARN)
1343+
1344
		monitor.setTextColor(colors.blue)
1345
		printRight("Coolant (mB)", 6, monitorIndex)
1346
		monitor.setTextColor(colors.white)
1347
		printRight(math.ceil(totalCoolantStored).."/"..maxCoolantStored, 7, monitorIndex)
1348-
		printLog("No valid monitor configuration found, generating...")
1348+
1349
		monitor.setTextColor(colors.blue)
1350-
		-- create assignments that reflect the setup before 0.3.17
1350+
1351
		monitor.setTextColor(colors.white)
1352-
		monitorAssignments[monitorNames[1]] = {type="Status", index=1}
1352+
1353-
		monitorIndex = monitorIndex + 1
1353+
1354
1355-
			if monitorIndex > #monitorList then
1355+
1356-
				break
1356+
	print{"Buffer: "..math.ceil(totalEnergy,3).."/"..totalMaxEnergyStored.." RF", 2, 12, monitorIndex}
1357
end -- function displayAllStatus()
1358-
			monitorAssignments[monitorNames[monitorIndex]] = {type="Reactor", index=monitorIndex, reactorName=reactorNames[reactorIndex], reactorIndex=reactorIndex}
1358+
1359-
			printLog(monitorNames[monitorIndex].." -> "..reactorNames[reactorIndex])
1359+
1360
-- Get turbine status
1361-
			monitorIndex = monitorIndex + 1
1361+
1362
	printLog("Called as displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1363
1364-
			if monitorIndex > #monitorList then
1364+
1365-
				break
1365+
1366
	monitor = monitorList[monitorIndex]
1367-
			monitorAssignments[monitorNames[monitorIndex]] = {type="Turbine", index=monitorIndex, turbineName=turbineNames[turbineIndex], turbineIndex=turbineIndex}
1367+
1368-
			printLog(monitorNames[monitorIndex].." -> "..turbineNames[turbineIndex])
1368+
1369
		return -- Invalid monitorIndex
1370-
			monitorIndex = monitorIndex + 1
1370+
1371
1372-
		if monitorIndex <= #monitorList then
1372+
1373-
			monitorAssignments[monitorNames[#monitorList]] = {type="Debug", index=#monitorList}
1373+
1374
	turbine = turbineList[turbineIndex]
1375
	if not turbine then
1376
		printLog("turbine["..turbineIndex.."] in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid Big Turbine.")
1377-
	tprint(monitorAssignments)
1377+
1378
	else
1379-
	saveMonitorAssignments()
1379+
1380
		if turbine.getConnected() then
1381-
end -- function assignMonitors()
1381+
1382
		else
1383-
local eventHandler
1383+
1384-
-- Replacement for sleep, which passes on events instead of dropping themo
1384+
1385-
-- Straight from http://computercraft.info/wiki/Os.sleep
1385+
1386-
local function wait(time)
1386+
1387-
	local timer = os.startTimer(time)
1387+
1388
	--local variable to match the view on the monitor
1389-
	while true do
1389+
1390-
		local event = {os.pullEvent()}
1390+
1391
	-- Draw border lines
1392-
		if (event[1] == "timer" and event[2] == timer) then
1392+
1393-
			break
1393+
1394
	for i=3, 6 do
1395-
			eventHandler(event[1], event[2], event[3], event[4])
1395+
1396
		monitor.write("|")
1397
	end
1398
1399
	drawLine(2,monitorIndex)
1400
	drawLine(7,monitorIndex)
1401
1402
	-- Allow controlling Turbine Flow Rate from GUI
1403
	-- Decrease flow rate button: 22X, 4Y
1404
	-- Increase flow rate button: 28X, 4Y
1405
	local turbineFlowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
1406
	if (xClick == 22) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1407
		printLog("Decrease to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1408
		--Decrease rod level by amount
1409
		newTurbineFlowRate = turbineFlowRate - flowRateAdjustAmount
1410
		if newTurbineFlowRate < 0 then
1411
			newTurbineFlowRate = 0
1412
		end
1413
		sideClick, xClick, yClick = 0, 0, 0
1414
1415
		-- Check bounds [0,2000]
1416
		if newTurbineFlowRate > 2000 then
1417
			newTurbineFlowRate = 2000
1418
		elseif newTurbineFlowRate < 0 then
1419
			newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
1420
		end
1421
1422
		turbine.setFluidFlowRateMax(newTurbineFlowRate)
1423
		_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newTurbineFlowRate
1424
		-- Save updated Turbine Flow Rate
1425
		turbineFlowRate = newTurbineFlowRate
1426
		config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1427
	elseif (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1428
		printLog("Increase to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1429
		--Increase rod level by amount
1430
		newTurbineFlowRate = turbineFlowRate + flowRateAdjustAmount
1431
		if newTurbineFlowRate > 2000 then
1432
			newTurbineFlowRate = 2000
1433
		end
1434
		sideClick, xClick, yClick = 0, 0, 0
1435
1436
		-- Check bounds [0,2000]
1437
		if newTurbineFlowRate > 2000 then
1438
			newTurbineFlowRate = 2000
1439
		elseif newTurbineFlowRate < 0 then
1440
			newTurbineFlowRate = 25 -- Don't go to zero, might as well power off
1441
		end
1442
1443
		turbine.setFluidFlowRateMax(newTurbineFlowRate)
1444
		
1445
		-- Save updated Turbine Flow Rate
1446
		turbineFlowRate = math.ceil(newTurbineFlowRate)
1447
		_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = turbineFlowRate
1448
		config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1449
	else
1450
		printLog("No change to Flow Rate requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1451
	end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1452
1453
	if (xClick == 22) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
1454
		printLog("Decrease to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1455
		rpmRateAdjustment = 909
1456
		newTurbineBaseSpeed = turbineBaseSpeed - rpmRateAdjustment
1457
		if newTurbineBaseSpeed < 908 then
1458
			newTurbineBaseSpeed = 908
1459
		end
1460
		sideClick, xClick, yClick = 0, 0, 0
1461
		_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
1462
		config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1463
	elseif (xClick == 29) and (yClick == 6) and (sideClick == monitorNames[monitorIndex]) then
1464
		printLog("Increase to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1465
		rpmRateAdjustment = 909
1466
		newTurbineBaseSpeed = turbineBaseSpeed + rpmRateAdjustment
1467
		if newTurbineBaseSpeed > 2726 then
1468
			newTurbineBaseSpeed = 2726
1469
		end
1470
		sideClick, xClick, yClick = 0, 0, 0
1471
		_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"] = newTurbineBaseSpeed
1472
		config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1473
	else
1474
		printLog("No change to Turbine RPM requested by "..progName.." GUI in displayTurbineBars(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1475
	end -- if (xClick == 29) and (yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1476
	print{"  mB/t",22,3,monitorIndex}
1477
	print{"<      >",22,4,monitorIndex}
1478
	print{stringTrim(turbineFlowRate),24,4,monitorIndex}
1479
	print{"  RPM",22,5,monitorIndex}
1480
	print{"<      >",22,6,monitorIndex}
1481
	print{stringTrim(tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])),24,6,monitorIndex}
1482
	local rotorSpeedString = "Speed: "
1483
	local energyBufferString = "Energy: "
1484
	local padding = math.max(string.len(rotorSpeedString), string.len(energyBufferString))
1485
1486
	local energyBuffer = turbine.getEnergyProducedLastTick()
1487
	print{energyBufferString,1,4,monitorIndex}
1488
	print{math.ceil(energyBuffer).." RF/t",padding+1,4,monitorIndex}
1489
1490
	local rotorSpeed = math.ceil(turbine.getRotorSpeed())
1491
	print{rotorSpeedString,1,5,monitorIndex}
1492
	print{rotorSpeed.." RPM",padding+1,5,monitorIndex}
1493
1494
	-- PaintUtils only outputs to term., not monitor.
1495
	-- See http://www.computercraft.info/forums2/index.php?/topic/15540-paintutils-on-a-monitor/
1496
1497
	-- Draw stored energy buffer bar
1498
	drawBar(1,9,28,9,colors.gray,monitorIndex)
1499
1500
	local curStoredEnergyPercent = getTurbineStoredEnergyBufferPercent(turbine)
1501
	if curStoredEnergyPercent > 4 then
1502
		drawBar(1, 9, math.floor(26*curStoredEnergyPercent/100)+2, 9, colors.yellow,monitorIndex)
1503
	elseif curStoredEnergyPercent > 0 then
1504
		drawPixel(1, 9, colors.yellow, monitorIndex)
1505
	end -- if curStoredEnergyPercent > 4 then
1506
1507
	print{"Energy Buffer",1,8,monitorIndex}
1508
	print{curStoredEnergyPercent, width-(string.len(curStoredEnergyPercent)+2),8,monitorIndex}
1509
	print{"%",28,8,monitorIndex}
1510
1511
	-- Print rod override status
1512
	local turbineFlowRateOverrideStatus = ""
1513
1514
	print{"Flow Auto-adjust:",2,10,monitorIndex}
1515
1516
	if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
1517
		turbineFlowRateOverrideStatus = "Enabled"
1518
		monitor.setTextColor(colors.green)
1519
	else
1520
		turbineFlowRateOverrideStatus = "Disabled"
1521
		monitor.setTextColor(colors.red)
1522
	end -- if not reactorRodOverride then
1523
1524
	print{turbineFlowRateOverrideStatus, width - string.len(turbineFlowRateOverrideStatus) - 1, 10, monitorIndex}
1525
	monitor.setTextColor(colors.white)
1526
1527
	monitor.setTextColor(colors.blue)
1528
	printCentered(_G[turbineNames[turbineIndex]]["TurbineOptions"]["turbineName"],12,monitorIndex)
1529
	monitor.setTextColor(colors.white)
1530
1531
	-- Need equation to figure out rotor efficiency and display
1532
end -- function displayTurbineBars(statusParams)
1533
1534
1535
-- Display turbine status
1536
local function turbineStatus(turbineIndex, monitorIndex)
1537
	-- Grab current monitor
1538
	local monitor = nil
1539
1540
	printLog("Called as turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..").")
1541-
				local localControlRodAdjustAmount = _G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"]
1541+
1542
	monitor = monitorList[monitorIndex]
1543
	if not monitor then
1544
		printLog("monitor["..monitorIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT a valid monitor.")
1545
		return -- Invalid monitorIndex
1546
	end
1547
1548
	-- Grab current turbine
1549-
							if (rodPercentage + (10 * localControlRodAdjustAmount)) > 99 then
1549+
1550
	turbine = turbineList[turbineIndex]
1551
	if not turbine then
1552-
								reactor.setAllControlRodLevels(rodPercentage + (10 * localControlRodAdjustAmount))
1552+
1553
		return -- Invalid turbineIndex
1554
	else
1555
		printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is a valid Big Turbine.")
1556
	end
1557
1558-
							_G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = diffAmount
1558+
1559-
							if (rodPercentage + diffAmount) > 99 then
1559+
1560
1561
	if turbine.getConnected() then
1562-
								reactor.setAllControlRodLevels(rodPercentage + diffAmount)
1562+
1563
		if turbine.getActive() then
1564
			turbineStatus = "ONLINE"
1565-
					elseif ((lastTempPoll - reactorTemp) < (reactorTemp * 0.005)) then
1565+
1566
		else
1567
			turbineStatus = "OFFLINE"
1568
			monitor.setTextColor(colors.red)
1569
		end -- if turbine.getActive() then
1570
1571
		if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1572
			if yClick == 1 then
1573
				turbine.setActive(not turbine.getActive()) -- Toggle turbine status
1574
				_G[turbineNames[turbineIndex]]["TurbineOptions"]["autoStart"] = turbine.getActive()
1575
				config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1576-
				elseif ((reactorTemp < localMinReactorTemp) and (rodPercentage ~=0)) or (steamRequested - steamDelivered > 0) then
1576+
				sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1577
			end -- if yClick == 1 then
1578-
					if (steamRequested > (steamDelivered*2)) then
1578+
		end -- if (xClick >= (width - string.len(turbineStatus) - 1)) and (xClick <= (width-1)) and (sideClick == monitorNames[monitorIndex]) then
1579-
						-- Bridge to machine room: Full steam ahead!
1579+
1580-
						reactor.setAllControlRodLevels(0)
1580+
		-- Allow disabling/enabling flow rate auto-adjust
1581-
					elseif (reactorTemp < lastTempPoll) then
1581+
		if (xClick > 23 and xClick < 28 and yClick == 4) and (sideClick == monitorNames[monitorIndex]) then
1582
			_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
1583
			sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1584
		elseif (xClick > 20 and xClick < 27 and yClick == 10) and (sideClick == monitorNames[monitorIndex]) then
1585-
							if (rodPercentage - (10 * localControlRodAdjustAmount)) < 0 then
1585+
1586
			if ((_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "true")) then
1587
				_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = false
1588-
								reactor.setAllControlRodLevels(rodPercentage - (10 * localControlRodAdjustAmount))
1588+
1589
				_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] = true
1590
			end
1591
			sideClick, xClick, yClick = 0, 0, 0 -- Reset click after we register it
1592
		end
1593
		config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1594-
							_G[reactorNames[reactorIndex]]["ReactorOptions"]["controlRodAdjustAmount"] = diffAmount
1594+
1595-
							if (rodPercentage - diffAmount) < 0 then
1595+
1596
		printLog("turbine["..turbineIndex.."] in turbineStatus(turbineIndex="..turbineIndex..",monitorIndex="..monitorIndex..") is NOT connected.")
1597
		turbineStatus = "DISCONNECTED"
1598-
								reactor.setAllControlRodLevels(rodPercentage - diffAmount)
1598+
1599
	end -- if turbine.getConnected() then
1600
1601
	print{turbineStatus, width - string.len(turbineStatus) - 1, 1, monitorIndex}
1602
	monitor.setTextColor(colors.white)
1603
end -- function function turbineStatus(turbineIndex, monitorIndex)
1604
1605
1606
-- Maintain Turbine flow rate at 900 or 1,800 RPM
1607
local function flowRateControl(turbineIndex)
1608
	if ((not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"]) or (_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] == "false")) then
1609
		
1610
		printLog("Called as flowRateControl(turbineIndex="..turbineIndex..").")
1611
1612
		-- Grab current turbine
1613
		local turbine = nil
1614-
				if ((reactorTemp > localMinReactorTemp) and (reactorTemp < localMaxReactorTemp)) and not (steamRequested - steamDelivered > 0) then
1614+
1615
1616
		-- assign for the duration of this run
1617
		local lastTurbineSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"])
1618
		local turbineBaseSpeed = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["BaseSpeed"])
1619
1620
		if not turbine then
1621
			printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT a valid Big Turbine.")
1622
			return -- Invalid turbineIndex
1623
		else
1624
			printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is a valid Big Turbine.")
1625
1626
			if turbine.getConnected() then
1627
				printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is connected.")
1628
			else
1629
				printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT connected.")
1630
			end -- if turbine.getConnected() then
1631
		end -- if not turbine then
1632
1633
		-- No point modifying control rod levels for temperature if the turbine is offline
1634
		if turbine.getActive() then
1635
			printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is active.")
1636
1637
			local flowRate = tonumber(_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"])
1638
			local flowRateUserMax = math.ceil(turbine.getFluidFlowRateMax())
1639
			local rotorSpeed = math.ceil(turbine.getRotorSpeed())
1640
			local newFlowRate = 0
1641
1642
			-- Going to control the turbine based on target RPM since changing the target flow rate bypasses this function
1643
			if (rotorSpeed < turbineBaseSpeed) then
1644
				printLog("BELOW COMMANDED SPEED")
1645
				if (rotorSpeed > lastTurbineSpeed) then
1646
					--we're still increasing, let's let it level off
1647
					--also lets the first control pass go by on startup
1648
				elseif (rotorSpeed < lastTurbineSpeed) then
1649
					--we're decreasing where we should be increasing, do something
1650
					if ((lastTurbineSpeed - rotorSpeed) > 100) then
1651
						--kick it harder
1652
						newFlowRate = 2000
1653
						printLog("HARD KICK")
1654
					else
1655
						--let's adjust based on proximity
1656
						flowAdjustment = (turbineBaseSpeed - rotorSpeed)/5
1657
						newFlowRate = flowRate + flowAdjustment
1658
						printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1659
					end
1660
				else
1661
					--we've stagnated, kick it.
1662
					flowAdjustment = (turbineBaseSpeed - lastTurbineSpeed)
1663
					newFlowRate = flowRate + flowAdjustment
1664
					printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1665
				end --if (rotorSpeed > lastTurbineSpeed) then
1666
			else
1667
				--we're above commanded turbine speed
1668
				printLog("ABOVE COMMANDED SPEED")
1669
				if (rotorSpeed < lastTurbineSpeed) then
1670
				--we're decreasing, let it level off
1671
				--also bypasses first control pass on startup
1672
				elseif (rotorSpeed > lastTurbineSpeed) then
1673
					--we're above and ascending.
1674
					if ((rotorSpeed - lastTurbineSpeed) > 100) then
1675
						--halt
1676
						newFlowRate = 25
1677
					else
1678
						--let's adjust based on proximity
1679
						flowAdjustment = (rotorSpeed - turbineBaseSpeed)/5
1680
						newFlowRate = flowRate - flowAdjustment
1681
						printLog("Light Kick: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1682
					end
1683
				else
1684
					--we've stagnated, kick it.
1685
					flowAdjustment = (lastTurbineSpeed - turbineBaseSpeed)
1686
					newFlowRate = flowRate - flowAdjustment
1687
					printLog("Stagnated: new flow rate is "..newFlowRate.." mB/t and flowAdjustment was "..flowAdjustment.." EOL")
1688
				end --if (rotorSpeed < lastTurbineSpeed) then
1689
			end --if (rotorSpeed < turbineBaseSpeed)
1690
1691
			--check to make sure an adjustment was made
1692
			if (newFlowRate == 0) then
1693
				--do nothing, we didn't ask for anything this pass
1694
			else
1695
				--boundary check
1696
				if newFlowRate > 2000 then
1697
					newFlowRate = 2000
1698
				elseif newFlowRate < 25 then
1699
					newFlowRate = 25 -- Don't go to zero, might as well power off
1700
				end -- if newFlowRate > 2000 then
1701
				--no sense running an adjustment if it's not necessary
1702
				if ((newFlowRate < flowRate) or (newFlowRate > flowRate)) then
1703
					printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is being commanded to "..newFlowRate.." mB/t flow")
1704
					newFlowRate = round(newFlowRate, 0)
1705
					turbine.setFluidFlowRateMax(newFlowRate)
1706
					_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastFlow"] = newFlowRate
1707
					config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1708
				end
1709
			end
1710
			--always set this
1711
			_G[turbineNames[turbineIndex]]["TurbineOptions"]["LastSpeed"] = rotorSpeed
1712
			config.save(turbineNames[turbineIndex]..".options", _G[turbineNames[turbineIndex]])
1713
		else
1714
			printLog("turbine["..turbineIndex.."] in flowRateControl(turbineIndex="..turbineIndex..") is NOT active.")
1715
		end -- if turbine.getActive() then
1716
	else
1717
		printLog("turbine["..turbineIndex.."] has flow override set to "..tostring(_G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"])..", bypassing flow control.")
1718
	end -- if not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] then
1719
end -- function flowRateControl(turbineIndex)
1720
1721
1722-
	monitor.setCursorPos(1, height)
1722+
1723-
	monitor.write("< ")
1723+
1724-
	monitor.setCursorPos(width-1, height)
1724+
1725-
	monitor.write(" >")
1725+
1726
	-- Get our initial list of connected monitors and reactors
1727
	-- and initialize every cycle in case the connected devices change
1728
	findMonitors()
1729
	findReactors()
1730
	findTurbines()
1731
1732
	while not finished do
1733
		local reactor = nil
1734
		local monitorIndex = 1
1735
1736
		-- For multiple reactors/monitors, monitor #1 is reserved for overall status
1737
		-- or for multiple reactors/turbines and only one monitor
1738
		if ( ( ((#reactorList + #turbineList) > 1) and (#monitorList >= 1) )   or
1739
		     ( ((#reactorList + #turbineList) >=1) and (#monitorList >  1) ) ) then
1740
			local monitor = nil
1741
			monitor = monitorList[monitorIndex]
1742
			if not monitor then
1743
				printLog("monitor["..monitorIndex.."] in main() is NOT a valid monitor.")
1744
				return -- Invalid monitorIndex
1745
			end
1746
1747
			clearMonitor(progName.." "..progVer, monitorIndex) -- Clear monitor and draw borders
1748
			printCentered(progName.." "..progVer, 1, monitorIndex)
1749
			displayAllStatus()
1750
			monitorIndex = 2 -- Next monitor, #1 is reserved for overall status
1751
		end
1752
1753
		-- Iterate through reactors, continue to run even if not enough monitors are connected
1754
		for reactorIndex = 1, #reactorList do
1755
			local monitor = nil
1756
			local reactorMonitorIndex = monitorIndex + reactorIndex - 1 -- reactorIndex starts at 1
1757
1758
			printLog("Attempting to display reactor["..reactorIndex.."] on monitor["..reactorMonitorIndex.."]...")
1759
1760
			reactor = reactorList[reactorIndex]
1761
			if not reactor then
1762
				printLog("reactor["..reactorIndex.."] in main() is NOT a valid Big Reactor.")
1763
				break -- Invalid reactorIndex
1764
			else
1765
				printLog("reactor["..reactorIndex.."] in main() is a valid Big Reactor.")
1766
			end --  if not reactor then
1767
1768
			-- Only attempt to assign a monitor if we have a monitor for this reactor
1769
			if (reactorMonitorIndex <= #monitorList) then
1770
				printLog("Displaying reactor["..reactorIndex.."] on monitor["..reactorMonitorIndex.."].")
1771
				monitor = monitorList[reactorMonitorIndex]
1772
1773
				if not monitor then
1774
					printLog("monitor["..reactorMonitorIndex.."] in main() is NOT a valid monitor.")
1775
				else
1776
					clearMonitor(progName, reactorMonitorIndex) -- Clear monitor and draw borders
1777
					printCentered(progName, 1, reactorMonitorIndex)
1778
1779
					-- Display reactor status, includes "Disconnected" but found reactors
1780
					reactorStatus{reactorIndex, reactorMonitorIndex}
1781
1782
					-- Draw the borders and bars for the current reactor on the current monitor
1783
					displayReactorBars{reactorIndex, reactorMonitorIndex}
1784
				end -- if not monitor
1785
			else
1786
				printLog("You may want "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines.")
1787
			end -- if (#monitorList ~= 1) and (reactorMonitorIndex < #monitorList) then
1788
1789
			if reactor.getConnected() then
1790
				printLog("reactor["..reactorIndex.."] is connected.")
1791
				local curStoredEnergyPercent = getReactorStoredEnergyBufferPercent(reactor)
1792
1793
				-- Shutdown reactor if current stored energy % is >= desired level, otherwise activate
1794
				-- First pass will have curStoredEnergyPercent=0 until displayBars() is run once
1795
				if curStoredEnergyPercent >= maxStoredEnergyPercent then
1796
					reactor.setActive(false)
1797
				-- Do not auto-start the reactor if it was manually powered off (autoStart=false)
1798
				elseif (curStoredEnergyPercent <= minStoredEnergyPercent) and (_G[reactorNames[reactorIndex]]["ReactorOptions"]["autoStart"] == true) then
1799
					reactor.setActive(true)
1800
				end -- if curStoredEnergyPercent >= maxStoredEnergyPercent then
1801
1802
				-- Don't try to auto-adjust control rods if manual control is requested
1803
				if not _G[reactorNames[reactorIndex]]["ReactorOptions"]["rodOverride"] then
1804
					temperatureControl(reactorIndex)
1805
				end -- if not reactorRodOverride then
1806
			else
1807
				printLog("reactor["..reactorIndex.."] is NOT connected.")
1808
			end -- if reactor.getConnected() then
1809
		end -- for reactorIndex = 1, #reactorList do
1810
1811
		-- Monitors for turbines start after turbineMonitorOffset
1812
		for turbineIndex = 1, #turbineList do
1813
			local monitor = nil
1814
			local turbineMonitorIndex = turbineIndex + turbineMonitorOffset
1815
1816
			printLog("Attempting to display turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."]...")
1817-
	-- monitor switch controls
1817+
1818-
	monitor.setCursorPos(1, height)
1818+
			-- Only attempt to assign a monitor if we found a monitor for this turbine
1819-
	monitor.write("<")
1819+
			if (turbineMonitorIndex <= #monitorList) then
1820-
	monitor.setCursorPos(width, height)
1820+
				printLog("Displaying turbine["..turbineIndex.."] on monitor["..turbineMonitorIndex.."].")
1821-
	monitor.write(">")
1821+
				monitor = monitorList[turbineMonitorIndex]
1822
				if not monitor then
1823
					printLog("monitor["..turbineMonitorIndex.."] in main() is NOT a valid monitor.")
1824
				else
1825
					clearMonitor(progName, turbineMonitorIndex) -- Clear monitor and draw borders
1826
					printCentered(progName, 1, turbineMonitorIndex)
1827
1828
					-- Display turbine status, includes "Disconnected" but found turbines
1829
					turbineStatus(turbineIndex, turbineMonitorIndex)
1830
1831
					-- Draw the borders and bars for the current turbine on the current monitor
1832
					displayTurbineBars(turbineIndex, turbineMonitorIndex)
1833
				end -- if not monitor
1834
			else
1835
				printLog("You may want "..(#reactorList + #turbineList + 1).." monitors for your "..#reactorList.." connected reactors and "..#turbineList.." connected turbines.")
1836
			end -- if (#monitorList ~= 1) and (turbineMonitorIndex < #monitorList) then
1837
1838
			turbine = turbineList[turbineIndex]
1839
			if not turbine then
1840
				printLog("turbine["..turbineIndex.."] in main() is NOT a valid Big Turbine.")
1841
				break -- Invalid turbineIndex
1842
			else
1843
				printLog("turbine["..turbineIndex.."] in main() is a valid Big Turbine.")
1844
			end -- if not turbine then
1845
1846
			if turbine.getConnected() then
1847
				printLog("turbine["..turbineIndex.."] is connected.")
1848
1849
				if not _G[turbineNames[turbineIndex]]["TurbineOptions"]["flowOverride"] then
1850
					flowRateControl(turbineIndex)
1851
				end -- if not turbineFlowRateOverride[turbineIndex] then
1852
			else
1853
				printLog("turbine["..turbineIndex.."] is NOT connected.")
1854
			end -- if turbine.getConnected() then
1855
		end -- for reactorIndex = 1, #reactorList do
1856
1857
		sleep(loopTime) -- Sleep
1858
		saveReactorOptions()
1859
	end -- while not finished do
1860
end -- main()
1861
1862
1863
local function eventHandler()
1864
	while not finished do
1865
		-- http://computercraft.info/wiki/Os.pullEvent
1866
		-- http://www.computercraft.info/forums2/index.php?/topic/1516-ospullevent-what-is-it-and-how-is-it-useful/
1867
		event, arg1, arg2, arg3 = os.pullEvent()
1868
1869
		if event == "monitor_touch" then
1870
			sideClick, xClick, yClick = arg1, math.floor(arg2), math.floor(arg3)
1871
			printLog("Side: "..arg1.." Monitor touch X: "..xClick.." Y: "..yClick)
1872
		elseif event == "char" and not inManualMode then
1873
			local ch = string.lower(arg1)
1874
			if ch == "q" then
1875
				finished = true
1876
			elseif ch == "r" then
1877-
	_G[reactorNames[reactorIndex]]["ReactorOptions"]["Status"] = reactorStatus
1877+
1878
				os.reboot()
1879
			end -- if ch == "q" then
1880
		end -- if event == "monitor_touch" then
1881
	end -- while not finished do
1882
end -- function eventHandler()
1883
1884-
-- Display all found reactors' status to selected monitor
1884+
1885
while not finished do
1886-
local function displayAllStatus(monitorIndex)
1886+
	parallel.waitForAny(eventHandler, main)
1887
	sleep(loopTime)
1888
end -- while not finished do
1889
1890
1891
-- Clear up after an exit
1892
term.clear()
1893
term.setCursorPos(1,1)