#229 element PSIL register/deletion, changes for RTU to use new PSIL register

This commit is contained in:
Mikayla Fischler
2023-05-13 08:50:13 -04:00
parent 36bd2c5e08
commit b2115fd077
6 changed files with 115 additions and 32 deletions

View File

@ -3,6 +3,7 @@
-- --
local core = require("graphics.core") local core = require("graphics.core")
local log = require("scada-common.log")
local element = {} local element = {}
@ -46,12 +47,18 @@ local element = {}
---|colormap_args ---|colormap_args
---|displaybox_args ---|displaybox_args
---|div_args ---|div_args
---|listbox_args
---|multipane_args ---|multipane_args
---|pipenet_args ---|pipenet_args
---|rectangle_args ---|rectangle_args
---|textbox_args ---|textbox_args
---|tiling_args ---|tiling_args
---@class element_subscription
---@field ps psil ps used
---@field key string data key
---@field func function callback
-- a base graphics element, should not be created on its own -- a base graphics element, should not be created on its own
---@nodiscard ---@nodiscard
---@param args graphics_args arguments ---@param args graphics_args arguments
@ -66,6 +73,7 @@ function element.new(args)
bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1 }, ---@class element_bounds bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1 }, ---@class element_bounds
next_y = 1, next_y = 1,
children = {}, children = {},
subscriptions = {},
mt = {} mt = {}
} }
@ -183,6 +191,17 @@ function element.new(args)
-- luacheck: push ignore -- luacheck: push ignore
---@diagnostic disable: unused-local, unused-vararg ---@diagnostic disable: unused-local, unused-vararg
-- dynamically insert a child element
---@param id string|integer element identifier
---@param elem graphics_element element
function protected.insert(id, elem)
end
-- dynamically remove a child element
---@param id string|integer element identifier
function protected.remove(id)
end
-- handle a mouse event -- handle a mouse event
---@param event mouse_interaction mouse interaction event ---@param event mouse_interaction mouse interaction event
function protected.handle_mouse(event) function protected.handle_mouse(event)
@ -281,7 +300,25 @@ function element.new(args)
---@nodiscard ---@nodiscard
function public.window() return protected.window end function public.window() return protected.window end
-- CHILD ELEMENTS -- -- delete this element (hide and unsubscribe from PSIL)
function public.delete()
-- hide + stop animations
public.hide()
-- unsubscribe from PSIL
for i = 1, #self.subscriptions do
local s = self.subscriptions[i] ---@type element_subscription
s.ps.unsubscribe(s.key, s.func)
end
-- delete all children
for k, v in pairs(self.children) do
v.delete()
self.children[k] = nil
end
end
-- ELEMENT TREE --
-- add a child element -- add a child element
---@nodiscard ---@nodiscard
@ -311,12 +348,18 @@ function element.new(args)
-- get a child element -- get a child element
---@nodiscard ---@nodiscard
---@param id element_id
---@return graphics_element ---@return graphics_element
function public.get_child(key) return self.children[key] end function public.get_child(id) return self.children[id] end
-- remove child -- remove a child element
---@param key string|integer ---@param id element_id
function public.remove(key) self.children[key] = nil end function public.remove(id)
if self.children[id] ~= nil then
self.children[id].delete()
self.children[id] = nil
end
end
-- attempt to get a child element by ID (does not include this element itself) -- attempt to get a child element by ID (does not include this element itself)
---@nodiscard ---@nodiscard
@ -335,6 +378,25 @@ function element.new(args)
return nil return nil
end end
-- DYNAMIC CHILD ELEMENTS --
-- insert an element as a contained child<br>
-- this is intended to be used dynamically, and depends on the target element type.<br>
-- not all elements support dynamic children.
---@param id string|integer element identifier
---@param elem graphics_element element
function public.insert_element(id, elem)
protected.insert(id, elem)
end
-- remove an element from contained children<br>
-- this is intended to be used dynamically, and depends on the target element type.<br>
-- not all elements support dynamic children.
---@param id string|integer element identifier
function public.remove_element(id)
protected.remove(id)
end
-- AUTO-PLACEMENT -- -- AUTO-PLACEMENT --
-- skip a line for automatically placed elements -- skip a line for automatically placed elements
@ -460,6 +522,16 @@ function element.new(args)
protected.response_callback(result) protected.response_callback(result)
end end
-- register a callback with a PSIL, allowing for automatic unregister on delete<br>
-- do not use graphics elements directly with PSIL subscribe()
---@param ps psil PSIL to subscribe to
---@param key string key to subscribe to
---@param func function function to link
function public.register(ps, key, func)
table.insert(self.subscriptions, { ps = ps, key = key, func = func })
ps.subscribe(key, func)
end
-- VISIBILITY -- -- VISIBILITY --
-- show the element -- show the element

View File

@ -7,9 +7,8 @@ local util = require("scada-common.util")
local databus = {} local databus = {}
local dbus_iface = { -- databus PSIL
ps = psil.create() databus.ps = psil.create()
}
---@enum RTU_UNIT_HW_STATE ---@enum RTU_UNIT_HW_STATE
local RTU_UNIT_HW_STATE = { local RTU_UNIT_HW_STATE = {
@ -22,54 +21,54 @@ local RTU_UNIT_HW_STATE = {
databus.RTU_UNIT_HW_STATE = RTU_UNIT_HW_STATE databus.RTU_UNIT_HW_STATE = RTU_UNIT_HW_STATE
-- call to toggle heartbeat signal -- call to toggle heartbeat signal
function databus.heartbeat() dbus_iface.ps.toggle("heartbeat") end function databus.heartbeat() databus.ps.toggle("heartbeat") end
-- transmit firmware versions across the bus -- transmit firmware versions across the bus
---@param rtu_v string RTU version ---@param rtu_v string RTU version
---@param comms_v string comms version ---@param comms_v string comms version
function databus.tx_versions(rtu_v, comms_v) function databus.tx_versions(rtu_v, comms_v)
dbus_iface.ps.publish("version", rtu_v) databus.ps.publish("version", rtu_v)
dbus_iface.ps.publish("comms_version", comms_v) databus.ps.publish("comms_version", comms_v)
end end
-- transmit hardware status for modem connection state -- transmit hardware status for modem connection state
---@param has_modem boolean ---@param has_modem boolean
function databus.tx_hw_modem(has_modem) function databus.tx_hw_modem(has_modem)
dbus_iface.ps.publish("has_modem", has_modem) databus.ps.publish("has_modem", has_modem)
end end
-- transmit unit hardware type across the bus -- transmit unit hardware type across the bus
---@param uid integer unit ID ---@param uid integer unit ID
---@param type RTU_UNIT_TYPE ---@param type RTU_UNIT_TYPE
function databus.tx_unit_hw_type(uid, type) function databus.tx_unit_hw_type(uid, type)
dbus_iface.ps.publish("unit_type_" .. uid, type) databus.ps.publish("unit_type_" .. uid, type)
end end
-- transmit unit hardware status across the bus -- transmit unit hardware status across the bus
---@param uid integer unit ID ---@param uid integer unit ID
---@param status RTU_UNIT_HW_STATE ---@param status RTU_UNIT_HW_STATE
function databus.tx_unit_hw_status(uid, status) function databus.tx_unit_hw_status(uid, status)
dbus_iface.ps.publish("unit_hw_" .. uid, status) databus.ps.publish("unit_hw_" .. uid, status)
end end
-- transmit thread (routine) statuses -- transmit thread (routine) statuses
---@param thread string thread name ---@param thread string thread name
---@param ok boolean thread state ---@param ok boolean thread state
function databus.tx_rt_status(thread, ok) function databus.tx_rt_status(thread, ok)
dbus_iface.ps.publish(util.c("routine__", thread), ok) databus.ps.publish(util.c("routine__", thread), ok)
end end
-- transmit supervisor link state across the bus -- transmit supervisor link state across the bus
---@param state integer ---@param state integer
function databus.tx_link_state(state) function databus.tx_link_state(state)
dbus_iface.ps.publish("link_state", state) databus.ps.publish("link_state", state)
end end
-- link a function to receive data from the bus -- link a function to receive data from the bus
---@param field string field name ---@param field string field name
---@param func function function to link ---@param func function function to link
function databus.rx_field(field, func) function databus.rx_field(field, func)
dbus_iface.ps.subscribe(field, func) databus.ps.subscribe(field, func)
end end
return databus return databus

View File

@ -49,22 +49,22 @@ local function init(panel, units)
on.update(true) on.update(true)
system.line_break() system.line_break()
databus.rx_field("heartbeat", heartbeat.update) heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)} local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)}
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}} local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
network.update(5) network.update(5)
system.line_break() system.line_break()
databus.rx_field("has_modem", modem.update) modem.register(databus.ps, "has_modem", modem.update)
databus.rx_field("link_state", network.update) network.register(databus.ps, "link_state", network.update)
local rt_main = LED{parent=system,label="RT MAIN",colors=cpair(colors.green,colors.green_off)} local rt_main = LED{parent=system,label="RT MAIN",colors=cpair(colors.green,colors.green_off)}
local rt_comm = LED{parent=system,label="RT COMMS",colors=cpair(colors.green,colors.green_off)} local rt_comm = LED{parent=system,label="RT COMMS",colors=cpair(colors.green,colors.green_off)}
system.line_break() system.line_break()
databus.rx_field("routine__main", rt_main.update) rt_main.register(databus.ps, "routine__main", rt_main.update)
databus.rx_field("routine__comms", rt_comm.update) rt_comm.register(databus.ps, "routine__comms", rt_comm.update)
-- --
-- about label -- about label
@ -74,8 +74,8 @@ local function init(panel, units)
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
databus.rx_field("version", function (version) fw_v.set_value(util.c("FW: ", version)) end) fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
databus.rx_field("comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end) comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
-- --
-- unit status list -- unit status list
@ -90,7 +90,7 @@ local function init(panel, units)
for i = 1, list_length do for i = 1, list_length do
TextBox{parent=threads,x=1,y=i,text=util.sprintf("%02d",i),height=1} TextBox{parent=threads,x=1,y=i,text=util.sprintf("%02d",i),height=1}
local rt_unit = LED{parent=threads,x=4,y=i,label="RT",colors=cpair(colors.green,colors.green_off)} local rt_unit = LED{parent=threads,x=4,y=i,label="RT",colors=cpair(colors.green,colors.green_off)}
databus.rx_field("routine__unit_" .. i, rt_unit.update) rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update)
end end
local unit_hw_statuses = Div{parent=panel,height=18,x=25,y=3} local unit_hw_statuses = Div{parent=panel,height=18,x=25,y=3}
@ -102,13 +102,13 @@ local function init(panel, units)
-- hardware status -- hardware status
local unit_hw = RGBLED{parent=unit_hw_statuses,y=i,label="",colors={colors.red,colors.orange,colors.yellow,colors.green}} local unit_hw = RGBLED{parent=unit_hw_statuses,y=i,label="",colors={colors.red,colors.orange,colors.yellow,colors.green}}
databus.rx_field("unit_hw_" .. i, unit_hw.update) unit_hw.register(databus.ps, "unit_hw_" .. i, unit_hw.update)
-- unit name identifier (type + index) -- unit name identifier (type + index)
local name = util.c(UNIT_TYPE_LABELS[unit.type + 1], " ", unit.index) local name = util.c(UNIT_TYPE_LABELS[unit.type + 1], " ", unit.index)
local name_box = TextBox{parent=unit_hw_statuses,y=i,x=3,text=name,height=1} local name_box = TextBox{parent=unit_hw_statuses,y=i,x=3,text=name,height=1}
databus.rx_field("unit_type_" .. i, function (t) name_box.register(databus.ps, "unit_type_" .. i, function (t)
name_box.set_value(util.c(UNIT_TYPE_LABELS[t + 1], " ", unit.index)) name_box.set_value(util.c(UNIT_TYPE_LABELS[t + 1], " ", unit.index))
end) end)

View File

@ -45,10 +45,8 @@ function renderer.close_ui()
-- stop blinking indicators -- stop blinking indicators
flasher.clear() flasher.clear()
-- hide to stop animation callbacks -- delete element tree
ui.display.hide() ui.display.delete()
-- clear root UI elements
ui.display = nil ui.display = nil
-- restore colors -- restore colors

View File

@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
local sps_rtu = require("rtu.dev.sps_rtu") local sps_rtu = require("rtu.dev.sps_rtu")
local turbinev_rtu = require("rtu.dev.turbinev_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu")
local RTU_VERSION = "v1.1.0" local RTU_VERSION = "v1.2.0"
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE

View File

@ -2,6 +2,8 @@
-- Publisher-Subscriber Interconnect Layer -- Publisher-Subscriber Interconnect Layer
-- --
local util = require("scada-common.util")
local psil = {} local psil = {}
-- instantiate a new PSI layer -- instantiate a new PSI layer
@ -36,6 +38,15 @@ function psil.create()
table.insert(self.ic[key].subscribers, { notify = func }) table.insert(self.ic[key].subscribers, { notify = func })
end end
-- 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
-- publish data to a given key, passing it to all subscribers if it has changed -- publish data to a given key, passing it to all subscribers if it has changed
---@param key string data key ---@param key string data key
---@param value any data value ---@param value any data value
@ -64,6 +75,9 @@ function psil.create()
end end
end end
-- clear the contents of the interconnect
function public.purge() self.ic = nil end
return public return public
end end