Merge branch 'devel' into color-update

This commit is contained in:
Mikayla Fischler 2024-03-05 22:03:24 -05:00
commit c714e49ad8
19 changed files with 240 additions and 174 deletions

View File

@ -95,9 +95,9 @@ function coordinator.load_config()
---@class monitors_struct
local monitors = {
primary = nil, ---@type table|nil
primary_name = "",
flow = nil, ---@type table|nil
main = nil, ---@type table|nil
main_name = "",
flow = nil, ---@type table|nil
flow_name = "",
unit_displays = {},
unit_name_map = {}
@ -121,11 +121,11 @@ function coordinator.load_config()
return 2, "Main monitor is not connected."
end
monitors.primary = ppm.get_periph(config.MainDisplay)
monitors.primary_name = config.MainDisplay
monitors.main = ppm.get_periph(config.MainDisplay)
monitors.main_name = config.MainDisplay
monitors.primary.setTextScale(0.5)
w, _ = ppm.monitor_block_size(monitors.primary.getSize())
monitors.main.setTextScale(0.5)
w, _ = ppm.monitor_block_size(monitors.main.getSize())
if w ~= 8 then
return 2, util.c("Main monitor width is incorrect (was ", w, ", must be 8).")
end

View File

@ -235,7 +235,10 @@ function iocontrol.init(conf, comms, temp_scale)
control_state = false,
burn_rate_cmd = 0.0,
radiation = types.new_zero_radiation_reading(),
sna_prod_rate = 0.0,
sna_peak_rate = 0.0,
sna_max_rate = 0.0,
sna_out_rate = 0.0,
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
@ -1064,12 +1067,14 @@ function iocontrol.update_unit_statuses(statuses)
-- solar neutron activator status info
if type(rtu_statuses.sna) == "table" then
unit.num_snas = rtu_statuses.sna[1] ---@type integer
unit.sna_prod_rate = rtu_statuses.sna[2] ---@type number
unit.sna_peak_rate = rtu_statuses.sna[3] ---@type number
unit.sna_peak_rate = rtu_statuses.sna[2] ---@type number
unit.sna_max_rate = rtu_statuses.sna[3] ---@type number
unit.sna_out_rate = rtu_statuses.sna[4] ---@type number
unit.unit_ps.publish("sna_count", unit.num_snas)
unit.unit_ps.publish("sna_prod_rate", unit.sna_prod_rate)
unit.unit_ps.publish("sna_peak_rate", unit.sna_peak_rate)
unit.unit_ps.publish("sna_max_rate", unit.sna_max_rate)
unit.unit_ps.publish("sna_out_rate", unit.sna_out_rate)
sna_count_sum = sna_count_sum + unit.num_snas
else
@ -1217,7 +1222,7 @@ function iocontrol.update_unit_statuses(statuses)
local u_spent_rate = waste_rate
local u_pu_rate = util.trinary(is_pu, waste_rate, 0.0)
local u_po_rate = util.trinary(not is_pu, math.min(waste_rate, unit.sna_prod_rate), 0.0)
local u_po_rate = unit.sna_out_rate
unit.unit_ps.publish("pu_rate", u_pu_rate)
unit.unit_ps.publish("po_rate", u_po_rate)
@ -1225,14 +1230,15 @@ function iocontrol.update_unit_statuses(statuses)
unit.unit_ps.publish("sna_in", util.trinary(is_pu, 0, burn_rate))
if unit.waste_product == types.WASTE_PRODUCT.POLONIUM then
u_spent_rate = u_po_rate
unit.unit_ps.publish("po_pl_rate", u_po_rate)
unit.unit_ps.publish("po_am_rate", 0)
po_pl_rate = po_pl_rate + u_po_rate
elseif unit.waste_product == types.WASTE_PRODUCT.ANTI_MATTER then
u_spent_rate = 0
unit.unit_ps.publish("po_pl_rate", 0)
unit.unit_ps.publish("po_am_rate", u_po_rate)
po_am_rate = po_am_rate + u_po_rate
u_spent_rate = 0
else
unit.unit_ps.publish("po_pl_rate", 0)
unit.unit_ps.publish("po_am_rate", 0)

View File

@ -52,6 +52,16 @@ local function _init_display(monitor)
end
end
-- print out that the monitor is too small
---@param monitor table monitor
local function _print_too_small(monitor)
monitor.setCursorPos(1, 1)
monitor.setBackgroundColor(colors.black)
monitor.setTextColor(colors.red)
monitor.clear()
monitor.write("monitor too small")
end
-- disable the flow view
---@param disable boolean
function renderer.legacy_disable_flow_view(disable)
@ -64,15 +74,15 @@ function renderer.set_displays(monitors)
engine.monitors = monitors
-- report to front panel as connected
iocontrol.fp_monitor_state("main", engine.monitors.primary ~= nil)
iocontrol.fp_monitor_state("main", engine.monitors.main ~= nil)
iocontrol.fp_monitor_state("flow", engine.monitors.flow ~= nil)
for i = 1, #engine.monitors.unit_displays do iocontrol.fp_monitor_state(i, true) end
end
-- init all displays in use by the renderer
function renderer.init_displays()
-- init primary and flow monitors
_init_display(engine.monitors.primary)
-- init main and flow monitors
_init_display(engine.monitors.main)
if not engine.disable_flow_view then _init_display(engine.monitors.flow) end
-- init unit displays
@ -94,8 +104,8 @@ end
-- initialize the dmesg output window
function renderer.init_dmesg()
local disp_w, disp_h = engine.monitors.primary.getSize()
engine.dmesg_window = window.create(engine.monitors.primary, 1, 1, disp_w, disp_h)
local disp_w, disp_h = engine.monitors.main.getSize()
engine.dmesg_window = window.create(engine.monitors.main, 1, 1, disp_w, disp_h)
log.direct_dmesg(engine.dmesg_window)
end
@ -166,8 +176,8 @@ function renderer.try_start_ui()
status, msg = pcall(function ()
-- show main view on main monitor
if engine.monitors.primary ~= nil then
engine.ui.main_display = DisplayBox{window=engine.monitors.primary,fg_bg=style.root}
if engine.monitors.main ~= nil then
engine.ui.main_display = DisplayBox{window=engine.monitors.main,fg_bg=style.root}
main_view(engine.ui.main_display)
end
@ -244,14 +254,14 @@ function renderer.handle_disconnect(device)
if not engine.monitors then return false end
if engine.monitors.primary == device then
if engine.monitors.main == device then
if engine.ui.main_display ~= nil then
-- delete element tree and clear root UI elements
engine.ui.main_display.delete()
end
is_used = true
engine.monitors.primary = nil
engine.monitors.main = nil
engine.ui.main_display = nil
iocontrol.fp_monitor_state("main", false)
@ -298,9 +308,9 @@ function renderer.handle_reconnect(name, device)
-- note: handle_resize is a more adaptive way of re-initializing a connected monitor
-- since it can handle a monitor being reconnected that isn't the right size
if engine.monitors.primary_name == name then
if engine.monitors.main_name == name then
is_used = true
engine.monitors.primary = device
engine.monitors.main = device
renderer.handle_resize(name)
elseif engine.monitors.flow_name == name then
@ -334,8 +344,8 @@ function renderer.handle_resize(name)
if not engine.monitors then return false, false end
if engine.monitors.primary_name == name and engine.monitors.primary then
local device = engine.monitors.primary ---@type table
if engine.monitors.main_name == name and engine.monitors.main then
local device = engine.monitors.main ---@type table
-- this is necessary if the bottom left block was broken and on reconnect
_init_display(device)
@ -343,9 +353,9 @@ function renderer.handle_resize(name)
is_used = true
-- resize dmesg window if needed, but don't make it thinner
local disp_w, disp_h = engine.monitors.primary.getSize()
local disp_w, disp_h = engine.monitors.main.getSize()
local dmsg_w, _ = engine.dmesg_window.getSize()
engine.dmesg_window.reposition(1, 1, math.max(disp_w, dmsg_w), disp_h, engine.monitors.primary)
engine.dmesg_window.reposition(1, 1, math.max(disp_w, dmsg_w), disp_h, engine.monitors.main)
if ui.main_display then
ui.main_display.delete()
@ -368,11 +378,7 @@ function renderer.handle_resize(name)
ui.main_display = nil
end
device.setCursorPos(1, 1)
device.setBackgroundColor(colors.black)
device.setTextColor(colors.red)
device.clear()
device.write("monitor too small")
_print_too_small(device)
iocontrol.fp_monitor_state("main", false)
is_ok = false
@ -407,11 +413,7 @@ function renderer.handle_resize(name)
ui.flow_display = nil
end
device.setCursorPos(1, 1)
device.setBackgroundColor(colors.black)
device.setTextColor(colors.red)
device.clear()
device.write("monitor too small")
_print_too_small(device)
iocontrol.fp_monitor_state("flow", false)
is_ok = false
@ -448,11 +450,7 @@ function renderer.handle_resize(name)
ui.unit_displays[idx] = nil
end
device.setCursorPos(1, 1)
device.setBackgroundColor(colors.black)
device.setTextColor(colors.red)
device.clear()
device.write("monitor too small")
_print_too_small(device)
iocontrol.fp_monitor_state(idx, false)
is_ok = false
@ -474,7 +472,7 @@ function renderer.handle_mouse(event)
if engine.fp_ready and event.monitor == "terminal" then
engine.ui.front_panel.handle_mouse(event)
elseif engine.ui_ready then
if event.monitor == engine.monitors.primary_name then
if event.monitor == engine.monitors.main_name then
if engine.ui.main_display then engine.ui.main_display.handle_mouse(event) end
elseif event.monitor == engine.monitors.flow_name then
if engine.ui.flow_display then engine.ui.flow_display.handle_mouse(event) end

View File

@ -22,9 +22,9 @@ local sounder = require("coordinator.sounder")
local apisessions = require("coordinator.session.apisessions")
local COORDINATOR_VERSION = "v1.2.7"
local COORDINATOR_VERSION = "v1.2.11"
local CHUNK_LOAD_DELAY_S = 20.0
local CHUNK_LOAD_DELAY_S = 30.0
local println = util.println
local println_ts = util.println_ts
@ -42,24 +42,33 @@ local log_crypto = coordinator.log_crypto
-- mount connected devices (required for monitor setup)
ppm.mount_all()
local wait_on_load = true
local loaded, monitors = coordinator.load_config()
-- if the computer just started, its chunk may have just loaded (...or the user rebooted)
-- if monitor config failed, maybe an adjacent chunk containing all or part of a monitor has not loaded yet, so keep trying
while loaded == 2 and os.clock() < CHUNK_LOAD_DELAY_S do
while wait_on_load and loaded == 2 and os.clock() < CHUNK_LOAD_DELAY_S do
term.clear()
term.setCursorPos(1, 1)
println("There was a monitor configuration problem at boot.\n")
println("Startup will keep trying every 2s in case of chunk load delays.\n")
println(util.sprintf("The configurator will be started in %ds if all attempts fail.\n", math.max(0, CHUNK_LOAD_DELAY_S - os.clock())))
println("(exit early with ctrl-t)")
println("(click to skip to the configurator)")
---@diagnostic disable-next-line: undefined-field
os.sleep(2)
local timer_id = util.start_timer(2)
-- remount and re-attempt
ppm.mount_all()
loaded, monitors = coordinator.load_config()
while true do
local event, param1 = util.pull_event()
if event == "timer" and param1 == timer_id then
-- remount and re-attempt
ppm.mount_all()
loaded, monitors = coordinator.load_config()
break
elseif event == "mouse_click" or event == "terminate" then
wait_on_load = false
break
end
end
end
if loaded ~= 0 then
@ -67,9 +76,13 @@ if loaded ~= 0 then
local success, error = configure.configure(loaded, monitors)
if success then
loaded, monitors = coordinator.load_config()
assert(loaded == 0, util.trinary(loaded == 1, "failed to load valid configuration", "monitor configuration invalid"))
if loaded ~= 0 then
println(util.trinary(loaded == 2, "monitor configuration invalid", "failed to load a valid configuration") .. ", please reconfigure")
return
end
else
assert(success, "coordinator configuration error: " .. error)
println("configuration error: " .. error)
return
end
end

View File

@ -187,10 +187,10 @@ local function make(parent, x, y, wide, unit)
local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
local pu_rate = DataIndicator{parent=waste,x=_wide(82,70),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=s_field}
local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=s_field}
local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=s_field}
local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=s_field}
local spent_rate = DataIndicator{parent=waste,x=_wide(117,99),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=s_field}
local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
local spent_rate = DataIndicator{parent=waste,x=_wide(117,98),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%8.3f",value=0,width=13,fg_bg=s_field}
waste_rate.register(unit.unit_ps, "act_burn_rate", waste_rate.update)
pu_rate.register(unit.unit_ps, "pu_rate", pu_rate.update)
@ -220,7 +220,7 @@ local function make(parent, x, y, wide, unit)
sna_act.register(unit.unit_ps, "po_rate", function (r) sna_act.update(r > 0) end)
sna_cnt.register(unit.unit_ps, "sna_count", sna_cnt.update)
sna_pk.register(unit.unit_ps, "sna_peak_rate", sna_pk.update)
sna_max.register(unit.unit_ps, "sna_prod_rate", sna_max.update)
sna_max.register(unit.unit_ps, "sna_max_rate", sna_max.update)
sna_in.register(unit.unit_ps, "sna_in", sna_in.update)
return root

View File

@ -351,7 +351,7 @@ local function init(main)
status.register(facility.sps_ps_tbl[1], "computed_status", status.update)
TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label}
local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.3f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=s_field}
local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=s_field}
sps_in.register(facility.ps, "po_am_rate", sps_in.update)
@ -373,8 +373,8 @@ local function init(main)
TextBox{parent=main,x=145,y=21,text="PROC. WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
local pr_waste = Rectangle{parent=main,x=145,y=22,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=s_hi_bright}
local pu = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17}
local po = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Po",unit="mB/t",format="%9.3f",value=0,width=17}
local popl = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="PoPl",unit="mB/t",format="%7.3f",value=0,width=17}
local po = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Po",unit="mB/t",format="%9.2f",value=0,width=17}
local popl = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="PoPl",unit="mB/t",format="%7.2f",value=0,width=17}
pu.register(facility.ps, "pu_rate", pu.update)
po.register(facility.ps, "po_rate", po.update)

View File

@ -18,7 +18,7 @@ local iocontrol = require("pocket.iocontrol")
local pocket = require("pocket.pocket")
local renderer = require("pocket.renderer")
local POCKET_VERSION = "v0.7.0-alpha"
local POCKET_VERSION = "v0.7.1-alpha"
local println = util.println
local println_ts = util.println_ts
@ -31,9 +31,13 @@ if not pocket.load_config() then
-- try to reconfigure (user action)
local success, error = configure.configure(true)
if success then
assert(pocket.load_config(), "failed to load valid configuration")
if not pocket.load_config() then
println("failed to load a valid configuration, please reconfigure")
return
end
else
assert(success, "pocket configuration error: " .. error)
println("configuration error: " .. error)
return
end
end

View File

@ -70,9 +70,9 @@ function databus.tx_link_state(state)
end
-- transmit reactor enable state across the bus
---@param active boolean reactor active
---@param active any reactor active
function databus.tx_reactor_state(active)
databus.ps.publish("reactor_active", active)
databus.ps.publish("reactor_active", active == true)
end
-- transmit RPS data across the bus

View File

@ -129,6 +129,21 @@ function plc.rps_init(reactor, is_formed)
end
end
-- check if the result of a peripheral call was OK, handle the failure if not
---@nodiscard
---@param result any PPM function call result
---@return boolean succeeded if the result is OK, false if it was a PPM failure
local function _check_and_handle_ppm_call(result)
if result == ppm.ACCESS_FAULT then
_set_fault()
elseif result == ppm.UNDEFINED_FIELD then
_set_fault()
self.formed = false
else return true end
return false
end
-- set emergency coolant control (if configured)
---@param state boolean true to enable emergency coolant, false to disable
local function _set_emer_cool(state)
@ -167,25 +182,20 @@ function plc.rps_init(reactor, is_formed)
-- check if the reactor is formed
local function _is_formed()
local formed = reactor.isFormed()
if formed == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
else
if _check_and_handle_ppm_call(formed) then
self.formed = formed
end
if not self.state[state_keys.sys_fail] then
self.state[state_keys.sys_fail] = not formed
end
-- always update, since some ppm failures constitute not being formed
if not self.state[state_keys.sys_fail] then
self.state[state_keys.sys_fail] = not self.formed
end
end
-- check if the reactor is force disabled
local function _is_force_disabled()
local disabled = reactor.isForceDisabled()
if disabled == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
else
if _check_and_handle_ppm_call(disabled) then
self.force_disabled = disabled
if not self.state[state_keys.force_disabled] then
@ -197,22 +207,16 @@ function plc.rps_init(reactor, is_formed)
-- check for high damage
local function _high_damage()
local damage_percent = reactor.getDamagePercent()
if damage_percent == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
elseif not self.state[state_keys.high_dmg] then
if _check_and_handle_ppm_call(damage_percent) and not self.state[state_keys.high_dmg] then
self.state[state_keys.high_dmg] = damage_percent >= RPS_LIMITS.MAX_DAMAGE_PERCENT
end
end
-- check if the reactor is at a critically high temperature
local function _high_temp()
-- mekanism: MAX_DAMAGE_TEMPERATURE = 1_200
-- mekanism: MAX_DAMAGE_TEMPERATURE = 1200K
local temp = reactor.getTemperature()
if temp == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
elseif not self.state[state_keys.high_temp] then
if _check_and_handle_ppm_call(temp) and not self.state[state_keys.high_temp] then
self.state[state_keys.high_temp] = temp >= RPS_LIMITS.MAX_DAMAGE_TEMPERATURE
end
end
@ -220,10 +224,7 @@ function plc.rps_init(reactor, is_formed)
-- check if there is very low coolant
local function _low_coolant()
local coolant_filled = reactor.getCoolantFilledPercentage()
if coolant_filled == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
elseif not self.state[state_keys.low_coolant] then
if _check_and_handle_ppm_call(coolant_filled) and not self.state[state_keys.low_coolant] then
self.state[state_keys.low_coolant] = coolant_filled < RPS_LIMITS.MIN_COOLANT_FILL
end
end
@ -231,10 +232,7 @@ function plc.rps_init(reactor, is_formed)
-- check for excess waste (>80% filled)
local function _excess_waste()
local w_filled = reactor.getWasteFilledPercentage()
if w_filled == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
elseif not self.state[state_keys.ex_waste] then
if _check_and_handle_ppm_call(w_filled) and not self.state[state_keys.ex_waste] then
self.state[state_keys.ex_waste] = w_filled > RPS_LIMITS.MAX_WASTE_FILL
end
end
@ -242,10 +240,7 @@ function plc.rps_init(reactor, is_formed)
-- check for heated coolant backup (>95% filled)
local function _excess_heated_coolant()
local hc_filled = reactor.getHeatedCoolantFilledPercentage()
if hc_filled == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
elseif not self.state[state_keys.ex_hcoolant] then
if _check_and_handle_ppm_call(hc_filled) and not self.state[state_keys.ex_hcoolant] then
self.state[state_keys.ex_hcoolant] = hc_filled > RPS_LIMITS.MAX_HEATED_COLLANT_FILL
end
end
@ -253,10 +248,7 @@ function plc.rps_init(reactor, is_formed)
-- check if there is no fuel
local function _insufficient_fuel()
local fuel = reactor.getFuelFilledPercentage()
if fuel == ppm.ACCESS_FAULT then
-- lost the peripheral or terminated, handled later
_set_fault()
elseif not self.state[state_keys.no_fuel] then
if _check_and_handle_ppm_call(fuel) and not self.state[state_keys.no_fuel] then
self.state[state_keys.no_fuel] = fuel <= RPS_LIMITS.NO_FUEL_FILL
end
end
@ -477,13 +469,22 @@ function plc.rps_init(reactor, is_formed)
self.tripped = false
self.trip_cause = RPS_TRIP_CAUSE.OK
for i = 1, #self.state do
self.state[i] = false
end
for i = 1, #self.state do self.state[i] = false end
if not quiet then log.info("RPS: reset") end
end
-- partial RPS reset that only clears fault and sys_fail
function public.reset_formed()
self.tripped = false
self.trip_cause = RPS_TRIP_CAUSE.OK
self.state[state_keys.fault] = false
self.state[state_keys.sys_fail] = false
log.info("RPS: partial reset on formed")
end
-- reset the automatic and timeout trip flags, then clear trip if that was the trip cause
function public.auto_reset()
self.state[state_keys.automatic] = false

View File

@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc")
local renderer = require("reactor-plc.renderer")
local threads = require("reactor-plc.threads")
local R_PLC_VERSION = "v1.6.11"
local R_PLC_VERSION = "v1.6.14"
local println = util.println
local println_ts = util.println_ts
@ -31,9 +31,13 @@ if not plc.load_config() then
-- try to reconfigure (user action)
local success, error = configure.configure(true)
if success then
assert(plc.load_config(), "failed to load valid configuration")
if not plc.load_config() then
println("failed to load a valid configuration, please reconfigure")
return
end
else
assert(success, "reactor PLC configuration error: " .. error)
println("configuration error: " .. error)
return
end
end
@ -131,15 +135,22 @@ local function main()
-- we need a reactor, can at least do some things even if it isn't formed though
if plc_state.no_reactor then
println("init> fission reactor not found");
println("init> fission reactor not found")
log.warning("init> no reactor on startup")
plc_state.init_ok = false
plc_state.degraded = true
elseif not smem_dev.reactor.isFormed() then
println("init> fission reactor not formed");
println("init> fission reactor is not formed")
log.warning("init> reactor logic adapter present, but reactor is not formed")
plc_state.degraded = true
plc_state.reactor_formed = false
elseif smem_dev.reactor.getStatus() == ppm.UNDEFINED_FIELD then
-- reactor formed after ppm.mount_all was called
println("init> fission reactor was not formed")
log.warning("init> reactor reported formed, but multiblock functions are not available")
plc_state.degraded = true
plc_state.reactor_formed = false
end

View File

@ -125,9 +125,8 @@ function threads.thread__main(smem, init)
plc_comms.reconnect_reactor(plc_dev.reactor)
end
-- reset RPS for newly connected reactor
-- without this, is_formed will be out of date and cause it to think its no longer formed again
rps.reset()
-- partial reset of RPS, specific to becoming formed
rps.reset_formed()
else
-- fully lost the reactor now :(
println_ts("reactor lost (failed reconnect)!")
@ -231,9 +230,8 @@ function threads.thread__main(smem, init)
plc_comms.reconnect_reactor(plc_dev.reactor)
end
-- reset RPS for newly connected reactor
-- without this, is_formed will be out of date and cause it to think its no longer formed again
rps.reset()
-- partial reset of RPS, specific to becoming formed
rps.reset_formed()
end
elseif networked and type == "modem" then
-- note, check init_ok first since nic will be nil if it is false

View File

@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
local sps_rtu = require("rtu.dev.sps_rtu")
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
local RTU_VERSION = "v1.7.13"
local RTU_VERSION = "v1.7.14"
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
@ -47,9 +47,13 @@ if not rtu.load_config() then
-- try to reconfigure (user action)
local success, error = configure.configure(true)
if success then
assert(rtu.load_config(), "failed to load valid configuration")
if not rtu.load_config() then
println("failed to load a valid configuration, please reconfigure")
return
end
else
assert(success, "RTU configuration error: " .. error)
println("configuration error: " .. error)
return
end
end

View File

@ -17,7 +17,7 @@ local max_distance = nil
local comms = {}
-- protocol/data version (protocol/data independent changes tracked by util.lua version)
comms.version = "2.4.4"
comms.version = "2.4.5"
---@enum PROTOCOL
local PROTOCOL = {

View File

@ -9,7 +9,7 @@ local util = require("scada-common.util")
local ppm = {}
local ACCESS_FAULT = nil ---@type nil
local UNDEFINED_FIELD = "undefined field"
local UNDEFINED_FIELD = "__PPM_UNDEF_FIELD__"
local VIRTUAL_DEVICE_TYPE = "ppm_vdev"
ppm.ACCESS_FAULT = ACCESS_FAULT
@ -155,7 +155,7 @@ local function peri_init(iface)
self.fault_counts[key] = self.fault_counts[key] + 1
return (function () return ACCESS_FAULT end)
return (function () return UNDEFINED_FIELD end)
end
}
@ -306,7 +306,7 @@ function ppm.log_mounts()
log.info(util.c("PPM: had found a ", mount.type, " (", iface, ")"))
end
if #ppm_sys.mounts == 0 then
if util.table_len(ppm_sys.mounts) == 0 then
log.warning("PPM: no devices had been found")
end
end

View File

@ -22,7 +22,7 @@ local t_pack = table.pack
local util = {}
-- scada-common version
util.version = "1.1.16"
util.version = "1.1.18"
util.TICK_TIME_S = 0.05
util.TICK_TIME_MS = 50
@ -284,11 +284,13 @@ function util.cancel_timer(timer) os.cancelTimer(timer) end
--#region PARALLELIZATION
-- protected sleep call so we still are in charge of catching termination
---@param t integer seconds
-- protected sleep call so we still are in charge of catching termination<br>
-- returns the result of pcall
---@param t number seconds
---@return boolean success, any result, any ...
--- EVENT_CONSUMER: this function consumes events
---@diagnostic disable-next-line: undefined-field
function util.psleep(t) pcall(os.sleep, t) end
function util.psleep(t) return pcall(os.sleep, t) end
-- no-op to provide a brief pause (1 tick) to yield<br>
--- EVENT_CONSUMER: this function consumes events

View File

@ -152,6 +152,8 @@ function facility.new(num_reactors, cooling_conf)
table.insert(self.test_tone_states, false)
end
-- PRIVATE FUNCTIONS --
-- check if all auto-controlled units completed ramping
---@nodiscard
local function _all_units_ramped()
@ -228,7 +230,7 @@ function facility.new(num_reactors, cooling_conf)
---@class facility
local public = {}
-- ADD/LINK DEVICES --
--#region Add/Link Devices
-- link a redstone RTU session
---@param rs_unit unit_session
@ -268,11 +270,9 @@ function facility.new(num_reactors, cooling_conf)
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (s) return s.get_session_id() ~= session end) end
end
-- UPDATE --
--#endregion
-- supervisor sessions reporting the list of active RTU sessions
---@param rtu_sessions table session list of all connected RTUs
function public.report_rtus(rtu_sessions) self.rtu_conn_count = #rtu_sessions end
--#region Update
-- update (iterate) the facility management
function public.update()
@ -323,7 +323,7 @@ function facility.new(num_reactors, cooling_conf)
-- Run Process Control --
-------------------------
--#region Process Control
--#region
local avg_charge = self.avg_charge.compute()
local avg_inflow = self.avg_inflow.compute()
@ -597,7 +597,7 @@ function facility.new(num_reactors, cooling_conf)
-- Evaluate Automatic SCRAM --
------------------------------
--#region Automatic SCRAM
--#region
local astatus = self.ascram_status
@ -727,6 +727,8 @@ function facility.new(num_reactors, cooling_conf)
-- Handle Redstone I/O --
-------------------------
--#region
if #self.redstone > 0 then
-- handle facility SCRAM
if self.io_ctl.digital_read(IO.F_SCRAM) then
@ -756,10 +758,14 @@ function facility.new(num_reactors, cooling_conf)
self.io_ctl.digital_write(IO.F_ALARM_ANY, has_any_alarm)
end
--#endregion
----------------
-- Unit Tasks --
----------------
--#region
local insufficent_po_rate = false
local need_emcool = false
@ -798,10 +804,14 @@ function facility.new(num_reactors, cooling_conf)
end
end
--#endregion
------------------------
-- Update Alarm Tones --
------------------------
--#region
local allow_test = self.allow_testing and self.test_tone_set
local alarms = { false, false, false, false, false, false, false, false, false, false, false, false }
@ -888,6 +898,8 @@ function facility.new(num_reactors, cooling_conf)
self.test_tone_set = false
self.test_tone_reset = true
end
--#endregion
end
-- call the update function of all units in the facility<br>
@ -900,7 +912,9 @@ function facility.new(num_reactors, cooling_conf)
end
end
-- COMMANDS --
--#endregion
--#region Commands
-- SCRAM all reactor units
function public.scram_all()
@ -988,7 +1002,9 @@ function facility.new(num_reactors, cooling_conf)
}
end
-- SETTINGS --
--#endregion
--#region Settings
-- set the automatic control group of a unit
---@param unit_id integer unit ID
@ -1029,7 +1045,9 @@ function facility.new(num_reactors, cooling_conf)
return self.pu_fallback
end
-- DIAGNOSTIC TESTING --
--#endregion
--#region Diagnostic Testing
-- attempt to set a test tone state
---@param id TONE|0 tone ID or 0 to disable all
@ -1069,7 +1087,9 @@ function facility.new(num_reactors, cooling_conf)
return self.allow_testing, self.test_alarm_states
end
-- READ STATES/PROPERTIES --
--#endregion
--#region Read States/Properties
-- get current alarm tone on/off states
---@nodiscard
@ -1183,6 +1203,12 @@ function facility.new(num_reactors, cooling_conf)
return status
end
--#endregion
-- supervisor sessions reporting the list of active RTU sessions
---@param rtu_sessions table session list of all connected RTUs
function public.report_rtus(rtu_sessions) self.rtu_conn_count = #rtu_sessions end
-- get the units in this facility
---@nodiscard
function public.get_units() return self.units end

View File

@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v1.2.8"
local SUPERVISOR_VERSION = "v1.2.11"
local println = util.println
local println_ts = util.println_ts
@ -34,9 +34,13 @@ if not supervisor.load_config() then
-- try to reconfigure (user action)
local success, error = configure.configure(true)
if success then
assert(supervisor.load_config(), "failed to load valid configuration")
if not supervisor.load_config() then
println("failed to load a valid configuration, please reconfigure")
return
end
else
assert(success, "supervisor configuration error: " .. error)
println("configuration error: " .. error)
return
end
end

View File

@ -77,7 +77,6 @@ function unit.new(reactor_id, num_boilers, num_turbines)
tanks = {},
snas = {},
envd = {},
sna_prod_rate = 0,
-- redstone control
io_ctl = nil, ---@type rs_controller
valves = {}, ---@type unit_valves
@ -256,7 +255,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
-- PRIVATE FUNCTIONS --
--#region time derivative utility functions
--#region Time Derivative Utility Functions
-- compute a change with respect to time of the given value
---@param key string value key
@ -331,7 +330,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
--#endregion
--#region redstone I/O
--#region Redstone I/O
-- create a generic valve interface
---@nodiscard
@ -398,8 +397,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
---@class reactor_unit
local public = {}
-- ADD/LINK DEVICES --
--#region
--#region Add/Link Devices
-- link the PLC
---@param plc_session plc_session_struct
@ -489,7 +487,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
--#endregion
-- UPDATE SESSION --
--#region Update Session
-- update (iterate) this unit
function public.update()
@ -557,8 +555,9 @@ function unit.new(reactor_id, num_boilers, num_turbines)
end
end
-- AUTO CONTROL OPERATIONS --
--#region
--#endregion
--#region Auto Control Operations
-- engage automatic control
function public.auto_engage()
@ -645,8 +644,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
--#endregion
-- OPERATIONS --
--#region
--#region Operations
-- queue a command to disable the reactor
function public.disable()
@ -726,8 +724,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
--#endregion
-- READ STATES/PROPERTIES --
--#region
--#region Read States/Properties
-- check if an alarm of at least a certain priority level is tripped
---@nodiscard
@ -857,13 +854,15 @@ function unit.new(reactor_id, num_boilers, num_turbines)
status.tanks[tank.get_device_idx()] = { tank.is_faulted(), db.formed, db.state, db.tanks }
end
-- basic SNA statistical information
local total_peak = 0
-- SNA statistical information
local total_peak, total_avail, total_out = 0, 0, 0
for i = 1, #self.snas do
local db = self.snas[i].get_db() ---@type sna_session_db
total_peak = total_peak + db.state.peak_production
total_avail = total_avail + db.state.production_rate
total_out = total_out + math.min(db.tanks.input.amount / 10, db.state.production_rate)
end
status.sna = { #self.snas, public.get_sna_rate(), total_peak }
status.sna = { #self.snas, total_peak, total_avail, total_out }
-- radiation monitors (environment detectors)
status.envds = {}
@ -876,7 +875,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
return status
end
-- get the current total [max] production rate is
-- get the current total max production rate
---@nodiscard
---@return number total_avail_rate
function public.get_sna_rate()

View File

@ -54,9 +54,7 @@ function logic.update_annunciator(self)
-- variables for boiler, or reactor if no boilers used
local total_boil_rate = 0.0
-------------
-- REACTOR --
-------------
--#region Reactor
annunc.AutoControl = self.auto_engaged
@ -143,9 +141,9 @@ function logic.update_annunciator(self)
self.plc_cache.ok = false
end
---------------
-- MISC RTUs --
---------------
--#endregion
--#region Misc RTUs
local max_rad, any_faulted = 0, false
@ -170,9 +168,9 @@ function logic.update_annunciator(self)
end
end
-------------
-- BOILERS --
-------------
--#endregion
--#region Boilers
local boilers_ready = num_boilers == #self.boilers
@ -230,9 +228,9 @@ function logic.update_annunciator(self)
boiler_water_dt_sum = _get_dt(DT_KEYS.ReactorCCool)
end
---------------------------
-- COOLANT FEED MISMATCH --
---------------------------
--#endregion
--#region Coolant Feed Mismatch
-- check coolant feed mismatch if using boilers, otherwise calculate with reactor
local cfmismatch = false
@ -263,9 +261,9 @@ function logic.update_annunciator(self)
annunc.CoolantFeedMismatch = cfmismatch
--------------
-- TURBINES --
--------------
--#endregion
--#region Turbines
local turbines_ready = num_turbines == #self.turbines
@ -340,6 +338,8 @@ function logic.update_annunciator(self)
annunc.TurbineTrip[idx] = has_steam and db.state.flow_rate == 0
end
--#endregion
-- update auto control ready state for this unit
self.db.control.ready = plc_ready and boilers_ready and turbines_ready
end