-
-
Save cjsturgess/24981190bd0680dab1a05d06e088e34d to your computer and use it in GitHub Desktop.
ComputerCraft program to control a Mekanism fission reactor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local state, data, reactor, turbine, info_window, rules_window | |
local STATES = { | |
READY = 1, -- Reactor is off and can be started with the lever | |
RUNNING = 2, -- Reactor is running and all rules are met | |
ESTOP = 3, -- Reactor is stopped due to rule(s) being violated | |
UNKNOWN = 4, -- Reactor or turbine peripherals are missing | |
} | |
local MIN_BURN_RATE = 1.0 | |
local MAX_BURN_RATE = 19.0 | |
local STEP_BURN_RATE = 1.0 | |
local TARGET_TURBINE_ENERGY = 0.6 | |
local MAX_TURBINE_ENERGY = 0.85 | |
------------------------------------------------ | |
local rules = {} | |
local function add_rule(name, fn) | |
table.insert(rules, function() | |
local ok, rule_met, value = pcall(fn) | |
if ok then | |
return rule_met, string.format("%s (%s)", name, value) | |
else | |
return false, name | |
end | |
end) | |
end | |
add_rule("REACTOR TEMPERATURE <= 745K", function() | |
local value = string.format("%3dK", math.ceil(data.reactor_temp)) | |
return data.reactor_temp <= 745, value | |
end) | |
add_rule("REACTOR DAMAGE <= 10%", function() | |
local value = string.format("%3d%%", math.ceil(data.reactor_damage * 100)) | |
return data.reactor_damage <= 0.10, value | |
end) | |
add_rule("REACTOR COOLANT LEVEL >= 95%", function() | |
local value = string.format("%3d%%", math.floor(data.reactor_coolant * 100)) | |
return data.reactor_coolant >= 0.95, value | |
end) | |
add_rule("REACTOR WASTE LEVEL <= 90%", function() | |
local value = string.format("%3d%%", math.ceil(data.reactor_waste * 100)) | |
return data.reactor_waste <= 0.90, value | |
end) | |
add_rule("TURBINE ENERGY LEVEL <= 95%", function() | |
local value = string.format("%3d%%", math.ceil(data.turbine_energy * 100)) | |
return data.turbine_energy <= 0.95, value | |
end) | |
local function all_rules_met() | |
for i, rule in ipairs(rules) do | |
if not rule() then | |
return false | |
end | |
end | |
-- Allow manual emergency stop with SCRAM button | |
return state ~= STATES.RUNNING or data.reactor_on | |
end | |
------------------------------------------------ | |
local function update_data() | |
data = { | |
lever_on = redstone.getInput("top"), | |
reactor_on = reactor.getStatus(), | |
reactor_burn_rate = reactor.getBurnRate(), | |
reactor_max_burn_rate = reactor.getMaxBurnRate(), | |
reactor_temp = reactor.getTemperature(), | |
reactor_damage = reactor.getDamagePercent(), | |
reactor_coolant = reactor.getCoolantFilledPercentage(), | |
reactor_waste = reactor.getWasteFilledPercentage(), | |
turbine_energy = turbine.getEnergyFilledPercentage(), | |
} | |
end | |
local function adjust_burn_rate() | |
local burn_rate_modifier = (TARGET_TURBINE_ENERGY - data.turbine_energy) / TARGET_TURBINE_ENERGY | |
local next_burn_rate = data.reactor_burn_rate + (STEP_BURN_RATE * burn_rate_modifier) | |
if data.turbine_energy > MAX_TURBINE_ENERGY then | |
next_burn_rate = 0.0 | |
end | |
pcall(reactor.setBurnRate, next_burn_rate) | |
end | |
------------------------------------------------ | |
local function colored(text, fg, bg) | |
term.setTextColor(fg or colors.white) | |
term.setBackgroundColor(bg or colors.black) | |
term.write(text) | |
end | |
local function make_section(name, x, y, w, h) | |
for row = 1, h do | |
term.setCursorPos(x, y + row - 1) | |
local char = (row == 1 or row == h) and "\127" or " " | |
colored("\127" .. string.rep(char, w - 2) .. "\127", colors.gray) | |
end | |
term.setCursorPos(x + 2, y) | |
colored(" " .. name .. " ") | |
return window.create(term.current(), x + 2, y + 2, w - 4, h - 4) | |
end | |
local function update_info() | |
local prev_term = term.redirect(info_window) | |
term.clear() | |
term.setCursorPos(1, 1) | |
if state == STATES.UNKNOWN then | |
colored("ERROR RETRIEVING DATA", colors.red) | |
return | |
end | |
colored("REACTOR: ") | |
colored(data.reactor_on and "ON " or "OFF", data.reactor_on and colors.green or colors.red) | |
colored(" LEVER: ") | |
colored(data.lever_on and "ON " or "OFF", data.lever_on and colors.green or colors.red) | |
colored(" R. LIMIT: ") | |
colored(string.format("%4.1f", data.reactor_burn_rate), colors.blue) | |
colored("/", colors.lightGray) | |
colored(string.format("%4.1f", data.reactor_max_burn_rate), colors.blue) | |
term.setCursorPos(1, 3) | |
colored("STATUS: ") | |
if state == STATES.READY then | |
colored("READY, flip lever to start", colors.blue) | |
elseif state == STATES.RUNNING then | |
colored("RUNNING, flip lever to stop", colors.green) | |
elseif state == STATES.ESTOP and not all_rules_met() then | |
colored("EMERGENCY STOP, safety rules violated", colors.red) | |
elseif state == STATES.ESTOP then | |
colored("EMERGENCY STOP, toggle lever to reset", colors.red) | |
end -- STATES.UNKNOWN cases handled above | |
term.redirect(prev_term) | |
end | |
local estop_reasons = {} | |
local function update_rules() | |
local prev_term = term.redirect(rules_window) | |
term.clear() | |
if state ~= STATES.ESTOP then | |
estop_reasons = {} | |
end | |
for i, rule in ipairs(rules) do | |
local ok, text = rule() | |
term.setCursorPos(1, i) | |
if ok and not estop_reasons[i] then | |
colored("[ OK ] ", colors.green) | |
colored(text, colors.lightGray) | |
else | |
colored("[ FAIL ] ", colors.red) | |
colored(text, colors.red) | |
estop_reasons[i] = true | |
end | |
end | |
term.redirect(prev_term) | |
end | |
------------------------------------------------ | |
local function main_loop() | |
-- Search for peripherals again if one or both are missing | |
if not state or state == STATES.UNKNOWN then | |
reactor = peripheral.find("fissionReactorLogicAdapter") | |
turbine = peripheral.find("turbineValve") | |
end | |
if not pcall(update_data) then | |
-- Error getting data (either reactor or turbine is nil?) | |
data = {} | |
state = STATES.UNKNOWN | |
elseif data.reactor_on == nil then | |
-- Reactor is not connected | |
state = STATES.UNKNOWN | |
elseif data.turbine_energy == nil then | |
-- Turbine is not connected | |
state = STATES.UNKNOWN | |
elseif not state then | |
-- Program just started, get current state from lever | |
state = data.lever_on and STATES.RUNNING or STATES.READY | |
elseif state == STATES.READY and data.lever_on then | |
-- READY -> RUNNING | |
state = STATES.RUNNING | |
-- Activate reactor | |
pcall(reactor.activate) | |
data.reactor_on = true | |
elseif state == STATES.RUNNING and not data.lever_on then | |
-- RUNNING -> READY | |
state = STATES.READY | |
elseif state == STATES.ESTOP and not data.lever_on then | |
-- ESTOP -> READY | |
state = STATES.READY | |
elseif state == STATES.UNKNOWN then | |
-- UNKNOWN -> ESTOP | |
state = data.lever_on and STATES.ESTOP or STATES.READY | |
estop_reasons = {} | |
end | |
-- Always enter ESTOP if safety rules are not met | |
if state ~= STATES.UNKNOWN and not all_rules_met() then | |
state = STATES.ESTOP | |
end | |
-- SCRAM reactor if not running | |
if state ~= STATES.RUNNING and reactor then | |
pcall(reactor.scram) | |
end | |
-- Update info and rules windows | |
pcall(update_info) | |
pcall(update_rules) | |
-- Adjust burn rate | |
pcall(adjust_burn_rate) | |
sleep() -- Other calls should already yield, this is just in case | |
return main_loop() | |
end | |
term.setPaletteColor(colors.black, 0x000000) | |
term.setPaletteColor(colors.gray, 0x343434) | |
term.setPaletteColor(colors.lightGray, 0xababab) | |
term.setPaletteColor(colors.red, 0xdb2d20) | |
term.setPaletteColor(colors.green, 0x01a252) | |
term.setPaletteColor(colors.blue, 0x01a0e4) | |
term.clear() | |
local width = term.getSize() | |
info_window = make_section("INFORMATION", 2, 2, width - 2, 7) | |
rules_window = make_section("SAFETY RULES", 2, 10, width - 2, 9) | |
parallel.waitForAny(main_loop, function() | |
os.pullEventRaw("terminate") | |
end) | |
os.reboot() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment