258 lines
7.5 KiB
Lua
258 lines
7.5 KiB
Lua
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
|
|
ENERGY = 5,
|
|
FUEL = 6,
|
|
}
|
|
|
|
------------------------------------------------
|
|
|
|
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_fuel,
|
|
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 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.ENERGY then
|
|
colored("STOPPED, Reached energy target", colors.yellow)
|
|
elseif state == STATES.FUEL then
|
|
colored("STOPPED, Not enough fuel", colors.yellow)
|
|
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 or state == STATES.RUNNING or STATES.ENERGY and data.turbine_energy > 50 then
|
|
-- RUNNING -> READY
|
|
pcall(reactor.scram)
|
|
state = STATES.ENERGY
|
|
elseif state == STATES.READY or state == STATES.RUNNING or STATES.FUEL and data.reactor_fuel < 20 then
|
|
-- RUNNING -> READY
|
|
pcall(reactor.scram)
|
|
state = STATES.FUEL
|
|
elseif (state = STATES.ENERGY and data.turbine_energy < 1) and (STATES.FUEL and data.reactor_fuel > 20) then
|
|
state = STATES.READY
|
|
elseif state == STATES.READY and data.lever_on then
|
|
-- READY -> RUNNING
|
|
state = STATES.RUNNING
|
|
-- Activate reactor
|
|
pcall(reactor.setBurnRate(1))
|
|
pcall(reactor.activate)
|
|
data.reactor_on = true
|
|
elseif state == STATES.RUNNING and data.reactor_coolant > 98 and data.lever_on and not (data.reactor_max_burn_rate == data.reactor_burn_rate) then
|
|
-- RUNNING -> READY
|
|
pcall(reactor.setBurnRate(data.reactor_burn_rate+1))
|
|
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)
|
|
|
|
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()
|