· 6 years ago · Apr 07, 2020, 05:30 PM
1--[[
2This program lets you monitor and actively control a Big Reactors reactor and turbines with an OpenComputers computer.
3
4This program was tested on and designed to work with the mod versions and configurations installed on Flawedspirit's Mental Instability Pack, though as the pack uses default settings for both Big Reactors and OpenComputers, there should be no issues using a different modpack, or even a modpack that makes changes to how BR works.
5
6- http://technicpack.net/modpack/mi-reloaded.604813
7- https://flawedspirit.com/minecraft/#pack
8
9Computer Design
10------------------------
11Please note that to keep the code (relatively) simple, the program makes a few assumptions; those being:
12
13- A Tier 3 Computer
14- A Tier 2 or better Graphics Card
15- An Internet Card
16- One Tier 2 screen OR one Tier 3 screen (for best results)
17- One reactor
18- Any number of (or zero) turbines
19
20Notes
21------------------------
22- Only one reactor has been tested with this program (additional reactors added AT OWN RISK)
23- Data for only 6 turbines will display onscreen if using a Tier 2 screen
24- Data for up to 28 turbines will display onscreen if using a Tier 3 screen
25- Data for ALL turbines will still be tallied in the 'totals' row
26- By default, the program updates once every 2 seconds, though the rate is adjustable
27
28Features
29------------------------
30- Dynamic tracking of reactor information, like temperature, fuel/waste levels, coolant levels*, steam output*, or RF storage*
31- Dynamic tracking of up to 6 turbines, including speed, steam input, RF generation, and RF storage
32- In-program control of reactor power and control rod settings
33- Real-time warning if certain parameters indicate abnormal or non-optimal operation of reactors/turbines
34- NEW! Turbine auto mode! Set it to either 900 or 1800 RPM by pressing T and the program will toggle your turbines' induction coils or active state to keep it at the right speed**
35
36* If applicable
37** Note: the author takes no responsibility if you bankrupt your base's energy stores because your turbines are all disengaged. Please use responsibly.
38
39Usage
40------------------------
41- Press the left and right arrow keys to toggle between page 1 (turbine/RF storage information) or 2 (control rod configuration)
42- Press L or , to lower/raise control rods by 1%
43- Press ; or . to lower/raise control rods by 5%
44- Press ' or / to lower/raise control rods by 10%
45- Press P to toggle reactor power
46- Press Q to exit the program and return to your computer's terminal prompt
47- Press T to toggle 'Auto Mode' on all turbines. This will engage the induction coil when your preferred rotational speed (900 or 1800 RPM) is reached
48
49Resources
50------------------------
51- This script is available from:
52 = http://pastebin.com/zWju7H0z
53- Official OpenComputers Site: http://ocdoc.cil.li/
54- Official Big Reactors Site: http://www.big-reactors.com/#/
55- Big Reactors API: http://wiki.technicpack.net/Reactor_Computer_Port
56
57Changelog
58------------------------
59- 0.1.6
60 - Fixed a bug that caused the program to crash when run on a Tier 2 screen
61- 0.1.5
62 - Addition of turbine auto mode
63 - Changes to make the program take better advantage of larger screen sizes
64 - Bug fixes
65- 0.1.4
66 - First release to Github! :D
67
68TODO
69------------------------
70- See https://github.com/Flawedspirit/FlawedspiritOC/issues for any outstanding issues.
71- Fix screen flickering issue on Page 1 (may not be possible at the moment)
72]]
73
74local component = require("component")
75local event = require("event")
76local keyboard = require("keyboard")
77local os = require("os")
78local term = require("term")
79
80local pollRate = 2 -- Change to whatever value you'd like; time is in seconds
81local active = true
82local currentPage = 1
83local turbineAutoMode = 0 -- This can be changed to 900 or 1800 if you want
84
85-- Static Text Elements
86local header = "Reactor: "
87local headerOutput = "Running at %d%% max rated output"
88local version = "v0.1.6"
89
90-- Components
91local gpu = component.gpu
92local reactor = component.br_reactor
93local w, h = gpu.getResolution()
94
95local turbine = {}
96local i = 1
97for address, type in component.list("br_turbine") do
98 turbine[i] = component.proxy(address)
99 i = i + 1
100end
101
102-- Reactor/turbine Data
103local totalControlRodLevel = 0
104local turbineMaxSteamIn = 0
105local controlRods = {}
106local reactorStatus = {}
107
108-- Colors Helper
109local colors = {}
110colors.white = 0xFFFFFF
111colors.orange = 0xFFA500
112colors.magenta = 0xFF00FF
113colors.lightblue = 0x00AEEF
114colors.yellow = 0xFFFF00
115colors.lime = 0x00FF00
116colors.pink = 0xFFC0CB
117colors.gray = 0x555555
118colors.grey = 0x555555
119colors.silver = 0xAAAAAA
120colors.cyan = 0x00FFFF
121colors.purple = 0x800080
122colors.blue = 0x0000FF
123colors.brown = 0x603913
124colors.green = 0x008000
125colors.red = 0xFF0000
126colors.black = 0x000000
127
128-- Returns a whole number expressing a percentage
129-- out of 100 e.g. 90 is 90%
130function percent(val, max)
131 return (val / max) * 100
132end
133
134function hLine(row)
135 gpu.fill(1, row, w, 1, "─")
136end
137
138function label(x, y, message, color, ...)
139 local color = color or gpu.getForeground()
140 local oldColor = gpu.getForeground()
141
142 gpu.setForeground(color)
143 term.setCursor(x, y)
144 print(string.format(message, ...))
145 gpu.setForeground(oldColor)
146end
147
148function box(row, message, color)
149 --gpu.fill(1, row, w, row + 3, " ")
150 local color = color or gpu.getForeground()
151 local oldColor = gpu.getForeground()
152
153 term.setCursor(1, row)
154 gpu.setForeground(color)
155
156 -- Corners
157 gpu.set(1, row, "╒")
158 gpu.set(w, row, "╕")
159 gpu.set(1, row + 2, "└")
160 gpu.set(w, row + 2, "┘")
161
162 -- Left and right
163 gpu.set(1, row + 1, "│")
164 gpu.set(w, row + 1, "│")
165
166 -- Top and bottom
167 gpu.fill(2, row, w - 2, 1, "═")
168 gpu.fill(2, row + 2, w - 2, 1, "─")
169
170 gpu.set(3, row + 1, message)
171 gpu.setForeground(oldColor)
172end
173
174-- Displayed if a reactor cannot be found at all
175-- and this program is rendered useless
176function printNoSignal()
177 icon = "[ ! ] "
178 message = "No Signal"
179
180 gpu.setBackground(colors.black)
181 gpu.fill(1, 1, w, h, " ")
182
183 gpu.setForeground(colors.red)
184 gpu.set(w / 2 - (string.len(icon) + string.len(message)) / 2 + 1, (h / 2) + 1, "[ ! ]")
185
186 gpu.setForeground(colors.white)
187 gpu.set((w / 2) - 1, (h / 2) + 1, message)
188end
189
190function printControlHelp()
191 hLine(h - 5)
192 label(1, h - 4, "%s", nil, "l / ,")
193 label(16, h - 4, "%s", nil, "; / .")
194 label(32, h - 4, "%s", nil, "' / /")
195 label(48, h - 4, "%s", nil, "p")
196 label(64, h - 4, "%s", nil, "q")
197
198 label(1, h - 3, "%s", colors.gray, "Rods +1/-1")
199 label(16, h - 3, "%s", colors.gray, "Rods +5/-5")
200 label(32, h - 3, "%s", colors.gray, "Rods +10/-10")
201 label(48, h - 3, "%s", colors.gray, "Reactor Power")
202 label(64, h - 3, "%s", colors.gray, "Quit")
203
204 if w > 80 then
205 label(80, h - 4, "%s", nil, "t")
206 label(80, h - 3, "%s", colors.gray, "Turbine Auto Mode")
207 end
208end
209function toggleAutoMode()
210 if turbineAutoMode == 0 then
211 turbineAutoMode = 900
212 elseif turbineAutoMode == 900 then
213 turbineAutoMode = 1800
214 elseif turbineAutoMode == 1800 then
215 turbineAutoMode = 0
216 else
217 turbineAutoMode = 0
218 end
219end
220
221-- Event handler for when a key is pressed
222function onKeyDown(key)
223 local event, address, _, key, _ = event.pull()
224 if key == keyboard.keys.right then
225 currentPage = 2
226 gpu.fill(1, 1, w, h, " ")
227 elseif key == keyboard.keys.left then
228 currentPage = 1
229 gpu.fill(1, 1, w, h, " ")
230 elseif key == keyboard.keys.l then
231 reactor.setAllControlRodLevels(totalControlRodLevel + 1)
232 elseif key == keyboard.keys.comma then
233 reactor.setAllControlRodLevels(totalControlRodLevel - 1)
234 elseif key == keyboard.keys.semicolon then
235 reactor.setAllControlRodLevels(totalControlRodLevel + 5)
236 elseif key == keyboard.keys.period then
237 reactor.setAllControlRodLevels(totalControlRodLevel -5)
238 elseif key == keyboard.keys.apostrophe then
239 reactor.setAllControlRodLevels(totalControlRodLevel + 10)
240 elseif key == keyboard.keys.slash then
241 reactor.setAllControlRodLevels(totalControlRodLevel - 10)
242 elseif key == keyboard.keys.p then
243 reactor.setActive(not reactor.getActive())
244 elseif key == keyboard.keys.q then
245 active = false
246 elseif key == keyboard.keys.t then
247 toggleAutoMode()
248 end
249end
250
251-- Listen for "key_down" event to control program flow
252event.listen("key_down", onKeyDown)
253
254while active do
255 gpu.fill(1, 1, w, h, " ")
256 if component.isAvailable("br_reactor") then
257 label(w - (string.len(version) + 1), h - 1, version, colors.gray)
258 box(1, header, nil)
259
260 -- Get and update values of each control rod
261 for i = 1, reactor.getNumberOfControlRods() - 1 do
262 controlRods[i] = reactor.getControlRodLevel(i)
263 end
264
265 -- Iterate through and take the sum of each control rod level
266 -- Average it to get total control rod level
267 totalControlRodLevel = 0
268 for i = 1, #controlRods do
269 totalControlRodLevel = totalControlRodLevel + controlRods[i]
270 end
271 totalControlRodLevel = totalControlRodLevel / #controlRods
272
273 -- Update reactor data
274 reactorStatus.temperature = {"Temp", reactor.getCasingTemperature()}
275 reactorStatus.fuel = {"Fuel", reactor.getFuelAmount()}
276 reactorStatus.waste = {"Waste", reactor.getWasteAmount()}
277 reactorStatus.fuelMax = {"Max Fuel", reactor.getFuelAmountMax()}
278 reactorStatus.burnRate = {"Burn Rate", reactor.getFuelConsumedLastTick()}
279 reactorStatus.reactivity = {"Reactivity", reactor.getFuelReactivity()}
280 reactorStatus.coolant = {"Coolant", reactor.getCoolantAmount()}
281 reactorStatus.coolantMax = {"Max Coolant", reactor.getCoolantAmountMax()}
282 reactorStatus.steam = {"Steam Out", reactor.getHotFluidProducedLastTick()}
283 reactorStatus.steamMax = {"Max Steam", reactor.getHotFluidAmountMax()}
284 reactorStatus.storedRF = {"Stored Power", reactor.getEnergyStored()}
285
286 label(1, 4, "%s: %.3f mB/t (%s: %d%%)", nil, reactorStatus.burnRate[1], reactorStatus.burnRate[2], reactorStatus.reactivity[1], reactorStatus.reactivity[2])
287
288 -- REACTOR TABLE
289 -- Table header
290 label(1, 6, "%s", nil, reactorStatus.temperature[1])
291 label(16, 6, "%s", nil, reactorStatus.fuel[1])
292 label(32, 6, "%s", nil, reactorStatus.waste[1])
293 label(48, 6, "%s", nil, reactorStatus.coolant[1])
294 label(64, 6, "%s", nil, reactorStatus.steam[1])
295 hLine(7)
296
297 -- Table body
298 label(1, 8, "%d °C", colors.red, reactorStatus.temperature[2])
299
300 if percent(reactorStatus.fuel[2], reactorStatus.fuelMax[2]) > 10 then
301 label(16, 8, "%d mB", nil, reactorStatus.fuel[2])
302 else
303 label(16, 8, "%d mB [!]", colors.red, reactorStatus.fuel[2])
304 end
305
306 if percent(reactorStatus.waste[2], reactorStatus.fuelMax[2]) < 90 then
307 label(32, 8, "%d mB", nil, reactorStatus.waste[2])
308 else
309 label(32, 8, "%d mB [!]", colors.red, reactorStatus.waste[2])
310 end
311
312 if percent(reactorStatus.coolant[2], reactorStatus.coolantMax[2]) > 10 then
313 label(48, 8, "%d mB", nil, reactorStatus.fuel[2])
314 else
315 label(48, 8, "%d mB [!]", colors.red, reactorStatus.coolant[2])
316 end
317
318 if reactorStatus.steam[2] >= turbineMaxSteamIn then
319 label(64, 8, "%d mB/t", nil, reactorStatus.steam[2])
320 else
321 label(64, 8, "%d mB/t [!]", colors.red, reactorStatus.steam[2])
322 end
323
324 -- Percentages
325 label(16, 9, "(%.1f%%)", colors.gray, percent(reactorStatus.fuel[2], reactorStatus.fuelMax[2]))
326 label(32, 9, "(%.1f%%)", colors.gray, percent(reactorStatus.waste[2], reactorStatus.fuelMax[2]))
327 label(48, 9, "(%.1f%%)", colors.gray, percent(reactorStatus.coolant[2], reactorStatus.coolantMax[2]))
328 label(64, 9, "(%.1f%%)", colors.gray, percent(reactorStatus.steam[2], reactorStatus.steamMax[2]))
329
330 if reactor.getActive() then
331 label(string.len(header) + 3, 2, "ON", colors.lime)
332 label(w - string.len(headerOutput), 2, headerOutput, nil, (100 - totalControlRodLevel))
333 else
334 label(string.len(header) + 3, 2, "OFF", colors.red)
335 label((w + 1) - string.len(headerOutput), 2, headerOutput, nil, 0)
336 end
337
338 if currentPage == 1 then
339 box(h - 2, "Press [→] to go to page 2", nil)
340
341 if h > 25 then
342 printControlHelp()
343 end
344
345 if component.isAvailable("br_turbine") then
346 -- TURBINE TABLE
347 -- Table header
348 label(1, 11, "%s", nil, "Turbine #")
349 label(16, 11, "%s", nil, "Speed")
350 label(32, 11, "%s", nil, "Steam In")
351 label(48, 11, "%s", nil, "RF Out")
352 label(64, 11, "%s", nil, "Stored RF")
353
354 if w > 80 then
355 label(80, 11, "%s", nil, "Coil State")
356 end
357 hLine(12)
358
359 -- Table body
360 -- Update turbine status
361 local turbineTotalSteamIn = 0
362 turbineMaxSteamIn = 0
363 local turbineTotalRFOut = 0
364 local turbineStoredRF = 0
365
366 -- by default, only 6 turbines will fit on the screen
367 local maxTurbines = 0
368
369 if h > 25 then
370 maxTurbines = math.min(#turbine, 28)
371 hOffset = 6
372 else
373 maxTurbines = math.min(#turbine, 6)
374 hOffset = 4
375 end
376
377 if #turbine > maxTurbines then
378 label(1, h - (hOffset + 2), "...", colors.orange)
379 label(16, h - (hOffset + 2), "%d %s", colors.orange, (#turbine - 6), "turbine(s) not shown. Totals shown for all turbines.")
380 end
381
382 for i = 1, #turbine do
383 turbineTotalSteamIn = turbineTotalSteamIn + turbine[i].getFluidFlowRate()
384 turbineMaxSteamIn = turbineMaxSteamIn + turbine[i].getFluidFlowRateMax()
385 turbineTotalRFOut = turbineTotalRFOut + turbine[i].getEnergyProducedLastTick()
386 turbineStoredRF = turbineStoredRF + turbine[i].getEnergyStored()
387
388 -- Auto Mode (TM but not really) fun
389 if turbineAutoMode == 900 or turbineAutoMode == 1800 then
390 if turbineAutoMode == 900 and turbine[i].getRotorSpeed() < 895 or
391 turbineAutoMode == 1800 and turbine[i].getRotorSpeed() < 1795 then
392 turbine[i].setInductorEngaged(false)
393 else
394 turbine[i].setInductorEngaged(true)
395 end
396
397 if turbineAutoMode == 900 and turbine[i].getRotorSpeed() > 920 or
398 turbineAutoMode == 1800 and turbine[i].getRotorSpeed() > 1820 then
399 turbine[i].setActive(false)
400 else
401 turbine[i].setActive(true)
402 end
403 end
404
405 if w > 80 then
406 label(80, 11, "%s", nil, "Coil State")
407 label(96, 11, "%s", nil, "Status")
408 end
409 end
410
411 for i = 1, maxTurbines do
412 label(1, 12 + i, "%d", nil, i)
413
414 if turbine[i].getRotorSpeed() >= 895 and turbine[i].getRotorSpeed() <= 905 or
415 turbine[i].getRotorSpeed() >= 1795 and turbine[i].getRotorSpeed() <= 1805 then
416 label(16, 12 + i, "%.1f RPM", colors.cyan, turbine[i].getRotorSpeed())
417 elseif turbine[i].getRotorSpeed() >= 880 and turbine[i].getRotorSpeed() <= 920 or
418 turbine[i].getRotorSpeed() >= 1780 and turbine[i].getRotorSpeed() <= 1820 then
419 label(16, 12 + i, "%.1f RPM", colors.lime, turbine[i].getRotorSpeed())
420 elseif turbine[i].getRotorSpeed() >= 1821 then
421 label(16, 12 + i, "%.1f RPM [!]", colors.red, turbine[i].getRotorSpeed())
422 else
423 label(16, 12 + i, "%.1f RPM", colors.orange, turbine[i].getRotorSpeed())
424 end
425 label(32, 12 + i, "%d mB/t", nil, turbine[i].getFluidFlowRate())
426 label(48, 12 + i, "%d RF/t", nil, turbine[i].getEnergyProducedLastTick())
427 label(64, 12 + i, "%d RF", nil, turbine[i].getEnergyStored())
428
429 if w > 80 then
430 if turbine[i].getInductorEngaged() then
431 label(80, 12 + i, "%s", colors.lime, "Engaged")
432 else
433 label(80, 12 + i, "%s", colors.red, "Disengaged")
434 end
435 if turbine[i].getRotorSpeed() >= 1821 then
436 label(96, 12 + i, "%s", colors.red, "Overspeed")
437 elseif turbine[i].getRotorSpeed() < 880 then
438 label(96, 12 + i, "%s", colors.red, "Underspeed")
439 elseif turbine[i].getActive() == false then
440 label(96, 12 + i, "%s", colors.gray, "Disabled")
441 else
442 label(96, 12 + i, "%s", nil, "Normal")
443 end
444 end
445 end
446
447 hLine(h - (hOffset + 1))
448 label(1, h - (hOffset), "%s", nil, "TOTAL")
449 label(16, h - (hOffset), "%s", nil, "--")
450 label(32, h - (hOffset), "%d mB/t", nil, turbineTotalSteamIn)
451 label(48, h - (hOffset), "%d RF/t", nil, turbineTotalRFOut)
452 label(64, h - (hOffset), "%d RF", nil, turbineStoredRF)
453
454 if turbineAutoMode == 0 then
455 label(80, h - (hOffset), "%s %s", nil, "Auto Mode:", "Off")
456 elseif turbineAutoMode == 900 then
457 label(80, h - (hOffset), "%s %s", nil, "Auto Mode:", "900 RPM")
458 elseif turbineAutoMode == 1800 then
459 label(80, h - (hOffset), "%s %s", nil, "Auto Mode:", "1800 RPM")
460 end
461 else
462 label(1, 11, "%s: %d RF", nil, reactorStatus.storedRF[1], reactorStatus.storedRF[2])
463 label(1, 13, "%s", nil, "No turbines were detected.")
464 end
465
466 os.sleep(pollRate)
467 elseif currentPage == 2 then
468 local maxBarLength = (w - 11) - 11
469
470 box(h - 2, "Press [←] to go to page 1", nil)
471 label(1, 11, "%s", nil, "Control Rods")
472 hLine(12)
473
474 gpu.fill(11, 13, math.ceil(maxBarLength * (totalControlRodLevel / 100) + 0.5), 1, "=")
475 label(1, 13, "%s", nil, "ALL RODS [")
476 label(w - 10, 13, "%s %d%%", nil, "]", totalControlRodLevel)
477
478 printControlHelp()
479 os.sleep(pollRate)
480 end
481 else
482 printNoSignal()
483 end
484end
485
486-- Unregister key_down event on program exit or things get... weird...
487event.ignore("key_down", onKeyDown)
488term.clear()