View difference between Paste ID: 1fN9bhUd and zdHPxvz7
SHOW: | | - or go back to the newest paste.
1
---------------------------------------------------
2
-- Mekanism Fission Reactor Monitoring Program   --
3
-- Created for CC:Tweaked and Mekanism           --
4
-- Originally created by InternetUnexplorer on   --
5
-- GitHub. Modified for personal use, by         --
6
-- iPlay_G on Minecraft, Derrick355 on GitHub.   --
7
-- As the original program did not contain any   --
8
-- license, this will not contain any license.   --
9
-- However, this software is provided "AS-IS",   --
10
-- with NO WARRANTY of ANY kind. There is no     --
11
-- guarentee of fitness for a particular         --
12
-- purpose.                                      --
13
-- Feel free to use it, but I, nor the           --
14
-- original author are responsible for any       --
15
-- issues arising from the use of this program.  --
16
--                    -=@=-                      --
17
-- Thank you for downloading this program.       --
18
---------------------------------------------------
19
20
21
--- NOTES ---
22
-- If you see 'CRITICAL' on your monitor as the 
23
-- state, that is OK. Critical is what real 
24
-- reactors are considered when operating normally.
25
26
-- It is generally recommended to edit settings
27
-- in an IDE or text editor for ease of use.
28
29
-- It is assumed you have a 6 wide, 4 tall monitor
30
-- attached via a modem.
31
32
-- References to an analog lever are common, since
33
-- I used the Create mod, however if you don't
34
-- have it, any way to input a variable-power
35
-- redstone signal should work.
36
37
-- Binary levers are just vanillia levers.
38
39
-- I recommend putting this program on a disk,
40
-- and having a startup script run this program.
41
-- I have a second computer that I use to edit
42
-- the program in-game so that the main one
43
-- only runs this program, and nothing else.
44
45
46
--- RECOMMENDED & ASSUMED COMPUTER I/O SETUP ---
47
-- TOP: Binary lever, used to enable/disable manual burn rate control
48
-- LEFT: Analog lever, used as coarse adjust on burn control
49
-- RIGHT: Analog lever, used as fine adjust on burn control
50
-- BOTTOM: Binary lever, used as reactor on/off control
51
-- BACK: Modem to connect to reactor, turbine, and monitor
52-
burn_rate_coarse_multi = 75
52+
53
54
55
--- VARIABLE SETTINGS ---
56-
burn_rate_fine_multi = 5
56+
57
-- Default = 75
58
burn_rate_coarse_multi = .1
59
60-
burn_rate_auto_max = 120
60+
61
-- Default = 5
62
burn_rate_fine_multi = 1
63
64
-- Maximum auto burn rate to establish
65-
min_turbine_power = 0.50
65+
66
burn_rate_auto_max = 15
67
68
-- What turbine power percent value should burn rate be max at?
69
-- For this value and all lower, burn rate will be at the max defined above.
70-
max_turbine_power = 0.75
70+
71
min_turbine_power = 0.8
72-
-- What value should the alarm go off if turbine power falls below?
72+
73-
-- Default = 0.46
73+
74-
turbine_min_power = 0.46
74+
75
-- Default = 0.75 (for 75%)
76
max_turbine_power = 0.9
77
78-
monitor_name = "monitor_2"
78+
79
-- Default = monitor_0
80
monitor_name = "monitor_0"
81
82
-- Are you playing on a server with Advanced peripherals?
83
-- This is used so that if a server is restarting the computer can
84
-- shot down the reactor. Note, the server must issue some kind of
85
-- restart warning beforehand.
86
-- Default = true
87
on_server = false
88
89
-- If the above is true, you must configure this line.
90
-- What message in chat should the program look for to see if the server
91
-- is restarting?
92
-- Default = "SCRAM"
93
restart_message = "SCRAM"
94
95-
incoming_restart = false
95+
96
97
------------------------------------------------
98
99
local state, data, reactor, turbine, info_window, rules_window
100
101
local STATES = {
102-
		if ok then
102+
103-
			return rule_met, string.format("%s (%s)", name, value)
103+
104
	ESTOP = 3, -- Reactor is stopped due to rule(s) being violated
105
	UNKNOWN = 4, -- Reactor or turbine peripherals are missing
106
}
107
108
------------------------------------------------
109
110
local function redstone_restart()
111
	if fs.exists("disk/temp/notify_restart") then
112
		redstone.setOutput("front", true)
113
	else
114
		redstone.setOutput("front", false)
115
	end
116
end
117
118
redstone_restart()
119
120-
add_rule("REACTOR COOLANT LEVEL    >=    95%  ", function()
120+
local function check_for_restart_scram()
121
	if fs.exists("disk/temp/scrammed") then
122
		incoming_restart = true
123
		fs.delete("disk/temp/scrammed")
124
		fs.makeDir("disk/temp/notify_restart")
125
		fs.makeDir("disk/temp/redstone_restart")
126
	else
127
		incoming_restart = false
128
	end
129
	return incoming_restart
130
end
131
132
incoming_restart = check_for_restart_scram()
133
134
local function check_for_restart_notify()
135-
add_rule("INCOMING SERVER RESTART  ==  FALSE  ", function()
135+
	if fs.exists("disk/temp/notify_restart") then
136-
	return incoming_restart, incoming_restart
136+
		notify_restart = true
137
		fs.delete("disk/temp/notify_restart")
138
	else
139
		notify_restart = false
140
	end
141
	return notify_restart
142
end
143
144
notify_restart = check_for_restart_notify()
145
146
local function pollChatbox()
147
    while true do
148
        local box = peripheral.find('chatBox', function(_, chatbox) return chatbox.getOperationCooldown('chatMessage') == 0 end)
149
        if box then
150
            return box
151
        end
152
        sleep(0.1)
153
    end
154
end
155
156
------------------------------------------------
157
158
local rules = {}
159
160
local function add_rule(name, fn)
161
	table.insert(rules, function()
162
		local ok, rule_met, value = pcall(fn)
163
		if ok and value ~= false then
164
			return rule_met, string.format("%s ( %s)", name, value)
165
	elseif ok then
166
		return rule_met, string.format("%s (%s)", name, value)
167
		else
168
			return false, name
169
		end
170
	end)
171
end
172
173
add_rule("REACTOR TEMPERATURE      <=  1100K  ", function()
174
	local value = string.format("%3dK", math.ceil(data.reactor_temp))
175
	return data.reactor_temp <= 1100, value
176
end)
177
178
add_rule("REACTOR DAMAGE           <=    10%  ", function()
179
	local value = string.format("%3d%%", math.ceil(data.reactor_damage * 100))
180
	return data.reactor_damage <= 0.10, value
181
end)
182
183
add_rule("COOLANT (COLD) LEVEL     >=    95%  ", function()
184
	local value = string.format("%3d%%", math.floor(data.reactor_coolant * 100))
185-
	term.write(text)
185+
186
end)
187
188
add_rule("COOLANT (HOT) LEVEL      >=    10%  ", function()
189
	local value = string.format("%3d%%", math.floor(data.reactor_hot_coolant * 100))
190
	return data.reactor_coolant >= 0.10, value
191
end)
192
193
add_rule("REACTOR WASTE LEVEL      <=    90%  ", function()
194
	local value = string.format("%3d%%", math.ceil(data.reactor_waste * 100))
195
	return data.reactor_waste <= 0.90, value
196
end)
197
198
add_rule("TURBINE ENERGY LEVEL     <=    95%  ", function()
199
	local value = string.format("%3d%%", math.ceil(data.turbine_energy * 100))
200
	return data.turbine_energy <= 0.95, value
201
end)
202
203
add_rule("REACTOR FUEL LEVEL       <=    10%  ", function()
204
	local value = string.format("%3d%%", math.ceil(data.reactor_fuel * 100))
205
	return data.turbine_energy <= .10, value
206
end)
207
208
if on_server then
209
	add_rule("INCOMING SERVER RESTART  ==  FALSE  ", function()
210
		return not incoming_restart, notify_restart
211
	end)
212
end
213
214
local function all_rules_met()
215
	for i, rule in ipairs(rules) do
216
		if not rule() then
217
			return false
218
		end
219
	end
220
	-- Allow manual emergency stop with SCRAM button
221
	return state ~= STATES.RUNNING or data.reactor_on
222
end
223
224
------------------------------------------------
225
226
local function update_data()
227
	coarse_adjust = (tonumber(redstone.getAnalogInput("left")) * burn_rate_coarse_multi)
228
	fine_adjust = (tonumber(redstone.getAnalogInput("right")) * burn_rate_fine_multi)
229
	set_burn_rate = (coarse_adjust + fine_adjust)
230
231
	data = {
232
		lever_on = redstone.getInput("bottom"),
233
		burn_rate_limit = set_burn_rate,
234
		burn_rate_limited = redstone.getInput("top"),
235
236
		reactor_on = reactor.getStatus(),
237-
		colored("READY - PULL LEVER TO STARTUP", colors.blue)
237+
238
		reactor_max_burn_rate = reactor.getMaxBurnRate(),
239
		reactor_temp = reactor.getTemperature(),
240
		reactor_damage = reactor.getDamagePercent(),
241-
		colored("SCRAM - SAFETY RULE VIOLATED", colors.red)
241+
242
		reactor_hot_coolant = reactor.getHeatedCoolantFilledPercentage(),
243-
		colored("SCRAM - TOGGLE LEVER TO RESET", colors.red)
243+
244
		reactor_fuel = reactor.getFuelFilledPercentage(),
245
		reactor_max_fuel = reactor.getFuelCapacity(),
246
		reactor_fuel_level = reactor.getFuel(),
247
248
		turbine_energy = turbine.getEnergyFilledPercentage(),
249
		turbine_power = turbine.getEnergy(),
250
		max_turbine_power = turbine.getMaxEnergy()
251
	}
252
253
end
254
------------------------------------------------
255
256-
	colored(string.format("%5.2f", (data.turbine_energy*100)), colors.green)
256+
257
258
term.redirect(monitor)
259
260
local function colored(text, fg, bg)
261
	term.setTextColor(fg or colors.white)
262
	term.setBackgroundColor(bg or colors.black)
263
	term.write(string.upper(text))
264
end
265
266
local function make_section(name, x, y, w, h, color)
267
	for row = 1, h do
268
		term.setCursorPos(x, y + row - 1)
269
		local char = (row == 1 or row == h) and "\127" or " "
270
		colored("\127" .. string.rep(char, w - 2) .. "\127", color or colors.gray)
271
	end
272
273
	term.setCursorPos(x + 2, y)
274
	colored(" " .. name .. " ")
275
276
	return window.create(term.current(), x + 2, y + 2, w - 4, h - 4)
277
end
278
279
local function update_info()
280
	local prev_term = term.redirect(info_window)
281
282
	term.clear()
283
	term.setCursorPos(1, 1)
284
285
	if state == STATES.UNKNOWN then
286
		colored("ERROR RETRIEVING DATA", colors.red)
287
		return
288
	end
289
290
	colored("REACTOR: ")
291
	colored(data.reactor_on and "CRITICAL" or "SHUTDOWN", data.reactor_on and colors.green or colors.red)
292
	colored(" LEVER: ")
293
	colored(data.lever_on and "POWERED" or "SECURED", data.lever_on and colors.green or colors.red)
294
	colored(" BURN RATE: ")
295
	if data.burn_rate_limited == false then
296
		colored(string.format("%4.1f", data.reactor_burn_rate), colors.blue)
297
	else
298
		colored(string.format("%4.1f", data.reactor_burn_rate), colors.yellow)
299
	end
300
	colored("/", colors.lightGray)
301
	colored(string.format("%4.0f", data.reactor_max_burn_rate), colors.blue)
302
303
	term.setCursorPos(34, 2)
304
	colored("SET LIMIT: ")
305
	if data.burn_rate_limited then
306
		colored(string.format("%4.1f", data.burn_rate_limit), colors.green)
307
	else
308
		colored(string.format("%4.1f", data.burn_rate_limit), colors.gray)
309
	end
310
311
	term.setCursorPos(1, 4)
312
313
	colored("STATUS: ")
314
	if state == STATES.READY then
315
		colored("READY   - PULL LEVER TO STARTUP", colors.blue)
316
	elseif state == STATES.RUNNING then
317
		colored("RUNNING - PULL LEVER TO SHUTDOWN", colors.green)
318
	elseif state == STATES.ESTOP and not all_rules_met() then
319
		colored("SCRAM   - SAFETY RULE VIOLATED", colors.red)
320
	elseif state == STATES.ESTOP then
321
		colored("SCRAM   - TOGGLE LEVER TO RESET", colors.red)
322
	end -- STATES.UNKNOWN cases handled above
323
324
	term.setCursorPos(1, 6)
325
	
326
	colored("STORED POWER: ")
327
	colored(string.format("%4.0f", (data.turbine_power*4/1000000)), colors.green)
328
	colored(" MFE", colors.green)
329
	colored("/", colors.lightGray)
330
331
	colored(string.format("%4.0f", (data.max_turbine_power*4/1000000)), colors.blue)
332
	colored(" MFE", colors.blue)
333
	colored(" (")
334
	colored(string.format("%6.2f", (data.turbine_energy*100)), colors.green)
335
	colored("%", colors.green)
336
	colored(")")
337
338
	term.setCursorPos(1, 7)
339
	
340
	colored("STORED FUEL:  ")
341
	colored(string.format("%4.0f", (data.reactor_fuel_level["amount"])), colors.green)
342
	colored(" mB", colors.green)
343
	colored("/", colors.lightGray)
344
345
	colored(string.format("%4.0f", (data.reactor_max_fuel)), colors.blue)
346
	colored(" mB", colors.blue)
347
	colored("  (")
348
	colored(string.format("%5.2f", (data.reactor_fuel*100)), colors.green)
349
	colored("%", colors.green)
350-
	info_window = make_section("INFORMATION", 2, 2, width - 2, 10, window_color)
350+
351-
	rules_window = make_section("SAFETY RULES", 2, 13, width - 2, 13, window_color)
351+
352
	term.setCursorPos(1, 8)
353
	colored("FUEL TYPE:    ", colors.white)
354
	fuel_name = data.reactor_fuel_level["name"]
355
	fuel_name = fuel_name:match"^.-:(.+)"
356
	fuel_name = fuel_name.gsub(fuel_name, "_", " ")
357
	colored(fuel_name, colors.green)
358
359
	term.redirect(prev_term)
360
end
361
362
363
local estop_reasons = {}
364
365
local function update_rules()
366-
	if data.turbine_energy < turbine_min_power then --Sound an alarm that's attached to the front of the computer if turbine power falls below 45%.
366+
367-
		redstone.setOutput("right", true)
367+
368
	term.clear()
369-
		redstone.setOutput("right", false)
369+
370
	if state ~= STATES.ESTOP then
371
		estop_reasons = {}
372
	end
373
374
	for i, rule in ipairs(rules) do
375
		local ok, text = rule()
376
		term.setCursorPos(1, i)
377
		if ok and not estop_reasons[i] then
378
			colored("[ SAFE ] ", colors.green)
379
			colored(text, colors.lightGray)
380
		else
381
			colored("[ FAIL ] ", colors.red)
382
			colored(text, colors.red)
383-
local function shutdown_on_restart_warning()
383+
384
		end
385
	end
386-
	--print("The 'chat' event was fired with the username " .. username .. " and the message " .. message)
386+
387-
	chatBox.sendMessage("username: " .. username .. "message: ".. message)
387+
388-
	if username == "sayCommand" and message == "SCRAM" then
388+
389
390-
		chatBox.sendMessage("SCRAM aye")
390+
391
392
local function main_loop()
393-
parallel.waitForAny(main_loop, function()
393+
394-
	os.pullEventRaw("terminate")
394+
395
		reactor = peripheral.find("fissionReactorLogicAdapter")
396
		turbine = peripheral.find("turbineValve")
397
	end
398
399
	if not pcall(update_data) then
400
		-- Error getting data (either reactor or turbine is nil?)
401
		data = {}
402
		state = STATES.UNKNOWN
403
	elseif data.reactor_on == nil then
404
		-- Reactor is not connected
405
		state = STATES.UNKNOWN
406
	elseif data.turbine_energy == nil then
407
		-- Turbine is not connected
408
		state = STATES.UNKNOWN
409
	elseif not state then
410
		-- Program just started, get current state from lever
411
		state = data.lever_on and STATES.RUNNING or STATES.READY
412
	elseif state == STATES.READY and data.lever_on then
413
		-- READY -> RUNNING
414
		state = STATES.RUNNING
415
		-- Activate reactor
416
		pcall(reactor.activate)
417
		data.reactor_on = true
418
	elseif state == STATES.RUNNING and not data.lever_on then
419
		-- RUNNING -> READY
420
		state = STATES.READY
421
	elseif state == STATES.ESTOP and not data.lever_on then
422
		-- ESTOP -> READY
423
		state = STATES.READY
424
	elseif state == STATES.UNKNOWN then
425
		-- UNKNOWN -> ESTOP
426
		state = data.lever_on and STATES.ESTOP or STATES.READY
427
		estop_reasons = {}
428
	end
429
430
	-- Always enter ESTOP if safety rules are not met
431
	if state ~= STATES.UNKNOWN and not all_rules_met() then
432
		state = STATES.ESTOP
433
	end
434
435
	-- SCRAM reactor if not running
436
	if state ~= STATES.RUNNING and reactor then
437
		pcall(reactor.scram)
438
	end
439
440
	-- Make Windows
441
	local width = term.getSize()
442
	if state == STATES.ESTOP then
443
		window_color = colors.red
444
	elseif state == STATES.READY then
445
		window_color = colors.green
446
	else
447
		window_color = colors.gray
448
	end
449
	info_window = make_section("STATUS", 2, 2, width - 2, 12, window_color)
450
	rules_window = make_section("SAFETY RULES", 2, 15, width - 2, 11, window_color)
451
452
	-- Update info and rules windows
453
	pcall(update_info)
454
	pcall(update_rules)
455
456
	-- Update max burn rate to keep turbine below 75% energy.
457
	if state ~= STATES.UNKNOWN and data.burn_rate_limited == false then
458
	
459
		local tgt_burn_pct = math.min(math.max(0.0, 1 - (data.turbine_energy - min_turbine_power) / (max_turbine_power - min_turbine_power)), 1.0)
460
		pcall(reactor.setBurnRate, tgt_burn_pct * burn_rate_auto_max)
461
	elseif state ~= STATES.UNKNOWN and data.burn_rate_limited == true then
462
		pcall(reactor.setBurnRate, data.burn_rate_limit) -- Let burn rate be limited by an analog lever
463
	end 
464
465
	sleep() -- Other calls should already yield, this is just in case
466
	return main_loop()
467
end
468
469
term.setPaletteColor(colors.black, 0x000000)
470
term.setPaletteColor(colors.gray, 0x343434)
471
term.setPaletteColor(colors.lightGray, 0xababab)
472
term.setPaletteColor(colors.red, 0xdb2d20)
473
term.setPaletteColor(colors.green, 0x01a252)
474
term.setPaletteColor(colors.blue, 0x01a0e4)
475
term.clear()
476
477
478
function scram_on_restart()
479
	chatBox = peripheral.find("chatBox")
480
	local event, username, message, uuid, isHidden = os.pullEvent("chat")
481
	pollChatbox().sendMessage("username: " .. username .. " message: `".. message .. "`")
482
	if message == restart_message and username == "sayCommand" then
483
		--fs.copy("/disk/temp/scram.lua", "/disk/temp/scrammed.lua")
484
		fs.makeDir("/disk/temp/scrammed")
485
		pollChatbox().sendMessage("SCRAM aye")
486
	end
487
end
488
489
if on_server then
490
	parallel.waitForAny(main_loop, scram_on_restart)
491
else
492
	parallel.waitForAny(main_loop, function()
493
		os.pullEventRaw("terminate")
494
	end)
495
end
496
497
os.reboot()