diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index e7a9ebd..d759a93 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -58,7 +58,7 @@ function coordinator.configure_monitors(num_units) end -- we need a certain number of monitors (1 per unit + 1 primary display) - if #names ~= num_units + 1 then + if #names < num_units + 1 then println("not enough monitors connected (need " .. num_units + 1 .. ")") log.warning("insufficient monitors present (need " .. num_units + 1 .. ")") return false @@ -258,6 +258,7 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa -- close the connection to the server function public.close() sv_watchdog.cancel() + self.sv_linked = false _send_sv(PROTOCOLS.SCADA_MGMT, SCADA_MGMT_TYPES.CLOSE, {}) end diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index f76c0b3..bbbd90e 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -54,6 +54,25 @@ function renderer.set_displays(monitors) engine.monitors = monitors end +-- check if the renderer is configured to use a given monitor peripheral +---@param periph table peripheral +---@return boolean is_used +function renderer.is_monitor_used(periph) + if engine.monitors ~= nil then + if engine.monitors.primary == periph then + return true + else + for i = 1, #engine.monitors.unit_displays do + if engine.monitors.unit_displays[i] == periph then + return true + end + end + end + end + + return false +end + -- reset all displays in use by the renderer ---@param recolor? boolean true to use color palette from style function renderer.reset(recolor) @@ -76,40 +95,44 @@ end -- start the coordinator GUI function renderer.start_ui() - -- hide dmesg - engine.dmesg_window.setVisible(false) + if not engine.ui_ready then + -- hide dmesg + engine.dmesg_window.setVisible(false) - -- show main view on main monitor - ui.main_layout = main_view(engine.monitors.primary) + -- show main view on main monitor + ui.main_layout = main_view(engine.monitors.primary) - -- show unit views on unit displays - for id, monitor in pairs(engine.monitors.unit_displays) do - table.insert(ui.unit_layouts, unit_view(monitor, id)) + -- show unit views on unit displays + for id, monitor in pairs(engine.monitors.unit_displays) do + table.insert(ui.unit_layouts, unit_view(monitor, id)) + end + + -- report ui as ready + engine.ui_ready = true end - - -- report ui as ready - engine.ui_ready = true end -- close out the UI function renderer.close_ui() - -- report ui as not ready - engine.ui_ready = false + if engine.ui_ready then + -- report ui as not ready + engine.ui_ready = false - -- hide to stop animation callbacks - ui.main_layout.hide() - for i = 1, #ui.unit_layouts do - ui.unit_layouts[i].hide() - engine.monitors.unit_displays[i].clear() + -- hide to stop animation callbacks + ui.main_layout.hide() + for i = 1, #ui.unit_layouts do + ui.unit_layouts[i].hide() + engine.monitors.unit_displays[i].clear() + end + + -- clear root UI elements + ui.main_layout = nil + ui.unit_layouts = {} + + -- re-draw dmesg + engine.dmesg_window.setVisible(true) + engine.dmesg_window.redraw() end - - -- clear root UI elements - ui.main_layout = nil - ui.unit_layouts = {} - - -- re-draw dmesg - engine.dmesg_window.setVisible(true) - engine.dmesg_window.redraw() end -- is the UI ready? diff --git a/coordinator/startup.lua b/coordinator/startup.lua index b008b59..4c30435 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -16,7 +16,7 @@ local config = require("coordinator.config") local coordinator = require("coordinator.coordinator") local renderer = require("coordinator.renderer") -local COORDINATOR_VERSION = "alpha-v0.4.10" +local COORDINATOR_VERSION = "alpha-v0.4.11" local print = util.print local println = util.println @@ -58,7 +58,7 @@ log.info("========================================") println(">> SCADA Coordinator " .. COORDINATOR_VERSION .. " <<") ---------------------------------------- --- startup +-- system startup ---------------------------------------- -- mount connected devices @@ -83,6 +83,10 @@ log_graphics("displays connected and reset") log_sys("system start on " .. os.date("%c")) log_boot("starting " .. COORDINATOR_VERSION) +---------------------------------------- +-- setup communications +---------------------------------------- + -- get the communications modem local modem = ppm.get_wireless_modem() if modem == nil then @@ -108,6 +112,10 @@ log_comms("comms initialized") local MAIN_CLOCK = 0.5 local loop_clock = util.new_clock(MAIN_CLOCK) +---------------------------------------- +-- connect to the supervisor +---------------------------------------- + -- attempt to connect to the supervisor or exit local function init_connect_sv() local tick_waiting, task_done = log_comms_connecting("attempting to connect to configured supervisor on channel " .. config.SCADA_SV_PORT) @@ -115,14 +123,20 @@ local function init_connect_sv() -- attempt to establish a connection with the supervisory computer if not coord_comms.sv_connect(60, tick_waiting, task_done) then log_comms("supervisor connection failed") - println("boot> failed to connect to supervisor") log.fatal("failed to connect to supervisor") - log_sys("system shutdown") - return + return false end + + return true end -init_connect_sv() +if not init_connect_sv() then + println("boot> failed to connect to supervisor") + log_sys("system shutdown") + return +else + log_sys("supervisor connected, proceeding to UI start") +end ---------------------------------------- -- start the UI @@ -158,10 +172,14 @@ local ui_ok = init_start_ui() -- main event loop ---------------------------------------- +local no_modem = false + -- start connection watchdog conn_watchdog.feed() log.debug("boot> conn watchdog started") +log_sys("system started successfully") + -- event loop -- ui_ok will never change in this loop, same as while true or exit if UI start failed while ui_ok do @@ -175,18 +193,28 @@ while ui_ok do if type == "modem" then -- we only really care if this is our wireless modem if device == modem then + no_modem = true log_sys("comms modem disconnected") println_ts("wireless modem disconnected!") log.error("comms modem disconnected!") -- close out UI renderer.close_ui() + + -- alert user to status + log_sys("awaiting comms modem reconnect...") else log_sys("non-comms modem disconnected") log.warning("non-comms modem disconnected") end elseif type == "monitor" then - ---@todo: handle monitor loss + if renderer.is_monitor_used(device) then + -- "halt and catch fire" style handling + log_sys("lost a configured monitor, system will now exit") + break + else + log_sys("lost unused monitor, ignoring") + end end end elseif event == "peripheral" then @@ -196,6 +224,7 @@ while ui_ok do if type == "modem" then if device.isWireless() then -- reconnected modem + no_modem = false modem = device coord_comms.reconnect_modem(modem) @@ -203,13 +232,13 @@ while ui_ok do println_ts("wireless modem reconnected.") -- re-init system - init_connect_sv() + if not init_connect_sv() then break end ui_ok = init_start_ui() else log_sys("wired modem reconnected") end elseif type == "monitor" then - ---@todo: handle monitor reconnect + -- not supported, system will exit on loss of in-use monitors end end elseif event == "timer" then @@ -231,9 +260,11 @@ while ui_ok do coord_comms.close() renderer.close_ui() - -- try to re-connect to the supervisor - init_connect_sv() - ui_ok = init_start_ui() + if not no_modem then + -- try to re-connect to the supervisor + if not init_connect_sv() then break end + ui_ok = init_start_ui() + end else -- a non-clock/main watchdog timer event @@ -250,13 +281,17 @@ while ui_ok do -- check if it was a disconnect if not coord_comms.is_linked() then + log_comms("supervisor closed connection") + -- close connection and UI coord_comms.close() renderer.close_ui() - -- try to re-connect to the supervisor - init_connect_sv() - ui_ok = init_start_ui() + if not no_modem then + -- try to re-connect to the supervisor + if not init_connect_sv() then break end + ui_ok = init_start_ui() + end end elseif event == "monitor_touch" then -- handle a monitor touch event @@ -265,10 +300,11 @@ while ui_ok do -- check for termination request if event == "terminate" or ppm.should_terminate() then - log_comms("terminate requested, closing supervisor connection") + println_ts("terminate requested, closing connections...") + log_comms("terminate requested, closing supervisor connection...") coord_comms.close() + log_comms("supervisor connection closed") log_comms("closing api sessions...") - println_ts("closing api sessions...") apisessions.close_all() log_comms("api sessions closed") break