2022-06-25 17:38:31 +00:00
|
|
|
--
|
|
|
|
-- Publisher-Subscriber Interconnect Layer
|
|
|
|
--
|
|
|
|
|
2023-05-13 12:50:13 +00:00
|
|
|
local util = require("scada-common.util")
|
|
|
|
|
2022-06-25 17:38:31 +00:00
|
|
|
local psil = {}
|
|
|
|
|
|
|
|
-- instantiate a new PSI layer
|
2023-02-21 15:31:05 +00:00
|
|
|
---@nodiscard
|
2022-06-25 17:38:31 +00:00
|
|
|
function psil.create()
|
|
|
|
local self = {
|
|
|
|
ic = {}
|
|
|
|
}
|
|
|
|
|
|
|
|
-- allocate a new interconnect field
|
|
|
|
---@key string data key
|
|
|
|
local function alloc(key)
|
2022-09-08 16:19:19 +00:00
|
|
|
self.ic[key] = { subscribers = {}, value = nil }
|
2022-06-25 17:38:31 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
---@class psil
|
|
|
|
local public = {}
|
|
|
|
|
2023-02-21 15:31:05 +00:00
|
|
|
-- subscribe to a data object in the interconnect<br>
|
2022-09-08 16:19:19 +00:00
|
|
|
-- will call func() right away if a value is already avaliable
|
2022-06-25 17:38:31 +00:00
|
|
|
---@param key string data key
|
|
|
|
---@param func function function to call on change
|
|
|
|
function public.subscribe(key, func)
|
2022-09-08 16:19:19 +00:00
|
|
|
-- allocate new key if not found or notify if value is found
|
|
|
|
if self.ic[key] == nil then
|
|
|
|
alloc(key)
|
|
|
|
elseif self.ic[key].value ~= nil then
|
|
|
|
func(self.ic[key].value)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- subscribe to key
|
2022-06-25 17:38:31 +00:00
|
|
|
table.insert(self.ic[key].subscribers, { notify = func })
|
|
|
|
end
|
|
|
|
|
2023-05-13 12:50:13 +00:00
|
|
|
-- unsubscribe a function from a given key
|
|
|
|
---@param key string data key
|
|
|
|
---@param func function function to unsubscribe
|
|
|
|
function public.unsubscribe(key, func)
|
|
|
|
if self.ic[key] ~= nil then
|
|
|
|
util.filter_table(self.ic[key].subscribers, function (s) return s.notify ~= func end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-06-25 17:38:31 +00:00
|
|
|
-- publish data to a given key, passing it to all subscribers if it has changed
|
|
|
|
---@param key string data key
|
|
|
|
---@param value any data value
|
|
|
|
function public.publish(key, value)
|
|
|
|
if self.ic[key] == nil then alloc(key) end
|
|
|
|
|
|
|
|
if self.ic[key].value ~= value then
|
|
|
|
for i = 1, #self.ic[key].subscribers do
|
|
|
|
self.ic[key].subscribers[i].notify(value)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
self.ic[key].value = value
|
|
|
|
end
|
|
|
|
|
2023-04-08 04:38:46 +00:00
|
|
|
-- publish a toggled boolean value to a given key, passing it to all subscribers if it has changed<br>
|
|
|
|
-- this is intended to be used to toggle boolean indicators such as heartbeats without extra state variables
|
|
|
|
---@param key string data key
|
|
|
|
function public.toggle(key)
|
|
|
|
if self.ic[key] == nil then alloc(key) end
|
|
|
|
|
|
|
|
self.ic[key].value = self.ic[key].value == false
|
|
|
|
|
|
|
|
for i = 1, #self.ic[key].subscribers do
|
|
|
|
self.ic[key].subscribers[i].notify(self.ic[key].value)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-05-13 12:50:13 +00:00
|
|
|
-- clear the contents of the interconnect
|
|
|
|
function public.purge() self.ic = nil end
|
|
|
|
|
2022-06-25 17:38:31 +00:00
|
|
|
return public
|
|
|
|
end
|
|
|
|
|
|
|
|
return psil
|