2022-05-04 17:37:01 +00:00
|
|
|
local log = require("scada-common.log")
|
2022-03-10 19:09:21 +00:00
|
|
|
|
|
|
|
--
|
|
|
|
-- Protected Peripheral Manager
|
|
|
|
--
|
|
|
|
|
2022-05-10 21:06:27 +00:00
|
|
|
---@class ppm
|
2022-05-04 17:37:01 +00:00
|
|
|
local ppm = {}
|
|
|
|
|
2022-05-10 21:06:27 +00:00
|
|
|
local ACCESS_FAULT = nil ---@type nil
|
2022-05-04 17:37:01 +00:00
|
|
|
|
|
|
|
ppm.ACCESS_FAULT = ACCESS_FAULT
|
2022-04-18 01:12:25 +00:00
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
----------------------------
|
|
|
|
-- PRIVATE DATA/FUNCTIONS --
|
|
|
|
----------------------------
|
|
|
|
|
2022-05-19 14:35:56 +00:00
|
|
|
local REPORT_FREQUENCY = 20 -- log every 20 faults per function
|
|
|
|
|
2022-04-24 16:04:31 +00:00
|
|
|
local _ppm_sys = {
|
2022-04-02 15:45:43 +00:00
|
|
|
mounts = {},
|
2022-04-18 01:12:25 +00:00
|
|
|
auto_cf = false,
|
|
|
|
faulted = false,
|
2022-05-19 13:19:51 +00:00
|
|
|
last_fault = "",
|
2022-04-18 14:31:24 +00:00
|
|
|
terminate = false,
|
2022-04-02 15:45:43 +00:00
|
|
|
mute = false
|
2022-03-10 19:12:07 +00:00
|
|
|
}
|
|
|
|
|
2022-05-10 21:06:27 +00:00
|
|
|
-- wrap peripheral calls with lua protected call as we don't want a disconnect to crash a program
|
|
|
|
---
|
|
|
|
---also provides peripheral-specific fault checks (auto-clear fault defaults to true)
|
|
|
|
---
|
|
|
|
---assumes iface is a valid peripheral
|
|
|
|
---@param iface string CC peripheral interface
|
2022-04-24 16:04:31 +00:00
|
|
|
local peri_init = function (iface)
|
|
|
|
local self = {
|
|
|
|
faulted = false,
|
2022-05-19 13:19:51 +00:00
|
|
|
last_fault = "",
|
2022-05-19 14:35:56 +00:00
|
|
|
fault_counts = {},
|
2022-04-24 16:04:31 +00:00
|
|
|
auto_cf = true,
|
|
|
|
type = peripheral.getType(iface),
|
|
|
|
device = peripheral.wrap(iface)
|
|
|
|
}
|
|
|
|
|
|
|
|
-- initialization process (re-map)
|
|
|
|
|
|
|
|
for key, func in pairs(self.device) do
|
2022-05-19 14:35:56 +00:00
|
|
|
self.fault_counts[key] = 0
|
2022-04-24 16:04:31 +00:00
|
|
|
self.device[key] = function (...)
|
2022-03-10 19:12:07 +00:00
|
|
|
local status, result = pcall(func, ...)
|
|
|
|
|
|
|
|
if status then
|
2022-04-18 01:12:25 +00:00
|
|
|
-- auto fault clear
|
|
|
|
if self.auto_cf then self.faulted = false end
|
2022-04-24 16:04:31 +00:00
|
|
|
if _ppm_sys.auto_cf then _ppm_sys.faulted = false end
|
2022-05-19 14:35:56 +00:00
|
|
|
|
|
|
|
self.fault_counts[key] = 0
|
|
|
|
|
2022-04-24 16:04:31 +00:00
|
|
|
return result
|
2022-03-10 19:12:07 +00:00
|
|
|
else
|
|
|
|
-- function failed
|
2022-04-18 01:12:25 +00:00
|
|
|
self.faulted = true
|
2022-05-19 13:19:51 +00:00
|
|
|
self.last_fault = result
|
|
|
|
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.faulted = true
|
2022-05-19 13:19:51 +00:00
|
|
|
_ppm_sys.last_fault = result
|
2022-04-18 14:31:24 +00:00
|
|
|
|
2022-05-19 14:35:56 +00:00
|
|
|
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 faults]"
|
|
|
|
end
|
|
|
|
|
|
|
|
log.error("PPM: protected " .. key .. "() -> " .. result .. count_str)
|
2022-04-02 15:45:43 +00:00
|
|
|
end
|
2022-04-18 14:31:24 +00:00
|
|
|
|
2022-05-19 14:35:56 +00:00
|
|
|
self.fault_counts[key] = self.fault_counts[key] + 1
|
|
|
|
|
2022-04-18 14:31:24 +00:00
|
|
|
if result == "Terminated" then
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.terminate = true
|
2022-04-18 14:31:24 +00:00
|
|
|
end
|
|
|
|
|
2022-04-18 01:12:25 +00:00
|
|
|
return ACCESS_FAULT
|
2022-03-10 19:09:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2022-04-24 16:04:31 +00:00
|
|
|
|
|
|
|
-- fault management functions
|
|
|
|
|
|
|
|
local clear_fault = function () self.faulted = false end
|
2022-05-19 13:19:51 +00:00
|
|
|
local get_last_fault = function () return self.last_fault end
|
2022-04-24 16:04:31 +00:00
|
|
|
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
|
2022-05-19 13:19:51 +00:00
|
|
|
self.device.__p_last_fault = get_last_fault
|
2022-04-24 16:04:31 +00:00
|
|
|
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
|
|
|
|
}
|
2022-03-10 19:12:07 +00:00
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
----------------------
|
|
|
|
-- PUBLIC FUNCTIONS --
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
-- REPORTING --
|
|
|
|
|
2022-04-02 15:45:43 +00:00
|
|
|
-- silence error prints
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.disable_reporting = function ()
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.mute = true
|
2022-04-02 15:45:43 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- allow error prints
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.enable_reporting = function ()
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.mute = false
|
2022-04-02 15:45:43 +00:00
|
|
|
end
|
|
|
|
|
2022-04-18 01:12:25 +00:00
|
|
|
-- FAULT MEMORY --
|
|
|
|
|
|
|
|
-- enable automatically clearing fault flag
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.enable_afc = function ()
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.auto_cf = true
|
2022-04-18 01:12:25 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- disable automatically clearing fault flag
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.disable_afc = function ()
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.auto_cf = false
|
2022-04-18 01:12:25 +00:00
|
|
|
end
|
|
|
|
|
2022-05-19 13:19:51 +00:00
|
|
|
-- clear fault flag
|
|
|
|
ppm.clear_fault = function ()
|
|
|
|
_ppm_sys.faulted = false
|
|
|
|
end
|
|
|
|
|
2022-04-18 01:12:25 +00:00
|
|
|
-- check fault flag
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.is_faulted = function ()
|
2022-04-24 16:04:31 +00:00
|
|
|
return _ppm_sys.faulted
|
2022-04-18 01:12:25 +00:00
|
|
|
end
|
|
|
|
|
2022-05-19 13:19:51 +00:00
|
|
|
-- get the last fault message
|
|
|
|
ppm.get_last_fault = function ()
|
|
|
|
return _ppm_sys.last_fault
|
2022-04-18 01:12:25 +00:00
|
|
|
end
|
|
|
|
|
2022-04-18 14:31:24 +00:00
|
|
|
-- TERMINATION --
|
|
|
|
|
|
|
|
-- if a caught error was a termination request
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.should_terminate = function ()
|
2022-04-24 16:04:31 +00:00
|
|
|
return _ppm_sys.terminate
|
2022-04-18 14:31:24 +00:00
|
|
|
end
|
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
-- MOUNTING --
|
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
-- mount all available peripherals (clears mounts first)
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.mount_all = function ()
|
2022-03-10 19:12:07 +00:00
|
|
|
local ifaces = peripheral.getNames()
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.mounts = {}
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
for i = 1, #ifaces do
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.mounts[ifaces[i]] = peri_init(ifaces[i])
|
2022-04-05 21:29:27 +00:00
|
|
|
|
2022-05-04 17:37:01 +00:00
|
|
|
log.info("PPM: found a " .. _ppm_sys.mounts[ifaces[i]].type .. " (" .. ifaces[i] .. ")")
|
2022-03-10 19:09:21 +00:00
|
|
|
end
|
2022-03-10 19:21:03 +00:00
|
|
|
|
|
|
|
if #ifaces == 0 then
|
2022-05-04 17:37:01 +00:00
|
|
|
log.warning("PPM: mount_all() -> no devices found")
|
2022-03-10 19:21:03 +00:00
|
|
|
end
|
2022-03-10 19:12:07 +00:00
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
-- mount a particular device
|
2022-05-10 21:06:27 +00:00
|
|
|
---@param iface string CC peripheral interface
|
|
|
|
---@return string|nil type, table|nil device
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.mount = function (iface)
|
2022-03-10 19:12:07 +00:00
|
|
|
local ifaces = peripheral.getNames()
|
|
|
|
local pm_dev = nil
|
2022-04-24 16:04:31 +00:00
|
|
|
local pm_type = nil
|
2022-03-10 19:12:07 +00:00
|
|
|
|
|
|
|
for i = 1, #ifaces do
|
2022-04-05 21:29:27 +00:00
|
|
|
if iface == ifaces[i] then
|
2022-04-24 16:04:31 +00:00
|
|
|
_ppm_sys.mounts[iface] = peri_init(iface)
|
2022-03-10 19:12:07 +00:00
|
|
|
|
2022-04-24 16:04:31 +00:00
|
|
|
pm_type = _ppm_sys.mounts[iface].type
|
|
|
|
pm_dev = _ppm_sys.mounts[iface].dev
|
2022-05-22 21:57:24 +00:00
|
|
|
|
|
|
|
log.info("PPM: mount(" .. iface .. ") -> found a " .. pm_type)
|
2022-03-10 19:12:07 +00:00
|
|
|
break
|
2022-03-10 19:09:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-04-24 16:04:31 +00:00
|
|
|
return pm_type, pm_dev
|
2022-03-10 19:12:07 +00:00
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
-- handle peripheral_detach event
|
2022-05-10 21:06:27 +00:00
|
|
|
---@param iface string CC peripheral interface
|
|
|
|
---@return string|nil type, table|nil device
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.handle_unmount = function (iface)
|
2022-05-10 21:06:27 +00:00
|
|
|
local pm_dev = nil
|
|
|
|
local pm_type = nil
|
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
-- what got disconnected?
|
2022-04-24 16:04:31 +00:00
|
|
|
local lost_dev = _ppm_sys.mounts[iface]
|
2022-04-22 14:58:18 +00:00
|
|
|
|
|
|
|
if lost_dev then
|
2022-05-10 21:06:27 +00:00
|
|
|
pm_type = lost_dev.type
|
|
|
|
pm_dev = lost_dev.dev
|
|
|
|
|
|
|
|
log.warning("PPM: lost device " .. pm_type .. " mounted to " .. iface)
|
2022-04-22 14:58:18 +00:00
|
|
|
else
|
2022-05-04 17:37:01 +00:00
|
|
|
log.error("PPM: lost device unknown to the PPM mounted to " .. iface)
|
2022-04-22 14:58:18 +00:00
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-05-10 21:06:27 +00:00
|
|
|
return pm_type, pm_dev
|
2022-03-10 19:12:07 +00:00
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
-- GENERAL ACCESSORS --
|
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
-- list all available peripherals
|
2022-05-10 21:06:27 +00:00
|
|
|
---@return table names
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.list_avail = function ()
|
2022-03-10 19:12:07 +00:00
|
|
|
return peripheral.getNames()
|
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
-- list mounted peripherals
|
2022-05-10 21:06:27 +00:00
|
|
|
---@return table mounts
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.list_mounts = function ()
|
2022-04-24 16:04:31 +00:00
|
|
|
return _ppm_sys.mounts
|
2022-03-10 19:12:07 +00:00
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
-- get a mounted peripheral by side/interface
|
2022-05-10 21:06:27 +00:00
|
|
|
---@param iface string CC peripheral interface
|
|
|
|
---@return table|nil device function table
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.get_periph = function (iface)
|
2022-04-24 16:04:31 +00:00
|
|
|
if _ppm_sys.mounts[iface] then
|
|
|
|
return _ppm_sys.mounts[iface].dev
|
2022-04-22 14:21:15 +00:00
|
|
|
else return nil end
|
2022-03-10 19:12:07 +00:00
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-03-15 15:58:22 +00:00
|
|
|
-- get a mounted peripheral type by side/interface
|
2022-05-10 21:06:27 +00:00
|
|
|
---@param iface string CC peripheral interface
|
|
|
|
---@return string|nil type
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.get_type = function (iface)
|
2022-04-24 16:04:31 +00:00
|
|
|
if _ppm_sys.mounts[iface] then
|
|
|
|
return _ppm_sys.mounts[iface].type
|
2022-04-22 14:21:15 +00:00
|
|
|
else return nil end
|
2022-03-15 15:58:22 +00:00
|
|
|
end
|
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
-- get all mounted peripherals by type
|
2022-05-10 21:06:27 +00:00
|
|
|
---@param name string type name
|
|
|
|
---@return table devices device function tables
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.get_all_devices = function (name)
|
2022-04-07 15:44:17 +00:00
|
|
|
local devices = {}
|
|
|
|
|
2022-05-22 21:57:24 +00:00
|
|
|
for _, data in pairs(_ppm_sys.mounts) do
|
2022-04-07 15:44:17 +00:00
|
|
|
if data.type == name then
|
|
|
|
table.insert(devices, data.dev)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return devices
|
|
|
|
end
|
|
|
|
|
|
|
|
-- get a mounted peripheral by type (if multiple, returns the first)
|
2022-05-10 21:06:27 +00:00
|
|
|
---@param name string type name
|
|
|
|
---@return table|nil device function table
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.get_device = function (name)
|
2022-03-10 19:12:07 +00:00
|
|
|
local device = nil
|
|
|
|
|
2022-04-24 16:04:31 +00:00
|
|
|
for side, data in pairs(_ppm_sys.mounts) do
|
2022-03-10 19:12:07 +00:00
|
|
|
if data.type == name then
|
2022-04-05 21:29:27 +00:00
|
|
|
device = data.dev
|
2022-03-10 19:12:07 +00:00
|
|
|
break
|
2022-03-10 19:09:21 +00:00
|
|
|
end
|
|
|
|
end
|
2022-05-10 15:35:52 +00:00
|
|
|
|
2022-03-10 19:12:07 +00:00
|
|
|
return device
|
|
|
|
end
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
-- SPECIFIC DEVICE ACCESSORS --
|
2022-03-10 19:09:21 +00:00
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
-- get the fission reactor (if multiple, returns the first)
|
2022-05-10 21:06:27 +00:00
|
|
|
---@return table|nil reactor function table
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.get_fission_reactor = function ()
|
|
|
|
return ppm.get_device("fissionReactor")
|
2022-04-07 15:44:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- get the wireless modem (if multiple, returns the first)
|
2022-05-10 21:06:27 +00:00
|
|
|
---@return table|nil modem function table
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.get_wireless_modem = function ()
|
2022-04-07 15:44:17 +00:00
|
|
|
local w_modem = nil
|
|
|
|
|
2022-05-22 21:57:24 +00:00
|
|
|
for _, device in pairs(_ppm_sys.mounts) do
|
2022-04-07 15:44:17 +00:00
|
|
|
if device.type == "modem" and device.dev.isWireless() then
|
|
|
|
w_modem = device.dev
|
|
|
|
break
|
2022-03-10 19:09:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-04-07 15:44:17 +00:00
|
|
|
return w_modem
|
|
|
|
end
|
|
|
|
|
|
|
|
-- list all connected monitors
|
2022-05-10 21:06:27 +00:00
|
|
|
---@return table monitors
|
2022-05-04 17:37:01 +00:00
|
|
|
ppm.list_monitors = function ()
|
|
|
|
return ppm.get_all_devices("monitor")
|
2022-03-10 19:09:21 +00:00
|
|
|
end
|
2022-05-04 17:37:01 +00:00
|
|
|
|
|
|
|
return ppm
|