#445 fixed PPM undefined field logic and improved RTU unit fault handling

This commit is contained in:
Mikayla Fischler 2024-03-09 13:24:06 -05:00
parent d1e4ea586e
commit 79c93f1562
11 changed files with 49 additions and 67 deletions

View File

@ -7,7 +7,7 @@ local boilerv_rtu = {}
---@param boiler table ---@param boiler table
---@return rtu_device interface, boolean faulted ---@return rtu_device interface, boolean faulted
function boilerv_rtu.new(boiler) function boilerv_rtu.new(boiler)
local unit = rtu.init_unit() local unit = rtu.init_unit(boiler)
-- disable auto fault clearing -- disable auto fault clearing
boiler.__p_clear_fault() boiler.__p_clear_fault()

View File

@ -7,7 +7,7 @@ local dynamicv_rtu = {}
---@param dynamic_tank table ---@param dynamic_tank table
---@return rtu_device interface, boolean faulted ---@return rtu_device interface, boolean faulted
function dynamicv_rtu.new(dynamic_tank) function dynamicv_rtu.new(dynamic_tank)
local unit = rtu.init_unit() local unit = rtu.init_unit(dynamic_tank)
-- disable auto fault clearing -- disable auto fault clearing
dynamic_tank.__p_clear_fault() dynamic_tank.__p_clear_fault()

View File

@ -7,7 +7,7 @@ local envd_rtu = {}
---@param envd table ---@param envd table
---@return rtu_device interface, boolean faulted ---@return rtu_device interface, boolean faulted
function envd_rtu.new(envd) function envd_rtu.new(envd)
local unit = rtu.init_unit() local unit = rtu.init_unit(envd)
-- disable auto fault clearing -- disable auto fault clearing
envd.__p_clear_fault() envd.__p_clear_fault()

View File

@ -7,7 +7,7 @@ local imatrix_rtu = {}
---@param imatrix table ---@param imatrix table
---@return rtu_device interface, boolean faulted ---@return rtu_device interface, boolean faulted
function imatrix_rtu.new(imatrix) function imatrix_rtu.new(imatrix)
local unit = rtu.init_unit() local unit = rtu.init_unit(imatrix)
-- disable auto fault clearing -- disable auto fault clearing
imatrix.__p_clear_fault() imatrix.__p_clear_fault()

View File

@ -7,7 +7,7 @@ local sna_rtu = {}
---@param sna table ---@param sna table
---@return rtu_device interface, boolean faulted ---@return rtu_device interface, boolean faulted
function sna_rtu.new(sna) function sna_rtu.new(sna)
local unit = rtu.init_unit() local unit = rtu.init_unit(sna)
-- disable auto fault clearing -- disable auto fault clearing
sna.__p_clear_fault() sna.__p_clear_fault()

View File

@ -7,7 +7,7 @@ local sps_rtu = {}
---@param sps table ---@param sps table
---@return rtu_device interface, boolean faulted ---@return rtu_device interface, boolean faulted
function sps_rtu.new(sps) function sps_rtu.new(sps)
local unit = rtu.init_unit() local unit = rtu.init_unit(sps)
-- disable auto fault clearing -- disable auto fault clearing
sps.__p_clear_fault() sps.__p_clear_fault()

View File

@ -7,7 +7,7 @@ local turbinev_rtu = {}
---@param turbine table ---@param turbine table
---@return rtu_device interface, boolean faulted ---@return rtu_device interface, boolean faulted
function turbinev_rtu.new(turbine) function turbinev_rtu.new(turbine)
local unit = rtu.init_unit() local unit = rtu.init_unit(turbine)
-- disable auto fault clearing -- disable auto fault clearing
turbine.__p_clear_fault() turbine.__p_clear_fault()

View File

@ -37,12 +37,7 @@ function modbus.new(rtu_dev, use_parallel_read)
end) end)
else else
readings[i], access_fault = rtu_dev.read_coil(addr) readings[i], access_fault = rtu_dev.read_coil(addr)
if access_fault then break end
if access_fault then
return_ok = false
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
break
end
end end
end end
@ -86,12 +81,7 @@ function modbus.new(rtu_dev, use_parallel_read)
end) end)
else else
readings[i], access_fault = rtu_dev.read_di(addr) readings[i], access_fault = rtu_dev.read_di(addr)
if access_fault then break end
if access_fault then
return_ok = false
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
break
end
end end
end end
@ -135,12 +125,7 @@ function modbus.new(rtu_dev, use_parallel_read)
end) end)
else else
readings[i], access_fault = rtu_dev.read_holding_reg(addr) readings[i], access_fault = rtu_dev.read_holding_reg(addr)
if access_fault then break end
if access_fault then
return_ok = false
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
break
end
end end
end end
@ -184,12 +169,7 @@ function modbus.new(rtu_dev, use_parallel_read)
end) end)
else else
readings[i], access_fault = rtu_dev.read_input_reg(addr) readings[i], access_fault = rtu_dev.read_input_reg(addr)
if access_fault then break end
if access_fault then
return_ok = false
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
break
end
end end
end end

View File

@ -67,9 +67,11 @@ function rtu.load_config()
return cfv.valid() return cfv.valid()
end end
-- create a new RTU unit -- create a new RTU unit<br>
-- if this is for a PPM peripheral, auto fault clearing MUST stay enabled once access begins
---@nodiscard ---@nodiscard
function rtu.init_unit() ---@param device table|nil peripheral device, if applicable
function rtu.init_unit(device)
local self = { local self = {
discrete_inputs = {}, discrete_inputs = {},
coils = {}, coils = {},
@ -86,6 +88,10 @@ function rtu.init_unit()
---@class rtu ---@class rtu
local protected = {} local protected = {}
-- function to check if the peripheral (if exists) is faulted
local function _is_faulted() return false end
if device then _is_faulted = device.__p_is_faulted end
-- refresh IO count -- refresh IO count
local function _count_io() local function _count_io()
self.io_count_cache = { #self.discrete_inputs, #self.coils, #self.input_regs, #self.holding_regs } self.io_count_cache = { #self.discrete_inputs, #self.coils, #self.input_regs, #self.holding_regs }
@ -112,9 +118,8 @@ function rtu.init_unit()
---@param di_addr integer ---@param di_addr integer
---@return any value, boolean access_fault ---@return any value, boolean access_fault
function public.read_di(di_addr) function public.read_di(di_addr)
ppm.clear_fault()
local value = self.discrete_inputs[di_addr].read() local value = self.discrete_inputs[di_addr].read()
return value, ppm.is_faulted() return value, _is_faulted()
end end
-- coils: single bit read-write -- coils: single bit read-write
@ -133,9 +138,8 @@ function rtu.init_unit()
---@param coil_addr integer ---@param coil_addr integer
---@return any value, boolean access_fault ---@return any value, boolean access_fault
function public.read_coil(coil_addr) function public.read_coil(coil_addr)
ppm.clear_fault()
local value = self.coils[coil_addr].read() local value = self.coils[coil_addr].read()
return value, ppm.is_faulted() return value, _is_faulted()
end end
-- write coil -- write coil
@ -143,9 +147,8 @@ function rtu.init_unit()
---@param value any ---@param value any
---@return boolean access_fault ---@return boolean access_fault
function public.write_coil(coil_addr, value) function public.write_coil(coil_addr, value)
ppm.clear_fault()
self.coils[coil_addr].write(value) self.coils[coil_addr].write(value)
return ppm.is_faulted() return _is_faulted()
end end
-- input registers: multi-bit read-only -- input registers: multi-bit read-only
@ -163,9 +166,8 @@ function rtu.init_unit()
---@param reg_addr integer ---@param reg_addr integer
---@return any value, boolean access_fault ---@return any value, boolean access_fault
function public.read_input_reg(reg_addr) function public.read_input_reg(reg_addr)
ppm.clear_fault()
local value = self.input_regs[reg_addr].read() local value = self.input_regs[reg_addr].read()
return value, ppm.is_faulted() return value, _is_faulted()
end end
-- holding registers: multi-bit read-write -- holding registers: multi-bit read-write
@ -184,9 +186,8 @@ function rtu.init_unit()
---@param reg_addr integer ---@param reg_addr integer
---@return any value, boolean access_fault ---@return any value, boolean access_fault
function public.read_holding_reg(reg_addr) function public.read_holding_reg(reg_addr)
ppm.clear_fault()
local value = self.holding_regs[reg_addr].read() local value = self.holding_regs[reg_addr].read()
return value, ppm.is_faulted() return value, _is_faulted()
end end
-- write holding register -- write holding register
@ -194,9 +195,8 @@ function rtu.init_unit()
---@param value any ---@param value any
---@return boolean access_fault ---@return boolean access_fault
function public.write_holding_reg(reg_addr, value) function public.write_holding_reg(reg_addr, value)
ppm.clear_fault()
self.holding_regs[reg_addr].write(value) self.holding_regs[reg_addr].write(value)
return ppm.is_faulted() return _is_faulted()
end end
-- public RTU device access -- public RTU device access

View File

@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
local sps_rtu = require("rtu.dev.sps_rtu") local sps_rtu = require("rtu.dev.sps_rtu")
local turbinev_rtu = require("rtu.dev.turbinev_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu")
local RTU_VERSION = "v1.7.14" local RTU_VERSION = "v1.7.15"
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE

View File

@ -131,31 +131,33 @@ local function peri_init(iface)
local mt = { local mt = {
__index = function (_, key) __index = function (_, key)
-- this will continuously be counting calls here as faults return (function ()
-- unlike other functions, faults here can't be cleared as it is just not defined -- this will continuously be counting calls here as faults
if self.fault_counts[key] == nil then -- unlike other functions, faults here can't be cleared as it is just not defined
self.fault_counts[key] = 0 if self.fault_counts[key] == nil then
end self.fault_counts[key] = 0
-- function failed
self.faulted = true
self.last_fault = UNDEFINED_FIELD
ppm_sys.faulted = true
ppm_sys.last_fault = UNDEFINED_FIELD
if not ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
local count_str = ""
if self.fault_counts[key] > 0 then
count_str = " [" .. self.fault_counts[key] .. " total calls]"
end end
log.error(util.c("PPM: caught undefined function ", key, "()", count_str)) -- function failed
end self.faulted = true
self.last_fault = UNDEFINED_FIELD
self.fault_counts[key] = self.fault_counts[key] + 1 ppm_sys.faulted = true
ppm_sys.last_fault = UNDEFINED_FIELD
return (function () return UNDEFINED_FIELD end) if not ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
local count_str = ""
if self.fault_counts[key] > 0 then
count_str = " [" .. self.fault_counts[key] .. " total calls]"
end
log.error(util.c("PPM: caught undefined function ", key, "()", count_str))
end
self.fault_counts[key] = self.fault_counts[key] + 1
return UNDEFINED_FIELD
end)
end end
} }