cc-mek-scada/scada-common/ppm.lua
2022-05-10 17:06:27 -04:00

294 lines
7.0 KiB
Lua

local log = require("scada-common.log")
--
-- Protected Peripheral Manager
--
---@class ppm
local ppm = {}
local ACCESS_FAULT = nil ---@type nil
ppm.ACCESS_FAULT = ACCESS_FAULT
----------------------------
-- PRIVATE DATA/FUNCTIONS --
----------------------------
local _ppm_sys = {
mounts = {},
auto_cf = false,
faulted = false,
terminate = false,
mute = false
}
-- 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
local peri_init = function (iface)
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, ...)
if status then
-- auto fault clear
if self.auto_cf then self.faulted = false end
if _ppm_sys.auto_cf then _ppm_sys.faulted = false end
return result
else
-- function failed
self.faulted = true
_ppm_sys.faulted = true
if not _ppm_sys.mute then
log.error("PPM: protected " .. key .. "() -> " .. result)
end
if result == "Terminated" then
_ppm_sys.terminate = true
end
return ACCESS_FAULT
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
----------------------
-- PUBLIC FUNCTIONS --
----------------------
-- REPORTING --
-- silence error prints
ppm.disable_reporting = function ()
_ppm_sys.mute = true
end
-- allow error prints
ppm.enable_reporting = function ()
_ppm_sys.mute = false
end
-- FAULT MEMORY --
-- enable automatically clearing fault flag
ppm.enable_afc = function ()
_ppm_sys.auto_cf = true
end
-- disable automatically clearing fault flag
ppm.disable_afc = function ()
_ppm_sys.auto_cf = false
end
-- check fault flag
ppm.is_faulted = function ()
return _ppm_sys.faulted
end
-- clear fault flag
ppm.clear_fault = function ()
_ppm_sys.faulted = false
end
-- TERMINATION --
-- if a caught error was a termination request
ppm.should_terminate = function ()
return _ppm_sys.terminate
end
-- MOUNTING --
-- mount all available peripherals (clears mounts first)
ppm.mount_all = function ()
local ifaces = peripheral.getNames()
_ppm_sys.mounts = {}
for i = 1, #ifaces do
_ppm_sys.mounts[ifaces[i]] = peri_init(ifaces[i])
log.info("PPM: found a " .. _ppm_sys.mounts[ifaces[i]].type .. " (" .. ifaces[i] .. ")")
end
if #ifaces == 0 then
log.warning("PPM: mount_all() -> no devices found")
end
end
-- mount a particular device
---@param iface string CC peripheral interface
---@return string|nil type, table|nil device
ppm.mount = function (iface)
local ifaces = peripheral.getNames()
local pm_dev = nil
local pm_type = nil
for i = 1, #ifaces do
if iface == ifaces[i] then
log.info("PPM: mount(" .. iface .. ") -> found a " .. peripheral.getType(iface))
_ppm_sys.mounts[iface] = peri_init(iface)
pm_type = _ppm_sys.mounts[iface].type
pm_dev = _ppm_sys.mounts[iface].dev
break
end
end
return pm_type, pm_dev
end
-- handle peripheral_detach event
---@param iface string CC peripheral interface
---@return string|nil type, table|nil device
ppm.handle_unmount = function (iface)
local pm_dev = nil
local pm_type = nil
-- what got disconnected?
local lost_dev = _ppm_sys.mounts[iface]
if lost_dev then
pm_type = lost_dev.type
pm_dev = lost_dev.dev
log.warning("PPM: lost device " .. pm_type .. " mounted to " .. iface)
else
log.error("PPM: lost device unknown to the PPM mounted to " .. iface)
end
return pm_type, pm_dev
end
-- GENERAL ACCESSORS --
-- list all available peripherals
---@return table names
ppm.list_avail = function ()
return peripheral.getNames()
end
-- list mounted peripherals
---@return table mounts
ppm.list_mounts = function ()
return _ppm_sys.mounts
end
-- get a mounted peripheral by side/interface
---@param iface string CC peripheral interface
---@return table|nil device function table
ppm.get_periph = function (iface)
if _ppm_sys.mounts[iface] then
return _ppm_sys.mounts[iface].dev
else return nil end
end
-- get a mounted peripheral type by side/interface
---@param iface string CC peripheral interface
---@return string|nil type
ppm.get_type = function (iface)
if _ppm_sys.mounts[iface] then
return _ppm_sys.mounts[iface].type
else return nil end
end
-- get all mounted peripherals by type
---@param name string type name
---@return table devices device function tables
ppm.get_all_devices = function (name)
local devices = {}
for side, data in pairs(_ppm_sys.mounts) do
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)
---@param name string type name
---@return table|nil device function table
ppm.get_device = function (name)
local device = nil
for side, data in pairs(_ppm_sys.mounts) do
if data.type == name then
device = data.dev
break
end
end
return device
end
-- SPECIFIC DEVICE ACCESSORS --
-- get the fission reactor (if multiple, returns the first)
---@return table|nil reactor function table
ppm.get_fission_reactor = function ()
return ppm.get_device("fissionReactor")
end
-- get the wireless modem (if multiple, returns the first)
---@return table|nil modem function table
ppm.get_wireless_modem = function ()
local w_modem = nil
for side, device in pairs(_ppm_sys.mounts) do
if device.type == "modem" and device.dev.isWireless() then
w_modem = device.dev
break
end
end
return w_modem
end
-- list all connected monitors
---@return table monitors
ppm.list_monitors = function ()
return ppm.get_all_devices("monitor")
end
return ppm