#249 coordinator handle monitor disconnects/reconnects
This commit is contained in:
@@ -223,6 +223,14 @@ function iocontrol.fp_has_speaker(has_speaker) io.fp.ps.publish("has_speaker", h
|
|||||||
---@param state integer
|
---@param state integer
|
||||||
function iocontrol.fp_link_state(state) io.fp.ps.publish("link_state", state) end
|
function iocontrol.fp_link_state(state) io.fp.ps.publish("link_state", state) end
|
||||||
|
|
||||||
|
-- report monitor connection state
|
||||||
|
---@param id integer unit ID or 0 for main
|
||||||
|
function iocontrol.fp_monitor_state(id, connected)
|
||||||
|
local name = "main_monitor"
|
||||||
|
if id > 0 then name = "unit_monitor_" .. id end
|
||||||
|
io.fp.ps.publish(name, connected)
|
||||||
|
end
|
||||||
|
|
||||||
-- report PKT firmware version and PKT session connection state
|
-- report PKT firmware version and PKT session connection state
|
||||||
---@param session_id integer PKT session
|
---@param session_id integer PKT session
|
||||||
---@param fw string firmware version
|
---@param fw string firmware version
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
local style = require("coordinator.ui.style")
|
local style = require("coordinator.ui.style")
|
||||||
local pgi = require("coordinator.ui.pgi")
|
local pgi = require("coordinator.ui.pgi")
|
||||||
|
|
||||||
@@ -48,24 +50,12 @@ end
|
|||||||
|
|
||||||
-- link to the monitor peripherals
|
-- link to the monitor peripherals
|
||||||
---@param monitors monitors_struct
|
---@param monitors monitors_struct
|
||||||
function renderer.set_displays(monitors) engine.monitors = monitors end
|
function renderer.set_displays(monitors)
|
||||||
|
engine.monitors = monitors
|
||||||
|
|
||||||
-- check if the renderer is configured to use a given monitor peripheral
|
-- report to front panel as connected
|
||||||
---@nodiscard
|
iocontrol.fp_monitor_state(0, true)
|
||||||
---@param periph table peripheral
|
for i = 1, #engine.monitors.unit_displays do iocontrol.fp_monitor_state(i, true) end
|
||||||
---@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 _, monitor in ipairs(engine.monitors.unit_displays) do
|
|
||||||
if monitor == periph then return true end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- init all displays in use by the renderer
|
-- init all displays in use by the renderer
|
||||||
@@ -127,7 +117,7 @@ function renderer.start_fp()
|
|||||||
if not engine.fp_ready then
|
if not engine.fp_ready then
|
||||||
-- show front panel view on terminal
|
-- show front panel view on terminal
|
||||||
engine.ui.front_panel = DisplayBox{window=term.native(),fg_bg=style.fp.root}
|
engine.ui.front_panel = DisplayBox{window=term.native(),fg_bg=style.fp.root}
|
||||||
panel_view(engine.ui.front_panel)
|
panel_view(engine.ui.front_panel, #engine.monitors.unit_displays)
|
||||||
|
|
||||||
-- start flasher callback task
|
-- start flasher callback task
|
||||||
flasher.run()
|
flasher.run()
|
||||||
@@ -137,30 +127,6 @@ function renderer.start_fp()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- start the coordinator GUI
|
|
||||||
function renderer.start_ui()
|
|
||||||
if not engine.ui_ready then
|
|
||||||
-- hide dmesg
|
|
||||||
engine.dmesg_window.setVisible(false)
|
|
||||||
|
|
||||||
-- show main view on main monitor
|
|
||||||
engine.ui.main_display = DisplayBox{window=engine.monitors.primary,fg_bg=style.root}
|
|
||||||
main_view(engine.ui.main_display)
|
|
||||||
|
|
||||||
-- show unit views on unit displays
|
|
||||||
for i = 1, #engine.monitors.unit_displays do
|
|
||||||
engine.ui.unit_displays[i] = DisplayBox{window=engine.monitors.unit_displays[i],fg_bg=style.root}
|
|
||||||
unit_view(engine.ui.unit_displays[i], i)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- start flasher callback task
|
|
||||||
flasher.run()
|
|
||||||
|
|
||||||
-- report ui as ready
|
|
||||||
engine.ui_ready = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- close out the front panel
|
-- close out the front panel
|
||||||
function renderer.close_fp()
|
function renderer.close_fp()
|
||||||
if engine.fp_ready then
|
if engine.fp_ready then
|
||||||
@@ -191,6 +157,32 @@ function renderer.close_fp()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- start the coordinator GUI
|
||||||
|
function renderer.start_ui()
|
||||||
|
if not engine.ui_ready then
|
||||||
|
-- hide dmesg
|
||||||
|
engine.dmesg_window.setVisible(false)
|
||||||
|
|
||||||
|
-- show main view on main monitor
|
||||||
|
if engine.monitors.primary ~= nil then
|
||||||
|
engine.ui.main_display = DisplayBox{window=engine.monitors.primary,fg_bg=style.root}
|
||||||
|
main_view(engine.ui.main_display)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- show unit views on unit displays
|
||||||
|
for idx, display in pairs(engine.monitors.unit_displays) do
|
||||||
|
engine.ui.unit_displays[idx] = DisplayBox{window=display,fg_bg=style.root}
|
||||||
|
unit_view(engine.ui.unit_displays[idx], idx)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- start flasher callback task
|
||||||
|
flasher.run()
|
||||||
|
|
||||||
|
-- report ui as ready
|
||||||
|
engine.ui_ready = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- close out the UI
|
-- close out the UI
|
||||||
function renderer.close_ui()
|
function renderer.close_ui()
|
||||||
if not engine.fp_ready then
|
if not engine.fp_ready then
|
||||||
@@ -200,7 +192,7 @@ function renderer.close_ui()
|
|||||||
|
|
||||||
-- delete element trees
|
-- delete element trees
|
||||||
if engine.ui.main_display ~= nil then engine.ui.main_display.delete() end
|
if engine.ui.main_display ~= nil then engine.ui.main_display.delete() end
|
||||||
for _, display in ipairs(engine.ui.unit_displays) do display.delete() end
|
for _, display in pairs(engine.ui.unit_displays) do display.delete() end
|
||||||
|
|
||||||
-- report ui as not ready
|
-- report ui as not ready
|
||||||
engine.ui_ready = false
|
engine.ui_ready = false
|
||||||
@@ -227,6 +219,95 @@ function renderer.fp_ready() return engine.fp_ready end
|
|||||||
---@return boolean ready
|
---@return boolean ready
|
||||||
function renderer.ui_ready() return engine.ui_ready end
|
function renderer.ui_ready() return engine.ui_ready end
|
||||||
|
|
||||||
|
-- handle a monitor peripheral being disconnected
|
||||||
|
---@param device table monitor
|
||||||
|
---@return boolean is_used if the monitor is one of the configured monitors
|
||||||
|
function renderer.handle_disconnect(device)
|
||||||
|
local is_used = false
|
||||||
|
|
||||||
|
if engine.monitors ~= nil then
|
||||||
|
if engine.monitors.primary == device then
|
||||||
|
if engine.ui.main_display ~= nil then
|
||||||
|
-- delete element tree and clear root UI elements
|
||||||
|
engine.ui.main_display.delete()
|
||||||
|
end
|
||||||
|
|
||||||
|
is_used = true
|
||||||
|
engine.monitors.primary = nil
|
||||||
|
engine.ui.main_display = nil
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state(0, false)
|
||||||
|
else
|
||||||
|
for idx, monitor in pairs(engine.monitors.unit_displays) do
|
||||||
|
if monitor == device then
|
||||||
|
if engine.ui.unit_displays[idx] ~= nil then
|
||||||
|
engine.ui.unit_displays[idx].delete()
|
||||||
|
end
|
||||||
|
|
||||||
|
is_used = true
|
||||||
|
engine.monitors.unit_displays[idx] = nil
|
||||||
|
engine.ui.unit_displays[idx] = nil
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state(idx, false)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return is_used
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle a monitor peripheral being reconnected
|
||||||
|
---@param name string monitor name
|
||||||
|
---@param device table monitor
|
||||||
|
---@return boolean is_used if the monitor is one of the configured monitors
|
||||||
|
function renderer.handle_reconnect(name, device)
|
||||||
|
local is_used = false
|
||||||
|
|
||||||
|
if engine.monitors ~= nil then
|
||||||
|
if engine.monitors.primary_name == name then
|
||||||
|
is_used = true
|
||||||
|
_init_display(device)
|
||||||
|
engine.monitors.primary = device
|
||||||
|
|
||||||
|
local disp_x, disp_y = engine.monitors.primary.getSize()
|
||||||
|
engine.dmesg_window.reposition(1, 1, disp_x, disp_y, engine.monitors.primary)
|
||||||
|
|
||||||
|
if engine.ui_ready and (engine.ui.main_display == nil) then
|
||||||
|
engine.dmesg_window.setVisible(false)
|
||||||
|
|
||||||
|
engine.ui.main_display = DisplayBox{window=device,fg_bg=style.root}
|
||||||
|
main_view(engine.ui.main_display)
|
||||||
|
else
|
||||||
|
engine.dmesg_window.setVisible(true)
|
||||||
|
engine.dmesg_window.redraw()
|
||||||
|
end
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state(0, true)
|
||||||
|
else
|
||||||
|
for idx, monitor in ipairs(engine.monitors.unit_name_map) do
|
||||||
|
if monitor == name then
|
||||||
|
is_used = true
|
||||||
|
_init_display(device)
|
||||||
|
engine.monitors.unit_displays[idx] = device
|
||||||
|
|
||||||
|
if engine.ui_ready and (engine.ui.unit_displays[idx] == nil) then
|
||||||
|
engine.ui.unit_displays[idx] = DisplayBox{window=device,fg_bg=style.root}
|
||||||
|
unit_view(engine.ui.unit_displays[idx], idx)
|
||||||
|
end
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state(idx, true)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return is_used
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- handle a touch event
|
-- handle a touch event
|
||||||
---@param event mouse_interaction|nil
|
---@param event mouse_interaction|nil
|
||||||
function renderer.handle_mouse(event)
|
function renderer.handle_mouse(event)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder")
|
|||||||
|
|
||||||
local apisessions = require("coordinator.session.apisessions")
|
local apisessions = require("coordinator.session.apisessions")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v0.19.3"
|
local COORDINATOR_VERSION = "v0.20.0"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@@ -251,13 +251,10 @@ local function main()
|
|||||||
log_sys("non-comms modem disconnected")
|
log_sys("non-comms modem disconnected")
|
||||||
end
|
end
|
||||||
elseif type == "monitor" then
|
elseif type == "monitor" then
|
||||||
if renderer.is_monitor_used(device) then
|
if renderer.handle_disconnect(device) then
|
||||||
---@todo will be handled properly in #249
|
log_sys("lost a configured monitor")
|
||||||
-- "halt and catch fire" style handling
|
|
||||||
log_sys("lost a configured monitor, system will now exit")
|
|
||||||
break
|
|
||||||
else
|
else
|
||||||
log_sys("lost unused monitor, ignoring")
|
log_sys("lost an unused monitor")
|
||||||
end
|
end
|
||||||
elseif type == "speaker" then
|
elseif type == "speaker" then
|
||||||
log_sys("lost alarm sounder speaker")
|
log_sys("lost alarm sounder speaker")
|
||||||
@@ -277,9 +274,12 @@ local function main()
|
|||||||
else
|
else
|
||||||
log_sys("wired modem reconnected")
|
log_sys("wired modem reconnected")
|
||||||
end
|
end
|
||||||
-- elseif type == "monitor" then
|
elseif type == "monitor" then
|
||||||
---@todo will be handled properly in #249
|
if renderer.handle_reconnect(param1, device) then
|
||||||
-- not supported, system will exit on loss of in-use monitors
|
log_sys(util.c("configured monitor ", param1, " reconnected"))
|
||||||
|
else
|
||||||
|
log_sys(util.c("unused monitor ", param1, " connected"))
|
||||||
|
end
|
||||||
elseif type == "speaker" then
|
elseif type == "speaker" then
|
||||||
log_sys("alarm sounder speaker reconnected")
|
log_sys("alarm sounder speaker reconnected")
|
||||||
sounder.reconnect(device)
|
sounder.reconnect(device)
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ local cpair = core.cpair
|
|||||||
|
|
||||||
-- create new front panel view
|
-- create new front panel view
|
||||||
---@param panel graphics_element main displaybox
|
---@param panel graphics_element main displaybox
|
||||||
local function init(panel)
|
---@param num_units integer number of units (number of unit monitors)
|
||||||
|
local function init(panel, num_units)
|
||||||
local ps = iocontrol.get_db().fp.ps
|
local ps = iocontrol.get_db().fp.ps
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.fp.header}
|
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.fp.header}
|
||||||
@@ -67,6 +68,18 @@ local function init(panel)
|
|||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)}
|
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)}
|
||||||
|
|
||||||
|
local monitors = Div{parent=main_page,width=16,height=17,x=18,y=2}
|
||||||
|
|
||||||
|
local main_monitor = LED{parent=monitors,label="MAIN MONITOR",colors=cpair(colors.green,colors.green_off)}
|
||||||
|
main_monitor.register(ps, "main_monitor", main_monitor.update)
|
||||||
|
|
||||||
|
monitors.line_break()
|
||||||
|
|
||||||
|
for i = 1, num_units do
|
||||||
|
local unit_monitor = LED{parent=monitors,label="UNIT "..i.." MONITOR",colors=cpair(colors.green,colors.green_off)}
|
||||||
|
unit_monitor.register(ps, "unit_monitor_" .. i, unit_monitor.update)
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|||||||
Reference in New Issue
Block a user