#31 PPM cannot assume that we will get a fault on failure, apparently sometimes we will only get a nil return so the system can no longer check ACCESS_OK, now each device has its own fault tracking

This commit is contained in:
Mikayla Fischler 2022-04-24 12:04:31 -04:00
parent 416255f41a
commit 86b0d155fa
6 changed files with 81 additions and 59 deletions

View File

@ -131,7 +131,8 @@ function iss_init(reactor)
log._warning("ISS: reactor SCRAM") log._warning("ISS: reactor SCRAM")
self.tripped = true self.tripped = true
self.trip_cause = status self.trip_cause = status
if self.reactor.scram() == ppm.ACCESS_FAULT then self.reactor.scram()
if self.reactor.__p_is_faulted() then
log._error("ISS: failed reactor SCRAM") log._error("ISS: failed reactor SCRAM")
end end
end end
@ -420,12 +421,14 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
-- disable the reactor -- disable the reactor
self.scrammed = true self.scrammed = true
plc_state.scram = true plc_state.scram = true
_send_ack(packet.type, self.reactor.scram() == ppm.ACCESS_OK) self.reactor.scram()
_send_ack(packet.type, self.reactor.__p_is_ok())
elseif packet.type == RPLC_TYPES.MEK_ENABLE then elseif packet.type == RPLC_TYPES.MEK_ENABLE then
-- enable the reactor -- enable the reactor
self.scrammed = false self.scrammed = false
plc_state.scram = false plc_state.scram = false
_send_ack(packet.type, self.reactor.activate() == ppm.ACCESS_OK) self.reactor.activate()
_send_ack(packet.type, self.reactor.__p_is_ok())
elseif packet.type == RPLC_TYPES.MEK_BURN_RATE then elseif packet.type == RPLC_TYPES.MEK_BURN_RATE then
-- set the burn rate -- set the burn rate
local success = false local success = false
@ -441,11 +444,12 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
-- if we know our max burn rate, update current burn rate if in range -- if we know our max burn rate, update current burn rate if in range
if max_burn_rate ~= ppm.ACCESS_FAULT then if max_burn_rate ~= ppm.ACCESS_FAULT then
if burn_rate > 0 and burn_rate <= max_burn_rate then if burn_rate > 0 and burn_rate <= max_burn_rate then
success = self.reactor.setBurnRate(burn_rate) self.reactor.setBurnRate(burn_rate)
success = self.reactor.__p_is_ok()
end end
end end
_send_ack(packet.type, success == ppm.ACCESS_OK) _send_ack(packet.type, success)
elseif packet.type == RPLC_TYPES.ISS_CLEAR then elseif packet.type == RPLC_TYPES.ISS_CLEAR then
-- clear the ISS status -- clear the ISS status
iss.reset() iss.reset()

View File

@ -10,7 +10,7 @@ os.loadAPI("scada-common/comms.lua")
os.loadAPI("config.lua") os.loadAPI("config.lua")
os.loadAPI("plc.lua") os.loadAPI("plc.lua")
local R_PLC_VERSION = "alpha-v0.2.5" local R_PLC_VERSION = "alpha-v0.2.6"
local print = util.print local print = util.print
local println = util.println local println = util.println
@ -281,7 +281,8 @@ while true do
log._warning("terminate requested, exiting...") log._warning("terminate requested, exiting...")
if plc_state.init_ok then if plc_state.init_ok then
plc_state.scram = true plc_state.scram = true
if reactor.scram() ~= ppm.ACCESS_FAULT then reactor.scram()
if reactor.__p_is_ok() then
println_ts("reactor disabled") println_ts("reactor disabled")
else else
-- send an alarm: plc_comms.send_alarm(ALARMS.PLC_LOST_CONTROL) ? -- send an alarm: plc_comms.send_alarm(ALARMS.PLC_LOST_CONTROL) ?

View File

@ -17,7 +17,7 @@ os.loadAPI("dev/boiler_rtu.lua")
os.loadAPI("dev/imatrix_rtu.lua") os.loadAPI("dev/imatrix_rtu.lua")
os.loadAPI("dev/turbine_rtu.lua") os.loadAPI("dev/turbine_rtu.lua")
local RTU_VERSION = "alpha-v0.2.3" local RTU_VERSION = "alpha-v0.2.4"
local print = util.print local print = util.print
local println = util.println local println = util.println

View File

@ -4,14 +4,13 @@
-- Protected Peripheral Manager -- Protected Peripheral Manager
-- --
ACCESS_OK = true
ACCESS_FAULT = nil ACCESS_FAULT = nil
---------------------------- ----------------------------
-- PRIVATE DATA/FUNCTIONS -- -- PRIVATE DATA/FUNCTIONS --
---------------------------- ----------------------------
local self = { local _ppm_sys = {
mounts = {}, mounts = {},
auto_cf = false, auto_cf = false,
faulted = false, faulted = false,
@ -20,38 +19,66 @@ local self = {
} }
-- wrap peripheral calls with lua protected call -- wrap peripheral calls with lua protected call
-- ex. reason: we don't want a disconnect to crash the program before a SCRAM -- we don't want a disconnect to crash a program
local peri_init = function (device) -- also provides peripheral-specific fault checks (auto-clear fault defaults to true)
for key, func in pairs(device) do local peri_init = function (iface)
device[key] = function (...) local self = {
faulted = false,
auto_cf = true,
type = peripheral.getType(iface),
device = peripheral.wrap(iface)
}
-- initialization process (re-map)
for key, func in pairs(self.device) do
self.device[key] = function (...)
local status, result = pcall(func, ...) local status, result = pcall(func, ...)
if status then if status then
-- auto fault clear -- auto fault clear
if self.auto_cf then self.faulted = false end if self.auto_cf then self.faulted = false end
if _ppm_sys.auto_cf then _ppm_sys.faulted = false end
-- assume nil is only for functions with no return, so return status return result
if result == nil then
return ACCESS_OK
else
return result
end
else else
-- function failed -- function failed
self.faulted = true self.faulted = true
_ppm_sys.faulted = true
if not mute then if not _ppm_sys.mute then
log._error("PPM: protected " .. key .. "() -> " .. result) log._error("PPM: protected " .. key .. "() -> " .. result)
end end
if result == "Terminated" then if result == "Terminated" then
self.terminate = true _ppm_sys.terminate = true
end end
return ACCESS_FAULT return ACCESS_FAULT
end end
end end
end end
-- fault management functions
local clear_fault = function () self.faulted = false end
local is_faulted = function () return self.faulted end
local is_ok = function () return not self.faulted end
local enable_afc = function () self.auto_cf = true end
local disable_afc = function () self.auto_cf = false end
-- append to device functions
self.device.__p_clear_fault = clear_fault
self.device.__p_is_faulted = is_faulted
self.device.__p_is_ok = is_ok
self.device.__p_enable_afc = enable_afc
self.device.__p_disable_afc = disable_afc
return {
type = self.type,
dev = self.device
}
end end
---------------------- ----------------------
@ -62,41 +89,41 @@ end
-- silence error prints -- silence error prints
function disable_reporting() function disable_reporting()
self.mute = true _ppm_sys.mute = true
end end
-- allow error prints -- allow error prints
function enable_reporting() function enable_reporting()
self.mute = false _ppm_sys.mute = false
end end
-- FAULT MEMORY -- -- FAULT MEMORY --
-- enable automatically clearing fault flag -- enable automatically clearing fault flag
function enable_afc() function enable_afc()
self.auto_cf = true _ppm_sys.auto_cf = true
end end
-- disable automatically clearing fault flag -- disable automatically clearing fault flag
function disable_afc() function disable_afc()
self.auto_cf = false _ppm_sys.auto_cf = false
end end
-- check fault flag -- check fault flag
function is_faulted() function is_faulted()
return self.faulted return _ppm_sys.faulted
end end
-- clear fault flag -- clear fault flag
function clear_fault() function clear_fault()
self.faulted = false _ppm_sys.faulted = false
end end
-- TERMINATION -- -- TERMINATION --
-- if a caught error was a termination request -- if a caught error was a termination request
function should_terminate() function should_terminate()
return self.terminate return _ppm_sys.terminate
end end
-- MOUNTING -- -- MOUNTING --
@ -105,18 +132,12 @@ end
function mount_all() function mount_all()
local ifaces = peripheral.getNames() local ifaces = peripheral.getNames()
self.mounts = {} _ppm_sys.mounts = {}
for i = 1, #ifaces do for i = 1, #ifaces do
local pm_dev = peripheral.wrap(ifaces[i]) _ppm_sys.mounts[ifaces[i]] = peri_init(ifaces[i])
peri_init(pm_dev)
self.mounts[ifaces[i]] = { log._info("PPM: found a " .. _ppm_sys.mounts[ifaces[i]].type .. " (" .. ifaces[i] .. ")")
type = peripheral.getType(ifaces[i]),
dev = pm_dev
}
log._info("PPM: found a " .. self.mounts[ifaces[i]].type .. " (" .. ifaces[i] .. ")")
end end
if #ifaces == 0 then if #ifaces == 0 then
@ -128,31 +149,27 @@ end
function mount(iface) function mount(iface)
local ifaces = peripheral.getNames() local ifaces = peripheral.getNames()
local pm_dev = nil local pm_dev = nil
local type = nil local pm_type = nil
for i = 1, #ifaces do for i = 1, #ifaces do
if iface == ifaces[i] then if iface == ifaces[i] then
log._info("PPM: mount(" .. iface .. ") -> found a " .. peripheral.getType(iface)) log._info("PPM: mount(" .. iface .. ") -> found a " .. peripheral.getType(iface))
type = peripheral.getType(iface) _ppm_sys.mounts[iface] = peri_init(iface)
pm_dev = peripheral.wrap(iface)
peri_init(pm_dev)
self.mounts[iface] = { pm_type = _ppm_sys.mounts[iface].type
type = peripheral.getType(iface), pm_dev = _ppm_sys.mounts[iface].dev
dev = pm_dev
}
break break
end end
end end
return type, pm_dev return pm_type, pm_dev
end end
-- handle peripheral_detach event -- handle peripheral_detach event
function handle_unmount(iface) function handle_unmount(iface)
-- what got disconnected? -- what got disconnected?
local lost_dev = self.mounts[iface] local lost_dev = _ppm_sys.mounts[iface]
if lost_dev then if lost_dev then
local type = lost_dev.type local type = lost_dev.type
@ -173,20 +190,20 @@ end
-- list mounted peripherals -- list mounted peripherals
function list_mounts() function list_mounts()
return self.mounts return _ppm_sys.mounts
end end
-- get a mounted peripheral by side/interface -- get a mounted peripheral by side/interface
function get_periph(iface) function get_periph(iface)
if self.mounts[iface] then if _ppm_sys.mounts[iface] then
return self.mounts[iface].dev return _ppm_sys.mounts[iface].dev
else return nil end else return nil end
end end
-- get a mounted peripheral type by side/interface -- get a mounted peripheral type by side/interface
function get_type(iface) function get_type(iface)
if self.mounts[iface] then if _ppm_sys.mounts[iface] then
return self.mounts[iface].type return _ppm_sys.mounts[iface].type
else return nil end else return nil end
end end
@ -194,7 +211,7 @@ end
function get_all_devices(name) function get_all_devices(name)
local devices = {} local devices = {}
for side, data in pairs(self.mounts) do for side, data in pairs(_ppm_sys.mounts) do
if data.type == name then if data.type == name then
table.insert(devices, data.dev) table.insert(devices, data.dev)
end end
@ -207,7 +224,7 @@ end
function get_device(name) function get_device(name)
local device = nil local device = nil
for side, data in pairs(self.mounts) do for side, data in pairs(_ppm_sys.mounts) do
if data.type == name then if data.type == name then
device = data.dev device = data.dev
break break
@ -228,7 +245,7 @@ end
function get_wireless_modem() function get_wireless_modem()
local w_modem = nil local w_modem = nil
for side, device in pairs(self.mounts) do for side, device in pairs(_ppm_sys.mounts) do
if device.type == "modem" and device.dev.isWireless() then if device.type == "modem" and device.dev.isWireless() then
w_modem = device.dev w_modem = device.dev
break break

View File

@ -330,7 +330,7 @@ function new_session(id, for_reactor, in_queue, out_queue)
-- handle queue -- -- handle queue --
------------------ ------------------
if ~self.in_q.empty() then if not self.in_q.empty() then
-- get a new message to process -- get a new message to process
local message = self.in_q.pop() local message = self.in_q.pop()

View File

@ -17,7 +17,7 @@ os.loadAPI("session/plc.lua")
os.loadAPI("session/coordinator.lua") os.loadAPI("session/coordinator.lua")
os.loadAPI("session/svsessions.lua") os.loadAPI("session/svsessions.lua")
local SUPERVISOR_VERSION = "alpha-v0.1.2" local SUPERVISOR_VERSION = "alpha-v0.1.3"
local print = util.print local print = util.print
local println = util.println local println = util.println