diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index 94071d4..fe81f5e 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -109,7 +109,7 @@ function renderer.start_ui() end -- start flasher callback task - flasher.init() + flasher.run() -- report ui as ready engine.ui_ready = true diff --git a/graphics/flasher.lua b/graphics/flasher.lua index 02f0dcd..5fa856a 100644 --- a/graphics/flasher.lua +++ b/graphics/flasher.lua @@ -42,17 +42,16 @@ local function callback_250ms() end end --- start the flasher periodic -function flasher.init() +-- start/resume the flasher periodic +function flasher.run() active = true - callback_counter = 0 - registry = { {}, {}, {} } callback_250ms() end -- clear all blinking indicators and stop the flasher periodic function flasher.clear() active = false + callback_counter = 0 registry = { {}, {}, {} } end diff --git a/scada-common/tcallbackdsp.lua b/scada-common/tcallbackdsp.lua index 3371c98..cdce1c0 100644 --- a/scada-common/tcallbackdsp.lua +++ b/scada-common/tcallbackdsp.lua @@ -13,8 +13,14 @@ local registry = {} ---@param time number seconds ---@param f function callback function function tcallbackdsp.dispatch(time, f) ----@diagnostic disable-next-line: undefined-field - registry[os.startTimer(time)] = { callback = f } + local timer = util.start_timer(time) + registry[timer] = { + callback = f, + duration = time, + expiry = time + util.time_s() + } + + log.debug("queued callback for " .. util.strval(f) .. " timer #" .. timer) end -- request a function to be called after the specified time, aborting any registered instances of that function reference @@ -25,25 +31,44 @@ function tcallbackdsp.dispatch_unique(time, f) for timer, entry in pairs(registry) do if entry.callback == f then -- found an instance of this function reference, abort it - log.debug(util.c("TCD: aborting duplicate timer callback (timer: ", timer, ", function: ", f, ")")) + log.debug(util.c("TCD: aborting duplicate timer callback (timer: ", timer, ", ", f, ")")) -- cancel event and remove from registry (even if it fires it won't call) ----@diagnostic disable-next-line: undefined-field - os.cancelTimer(timer) + util.cancel_timer(timer) registry[timer] = nil end end ----@diagnostic disable-next-line: undefined-field - registry[os.startTimer(time)] = { callback = f } + local timer = util.start_timer(time) + registry[timer] = { + callback = f, + duration = time, + expiry = time + util.time_s() + } + + log.debug("queued callback for " .. util.strval(f) .. " timer #" .. timer) end -- lookup a timer event and execute the callback if found ---@param event integer timer event timer ID function tcallbackdsp.handle(event) if registry[event] ~= nil then - registry[event].callback() + local callback = registry[event].callback + -- clear first so that dispatch_unique call from inside callback won't throw a debug message registry[event] = nil + callback() + end +end + +-- identify any overdo callbacks +-- +-- prints to log debug output +function tcallbackdsp.diagnostics() + for timer, entry in pairs(registry) do + if entry.expiry >= util.time_s() then + local overtime = util.time_s() - entry.expiry + log.debug(util.c("TCD: unserviced timer ", timer, " for callback ", entry.callback, " is at least ", overtime, "s late")) + end end end diff --git a/scada-common/util.lua b/scada-common/util.lua index aa1e385..cb8cdcb 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -202,6 +202,33 @@ function util.pull_event(target_event) return os.pullEventRaw(target_event) end +-- OS queue event raw wrapper with types +---@param event os_event +---@param param1 any +---@param param2 any +---@param param3 any +---@param param4 any +---@param param5 any +function util.push_event(event, param1, param2, param3, param4, param5) +---@diagnostic disable-next-line: undefined-field + return os.queueEvent(event, param1, param2, param3, param4, param5) +end + +-- start an OS timer +---@param t number timer duration in seconds +---@return integer timer ID +function util.start_timer(t) +---@diagnostic disable-next-line: undefined-field + return os.startTimer(t) +end + +-- cancel an OS timer +---@param timer integer timer ID +function util.cancel_timer(timer) +---@diagnostic disable-next-line: undefined-field + os.cancelTimer(timer) +end + -- PARALLELIZATION -- -- protected sleep call so we still are in charge of catching termination @@ -312,14 +339,9 @@ end --- --- triggers a timer event if not fed within 'timeout' seconds function util.new_watchdog(timeout) ----@diagnostic disable-next-line: undefined-field - local start_timer = os.startTimer ----@diagnostic disable-next-line: undefined-field - local cancel_timer = os.cancelTimer - local self = { timeout = timeout, - wd_timer = start_timer(timeout) + wd_timer = util.start_timer(timeout) } ---@class watchdog @@ -333,15 +355,15 @@ function util.new_watchdog(timeout) -- satiate the beast function public.feed() if self.wd_timer ~= nil then - cancel_timer(self.wd_timer) + util.cancel_timer(self.wd_timer) end - self.wd_timer = start_timer(self.timeout) + self.wd_timer = util.start_timer(self.timeout) end -- cancel the watchdog function public.cancel() if self.wd_timer ~= nil then - cancel_timer(self.wd_timer) + util.cancel_timer(self.wd_timer) end end @@ -355,9 +377,6 @@ end --- --- fires a timer event at the specified period, does not start at construct time function util.new_clock(period) ----@diagnostic disable-next-line: undefined-field - local start_timer = os.startTimer - local self = { period = period, timer = nil @@ -373,7 +392,7 @@ function util.new_clock(period) -- start the clock function public.start() - self.timer = start_timer(self.period) + self.timer = util.start_timer(self.period) end return public