mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#367 logic for missing device detection and user-friendly messages
This commit is contained in:
parent
01a1c374ab
commit
12f187f596
@ -285,7 +285,18 @@ function facility.new(config)
|
|||||||
|
|
||||||
-- link an environment detector RTU session
|
-- link an environment detector RTU session
|
||||||
---@param envd unit_session
|
---@param envd unit_session
|
||||||
function public.add_envd(envd) table.insert(self.envd, envd) end
|
---@return boolean linked environment detector accepted
|
||||||
|
function public.add_envd(envd)
|
||||||
|
local fail_code, fail_str = svsessions.check_rtu_id(envd, self.envd, 99)
|
||||||
|
|
||||||
|
if fail_code == 0 then
|
||||||
|
table.insert(self.envd, envd)
|
||||||
|
else
|
||||||
|
log.warning(util.c("FAC: rejected environment detector linking due to failure code ", fail_code, " (", fail_str, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
return fail_code == 0
|
||||||
|
end
|
||||||
|
|
||||||
-- purge devices associated with the given RTU session ID
|
-- purge devices associated with the given RTU session ID
|
||||||
---@param session integer RTU session ID
|
---@param session integer RTU session ID
|
||||||
@ -575,6 +586,22 @@ function facility.new(config)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check which RTUs are connected
|
||||||
|
---@nodiscard
|
||||||
|
function public.check_rtu_conns()
|
||||||
|
local conns = {}
|
||||||
|
|
||||||
|
conns.induction = #self.induction > 0
|
||||||
|
conns.sps = #self.sps > 0
|
||||||
|
|
||||||
|
conns.tanks = {}
|
||||||
|
for i = 1, #self.tanks do
|
||||||
|
conns.tanks[self.tanks[i].get_device_idx()] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
return conns
|
||||||
|
end
|
||||||
|
|
||||||
-- get RTU statuses
|
-- get RTU statuses
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.get_rtu_statuses()
|
function public.get_rtu_statuses()
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
-- RTU ID Check Failure Entry
|
-- RTU ID Check Failure Entry
|
||||||
--
|
--
|
||||||
|
|
||||||
local databus = require("supervisor.databus")
|
|
||||||
|
|
||||||
local style = require("supervisor.panel.style")
|
local style = require("supervisor.panel.style")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
@ -17,9 +15,9 @@ local cpair = core.cpair
|
|||||||
|
|
||||||
-- create an ID check list entry
|
-- create an ID check list entry
|
||||||
---@param parent graphics_element parent
|
---@param parent graphics_element parent
|
||||||
---@param unit unit_session RTU session
|
---@param msg string message
|
||||||
---@param fail_code integer failure code
|
---@param fail_code integer failure code
|
||||||
local function init(parent, unit, fail_code, cmp_id)
|
local function init(parent, msg, fail_code, cmp_id)
|
||||||
local s_hi_box = style.theme.highlight_box
|
local s_hi_box = style.theme.highlight_box
|
||||||
|
|
||||||
local label_fg = style.fp.label_fg
|
local label_fg = style.fp.label_fg
|
||||||
@ -42,17 +40,13 @@ local function init(parent, unit, fail_code, cmp_id)
|
|||||||
TextBox{parent=entry,text="",width=11,fg_bg=cpair(colors.black,colors.yellow)}
|
TextBox{parent=entry,text="",width=11,fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
end
|
end
|
||||||
|
|
||||||
if fail_code ~= 4 and cmp_id then
|
if fail_code == 4 then
|
||||||
local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
TextBox{parent=entry,x=13,y=2,text=msg}
|
||||||
|
else
|
||||||
|
TextBox{parent=entry,x=13,y=2,text="@ C "..cmp_id,alignment=ALIGN.CENTER,width=8,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||||
|
TextBox{parent=entry,x=21,y=2,text=msg}
|
||||||
end
|
end
|
||||||
|
|
||||||
if fail_code ~= 4 and cmp_id then
|
|
||||||
local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
|
||||||
end
|
|
||||||
|
|
||||||
TextBox{parent=entry,x=21,y=2,text="FW:",width=3}
|
|
||||||
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
|
||||||
|
|
||||||
return root
|
return root
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ local data = {
|
|||||||
pdg_entry = nil, ---@type function
|
pdg_entry = nil, ---@type function
|
||||||
chk_entry = nil, ---@type function
|
chk_entry = nil, ---@type function
|
||||||
-- list entries
|
-- list entries
|
||||||
entries = { rtu = {}, pdg = {}, chk = {} }
|
entries = { rtu = {}, pdg = {}, chk = {}, missing = {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- link list boxes
|
-- link list boxes
|
||||||
@ -111,14 +111,15 @@ end
|
|||||||
-- add a device ID check failure entry to the CHK list
|
-- add a device ID check failure entry to the CHK list
|
||||||
---@param unit unit_session RTU session
|
---@param unit unit_session RTU session
|
||||||
---@param fail_code integer failure code
|
---@param fail_code integer failure code
|
||||||
---@param cmp_id integer|nil computer ID if this isn't a 'missing' entry
|
---@param cmp_id integer computer ID
|
||||||
function pgi.create_chk_entry(unit, fail_code, cmp_id)
|
---@param msg string description to show the user
|
||||||
|
function pgi.create_chk_entry(unit, fail_code, cmp_id, msg)
|
||||||
local gw_session = unit.get_session_id()
|
local gw_session = unit.get_session_id()
|
||||||
|
|
||||||
if data.chk_list ~= nil and data.chk_entry ~= nil then
|
if data.chk_list ~= nil and data.chk_entry ~= nil then
|
||||||
if not data.entries.chk[gw_session] then data.entries.chk[gw_session] = {} end
|
if not data.entries.chk[gw_session] then data.entries.chk[gw_session] = {} end
|
||||||
|
|
||||||
local success, result = pcall(data.chk_entry, data.chk_list, unit, fail_code, cmd_id)
|
local success, result = pcall(data.chk_entry, data.chk_list, msg, fail_code, cmp_id)
|
||||||
|
|
||||||
if success then
|
if success then
|
||||||
data.entries.chk[gw_session][unit.get_unit_id()] = result
|
data.entries.chk[gw_session][unit.get_unit_id()] = result
|
||||||
@ -149,4 +150,36 @@ function pgi.delete_chk_entry(unit)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- add a device ID missing entry to the CHK list
|
||||||
|
---@param message string missing device message
|
||||||
|
function pgi.create_missing_entry(message)
|
||||||
|
if data.chk_list ~= nil and data.chk_entry ~= nil then
|
||||||
|
local success, result = pcall(data.chk_entry, data.chk_list, message, 4, -1)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
data.entries.missing[message] = result
|
||||||
|
log.debug(util.c("PGI: created missing CHK entry (", message, ")"))
|
||||||
|
else
|
||||||
|
log.error(util.c("PGI: failed to create missing CHK entry (", result, ")"), true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delete a device ID missing entry from the CHK list
|
||||||
|
---@param message string missing device message
|
||||||
|
function pgi.delete_missing_entry(message)
|
||||||
|
if data.entries.missing[message] ~= nil then
|
||||||
|
local success, result = pcall(data.entries.missing[message].delete)
|
||||||
|
data.entries.missing[message] = nil
|
||||||
|
|
||||||
|
if success then
|
||||||
|
log.debug(util.c("PGI: deleted missing CHK entry \"", message, "\""))
|
||||||
|
else
|
||||||
|
log.error(util.c("PGI: failed to delete missing CHK entry (", result, ")"), true)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.warning(util.c("PGI: tried to delete unknown missing CHK entry \"", message, "\""))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return pgi
|
return pgi
|
||||||
|
@ -150,6 +150,9 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t
|
|||||||
-- get the unit ID
|
-- get the unit ID
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.get_unit_id() return unit_id end
|
function public.get_unit_id() return unit_id end
|
||||||
|
-- get the RTU type
|
||||||
|
---@nodiscard
|
||||||
|
function public.get_unit_type() return advert.type end
|
||||||
-- get the device index
|
-- get the device index
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.get_device_idx() return self.device_index or 0 end
|
function public.get_device_idx() return self.device_index or 0 end
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
|
--
|
||||||
|
-- Supervisor Sessions Handler
|
||||||
|
--
|
||||||
|
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local mqueue = require("scada-common.mqueue")
|
local mqueue = require("scada-common.mqueue")
|
||||||
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local databus = require("supervisor.databus")
|
local databus = require("supervisor.databus")
|
||||||
@ -12,12 +17,13 @@ local pocket = require("supervisor.session.pocket")
|
|||||||
local rtu = require("supervisor.session.rtu")
|
local rtu = require("supervisor.session.rtu")
|
||||||
local svqtypes = require("supervisor.session.svqtypes")
|
local svqtypes = require("supervisor.session.svqtypes")
|
||||||
|
|
||||||
-- Supervisor Sessions Handler
|
local RTU_TYPES = types.RTU_UNIT_TYPE
|
||||||
|
|
||||||
local SV_Q_DATA = svqtypes.SV_Q_DATA
|
local SV_Q_DATA = svqtypes.SV_Q_DATA
|
||||||
|
|
||||||
local PLC_S_CMDS = plc.PLC_S_CMDS
|
local PLC_S_CMDS = plc.PLC_S_CMDS
|
||||||
local PLC_S_DATA = plc.PLC_S_DATA
|
local PLC_S_DATA = plc.PLC_S_DATA
|
||||||
|
|
||||||
local CRD_S_DATA = coordinator.CRD_S_DATA
|
local CRD_S_DATA = coordinator.CRD_S_DATA
|
||||||
|
|
||||||
local svsessions = {}
|
local svsessions = {}
|
||||||
@ -192,18 +198,71 @@ local function _find_session(list, s_addr)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- periodically remove disconnected RTU gateway's RTU ID warnings and update the missing device list
|
||||||
local function _update_dev_dbg()
|
local function _update_dev_dbg()
|
||||||
|
-- remove disconnected units from check failures lists
|
||||||
|
|
||||||
local f = function (unit) return unit.is_connected() end
|
local f = function (unit) return unit.is_connected() end
|
||||||
|
|
||||||
util.filter_table(self.dev_dbg.duplicate, f, pgi.delete_chk_entry)
|
util.filter_table(self.dev_dbg.duplicate, f, pgi.delete_chk_entry)
|
||||||
util.filter_table(self.dev_dbg.out_of_range, f, pgi.delete_chk_entry)
|
util.filter_table(self.dev_dbg.out_of_range, f, pgi.delete_chk_entry)
|
||||||
|
|
||||||
|
-- update missing list
|
||||||
|
|
||||||
local conns = self.dev_dbg.connected
|
local conns = self.dev_dbg.connected
|
||||||
local units = self.facility.get_units()
|
local units = self.facility.get_units()
|
||||||
for i = 1, #units do
|
local rtu_conns = self.facility.check_rtu_conns()
|
||||||
local unit = units[i] ---@type reactor_unit
|
|
||||||
local rtus = unit.check_rtu_conns()
|
|
||||||
|
|
||||||
|
local function report(disconnected, msg)
|
||||||
|
if disconnected then pgi.create_missing_entry(msg) else pgi.delete_missing_entry(msg) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- look for disconnected facility RTUs
|
||||||
|
|
||||||
|
if rtu_conns.induction ~= conns.induction then
|
||||||
|
report(conns.induction, util.c("the facility's induction matrix"))
|
||||||
|
conns.induction = rtu_conns.induction
|
||||||
|
end
|
||||||
|
|
||||||
|
if rtu_conns.sps ~= conns.sps then
|
||||||
|
report(conns.sps, util.c("the facility's SPS"))
|
||||||
|
conns.sps = rtu_conns.sps
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #conns.tanks do
|
||||||
|
if (rtu_conns.tanks[i] or false) ~= conns.tanks[i] then
|
||||||
|
report(conns.tanks[i], util.c("the facility's #", i, " dynamic tank"))
|
||||||
|
conns.tanks[i] = rtu_conns.tanks[i] or false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- look for disconnected unit RTUs
|
||||||
|
|
||||||
|
for u = 1, #units do
|
||||||
|
local u_conns = conns.units[u]
|
||||||
|
|
||||||
|
rtu_conns = units[u].check_rtu_conns()
|
||||||
|
|
||||||
|
for i = 1, #u_conns.boilers do
|
||||||
|
if (rtu_conns.boilers[i] or false) ~= u_conns.boilers[i] then
|
||||||
|
report(u_conns.boilers[i], util.c("unit ", u, "'s #", i, " boiler"))
|
||||||
|
u_conns.boilers[i] = rtu_conns.boilers[i] or false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #u_conns.turbines do
|
||||||
|
if (rtu_conns.turbines[i] or false) ~= u_conns.turbines[i] then
|
||||||
|
report(u_conns.turbines[i], util.c("unit ", u, "'s #", i, " turbine"))
|
||||||
|
u_conns.turbines[i] = rtu_conns.turbines[i] or false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #u_conns.tanks do
|
||||||
|
if (rtu_conns.tanks[i] or false) ~= u_conns.tanks[i] then
|
||||||
|
report(u_conns.tanks[i], util.c("unit ", u, "'s dynamic tank"))
|
||||||
|
u_conns.tanks[i] = rtu_conns.tanks[i] or false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -211,6 +270,7 @@ end
|
|||||||
|
|
||||||
--#region PUBLIC FUNCTIONS
|
--#region PUBLIC FUNCTIONS
|
||||||
|
|
||||||
|
-- on attempted link of an RTU to a facility or unit object, verify its ID and report a problem if it can't be accepted
|
||||||
---@param unit unit_session RTU session
|
---@param unit unit_session RTU session
|
||||||
---@param list table table of RTU sessions
|
---@param list table table of RTU sessions
|
||||||
---@param max integer max of this type of RTU
|
---@param max integer max of this type of RTU
|
||||||
@ -240,7 +300,7 @@ function svsessions.check_rtu_id(unit, list, max)
|
|||||||
|
|
||||||
-- add to the list for the user
|
-- add to the list for the user
|
||||||
if fail_code > 0 and fail_code ~= 3 then
|
if fail_code > 0 and fail_code ~= 3 then
|
||||||
local cmp_id
|
local cmp_id = -1
|
||||||
|
|
||||||
for i = 1, #self.sessions.rtu do
|
for i = 1, #self.sessions.rtu do
|
||||||
if self.sessions.rtu[i].instance.get_id() == unit.get_session_id() then
|
if self.sessions.rtu[i].instance.get_id() == unit.get_session_id() then
|
||||||
@ -249,7 +309,42 @@ function svsessions.check_rtu_id(unit, list, max)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
pgi.create_chk_entry(unit, fail_code, cmp_id)
|
local r_id = unit.get_reactor()
|
||||||
|
local idx = unit.get_device_idx()
|
||||||
|
local type = unit.get_unit_type()
|
||||||
|
local msg = "? (error)"
|
||||||
|
|
||||||
|
if r_id == 0 then
|
||||||
|
msg = "the facility's "
|
||||||
|
|
||||||
|
if type == RTU_TYPES.IMATRIX then
|
||||||
|
msg = msg .. "induction matrix"
|
||||||
|
elseif type == RTU_TYPES.SPS then
|
||||||
|
msg = msg .. "SPS"
|
||||||
|
elseif type == RTU_TYPES.DYNAMIC_VALVE then
|
||||||
|
msg = util.c(msg, "#", idx, " dynamic tank")
|
||||||
|
elseif type == RTU_TYPES.ENV_DETECTOR then
|
||||||
|
msg = util.c(msg, "#", idx, " environment detector")
|
||||||
|
else
|
||||||
|
msg = msg .. " ? (error)"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
msg = util.c("unit ", r_id, "'s ")
|
||||||
|
|
||||||
|
if type == RTU_TYPES.BOILER_VALVE then
|
||||||
|
msg = util.c(msg, "#", idx, " boiler")
|
||||||
|
elseif type == RTU_TYPES.TURBINE_VALVE then
|
||||||
|
msg = util.c(msg, "#", idx, " turbine")
|
||||||
|
elseif type == RTU_TYPES.DYNAMIC_VALVE then
|
||||||
|
msg = msg .. "dynamic tank"
|
||||||
|
elseif type == RTU_TYPES.ENV_DETECTOR then
|
||||||
|
msg = util.c(msg, "#", idx, " environment detector")
|
||||||
|
else
|
||||||
|
msg = msg .. " ? (error)"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
pgi.create_chk_entry(unit, fail_code, cmp_id, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
return fail_code, fail_str
|
return fail_code, fail_str
|
||||||
@ -266,10 +361,29 @@ function svsessions.init(nic, fp_ok, config, facility)
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.facility = facility
|
self.facility = facility
|
||||||
|
|
||||||
-- initialize connection tracking table
|
-- initialize connection tracking table by setting all expected devices to true
|
||||||
self.dev_dbg.connected = { imatrix = nil, sps = nil, tanks = {}, units = {} }
|
-- if connections are missing, missing entries will then be created on the next update
|
||||||
|
|
||||||
|
self.dev_dbg.connected = { induction = true, sps = true, tanks = {}, units = {} }
|
||||||
|
|
||||||
|
local cool_conf = facility.get_cooling_conf()
|
||||||
|
|
||||||
|
for i = 1, #cool_conf.fac_tank_list do
|
||||||
|
self.dev_dbg.connected.tanks[i] = true
|
||||||
|
end
|
||||||
|
|
||||||
for i = 1, config.UnitCount do
|
for i = 1, config.UnitCount do
|
||||||
self.dev_dbg.connected.units[i] = { boilers = {}, turbines = {}, tanks = {} }
|
local r_cool = cool_conf.r_cool[i]
|
||||||
|
local conns = { boilers = {}, turbines = {}, tanks = {} }
|
||||||
|
|
||||||
|
for b = 1, r_cool.BoilerCount do conns.boilers[b] = true end
|
||||||
|
for t = 1, r_cool.TurbineCount do conns.boilers[t] = true end
|
||||||
|
|
||||||
|
if r_cool.TankConnection and cool_conf.fac_tank_defs[i] == 1 then
|
||||||
|
conns.tanks[1] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
self.dev_dbg.connected.units[i] = conns
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
|
|
||||||
-- link an environment detector RTU session
|
-- link an environment detector RTU session
|
||||||
---@param envd unit_session
|
---@param envd unit_session
|
||||||
---@return boolean linked environment detector accepted (max 1)
|
---@return boolean linked environment detector accepted
|
||||||
function public.add_envd(envd)
|
function public.add_envd(envd)
|
||||||
local fail_code, fail_str = svsessions.check_rtu_id(envd, self.envd, 99)
|
local fail_code, fail_str = svsessions.check_rtu_id(envd, self.envd, 99)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user