diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 6cc66d1..ab3783a 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -102,7 +102,8 @@ pocket.APP_ID = APP_ID ---@param render_queue mqueue function pocket.init_nav(render_queue) local self = { - pane = nil, ---@type graphics_element + pane = nil, ---@type graphics_element + sidebar = nil, ---@type graphics_element apps = {}, containers = {}, help_map = {}, @@ -117,23 +118,22 @@ function pocket.init_nav(render_queue) -- set the root pane element to switch between apps with ---@param root_pane graphics_element - function nav.set_pane(root_pane) - self.pane = root_pane - end + function nav.set_pane(root_pane) self.pane = root_pane end - function nav.set_sidebar(sidebar) - self.sidebar = sidebar - end + -- link sidebar element + ---@param sidebar graphics_element + function nav.set_sidebar(sidebar) self.sidebar = sidebar end -- register an app ---@param app_id POCKET_APP_ID app ID ---@param container graphics_element element that contains this app (usually a Div) ---@param pane graphics_element? multipane if this is a simple paned app, then nav_to must be a number - function nav.register_app(app_id, container, pane) + ---@param require_sv boolean? true or false/nil otherwise to specifiy if this app should be unloaded when the supervisor connection is lost + ---@param require_api boolean? true or false/nil otherwise to specifiy if this app should be unloaded when the api connection is lost + function nav.register_app(app_id, container, pane, require_sv, require_api) ---@class pocket_app local app = { loaded = false, - load = nil, cur_page = nil, ---@type nav_tree_page pane = pane, paned_pages = {}, @@ -141,6 +141,11 @@ function pocket.init_nav(render_queue) } app.load = function () app.loaded = true end + app.unload = function () app.loaded = false end + + -- check which connections this requires + ---@return boolean requires_sv, boolean requires_api + function app.check_requires() return require_sv or false, require_api or false end -- delayed set of the pane if it wasn't ready at the start ---@param root_pane graphics_element multipane @@ -155,13 +160,22 @@ function pocket.init_nav(render_queue) -- function to run on initial load into memory ---@param on_load function callback - function app.set_on_load(on_load) + function app.set_load(on_load) app.load = function () on_load() app.loaded = true end end + -- function to run to close out the app + ---@param on_unload function callback + function app.set_unload(on_unload) + app.unload = function () + on_unload() + app.loaded = false + end + end + -- if a pane was provided, this will switch between numbered pages ---@param idx integer page index function app.switcher(idx) @@ -207,6 +221,12 @@ function pocket.init_nav(render_queue) return page end + -- delete paned pages and clear the current page + function app.delete_pages() + app.paned_pages = {} + app.cur_page = nil + end + -- get the currently active page function app.get_current_page() return app.cur_page end @@ -251,6 +271,22 @@ function pocket.init_nav(render_queue) self.apps[app_id].load() end + -- unload api-dependent apps + function nav.unload_api() + for _, app in pairs(self.apps) do + local _, api = app.check_requires() + if app.loaded and api then app.unload() end + end + end + + -- unload supervisor-dependent apps + function nav.unload_sv() + for _, app in pairs(self.apps) do + local sv, _ = app.check_requires() + if app.loaded and sv then app.unload() end + end + end + -- get a list of the app containers (usually Div elements) function nav.get_containers() return self.containers end @@ -300,7 +336,8 @@ end ---@param nic nic network interface device ---@param sv_watchdog watchdog ---@param api_watchdog watchdog -function pocket.comms(version, nic, sv_watchdog, api_watchdog) +---@param nav pocket_nav +function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) local self = { sv = { linked = false, @@ -399,6 +436,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) -- close connection to the supervisor function public.close_sv() sv_watchdog.cancel() + nav.unload_sv() self.sv.linked = false self.sv.r_seq_num = nil self.sv.addr = comms.BROADCAST @@ -408,6 +446,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) -- close connection to coordinator API server function public.close_api() api_watchdog.cancel() + nav.unload_api() self.api.linked = false self.api.r_seq_num = nil self.api.addr = comms.BROADCAST @@ -589,6 +628,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) elseif packet.type == MGMT_TYPE.CLOSE then -- handle session close api_watchdog.cancel() + nav.unload_api() self.api.linked = false self.api.r_seq_num = nil self.api.addr = comms.BROADCAST @@ -694,6 +734,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) elseif packet.type == MGMT_TYPE.CLOSE then -- handle session close sv_watchdog.cancel() + nav.unload_sv() self.sv.linked = false self.sv.r_seq_num = nil self.sv.addr = comms.BROADCAST diff --git a/pocket/startup.lua b/pocket/startup.lua index a55d376..0d2be64 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -122,6 +122,8 @@ local function main() -- setup system ---------------------------------------- + smem_sys.nav = pocket.init_nav(__shared_memory.q.mq_render) + -- message authentication init if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then network.init_mac(config.AuthKey) @@ -145,11 +147,10 @@ local function main() -- create network interface then setup comms smem_sys.nic = network.nic(smem_dev.modem) - smem_sys.pocket_comms = pocket.comms(POCKET_VERSION, smem_sys.nic, smem_sys.sv_wd, smem_sys.api_wd) + smem_sys.pocket_comms = pocket.comms(POCKET_VERSION, smem_sys.nic, smem_sys.sv_wd, smem_sys.api_wd, smem_sys.nav) log.debug("startup> comms init") - -- init nav and I/O handler - smem_sys.nav = pocket.init_nav(__shared_memory.q.mq_render) + -- init I/O control iocontrol.init_core(smem_sys.pocket_comms, smem_sys.nav) ---------------------------------------- diff --git a/pocket/threads.lua b/pocket/threads.lua index 6c241f7..5e05940 100644 --- a/pocket/threads.lua +++ b/pocket/threads.lua @@ -1,13 +1,13 @@ -local log = require("scada-common.log") -local mqueue = require("scada-common.mqueue") -local ppm = require("scada-common.ppm") -local tcd = require("scada-common.tcd") -local util = require("scada-common.util") +local log = require("scada-common.log") +local mqueue = require("scada-common.mqueue") +local ppm = require("scada-common.ppm") +local tcd = require("scada-common.tcd") +local util = require("scada-common.util") -local pocket = require("pocket.pocket") -local renderer = require("pocket.renderer") +local pocket = require("pocket.pocket") +local renderer = require("pocket.renderer") -local core = require("graphics.core") +local core = require("graphics.core") local threads = {} diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 61e5884..2fea06a 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -55,16 +55,21 @@ local function new_view(root) local btn_active = cpair(colors.white, colors.black) local btn_disable = cpair(colors.gray, colors.black) - local list = { - { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }, - { label = " \x14 ", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(1) end }, - { label = "__?", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(2) end } - } + app.set_sidebar({{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }}) - app.set_sidebar(list) + local page_div = nil ---@type nil|graphics_element + -- load the app (create the elements) local function load() - local page_div = Div{parent=main,y=2} + local list = { + { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }, + { label = " \x14 ", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(1) end }, + { label = "__?", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(2) end } + } + + app.set_sidebar(list) + + page_div = Div{parent=main,y=2} local p_width = page_div.get_width() - 2 local main_page = app.new_page(nil, 1) @@ -219,7 +224,22 @@ local function new_view(root) load_pane.set_value(2) end - app.set_on_load(load) + -- delete the elements and switch back to the loading screen + local function unload() + if page_div then + page_div.delete() + page_div = nil + end + + app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } }) + app.delete_pages() + + -- show loading screen + load_pane.set_value(1) + end + + app.set_load(load) + app.set_unload(unload) return main end diff --git a/pocket/ui/apps/unit.lua b/pocket/ui/apps/unit.lua index 6c39785..8a11d88 100644 --- a/pocket/ui/apps/unit.lua +++ b/pocket/ui/apps/unit.lua @@ -56,7 +56,7 @@ local function new_view(root) local frame = Div{parent=root,x=1,y=1} - local app = db.nav.register_app(APP_ID.UNITS, frame) + local app = db.nav.register_app(APP_ID.UNITS, frame, nil, false, true) local load_div = Div{parent=frame,x=1,y=1} local main = Div{parent=frame,x=1,y=1} @@ -66,13 +66,15 @@ local function new_view(root) local load_pane = MultiPane{parent=main,x=1,y=1,panes={load_div,main}} - TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER} + app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } }) local btn_fg_bg = cpair(colors.yellow, colors.black) local btn_active = cpair(colors.white, colors.black) local nav_links = {} + local page_div = nil ---@type nil|graphics_element + -- set sidebar to display unit-specific fields based on a specified unit local function set_sidebar(id) local unit = db.units[id] ---@type pioctl_unit @@ -96,8 +98,9 @@ local function new_view(root) app.set_sidebar(list) end + -- load the app (create the elements) local function load() - local page_div = Div{parent=main,y=2,width=main.get_width()} + page_div = Div{parent=main,y=2,width=main.get_width()} local panes = {} @@ -376,7 +379,22 @@ local function new_view(root) load_pane.set_value(2) end - app.set_on_load(load) + -- delete the elements and switch back to the loading screen + local function unload() + if page_div then + page_div.delete() + page_div = nil + end + + app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } }) + app.delete_pages() + + -- show loading screen + load_pane.set_value(1) + end + + app.set_load(load) + app.set_unload(unload) return main end