Add reactor.lua
This commit is contained in:
257
reactor.lua
Normal file
257
reactor.lua
Normal file
@@ -0,0 +1,257 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user