From 2f99aaeedb72becbeb82055ff11f116651b5e6e1 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 5 Mar 2024 10:56:27 -0500 Subject: [PATCH] #431 handle ppm mount of unformed reactor race condition --- reactor-plc/plc.lua | 64 ++++++++++++++++++----------------------- reactor-plc/startup.lua | 13 +++++++-- scada-common/ppm.lua | 2 +- scada-common/util.lua | 2 +- 4 files changed, 40 insertions(+), 41 deletions(-) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index c5e6be3..64cff92 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -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 diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index e667e46..dd18301 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -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.12" local println = util.println local println_ts = util.println_ts @@ -131,15 +131,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 diff --git a/scada-common/ppm.lua b/scada-common/ppm.lua index 6acb7aa..b11e203 100644 --- a/scada-common/ppm.lua +++ b/scada-common/ppm.lua @@ -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 diff --git a/scada-common/util.lua b/scada-common/util.lua index 308bb69..c694fd5 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -22,7 +22,7 @@ local t_pack = table.pack local util = {} -- scada-common version -util.version = "1.1.16" +util.version = "1.1.17" util.TICK_TIME_S = 0.05 util.TICK_TIME_MS = 50