#434 #454 PPM improvements and undefined function overhaul

This commit is contained in:
Mikayla Fischler 2024-03-23 00:26:58 -04:00
parent 1c0f61b3e0
commit 19869416af
12 changed files with 186 additions and 265 deletions

View File

@ -144,9 +144,9 @@ function plc.rps_init(reactor, is_formed)
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
-- if undefined, then the reactor isn't formed
if reactor.__p_last_fault() == ppm.UNDEFINED_FIELD then self.formed = false end
else return true end
return 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.7.0"
local R_PLC_VERSION = "v1.7.1"
local println = util.println
local println_ts = util.println_ts
@ -144,13 +144,6 @@ local function main()
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
@ -185,6 +178,7 @@ local function main()
local message
plc_state.fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode)
-- ...or not
if not plc_state.fp_ok then
println_ts(util.c("UI error: ", message))
println("init> running without front panel")

View File

@ -9,58 +9,49 @@ local boilerv_rtu = {}
function boilerv_rtu.new(boiler)
local unit = rtu.init_unit(boiler)
-- disable auto fault clearing
boiler.__p_clear_fault()
boiler.__p_disable_afc()
-- discrete inputs --
unit.connect_di(boiler.isFormed)
unit.connect_di("isFormed")
-- coils --
-- none
-- input registers --
-- multiblock properties
unit.connect_input_reg(boiler.getLength)
unit.connect_input_reg(boiler.getWidth)
unit.connect_input_reg(boiler.getHeight)
unit.connect_input_reg(boiler.getMinPos)
unit.connect_input_reg(boiler.getMaxPos)
unit.connect_input_reg("getLength")
unit.connect_input_reg("getWidth")
unit.connect_input_reg("getHeight")
unit.connect_input_reg("getMinPos")
unit.connect_input_reg("getMaxPos")
-- build properties
unit.connect_input_reg(boiler.getBoilCapacity)
unit.connect_input_reg(boiler.getSteamCapacity)
unit.connect_input_reg(boiler.getWaterCapacity)
unit.connect_input_reg(boiler.getHeatedCoolantCapacity)
unit.connect_input_reg(boiler.getCooledCoolantCapacity)
unit.connect_input_reg(boiler.getSuperheaters)
unit.connect_input_reg(boiler.getMaxBoilRate)
unit.connect_input_reg("getBoilCapacity")
unit.connect_input_reg("getSteamCapacity")
unit.connect_input_reg("getWaterCapacity")
unit.connect_input_reg("getHeatedCoolantCapacity")
unit.connect_input_reg("getCooledCoolantCapacity")
unit.connect_input_reg("getSuperheaters")
unit.connect_input_reg("getMaxBoilRate")
-- current state
unit.connect_input_reg(boiler.getTemperature)
unit.connect_input_reg(boiler.getBoilRate)
unit.connect_input_reg(boiler.getEnvironmentalLoss)
unit.connect_input_reg("getTemperature")
unit.connect_input_reg("getBoilRate")
unit.connect_input_reg("getEnvironmentalLoss")
-- tanks
unit.connect_input_reg(boiler.getSteam)
unit.connect_input_reg(boiler.getSteamNeeded)
unit.connect_input_reg(boiler.getSteamFilledPercentage)
unit.connect_input_reg(boiler.getWater)
unit.connect_input_reg(boiler.getWaterNeeded)
unit.connect_input_reg(boiler.getWaterFilledPercentage)
unit.connect_input_reg(boiler.getHeatedCoolant)
unit.connect_input_reg(boiler.getHeatedCoolantNeeded)
unit.connect_input_reg(boiler.getHeatedCoolantFilledPercentage)
unit.connect_input_reg(boiler.getCooledCoolant)
unit.connect_input_reg(boiler.getCooledCoolantNeeded)
unit.connect_input_reg(boiler.getCooledCoolantFilledPercentage)
unit.connect_input_reg("getSteam")
unit.connect_input_reg("getSteamNeeded")
unit.connect_input_reg("getSteamFilledPercentage")
unit.connect_input_reg("getWater")
unit.connect_input_reg("getWaterNeeded")
unit.connect_input_reg("getWaterFilledPercentage")
unit.connect_input_reg("getHeatedCoolant")
unit.connect_input_reg("getHeatedCoolantNeeded")
unit.connect_input_reg("getHeatedCoolantFilledPercentage")
unit.connect_input_reg("getCooledCoolant")
unit.connect_input_reg("getCooledCoolantNeeded")
unit.connect_input_reg("getCooledCoolantFilledPercentage")
-- holding registers --
-- none
-- check if any calls faulted
local faulted = boiler.__p_is_faulted()
boiler.__p_clear_fault()
boiler.__p_enable_afc()
return unit.interface(), faulted
return unit.interface(), false
end
return boilerv_rtu

View File

@ -9,12 +9,8 @@ local dynamicv_rtu = {}
function dynamicv_rtu.new(dynamic_tank)
local unit = rtu.init_unit(dynamic_tank)
-- disable auto fault clearing
dynamic_tank.__p_clear_fault()
dynamic_tank.__p_disable_afc()
-- discrete inputs --
unit.connect_di(dynamic_tank.isFormed)
unit.connect_di("isFormed")
-- coils --
unit.connect_coil(function () dynamic_tank.incrementContainerEditMode() end, function () end)
@ -22,27 +18,22 @@ function dynamicv_rtu.new(dynamic_tank)
-- input registers --
-- multiblock properties
unit.connect_input_reg(dynamic_tank.getLength)
unit.connect_input_reg(dynamic_tank.getWidth)
unit.connect_input_reg(dynamic_tank.getHeight)
unit.connect_input_reg(dynamic_tank.getMinPos)
unit.connect_input_reg(dynamic_tank.getMaxPos)
unit.connect_input_reg("getLength")
unit.connect_input_reg("getWidth")
unit.connect_input_reg("getHeight")
unit.connect_input_reg("getMinPos")
unit.connect_input_reg("getMaxPos")
-- build properties
unit.connect_input_reg(dynamic_tank.getTankCapacity)
unit.connect_input_reg(dynamic_tank.getChemicalTankCapacity)
unit.connect_input_reg("getTankCapacity")
unit.connect_input_reg("getChemicalTankCapacity")
-- tanks/containers
unit.connect_input_reg(dynamic_tank.getStored)
unit.connect_input_reg(dynamic_tank.getFilledPercentage)
unit.connect_input_reg("getStored")
unit.connect_input_reg("getFilledPercentage")
-- holding registers --
unit.connect_holding_reg(dynamic_tank.getContainerEditMode, dynamic_tank.setContainerEditMode)
unit.connect_holding_reg("getContainerEditMode", "setContainerEditMode")
-- check if any calls faulted
local faulted = dynamic_tank.__p_is_faulted()
dynamic_tank.__p_clear_fault()
dynamic_tank.__p_enable_afc()
return unit.interface(), faulted
return unit.interface(), false
end
return dynamicv_rtu

View File

@ -9,45 +9,36 @@ local imatrix_rtu = {}
function imatrix_rtu.new(imatrix)
local unit = rtu.init_unit(imatrix)
-- disable auto fault clearing
imatrix.__p_clear_fault()
imatrix.__p_disable_afc()
-- discrete inputs --
unit.connect_di(imatrix.isFormed)
unit.connect_di("isFormed")
-- coils --
-- none
-- input registers --
-- multiblock properties
unit.connect_input_reg(imatrix.getLength)
unit.connect_input_reg(imatrix.getWidth)
unit.connect_input_reg(imatrix.getHeight)
unit.connect_input_reg(imatrix.getMinPos)
unit.connect_input_reg(imatrix.getMaxPos)
unit.connect_input_reg("getLength")
unit.connect_input_reg("getWidth")
unit.connect_input_reg("getHeight")
unit.connect_input_reg("getMinPos")
unit.connect_input_reg("getMaxPos")
-- build properties
unit.connect_input_reg(imatrix.getMaxEnergy)
unit.connect_input_reg(imatrix.getTransferCap)
unit.connect_input_reg(imatrix.getInstalledCells)
unit.connect_input_reg(imatrix.getInstalledProviders)
unit.connect_input_reg("getMaxEnergy")
unit.connect_input_reg("getTransferCap")
unit.connect_input_reg("getInstalledCells")
unit.connect_input_reg("getInstalledProviders")
-- I/O rates
unit.connect_input_reg(imatrix.getLastInput)
unit.connect_input_reg(imatrix.getLastOutput)
unit.connect_input_reg("getLastInput")
unit.connect_input_reg("getLastOutput")
-- tanks
unit.connect_input_reg(imatrix.getEnergy)
unit.connect_input_reg(imatrix.getEnergyNeeded)
unit.connect_input_reg(imatrix.getEnergyFilledPercentage)
unit.connect_input_reg("getEnergy")
unit.connect_input_reg("getEnergyNeeded")
unit.connect_input_reg("getEnergyFilledPercentage")
-- holding registers --
-- none
-- check if any calls faulted
local faulted = imatrix.__p_is_faulted()
imatrix.__p_clear_fault()
imatrix.__p_enable_afc()
return unit.interface(), faulted
return unit.interface(), false
end
return imatrix_rtu

View File

@ -9,50 +9,41 @@ local sps_rtu = {}
function sps_rtu.new(sps)
local unit = rtu.init_unit(sps)
-- disable auto fault clearing
sps.__p_clear_fault()
sps.__p_disable_afc()
-- discrete inputs --
unit.connect_di(sps.isFormed)
unit.connect_di("isFormed")
-- coils --
-- none
-- input registers --
-- multiblock properties
unit.connect_input_reg(sps.getLength)
unit.connect_input_reg(sps.getWidth)
unit.connect_input_reg(sps.getHeight)
unit.connect_input_reg(sps.getMinPos)
unit.connect_input_reg(sps.getMaxPos)
unit.connect_input_reg("getLength")
unit.connect_input_reg("getWidth")
unit.connect_input_reg("getHeight")
unit.connect_input_reg("getMinPos")
unit.connect_input_reg("getMaxPos")
-- build properties
unit.connect_input_reg(sps.getCoils)
unit.connect_input_reg(sps.getInputCapacity)
unit.connect_input_reg(sps.getOutputCapacity)
unit.connect_input_reg(sps.getMaxEnergy)
unit.connect_input_reg("getCoils")
unit.connect_input_reg("getInputCapacity")
unit.connect_input_reg("getOutputCapacity")
unit.connect_input_reg("getMaxEnergy")
-- current state
unit.connect_input_reg(sps.getProcessRate)
unit.connect_input_reg("getProcessRate")
-- tanks
unit.connect_input_reg(sps.getInput)
unit.connect_input_reg(sps.getInputNeeded)
unit.connect_input_reg(sps.getInputFilledPercentage)
unit.connect_input_reg(sps.getOutput)
unit.connect_input_reg(sps.getOutputNeeded)
unit.connect_input_reg(sps.getOutputFilledPercentage)
unit.connect_input_reg(sps.getEnergy)
unit.connect_input_reg(sps.getEnergyNeeded)
unit.connect_input_reg(sps.getEnergyFilledPercentage)
unit.connect_input_reg("getInput")
unit.connect_input_reg("getInputNeeded")
unit.connect_input_reg("getInputFilledPercentage")
unit.connect_input_reg("getOutput")
unit.connect_input_reg("getOutputNeeded")
unit.connect_input_reg("getOutputFilledPercentage")
unit.connect_input_reg("getEnergy")
unit.connect_input_reg("getEnergyNeeded")
unit.connect_input_reg("getEnergyFilledPercentage")
-- holding registers --
-- none
-- check if any calls faulted
local faulted = sps.__p_is_faulted()
sps.__p_clear_fault()
sps.__p_enable_afc()
return unit.interface(), faulted
return unit.interface(), false
end
return sps_rtu

View File

@ -9,12 +9,8 @@ local turbinev_rtu = {}
function turbinev_rtu.new(turbine)
local unit = rtu.init_unit(turbine)
-- disable auto fault clearing
turbine.__p_clear_fault()
turbine.__p_disable_afc()
-- discrete inputs --
unit.connect_di(turbine.isFormed)
unit.connect_di("isFormed")
-- coils --
unit.connect_coil(function () turbine.incrementDumpingMode() end, function () end)
@ -22,44 +18,39 @@ function turbinev_rtu.new(turbine)
-- input registers --
-- multiblock properties
unit.connect_input_reg(turbine.getLength)
unit.connect_input_reg(turbine.getWidth)
unit.connect_input_reg(turbine.getHeight)
unit.connect_input_reg(turbine.getMinPos)
unit.connect_input_reg(turbine.getMaxPos)
unit.connect_input_reg("getLength")
unit.connect_input_reg("getWidth")
unit.connect_input_reg("getHeight")
unit.connect_input_reg("getMinPos")
unit.connect_input_reg("getMaxPos")
-- build properties
unit.connect_input_reg(turbine.getBlades)
unit.connect_input_reg(turbine.getCoils)
unit.connect_input_reg(turbine.getVents)
unit.connect_input_reg(turbine.getDispersers)
unit.connect_input_reg(turbine.getCondensers)
unit.connect_input_reg(turbine.getSteamCapacity)
unit.connect_input_reg(turbine.getMaxEnergy)
unit.connect_input_reg(turbine.getMaxFlowRate)
unit.connect_input_reg(turbine.getMaxProduction)
unit.connect_input_reg(turbine.getMaxWaterOutput)
unit.connect_input_reg("getBlades")
unit.connect_input_reg("getCoils")
unit.connect_input_reg("getVents")
unit.connect_input_reg("getDispersers")
unit.connect_input_reg("getCondensers")
unit.connect_input_reg("getSteamCapacity")
unit.connect_input_reg("getMaxEnergy")
unit.connect_input_reg("getMaxFlowRate")
unit.connect_input_reg("getMaxProduction")
unit.connect_input_reg("getMaxWaterOutput")
-- current state
unit.connect_input_reg(turbine.getFlowRate)
unit.connect_input_reg(turbine.getProductionRate)
unit.connect_input_reg(turbine.getLastSteamInputRate)
unit.connect_input_reg(turbine.getDumpingMode)
unit.connect_input_reg("getFlowRate")
unit.connect_input_reg("getProductionRate")
unit.connect_input_reg("getLastSteamInputRate")
unit.connect_input_reg("getDumpingMode")
-- tanks/containers
unit.connect_input_reg(turbine.getSteam)
unit.connect_input_reg(turbine.getSteamNeeded)
unit.connect_input_reg(turbine.getSteamFilledPercentage)
unit.connect_input_reg(turbine.getEnergy)
unit.connect_input_reg(turbine.getEnergyNeeded)
unit.connect_input_reg(turbine.getEnergyFilledPercentage)
unit.connect_input_reg("getSteam")
unit.connect_input_reg("getSteamNeeded")
unit.connect_input_reg("getSteamFilledPercentage")
unit.connect_input_reg("getEnergy")
unit.connect_input_reg("getEnergyNeeded")
unit.connect_input_reg("getEnergyFilledPercentage")
-- holding registers --
unit.connect_holding_reg(turbine.getDumpingMode, turbine.setDumpingMode)
unit.connect_holding_reg("getDumpingMode", "setDumpingMode")
-- check if any calls faulted
local faulted = turbine.__p_is_faulted()
turbine.__p_clear_fault()
turbine.__p_enable_afc()
return unit.interface(), faulted
return unit.interface(), false
end
return turbinev_rtu

View File

@ -92,6 +92,8 @@ function rtu.init_unit(device)
local insert = table.insert
local stub = function () log.warning("tried to call an RTU function stub") end
---@class rtu_device
local public = {}
@ -113,13 +115,26 @@ function rtu.init_unit(device)
return self.io_count_cache[1], self.io_count_cache[2], self.io_count_cache[3], self.io_count_cache[4]
end
-- pass a function through or generate one to call a function by name from the device
---@param f function|string function or device function name
local function _as_func(f)
if type(f) == "string" then
local name = f
if device then
f = function (...) return device[name](...) end
else f = stub end
end
return f
end
-- discrete inputs: single bit read-only
-- connect discrete input
---@param f function
---@param f function|string function or function name
---@return integer count count of discrete inputs
function protected.connect_di(f)
insert(self.discrete_inputs, { read = f })
insert(self.discrete_inputs, { read = _as_func(f) })
_count_io()
return #self.discrete_inputs
end
@ -135,11 +150,11 @@ function rtu.init_unit(device)
-- coils: single bit read-write
-- connect coil
---@param f_read function
---@param f_write function
---@param f_read function|string function or function name
---@param f_write function|string function or function name
---@return integer count count of coils
function protected.connect_coil(f_read, f_write)
insert(self.coils, { read = f_read, write = f_write })
insert(self.coils, { read = _as_func(f_read), write = _as_func(f_write) })
_count_io()
return #self.coils
end
@ -164,10 +179,10 @@ function rtu.init_unit(device)
-- input registers: multi-bit read-only
-- connect input register
---@param f function
---@param f function|string function or function name
---@return integer count count of input registers
function protected.connect_input_reg(f)
insert(self.input_regs, { read = f })
insert(self.input_regs, { read = _as_func(f) })
_count_io()
return #self.input_regs
end
@ -183,11 +198,11 @@ function rtu.init_unit(device)
-- holding registers: multi-bit read-write
-- connect holding register
---@param f_read function
---@param f_write function
---@param f_read function|string function or function name
---@param f_write function|string function or function name
---@return integer count count of holding registers
function protected.connect_holding_reg(f_read, f_write)
insert(self.holding_regs, { read = f_read, write = f_write })
insert(self.holding_regs, { read = _as_func(f_read), write = _as_func(f_write) })
_count_io()
return #self.holding_regs
end

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.8.0"
local RTU_VERSION = "v1.9.0"
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
@ -342,7 +342,7 @@ local function main()
is_multiblock = true
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
if formed == ppm.ACCESS_FAULT then
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed boiler multiblock"))
return false
@ -357,7 +357,7 @@ local function main()
is_multiblock = true
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
if formed == ppm.ACCESS_FAULT then
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed turbine multiblock"))
return false
@ -377,7 +377,7 @@ local function main()
is_multiblock = true
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
if formed == ppm.ACCESS_FAULT then
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed dynamic tank multiblock"))
return false
@ -391,7 +391,7 @@ local function main()
is_multiblock = true
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
if formed == ppm.ACCESS_FAULT then
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed induction matrix multiblock"))
return false
@ -405,7 +405,7 @@ local function main()
is_multiblock = true
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
if formed == ppm.ACCESS_FAULT then
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed SPS multiblock"))
return false
@ -471,7 +471,8 @@ local function main()
for_message = util.c("reactor ", for_reactor)
end
log.info(util.c("sys_config> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ") [", index, "] for ", for_message))
local index_str = util.trinary(index ~= nil, util.c(" [", index, "]"), "")
log.info(util.c("sys_config> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ")", index_str, " for ", for_message))
rtu_unit.uid = #units

View File

@ -517,82 +517,23 @@ function threads.thread__unit_comms(smem, unit)
-- check if multiblock is still formed if this is a multiblock
if unit.is_multiblock and (util.time_ms() - last_f_check > 250) then
local is_formed = unit.device.isFormed()
last_f_check = util.time_ms()
local is_formed = unit.device.isFormed()
if unit.formed == nil then
unit.formed = is_formed
if is_formed then unit.hw_state = UNIT_HW_STATE.OK end
elseif not unit.formed then
unit.hw_state = UNIT_HW_STATE.UNFORMED
end
if not unit.formed then unit.hw_state = UNIT_HW_STATE.UNFORMED end
if (not unit.formed) and is_formed then
-- newly re-formed
local iface = ppm.get_iface(unit.device)
if iface then
log.info(util.c("unmounting and remounting reformed RTU unit ", detail_name))
ppm.unmount(unit.device)
local type, device = ppm.mount(iface)
local faulted = false
if device ~= nil then
if type == "boilerValve" and unit.type == RTU_UNIT_TYPE.BOILER_VALVE then
-- boiler multiblock
unit.device = device
unit.rtu, faulted = boilerv_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
elseif type == "turbineValve" and unit.type == RTU_UNIT_TYPE.TURBINE_VALVE then
-- turbine multiblock
unit.device = device
unit.rtu, faulted = turbinev_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
elseif type == "dynamicValve" and unit.type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
-- dynamic tank multiblock
unit.device = device
unit.rtu, faulted = dynamicv_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
elseif type == "inductionPort" and unit.type == RTU_UNIT_TYPE.IMATRIX then
-- induction matrix multiblock
unit.device = device
unit.rtu, faulted = imatrix_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
elseif type == "spsPort" and unit.type == RTU_UNIT_TYPE.SPS then
-- SPS multiblock
unit.device = device
unit.rtu, faulted = sps_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
else
log.error("illegal remount of non-multiblock RTU or type change attempted for " .. short_name, true)
end
if unit.formed and faulted then
-- something is still wrong = can't mark as formed yet
unit.formed = false
unit.hw_state = UNIT_HW_STATE.UNFORMED
log.info(util.c("assuming ", unit.name, " is not formed due to PPM faults while initializing"))
else
unit.hw_state = UNIT_HW_STATE.OK
rtu_comms.send_remounted(unit.uid)
end
local type_name = types.rtu_type_to_string(unit.type)
log.info(util.c("reconnected the ", type_name, " on interface ", unit.name))
else
-- fully lost the peripheral now :(
log.error(util.c(unit.name, " lost (failed reconnect)"))
end
else
log.error("failed to get interface of previously connected RTU unit " .. detail_name, true)
end
if (is_formed == true) and not unit.formed then
unit.hw_state = UNIT_HW_STATE.OK
log.info(util.c(detail_name, " is now formed"))
rtu_comms.send_remounted(unit.uid)
elseif (is_formed == false) and unit.formed then
log.warning(util.c(detail_name, " is no longer formed"))
end
unit.formed = is_formed

View File

@ -51,11 +51,13 @@ local function peri_init(iface)
self.device = peripheral.wrap(iface)
end
-- initialization process (re-map)
for key, func in pairs(self.device) do
self.fault_counts[key] = 0
self.device[key] = function (...)
-- create a protected version of a peripheral function call
---@nodiscard
---@param key string function name
---@param func function function
---@return function method protected version of the function
local function protect_peri_function(key, func)
return function (...)
local return_table = table.pack(pcall(func, ...))
local status = return_table[1]
@ -85,20 +87,24 @@ local function peri_init(iface)
count_str = " [" .. self.fault_counts[key] .. " total faults]"
end
log.error(util.c("PPM: protected ", key, "() -> ", result, count_str))
log.error(util.c("PPM: [@", iface, "] protected ", key, "() -> ", result, count_str))
end
self.fault_counts[key] = self.fault_counts[key] + 1
if result == "Terminated" then
ppm_sys.terminate = true
end
if result == "Terminated" then ppm_sys.terminate = true end
return ACCESS_FAULT
return ACCESS_FAULT, result
end
end
end
-- initialization process (re-map)
for key, func in pairs(self.device) do
self.fault_counts[key] = 0
self.device[key] = protect_peri_function(key, func)
end
-- fault management & monitoring functions
local function clear_fault() self.faulted = false end
@ -131,12 +137,21 @@ local function peri_init(iface)
local mt = {
__index = function (_, key)
-- try to find the function in case it was added (multiblock formed)
local funcs = peripheral.wrap(iface)
if (type(funcs) == "table") and (type(funcs[key]) == "function") then
-- add this function then return it
self.device[key] = protect_peri_function(key, funcs[key])
log.info(util.c("PPM: [@", iface, "] initialized previously undefined field ", key, "()"))
return self.device[key]
end
-- function still missing, return an undefined function handler
-- note: code should avoid storing functions for multiblocks and instead try to index them again
return (function ()
-- this will continuously be counting calls here as faults
-- unlike other functions, faults here can't be cleared as it is just not defined
if self.fault_counts[key] == nil then
self.fault_counts[key] = 0
end
if self.fault_counts[key] == nil then self.fault_counts[key] = 0 end
-- function failed
self.faulted = true
@ -151,12 +166,12 @@ local function peri_init(iface)
count_str = " [" .. self.fault_counts[key] .. " total calls]"
end
log.error(util.c("PPM: caught undefined function ", key, "()", count_str))
log.error(util.c("PPM: [@", iface, "] caught undefined function ", key, "()", count_str))
end
self.fault_counts[key] = self.fault_counts[key] + 1
return UNDEFINED_FIELD
return ACCESS_FAULT, UNDEFINED_FIELD
end)
end
}

View File

@ -22,7 +22,7 @@ local t_pack = table.pack
local util = {}
-- scada-common version
util.version = "1.1.19"
util.version = "1.2.0"
util.TICK_TIME_S = 0.05
util.TICK_TIME_MS = 50