diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index da24e11..04db0a0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,11 +22,10 @@ jobs: uses: lunarmodules/luacheck@v1.1.0 with: # Argument Explanations - # -a = Disable warning for unused arguments # -i 121 = Setting a read-only global variable # 512 = Loop can be executed at most once # 542 = An empty if branch # --no-max-line-length = Disable warnings for long line lengths # --exclude-files ... = Exclude lockbox library (external) and config files # --globals ... = Override all globals overridden in .vscode/settings.json AND 'os' since CraftOS 'os' differs from Lua's 'os' - args: . --no-max-line-length -a -i 121 512 542 --exclude-files ./lockbox/* ./*/config.lua --globals os _HOST bit colors fs http parallel periphemu peripheral read rs settings shell term textutils window + args: . --no-max-line-length -i 121 512 542 --exclude-files ./lockbox/* ./*/config.lua --globals os _HOST bit colors fs http parallel periphemu peripheral read rs settings shell term textutils window diff --git a/coordinator/config.lua b/coordinator/config.lua index 8e12c53..3196e80 100644 --- a/coordinator/config.lua +++ b/coordinator/config.lua @@ -28,5 +28,7 @@ config.LOG_PATH = "/log.txt" -- 0 = APPEND (adds to existing file on start) -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 +-- true to log verbose debug messages +config.LOG_DEBUG = false return config diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index e26546d..24e839c 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -18,11 +18,15 @@ local iocontrol = {} ---@class ioctl local io = {} +-- luacheck: no unused args + -- placeholder acknowledge function for type hinting ---@param success boolean ---@diagnostic disable-next-line: unused-local local function __generic_ack(success) end +-- luacheck: unused args + -- initialize the coordinator IO controller ---@param conf facility_conf configuration ---@param comms coord_comms comms reference diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index 4aa0b53..ffb36bc 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -163,9 +163,9 @@ end function renderer.ui_ready() return engine.ui_ready end -- handle a touch event ----@param event mouse_interaction +---@param event mouse_interaction|nil function renderer.handle_mouse(event) - if engine.ui_ready then + if engine.ui_ready and event ~= nil then if event.monitor == engine.monitors.primary_name then engine.ui.main_display.handle_mouse(event) else diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 97408da..2944c14 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -20,7 +20,7 @@ local sounder = require("coordinator.sounder") local apisessions = require("coordinator.session.apisessions") -local COORDINATOR_VERSION = "v0.13.8" +local COORDINATOR_VERSION = "v0.14.0" local println = util.println local println_ts = util.println_ts @@ -50,6 +50,7 @@ cfv.assert_type_num(config.SOUNDER_VOLUME) cfv.assert_type_bool(config.TIME_24_HOUR) cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_int(config.LOG_MODE) +cfv.assert_type_bool(config.LOG_DEBUG) assert(cfv.valid(), "bad config file: missing/invalid fields") @@ -57,7 +58,7 @@ assert(cfv.valid(), "bad config file: missing/invalid fields") -- log init ---------------------------------------- -log.init(config.LOG_PATH, config.LOG_MODE) +log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG) log.info("========================================") log.info("BOOTING coordinator.startup " .. COORDINATOR_VERSION) @@ -358,7 +359,7 @@ local function main() end elseif event == "monitor_touch" then -- handle a monitor touch event - renderer.handle_mouse(core.events.touch(param1, param2, param3)) + renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) elseif event == "speaker_audio_empty" then -- handle speaker buffer emptied sounder.continue() diff --git a/coordinator/ui/components/boiler.lua b/coordinator/ui/components/boiler.lua index c4a433b..9ed497c 100644 --- a/coordinator/ui/components/boiler.lua +++ b/coordinator/ui/components/boiler.lua @@ -9,8 +9,8 @@ local DataIndicator = require("graphics.elements.indicators.data") local StateIndicator = require("graphics.elements.indicators.state") local VerticalBar = require("graphics.elements.indicators.vbar") -local cpair = core.graphics.cpair -local border = core.graphics.border +local cpair = core.cpair +local border = core.border -- new boiler view ---@param root graphics_element parent diff --git a/coordinator/ui/components/imatrix.lua b/coordinator/ui/components/imatrix.lua index 2910fbb..c94ff9f 100644 --- a/coordinator/ui/components/imatrix.lua +++ b/coordinator/ui/components/imatrix.lua @@ -13,10 +13,10 @@ local PowerIndicator = require("graphics.elements.indicators.power") local StateIndicator = require("graphics.elements.indicators.state") local VerticalBar = require("graphics.elements.indicators.vbar") -local cpair = core.graphics.cpair -local border = core.graphics.border +local cpair = core.cpair +local border = core.border -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -- new induction matrix view ---@param root graphics_element parent diff --git a/coordinator/ui/components/processctl.lua b/coordinator/ui/components/processctl.lua index 5b8d8ae..8719968 100644 --- a/coordinator/ui/components/processctl.lua +++ b/coordinator/ui/components/processctl.lua @@ -21,10 +21,10 @@ local HazardButton = require("graphics.elements.controls.hazard_button") local RadioButton = require("graphics.elements.controls.radio_button") local SpinboxNumeric = require("graphics.elements.controls.spinbox_numeric") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local cpair = core.graphics.cpair -local border = core.graphics.border +local cpair = core.cpair +local border = core.border local period = core.flasher.PERIOD diff --git a/coordinator/ui/components/reactor.lua b/coordinator/ui/components/reactor.lua index db75fb1..578bbba 100644 --- a/coordinator/ui/components/reactor.lua +++ b/coordinator/ui/components/reactor.lua @@ -11,8 +11,8 @@ local DataIndicator = require("graphics.elements.indicators.data") local HorizontalBar = require("graphics.elements.indicators.hbar") local StateIndicator = require("graphics.elements.indicators.state") -local cpair = core.graphics.cpair -local border = core.graphics.border +local cpair = core.cpair +local border = core.border -- create new reactor view ---@param root graphics_element parent diff --git a/coordinator/ui/components/turbine.lua b/coordinator/ui/components/turbine.lua index e4d6967..3bfd731 100644 --- a/coordinator/ui/components/turbine.lua +++ b/coordinator/ui/components/turbine.lua @@ -12,8 +12,8 @@ local PowerIndicator = require("graphics.elements.indicators.power") local StateIndicator = require("graphics.elements.indicators.state") local VerticalBar = require("graphics.elements.indicators.vbar") -local cpair = core.graphics.cpair -local border = core.graphics.border +local cpair = core.cpair +local border = core.border -- new turbine view ---@param root graphics_element parent diff --git a/coordinator/ui/components/unit_detail.lua b/coordinator/ui/components/unit_detail.lua index a29dd4b..7181430 100644 --- a/coordinator/ui/components/unit_detail.lua +++ b/coordinator/ui/components/unit_detail.lua @@ -26,10 +26,10 @@ local PushButton = require("graphics.elements.controls.push_button") local RadioButton = require("graphics.elements.controls.radio_button") local SpinboxNumeric = require("graphics.elements.controls.spinbox_numeric") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local cpair = core.graphics.cpair -local border = core.graphics.border +local cpair = core.cpair +local border = core.border local period = core.flasher.PERIOD diff --git a/coordinator/ui/components/unit_overview.lua b/coordinator/ui/components/unit_overview.lua index 24bc02e..bd341bf 100644 --- a/coordinator/ui/components/unit_overview.lua +++ b/coordinator/ui/components/unit_overview.lua @@ -14,9 +14,9 @@ local Div = require("graphics.elements.div") local PipeNetwork = require("graphics.elements.pipenet") local TextBox = require("graphics.elements.textbox") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local pipe = core.graphics.pipe +local pipe = core.pipe -- make a new unit overview window ---@param parent graphics_element parent diff --git a/coordinator/ui/layout/main_view.lua b/coordinator/ui/layout/main_view.lua index a7f8ae2..5510382 100644 --- a/coordinator/ui/layout/main_view.lua +++ b/coordinator/ui/layout/main_view.lua @@ -18,9 +18,9 @@ local TextBox = require("graphics.elements.textbox") local DataIndicator = require("graphics.elements.indicators.data") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local cpair = core.graphics.cpair +local cpair = core.cpair -- create new main view ---@param main graphics_element main displaybox diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua index 74923f2..b78fc91 100644 --- a/coordinator/ui/style.lua +++ b/coordinator/ui/style.lua @@ -6,7 +6,7 @@ local core = require("graphics.core") local style = {} -local cpair = core.graphics.cpair +local cpair = core.cpair -- GLOBAL -- diff --git a/graphics/core.lua b/graphics/core.lua index d03e551..58b6b8c 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -1,96 +1,19 @@ -- --- Graphics Core Functions and Objects +-- Graphics Core Types, Checks, and Constructors -- +local events = require("graphics.events") +local flasher = require("graphics.flasher") + local core = {} -local flasher = require("graphics.flasher") - core.flasher = flasher - -local events = {} - ----@enum click_type -events.click_type = { - VIRTUAL = 0, - LEFT_BUTTON = 1, - RIGHT_BUTTON = 2, - MID_BUTTON = 3 -} - ----@class mouse_interaction ----@field monitor string ----@field button integer ----@field x integer ----@field y integer - --- create a new monitor touch mouse interaction event ----@nodiscard ----@param monitor string ----@param x integer ----@param y integer ----@return mouse_interaction -function events.touch(monitor, x, y) - return { - monitor = monitor, - button = events.click_type.LEFT_BUTTON, - x = x, - y = y - } -end - --- create a new mouse click mouse interaction event ----@nodiscard ----@param button click_type ----@param x integer ----@param y integer ----@return mouse_interaction -function events.click(button, x, y) - return { - monitor = "terminal", - button = button, - x = x, - y = y - } -end - --- create a new transposed mouse interaction event using the event's monitor/button fields ----@nodiscard ----@param event mouse_interaction ----@param new_x integer ----@param new_y integer ----@return mouse_interaction -function events.mouse_transposed(event, new_x, new_y) - return { - monitor = event.monitor, - button = event.button, - x = new_x, - y = new_y - } -end - --- create a new generic mouse interaction event ----@nodiscard ----@param monitor string ----@param button click_type ----@param x integer ----@param y integer ----@return mouse_interaction -function events.mouse_generic(monitor, button, x, y) - return { - monitor = monitor, - button = button, - x = x, - y = y - } -end - core.events = events -local graphics = {} +-- Core Types ---@enum TEXT_ALIGN -graphics.TEXT_ALIGN = { +core.TEXT_ALIGN = { LEFT = 1, CENTER = 2, RIGHT = 3 @@ -109,7 +32,7 @@ graphics.TEXT_ALIGN = { ---@param color color border color ---@param even? boolean whether to pad width extra to account for rectangular pixels, defaults to false ---@return graphics_border -function graphics.border(width, color, even) +function core.border(width, color, even) return { width = width, color = color, @@ -130,7 +53,7 @@ end ---@param w integer ---@param h integer ---@return graphics_frame -function graphics.gframe(x, y, w, h) +function core.gframe(x, y, w, h) return { x = x, y = y, @@ -154,7 +77,7 @@ end ---@param a color ---@param b color ---@return cpair -function graphics.cpair(a, b) +function core.cpair(a, b) return { -- color pairs color_a = a, @@ -191,7 +114,7 @@ end ---@param thin? boolean true for 1 subpixel, false (default) for 2 ---@param align_tr? boolean false to align bottom left (default), true to align top right ---@return pipe -function graphics.pipe(x1, y1, x2, y2, color, thin, align_tr) +function core.pipe(x1, y1, x2, y2, color, thin, align_tr) return { x1 = x1, y1 = y1, @@ -205,6 +128,4 @@ function graphics.pipe(x1, y1, x2, y2, color, thin, align_tr) } end -core.graphics = graphics - return core diff --git a/graphics/element.lua b/graphics/element.lua index 2265888..edcbdcc 100644 --- a/graphics/element.lua +++ b/graphics/element.lua @@ -28,6 +28,7 @@ local element = {} ---|sidebar_args ---|spinbox_args ---|switch_button_args +---|tabbar_args ---|alarm_indicator_light ---|core_map_args ---|data_indicator_args @@ -59,10 +60,10 @@ function element.new(args) id = -1, elem_type = debug.getinfo(2).name, define_completed = false, - p_window = nil, ---@type table - position = { x = 1, y = 1 }, + p_window = nil, ---@type table + position = { x = 1, y = 1 }, ---@type coordinate_2d child_offset = { x = 0, y = 0 }, - bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1}, + bounds = { x1 = 1, y1 = 1, x2 = 1, y2 = 1 }, ---@class element_bounds next_y = 1, children = {}, mt = {} @@ -73,15 +74,26 @@ function element.new(args) enabled = true, value = nil, ---@type any window = nil, ---@type table - fg_bg = core.graphics.cpair(colors.white, colors.black), - frame = core.graphics.gframe(1, 1, 1, 1) + fg_bg = core.cpair(colors.white, colors.black), + frame = core.gframe(1, 1, 1, 1) } + local name_brief = "graphics.element{" .. self.elem_type .. "}: " + -- element as string function self.mt.__tostring() return "graphics.element{" .. self.elem_type .. "} @ " .. tostring(self) end + -- check if a coordinate is within the bounds of this element + ---@param x integer + ---@param y integer + local function _in_bounds(x, y) + local in_x = x >= self.bounds.x1 and x <= self.bounds.x2 + local in_y = y >= self.bounds.y1 and y <= self.bounds.y2 + return in_x and in_y + end + ---@class graphics_element local public = {} @@ -138,10 +150,10 @@ function element.new(args) end -- check frame - assert(f.x >= 1, "graphics.element{" .. self.elem_type .. "}: frame x not >= 1") - assert(f.y >= 1, "graphics.element{" .. self.elem_type .. "}: frame y not >= 1") - assert(f.w >= 1, "graphics.element{" .. self.elem_type .. "}: frame width not >= 1") - assert(f.h >= 1, "graphics.element{" .. self.elem_type .. "}: frame height not >= 1") + assert(f.x >= 1, name_brief .. "frame x not >= 1") + assert(f.y >= 1, name_brief .. "frame y not >= 1") + assert(f.w >= 1, name_brief .. "frame width not >= 1") + assert(f.h >= 1, name_brief .. "frame height not >= 1") -- create window protected.window = window.create(self.p_window, f.x, f.y, f.w, f.h, true) @@ -168,6 +180,7 @@ function element.new(args) self.bounds.y2 = self.position.y + f.h - 1 end +-- luacheck: push ignore ---@diagnostic disable: unused-local, unused-vararg -- handle a mouse event @@ -224,6 +237,7 @@ function element.new(args) function protected.resize(...) end +-- luacheck: pop ---@diagnostic enable: unused-local, unused-vararg -- start animations @@ -250,7 +264,7 @@ function element.new(args) end -- check window - assert(self.p_window, "graphics.element{" .. self.elem_type .. "}: no parent window provided") + assert(self.p_window, name_brief .. "no parent window provided") -- prepare the template if args.parent == nil then @@ -419,17 +433,18 @@ function element.new(args) -- handle a monitor touch or mouse click ---@param event mouse_interaction mouse interaction event function public.handle_mouse(event) - local in_x = event.x >= self.bounds.x1 and event.x <= self.bounds.x2 - local in_y = event.y >= self.bounds.y1 and event.y <= self.bounds.y2 + local x_ini, y_ini, x_cur, y_cur = event.initial.x, event.initial.y, event.current.x, event.current.y - if in_x and in_y then - local event_T = core.events.mouse_transposed(event, (event.x - self.position.x) + 1, (event.y - self.position.y) + 1) + local ini_in = _in_bounds(x_ini, y_ini) + local cur_in = _in_bounds(x_cur, y_cur) - -- handle the touch event, transformed into the window frame + if ini_in then + local event_T = core.events.mouse_transposed(event, self.position.x, self.position.y) + if not cur_in then event_T.type = core.events.CLICK_TYPE.EXITED end + + -- handle the mouse event then pass to children protected.handle_mouse(event_T) - - -- pass on touch event to children - for _, val in pairs(self.children) do val.handle_mouse(event_T) end + for _, child in pairs(self.children) do child.handle_mouse(event_T) end end end diff --git a/graphics/elements/controls/hazard_button.lua b/graphics/elements/controls/hazard_button.lua index e9f2bf4..4dca5c4 100644 --- a/graphics/elements/controls/hazard_button.lua +++ b/graphics/elements/controls/hazard_button.lua @@ -142,24 +142,25 @@ local function hazard_button(args) -- handle mouse interaction ---@param event mouse_interaction mouse event ----@diagnostic disable-next-line: unused-local function e.handle_mouse(event) if e.enabled then - -- change text color to indicate clicked - e.window.setTextColor(args.accent) - e.window.setCursorPos(3, 2) - e.window.write(args.text) + if core.events.was_clicked(event.type) then + -- change text color to indicate clicked + e.window.setTextColor(args.accent) + e.window.setCursorPos(3, 2) + e.window.write(args.text) - -- abort any other callbacks - tcd.abort(on_timeout) - tcd.abort(on_success) - tcd.abort(on_failure) + -- abort any other callbacks + tcd.abort(on_timeout) + tcd.abort(on_success) + tcd.abort(on_failure) - -- 1.5 second timeout - tcd.dispatch(1.5, on_timeout) + -- 1.5 second timeout + tcd.dispatch(1.5, on_timeout) - -- call the touch callback - args.callback() + -- call the touch callback + args.callback() + end end end @@ -167,18 +168,13 @@ local function hazard_button(args) ---@param result boolean true for success, false for failure function e.response_callback(result) tcd.abort(on_timeout) - - if result then - on_success() - else - on_failure(0) - end + if result then on_success() else on_failure(0) end end -- set the value (true simulates pressing the button) ---@param val boolean new value function e.set_value(val) - if val then e.handle_mouse(core.events.mouse_generic("", core.events.click_type.VIRTUAL, 1, 1)) end + if val then e.handle_mouse(core.events.mouse_generic(core.events.CLICK_TYPE.UP, 1, 1)) end end -- show the button as disabled diff --git a/graphics/elements/controls/multi_button.lua b/graphics/elements/controls/multi_button.lua index 2549e2b..e44bad0 100644 --- a/graphics/elements/controls/multi_button.lua +++ b/graphics/elements/controls/multi_button.lua @@ -2,13 +2,13 @@ local util = require("scada-common.util") +local core = require("graphics.core") local element = require("graphics.element") ---@class button_option ---@field text string ---@field fg_bg cpair ---@field active_fg_bg cpair ----@field _lpad integer automatically calculated left pad ---@field _start_x integer starting touch x range (inclusive) ---@field _end_x integer ending touch x range (inclusive) @@ -62,9 +62,7 @@ local function multi_button(args) local next_x = 2 for i = 1, #args.options do local opt = args.options[i] ---@type button_option - local w = string.len(opt.text) - opt._lpad = math.floor((e.frame.w - w) / 2) opt._start_x = next_x opt._end_x = next_x + button_width - 1 @@ -92,20 +90,32 @@ local function multi_button(args) end end + -- check which button a given x is within + ---@return integer|nil button index or nil if not within a button + local function which_button(x) + for i = 1, #args.options do + local opt = args.options[i] ---@type button_option + if x >= opt._start_x and x <= opt._end_x then return i end + end + + return nil + end + -- handle mouse interaction ---@param event mouse_interaction mouse event ----@diagnostic disable-next-line: unused-local function e.handle_mouse(event) - -- determine what was pressed - if e.enabled and event.y == 1 then - for i = 1, #args.options do - local opt = args.options[i] ---@type button_option + -- if enabled and the button row was pressed... + if e.enabled and core.events.was_clicked(event.type) then + -- a button may have been pressed, which one was it? + local button_ini = which_button(event.initial.x) + local button_cur = which_button(event.current.x) - if event.x >= opt._start_x and event.x <= opt._end_x then - e.value = i - draw() - args.callback(e.value) - end + -- mouse up must always have started with a mouse down on the same button to count as a click + -- tap always has identical coordinates, so this always passes for taps + if button_ini == button_cur and button_cur ~= nil then + e.value = button_cur + draw() + args.callback(e.value) end end end diff --git a/graphics/elements/controls/push_button.lua b/graphics/elements/controls/push_button.lua index d0c1299..ed0fc2a 100644 --- a/graphics/elements/controls/push_button.lua +++ b/graphics/elements/controls/push_button.lua @@ -5,6 +5,8 @@ local tcd = require("scada-common.tcallbackdsp") local core = require("graphics.core") local element = require("graphics.element") +local CLICK_TYPE = core.events.CLICK_TYPE + ---@class push_button_args ---@field text string button text ---@field callback function function to call on touch @@ -24,6 +26,8 @@ local element = require("graphics.element") local function push_button(args) assert(type(args.text) == "string", "graphics.elements.controls.push_button: text is a required field") assert(type(args.callback) == "function", "graphics.elements.controls.push_button: callback is a required field") + assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), + "graphics.elements.controls.push_button: min_width must be nil or a number > 0") local text_width = string.len(args.text) @@ -47,38 +51,50 @@ local function push_button(args) e.window.write(args.text) end + -- draw the button as pressed (if active_fg_bg set) + local function show_pressed() + if e.enabled and args.active_fg_bg ~= nil then + e.value = true + e.window.setTextColor(args.active_fg_bg.fgd) + e.window.setBackgroundColor(args.active_fg_bg.bkg) + draw() + end + end + + -- draw the button as unpressed (if active_fg_bg set) + local function show_unpressed() + if e.enabled and args.active_fg_bg ~= nil then + e.value = false + e.window.setTextColor(e.fg_bg.fgd) + e.window.setBackgroundColor(e.fg_bg.bkg) + draw() + end + end + -- handle mouse interaction ---@param event mouse_interaction mouse event ----@diagnostic disable-next-line: unused-local function e.handle_mouse(event) if e.enabled then - if args.active_fg_bg ~= nil then - -- show as pressed - e.value = true - e.window.setTextColor(args.active_fg_bg.fgd) - e.window.setBackgroundColor(args.active_fg_bg.bkg) - draw() - + if event.type == CLICK_TYPE.TAP then + show_pressed() -- show as unpressed in 0.25 seconds - tcd.dispatch(0.25, function () - e.value = false - if e.enabled then - e.window.setTextColor(e.fg_bg.fgd) - e.window.setBackgroundColor(e.fg_bg.bkg) - end - draw() - end) + if args.active_fg_bg ~= nil then tcd.dispatch(0.25, show_unpressed) end + args.callback() + elseif event.type == CLICK_TYPE.DOWN then + show_pressed() + elseif event.type == CLICK_TYPE.UP then + show_unpressed() + args.callback() + elseif event.type == CLICK_TYPE.EXITED then + show_unpressed() end - - -- call the touch callback - args.callback() end end -- set the value (true simulates pressing the button) ---@param val boolean new value function e.set_value(val) - if val then e.handle_mouse(core.events.mouse_generic("", core.events.click_type.VIRTUAL, 1, 1)) end + if val then e.handle_mouse(core.events.mouse_generic(core.events.CLICK_TYPE.UP, 1, 1)) end end -- show butten as enabled diff --git a/graphics/elements/controls/radio_button.lua b/graphics/elements/controls/radio_button.lua index 3b2a593..050bf39 100644 --- a/graphics/elements/controls/radio_button.lua +++ b/graphics/elements/controls/radio_button.lua @@ -1,5 +1,6 @@ -- Radio Button Graphics Element +local core = require("graphics.core") local element = require("graphics.element") ---@class radio_button_args @@ -82,10 +83,10 @@ local function radio_button(args) -- handle mouse interaction ---@param event mouse_interaction mouse event function e.handle_mouse(event) - -- determine what was pressed - if e.enabled then - if args.options[event.y] ~= nil then - e.value = event.y + if e.enabled and core.events.was_clicked(event.type) and (event.initial.y == event.current.y) then + -- determine what was pressed + if args.options[event.current.y] ~= nil then + e.value = event.current.y draw() args.callback(e.value) end diff --git a/graphics/elements/controls/sidebar.lua b/graphics/elements/controls/sidebar.lua index 885761d..997b372 100644 --- a/graphics/elements/controls/sidebar.lua +++ b/graphics/elements/controls/sidebar.lua @@ -2,8 +2,11 @@ local tcd = require("scada-common.tcallbackdsp") +local core = require("graphics.core") local element = require("graphics.element") +local CLICK_TYPE = core.events.CLICK_TYPE + ---@class sidebar_tab ---@field char string character identifier ---@field color cpair tab colors (fg/bg) @@ -39,7 +42,10 @@ local function sidebar(args) -- show the button state ---@param pressed boolean if the currently selected tab should appear as actively pressed - local function draw(pressed) + ---@param pressed_idx? integer optional index to show as held (that is not yet selected) + local function draw(pressed, pressed_idx) + pressed_idx = pressed_idx or e.value + for i = 1, #args.tabs do local tab = args.tabs[i] ---@type sidebar_tab @@ -47,7 +53,7 @@ local function sidebar(args) e.window.setCursorPos(1, y) - if pressed and e.value == i then + if pressed and i == pressed_idx then e.window.setTextColor(e.fg_bg.fgd) e.window.setBackgroundColor(e.fg_bg.bkg) else @@ -74,16 +80,27 @@ local function sidebar(args) function e.handle_mouse(event) -- determine what was pressed if e.enabled then - local idx = math.ceil(event.y / 3) + local cur_idx = math.ceil(event.current.y / 3) + local ini_idx = math.ceil(event.initial.y / 3) - if args.tabs[idx] ~= nil then - e.value = idx - draw(true) - - -- show as unpressed in 0.25 seconds - tcd.dispatch(0.25, function () draw(false) end) - - args.callback(e.value) + if args.tabs[cur_idx] ~= nil then + if event.type == CLICK_TYPE.TAP then + e.value = cur_idx + draw(true) + -- show as unpressed in 0.25 seconds + tcd.dispatch(0.25, function () draw(false) end) + args.callback(e.value) + elseif event.type == CLICK_TYPE.DOWN then + draw(true, cur_idx) + elseif event.type == CLICK_TYPE.UP then + if cur_idx == ini_idx then + e.value = cur_idx + draw(false) + args.callback(e.value) + else draw(false) end + elseif event.type == CLICK_TYPE.EXITED then + draw(false) + end end end end diff --git a/graphics/elements/controls/spinbox_numeric.lua b/graphics/elements/controls/spinbox_numeric.lua index 15e0e76..6b88c0e 100644 --- a/graphics/elements/controls/spinbox_numeric.lua +++ b/graphics/elements/controls/spinbox_numeric.lua @@ -2,6 +2,7 @@ local util = require("scada-common.util") +local core = require("graphics.core") local element = require("graphics.element") ---@class spinbox_args @@ -130,19 +131,22 @@ local function spinbox(args) ---@param event mouse_interaction mouse event function e.handle_mouse(event) -- only handle if on an increment or decrement arrow - if e.enabled and event.x ~= dec_point_x then - local idx = util.trinary(event.x > dec_point_x, event.x - 1, event.x) - if digits[idx] ~= nil then - if event.y == 1 then - -- increment - digits[idx] = digits[idx] + 1 - elseif event.y == 3 then - -- decrement - digits[idx] = digits[idx] - 1 - end + if e.enabled and core.events.was_clicked(event.type) and + (event.current.x ~= dec_point_x) and (event.current.y ~= 2) then + if event.current.x == event.initial.x and event.current.y == event.initial.y then + local idx = util.trinary(event.current.x > dec_point_x, event.current.x - 1, event.current.x) + if digits[idx] ~= nil then + if event.current.y == 1 then + -- increment + digits[idx] = digits[idx] + 1 + elseif event.current.y == 3 then + -- decrement + digits[idx] = digits[idx] - 1 + end - update_value() - show_num() + update_value() + show_num() + end end end end diff --git a/graphics/elements/controls/switch_button.lua b/graphics/elements/controls/switch_button.lua index 133ea45..645bf8a 100644 --- a/graphics/elements/controls/switch_button.lua +++ b/graphics/elements/controls/switch_button.lua @@ -1,5 +1,6 @@ -- Button Graphics Element +local core = require("graphics.core") local element = require("graphics.element") ---@class switch_button_args @@ -22,13 +23,15 @@ local function switch_button(args) assert(type(args.text) == "string", "graphics.elements.controls.switch_button: text is a required field") assert(type(args.callback) == "function", "graphics.elements.controls.switch_button: callback is a required field") assert(type(args.active_fg_bg) == "table", "graphics.elements.controls.switch_button: active_fg_bg is a required field") + assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), + "graphics.elements.controls.switch_button: min_width must be nil or a number > 0") - -- single line - args.height = 1 - - -- determine widths local text_width = string.len(args.text) - args.width = math.max(text_width + 2, args.min_width) + + -- single line height, calculate width + args.height = 1 + args.min_width = args.min_width or 0 + args.width = math.max(text_width, args.min_width) -- create new graphics element base object local e = element.new(args) @@ -64,9 +67,8 @@ local function switch_button(args) -- handle mouse interaction ---@param event mouse_interaction mouse event ----@diagnostic disable-next-line: unused-local function e.handle_mouse(event) - if e.enabled then + if e.enabled and core.events.was_clicked(event.type) then -- toggle state e.value = not e.value draw_state() diff --git a/graphics/elements/controls/tabbar.lua b/graphics/elements/controls/tabbar.lua new file mode 100644 index 0000000..6249951 --- /dev/null +++ b/graphics/elements/controls/tabbar.lua @@ -0,0 +1,130 @@ +-- Tab Bar Graphics Element + +local util = require("scada-common.util") + +local core = require("graphics.core") +local element = require("graphics.element") + +---@class tabbar_tab +---@field name string tab name +---@field color cpair tab colors (fg/bg) +---@field _start_x integer starting touch x range (inclusive) +---@field _end_x integer ending touch x range (inclusive) + +---@class tabbar_args +---@field tabs table tab options +---@field callback function function to call on tab change +---@field min_width? integer text length + 2 if omitted +---@field parent graphics_element +---@field id? string element id +---@field x? integer 1 if omitted +---@field y? integer 1 if omitted +---@field width? integer parent width if omitted +---@field fg_bg? cpair foreground/background colors + +-- new tab selector +---@param args tabbar_args +---@return graphics_element element, element_id id +local function tabbar(args) + assert(type(args.tabs) == "table", "graphics.elements.controls.tabbar: tabs is a required field") + assert(#args.tabs > 0, "graphics.elements.controls.tabbar: at least one tab is required") + assert(type(args.callback) == "function", "graphics.elements.controls.tabbar: callback is a required field") + assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), + "graphics.elements.controls.tabbar: min_width must be nil or a number > 0") + + -- always 1 tall + args.height = 1 + + -- determine widths + local max_width = 1 + for i = 1, #args.tabs do + local opt = args.tabs[i] ---@type tabbar_tab + if string.len(opt.name) > max_width then + max_width = string.len(opt.name) + end + end + + local button_width = math.max(max_width, args.min_width or 0) + + -- create new graphics element base object + local e = element.new(args) + + assert(e.frame.w >= (button_width * #args.tabs), "graphics.elements.controls.tabbar: width insufficent to display all tabs") + + -- default to 1st tab + e.value = 1 + + -- calculate required tab dimension information + local next_x = 1 + for i = 1, #args.tabs do + local tab = args.tabs[i] ---@type tabbar_tab + + tab._start_x = next_x + tab._end_x = next_x + button_width - 1 + + next_x = next_x + button_width + end + + -- show the tab state + local function draw() + for i = 1, #args.tabs do + local tab = args.tabs[i] ---@type tabbar_tab + + e.window.setCursorPos(tab._start_x, 1) + + if e.value == i then + e.window.setTextColor(tab.color.fgd) + e.window.setBackgroundColor(tab.color.bkg) + else + e.window.setTextColor(e.fg_bg.fgd) + e.window.setBackgroundColor(e.fg_bg.bkg) + end + + e.window.write(util.pad(tab.name, button_width)) + end + end + + -- check which tab a given x is within + ---@return integer|nil button index or nil if not within a tab + local function which_tab(x) + for i = 1, #args.tabs do + local tab = args.tabs[i] ---@type tabbar_tab + if x >= tab._start_x and x <= tab._end_x then return i end + end + + return nil + end + + -- handle mouse interaction + ---@param event mouse_interaction mouse event + function e.handle_mouse(event) + -- determine what was pressed + if e.enabled and core.events.was_clicked(event.type) then + -- a button may have been pressed, which one was it? + local tab_ini = which_tab(event.initial.x) + local tab_cur = which_tab(event.current.x) + + -- mouse up must always have started with a mouse down on the same tab to count as a click + -- tap always has identical coordinates, so this always passes for taps + if tab_ini == tab_cur and tab_cur ~= nil then + e.value = tab_cur + draw() + args.callback(e.value) + end + end + end + + -- set the value + ---@param val integer new value + function e.set_value(val) + e.value = val + draw() + end + + -- initial draw + draw() + + return e.get() +end + +return tabbar diff --git a/graphics/elements/indicators/coremap.lua b/graphics/elements/indicators/coremap.lua index 323e17c..05434a3 100644 --- a/graphics/elements/indicators/coremap.lua +++ b/graphics/elements/indicators/coremap.lua @@ -26,7 +26,7 @@ local function core_map(args) args.height = 18 -- inherit only foreground color - args.fg_bg = core.graphics.cpair(args.parent.get_fg_bg().fgd, colors.gray) + args.fg_bg = core.cpair(args.parent.get_fg_bg().fgd, colors.gray) -- create new graphics element base object local e = element.new(args) diff --git a/graphics/elements/pipenet.lua b/graphics/elements/pipenet.lua index 71ee9fd..8a1d29b 100644 --- a/graphics/elements/pipenet.lua +++ b/graphics/elements/pipenet.lua @@ -37,7 +37,7 @@ local function pipenet(args) args.y = args.y or 1 if args.bg ~= nil then - args.fg_bg = core.graphics.cpair(args.bg, args.bg) + args.fg_bg = core.cpair(args.bg, args.bg) end -- create new graphics element base object @@ -55,7 +55,7 @@ local function pipenet(args) e.window.setCursorPos(x, y) - local c = core.graphics.cpair(pipe.color, e.fg_bg.bkg) + local c = core.cpair(pipe.color, e.fg_bg.bkg) if pipe.align_tr then -- cross width then height diff --git a/graphics/elements/textbox.lua b/graphics/elements/textbox.lua index c911677..9066deb 100644 --- a/graphics/elements/textbox.lua +++ b/graphics/elements/textbox.lua @@ -5,7 +5,7 @@ local util = require("scada-common.util") local core = require("graphics.core") local element = require("graphics.element") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN ---@class textbox_args ---@field text string text to show diff --git a/graphics/events.lua b/graphics/events.lua new file mode 100644 index 0000000..7370481 --- /dev/null +++ b/graphics/events.lua @@ -0,0 +1,162 @@ +-- +-- Graphics Events and Event Handlers +-- + +local util = require("scada-common.util") + +local events = {} + +---@enum CLICK_BUTTON +events.CLICK_BUTTON = { + GENERIC = 0, + LEFT_BUTTON = 1, + RIGHT_BUTTON = 2, + MID_BUTTON = 3 +} + +---@enum CLICK_TYPE +events.CLICK_TYPE = { + TAP = 1, -- screen tap (complete click) + DOWN = 2, -- button down + UP = 3, -- button up (completed a click) + DRAG = 4, -- mouse dragged + SCROLL_DOWN = 5, -- scroll down + SCROLL_UP = 6, -- scroll up + EXITED = 7 -- cursor exited bounds of element +} + +-- create a new 2D coordinate +---@param x integer +---@param y integer +---@return coordinate_2d +local function _coord2d(x, y) return { x = x, y = y } end + +---@class mouse_interaction +---@field monitor string +---@field button CLICK_BUTTON +---@field type CLICK_TYPE +---@field initial coordinate_2d +---@field current coordinate_2d + +local handler = { + -- left, right, middle button down tracking + button_down = { + _coord2d(0, 0), + _coord2d(0, 0), + _coord2d(0, 0) + } +} + +-- create a new monitor touch mouse interaction event +---@nodiscard +---@param monitor string +---@param x integer +---@param y integer +---@return mouse_interaction +local function _monitor_touch(monitor, x, y) + return { + monitor = monitor, + button = events.CLICK_BUTTON.GENERIC, + type = events.CLICK_TYPE.TAP, + initial = _coord2d(x, y), + current = _coord2d(x, y) + } +end + +-- create a new mouse button mouse interaction event +---@nodiscard +---@param button CLICK_BUTTON mouse button +---@param type CLICK_TYPE click type +---@param x1 integer initial x +---@param y1 integer initial y +---@param x2 integer current x +---@param y2 integer current y +---@return mouse_interaction +local function _mouse_event(button, type, x1, y1, x2, y2) + return { + monitor = "terminal", + button = button, + type = type, + initial = _coord2d(x1, y1), + current = _coord2d(x2, y2) + } +end + +-- create a new generic mouse interaction event +---@nodiscard +---@param type CLICK_TYPE +---@param x integer +---@param y integer +---@return mouse_interaction +function events.mouse_generic(type, x, y) + return { + monitor = "", + button = events.CLICK_BUTTON.GENERIC, + type = type, + initial = _coord2d(x, y), + current = _coord2d(x, y) + } +end + +-- create a new transposed mouse interaction event using the event's monitor/button fields +---@nodiscard +---@param event mouse_interaction +---@param elem_pos_x integer element's x position: new x = (event x - element x) + 1 +---@param elem_pos_y integer element's y position: new y = (event y - element y) + 1 +---@return mouse_interaction +function events.mouse_transposed(event, elem_pos_x, elem_pos_y) + return { + monitor = event.monitor, + button = event.button, + type = event.type, + initial = _coord2d((event.initial.x - elem_pos_x) + 1, (event.initial.y - elem_pos_y) + 1), + current = _coord2d((event.current.x - elem_pos_x) + 1, (event.current.y - elem_pos_y) + 1) + } +end + +-- check if an event qualifies as a click (tap or up) +---@nodiscard +---@param t CLICK_TYPE +function events.was_clicked(t) return t == events.CLICK_TYPE.TAP or t == events.CLICK_TYPE.UP end + +-- create a new mouse event to pass onto graphics renderer
+-- supports: mouse_click, mouse_up, mouse_drag, mouse_scroll, and monitor_touch +---@param event_type os_event OS event to handle +---@param opt integer|string button, scroll direction, or monitor for monitor touch +---@param x integer x coordinate +---@param y integer y coordinate +---@return mouse_interaction|nil +function events.new_mouse_event(event_type, opt, x, y) + if event_type == "mouse_click" then + ---@cast opt 1|2|3 + handler.button_down[opt] = _coord2d(x, y) + return _mouse_event(opt, events.CLICK_TYPE.DOWN, x, y, x, y) + elseif event_type == "mouse_up" then + ---@cast opt 1|2|3 + local initial = handler.button_down[opt] ---@type coordinate_2d + return _mouse_event(opt, events.CLICK_TYPE.UP, initial.x, initial.y, x, y) + elseif event_type == "monitor_touch" then + ---@cast opt string + return _monitor_touch(opt, x, y) + elseif event_type == "mouse_drag" then + ---@cast opt 1|2|3 + local initial = handler.button_down[opt] ---@type coordinate_2d + return _mouse_event(opt, events.CLICK_TYPE.DRAG, initial.x, initial.y, x, y) + elseif event_type == "mouse_scroll" then + ---@cast opt 1|-1 + local scroll_direction = util.trinary(opt == 1, events.CLICK_TYPE.SCROLL_DOWN, events.CLICK_TYPE.SCROLL_UP) + return _mouse_event(events.CLICK_BUTTON.GENERIC, scroll_direction, x, y, x, y) + end +end + +-- create a new key event to pass onto graphics renderer
+-- supports: char, key, and key_up +---@param event_type os_event +function events.new_key_event(event_type) + if event_type == "char" then + elseif event_type == "key" then + elseif event_type == "key_up" then + end +end + +return events diff --git a/install_manifest.json b/install_manifest.json index 91054c0..5da3a0c 100644 --- a/install_manifest.json +++ b/install_manifest.json @@ -1 +1 @@ -{"versions": {"installer": "v1.0", "bootloader": "0.2", "comms": "1.4.1", "reactor-plc": "v1.1.17", "rtu": "v1.0.5", "supervisor": "v0.15.5", "coordinator": "v0.13.8", "pocket": "alpha-v0.2.6"}, "files": {"system": ["initenv.lua", "startup.lua"], "common": ["scada-common/crypto.lua", "scada-common/ppm.lua", "scada-common/comms.lua", "scada-common/psil.lua", "scada-common/tcallbackdsp.lua", "scada-common/rsio.lua", "scada-common/constants.lua", "scada-common/mqueue.lua", "scada-common/crash.lua", "scada-common/log.lua", "scada-common/types.lua", "scada-common/util.lua"], "graphics": ["graphics/element.lua", "graphics/flasher.lua", "graphics/core.lua", "graphics/elements/textbox.lua", "graphics/elements/displaybox.lua", "graphics/elements/pipenet.lua", "graphics/elements/rectangle.lua", "graphics/elements/div.lua", "graphics/elements/multipane.lua", "graphics/elements/tiling.lua", "graphics/elements/colormap.lua", "graphics/elements/indicators/alight.lua", "graphics/elements/indicators/icon.lua", "graphics/elements/indicators/power.lua", "graphics/elements/indicators/rad.lua", "graphics/elements/indicators/state.lua", "graphics/elements/indicators/light.lua", "graphics/elements/indicators/vbar.lua", "graphics/elements/indicators/led.lua", "graphics/elements/indicators/coremap.lua", "graphics/elements/indicators/data.lua", "graphics/elements/indicators/ledpair.lua", "graphics/elements/indicators/hbar.lua", "graphics/elements/indicators/trilight.lua", "graphics/elements/indicators/ledrgb.lua", "graphics/elements/controls/switch_button.lua", "graphics/elements/controls/spinbox_numeric.lua", "graphics/elements/controls/hazard_button.lua", "graphics/elements/controls/push_button.lua", "graphics/elements/controls/radio_button.lua", "graphics/elements/controls/multi_button.lua", "graphics/elements/controls/sidebar.lua", "graphics/elements/animations/waiting.lua"], "lockbox": ["lockbox/init.lua", "lockbox/LICENSE", "lockbox/kdf/pbkdf2.lua", "lockbox/util/bit.lua", "lockbox/util/array.lua", "lockbox/util/stream.lua", "lockbox/util/queue.lua", "lockbox/digest/sha2_224.lua", "lockbox/digest/sha1.lua", "lockbox/digest/sha2_256.lua", "lockbox/cipher/aes128.lua", "lockbox/cipher/aes256.lua", "lockbox/cipher/aes192.lua", "lockbox/cipher/mode/ofb.lua", "lockbox/cipher/mode/cbc.lua", "lockbox/cipher/mode/ctr.lua", "lockbox/cipher/mode/cfb.lua", "lockbox/mac/hmac.lua", "lockbox/padding/ansix923.lua", "lockbox/padding/pkcs7.lua", "lockbox/padding/zero.lua", "lockbox/padding/isoiec7816.lua"], "reactor-plc": ["reactor-plc/renderer.lua", "reactor-plc/threads.lua", "reactor-plc/databus.lua", "reactor-plc/plc.lua", "reactor-plc/config.lua", "reactor-plc/startup.lua", "reactor-plc/panel/front_panel.lua", "reactor-plc/panel/style.lua"], "rtu": ["rtu/renderer.lua", "rtu/threads.lua", "rtu/rtu.lua", "rtu/databus.lua", "rtu/modbus.lua", "rtu/config.lua", "rtu/startup.lua", "rtu/panel/front_panel.lua", "rtu/panel/style.lua", "rtu/dev/sps_rtu.lua", "rtu/dev/envd_rtu.lua", "rtu/dev/boilerv_rtu.lua", "rtu/dev/redstone_rtu.lua", "rtu/dev/sna_rtu.lua", "rtu/dev/imatrix_rtu.lua", "rtu/dev/turbinev_rtu.lua"], "supervisor": ["supervisor/supervisor.lua", "supervisor/unit.lua", "supervisor/config.lua", "supervisor/startup.lua", "supervisor/unitlogic.lua", "supervisor/facility.lua", "supervisor/session/coordinator.lua", "supervisor/session/svqtypes.lua", "supervisor/session/pocket.lua", "supervisor/session/svsessions.lua", "supervisor/session/rtu.lua", "supervisor/session/plc.lua", "supervisor/session/rsctl.lua", "supervisor/session/rtu/boilerv.lua", "supervisor/session/rtu/txnctrl.lua", "supervisor/session/rtu/unit_session.lua", "supervisor/session/rtu/turbinev.lua", "supervisor/session/rtu/envd.lua", "supervisor/session/rtu/imatrix.lua", "supervisor/session/rtu/sps.lua", "supervisor/session/rtu/qtypes.lua", "supervisor/session/rtu/sna.lua", "supervisor/session/rtu/redstone.lua"], "coordinator": ["coordinator/coordinator.lua", "coordinator/renderer.lua", "coordinator/iocontrol.lua", "coordinator/sounder.lua", "coordinator/config.lua", "coordinator/startup.lua", "coordinator/process.lua", "coordinator/ui/dialog.lua", "coordinator/ui/style.lua", "coordinator/ui/layout/main_view.lua", "coordinator/ui/layout/unit_view.lua", "coordinator/ui/components/reactor.lua", "coordinator/ui/components/processctl.lua", "coordinator/ui/components/unit_overview.lua", "coordinator/ui/components/boiler.lua", "coordinator/ui/components/unit_detail.lua", "coordinator/ui/components/imatrix.lua", "coordinator/ui/components/turbine.lua", "coordinator/session/api.lua", "coordinator/session/apisessions.lua"], "pocket": ["pocket/pocket.lua", "pocket/renderer.lua", "pocket/config.lua", "pocket/coreio.lua", "pocket/startup.lua", "pocket/ui/main.lua", "pocket/ui/style.lua", "pocket/ui/components/turbine_page.lua", "pocket/ui/components/reactor_page.lua", "pocket/ui/components/home_page.lua", "pocket/ui/components/unit_page.lua", "pocket/ui/components/boiler_page.lua", "pocket/ui/components/conn_waiting.lua"]}, "depends": {"reactor-plc": ["system", "common", "graphics"], "rtu": ["system", "common", "graphics"], "supervisor": ["system", "common"], "coordinator": ["system", "common", "graphics"], "pocket": ["system", "common", "graphics"]}, "sizes": {"manifest": 5467, "system": 1991, "common": 90337, "graphics": 115781, "lockbox": 100797, "reactor-plc": 95517, "rtu": 100134, "supervisor": 282706, "coordinator": 195981, "pocket": 36123}} \ No newline at end of file +{"versions": {"installer": "v1.0", "bootloader": "0.2", "comms": "1.4.1", "reactor-plc": "v1.2.0", "rtu": "v1.1.0", "supervisor": "v0.15.7", "coordinator": "v0.14.0", "pocket": "alpha-v0.2.6"}, "files": {"system": ["initenv.lua", "startup.lua"], "common": ["scada-common/crypto.lua", "scada-common/ppm.lua", "scada-common/comms.lua", "scada-common/psil.lua", "scada-common/tcallbackdsp.lua", "scada-common/rsio.lua", "scada-common/constants.lua", "scada-common/mqueue.lua", "scada-common/crash.lua", "scada-common/log.lua", "scada-common/types.lua", "scada-common/util.lua"], "graphics": ["graphics/element.lua", "graphics/events.lua", "graphics/flasher.lua", "graphics/core.lua", "graphics/elements/textbox.lua", "graphics/elements/displaybox.lua", "graphics/elements/pipenet.lua", "graphics/elements/rectangle.lua", "graphics/elements/div.lua", "graphics/elements/multipane.lua", "graphics/elements/tiling.lua", "graphics/elements/colormap.lua", "graphics/elements/indicators/alight.lua", "graphics/elements/indicators/icon.lua", "graphics/elements/indicators/power.lua", "graphics/elements/indicators/rad.lua", "graphics/elements/indicators/state.lua", "graphics/elements/indicators/light.lua", "graphics/elements/indicators/vbar.lua", "graphics/elements/indicators/led.lua", "graphics/elements/indicators/coremap.lua", "graphics/elements/indicators/data.lua", "graphics/elements/indicators/ledpair.lua", "graphics/elements/indicators/hbar.lua", "graphics/elements/indicators/trilight.lua", "graphics/elements/indicators/ledrgb.lua", "graphics/elements/controls/switch_button.lua", "graphics/elements/controls/spinbox_numeric.lua", "graphics/elements/controls/hazard_button.lua", "graphics/elements/controls/push_button.lua", "graphics/elements/controls/radio_button.lua", "graphics/elements/controls/multi_button.lua", "graphics/elements/controls/tabbar.lua", "graphics/elements/controls/sidebar.lua", "graphics/elements/animations/waiting.lua"], "lockbox": ["lockbox/init.lua", "lockbox/LICENSE", "lockbox/kdf/pbkdf2.lua", "lockbox/util/bit.lua", "lockbox/util/array.lua", "lockbox/util/stream.lua", "lockbox/util/queue.lua", "lockbox/digest/sha2_224.lua", "lockbox/digest/sha1.lua", "lockbox/digest/sha2_256.lua", "lockbox/cipher/aes128.lua", "lockbox/cipher/aes256.lua", "lockbox/cipher/aes192.lua", "lockbox/cipher/mode/ofb.lua", "lockbox/cipher/mode/cbc.lua", "lockbox/cipher/mode/ctr.lua", "lockbox/cipher/mode/cfb.lua", "lockbox/mac/hmac.lua", "lockbox/padding/ansix923.lua", "lockbox/padding/pkcs7.lua", "lockbox/padding/zero.lua", "lockbox/padding/isoiec7816.lua"], "reactor-plc": ["reactor-plc/renderer.lua", "reactor-plc/threads.lua", "reactor-plc/databus.lua", "reactor-plc/plc.lua", "reactor-plc/config.lua", "reactor-plc/startup.lua", "reactor-plc/panel/front_panel.lua", "reactor-plc/panel/style.lua"], "rtu": ["rtu/renderer.lua", "rtu/threads.lua", "rtu/rtu.lua", "rtu/databus.lua", "rtu/modbus.lua", "rtu/config.lua", "rtu/startup.lua", "rtu/panel/front_panel.lua", "rtu/panel/style.lua", "rtu/dev/sps_rtu.lua", "rtu/dev/envd_rtu.lua", "rtu/dev/boilerv_rtu.lua", "rtu/dev/redstone_rtu.lua", "rtu/dev/sna_rtu.lua", "rtu/dev/imatrix_rtu.lua", "rtu/dev/turbinev_rtu.lua"], "supervisor": ["supervisor/supervisor.lua", "supervisor/unit.lua", "supervisor/config.lua", "supervisor/startup.lua", "supervisor/unitlogic.lua", "supervisor/facility.lua", "supervisor/session/coordinator.lua", "supervisor/session/svqtypes.lua", "supervisor/session/pocket.lua", "supervisor/session/svsessions.lua", "supervisor/session/rtu.lua", "supervisor/session/plc.lua", "supervisor/session/rsctl.lua", "supervisor/session/rtu/boilerv.lua", "supervisor/session/rtu/txnctrl.lua", "supervisor/session/rtu/unit_session.lua", "supervisor/session/rtu/turbinev.lua", "supervisor/session/rtu/envd.lua", "supervisor/session/rtu/imatrix.lua", "supervisor/session/rtu/sps.lua", "supervisor/session/rtu/qtypes.lua", "supervisor/session/rtu/sna.lua", "supervisor/session/rtu/redstone.lua"], "coordinator": ["coordinator/coordinator.lua", "coordinator/renderer.lua", "coordinator/iocontrol.lua", "coordinator/sounder.lua", "coordinator/config.lua", "coordinator/startup.lua", "coordinator/process.lua", "coordinator/ui/dialog.lua", "coordinator/ui/style.lua", "coordinator/ui/layout/main_view.lua", "coordinator/ui/layout/unit_view.lua", "coordinator/ui/components/reactor.lua", "coordinator/ui/components/processctl.lua", "coordinator/ui/components/unit_overview.lua", "coordinator/ui/components/boiler.lua", "coordinator/ui/components/unit_detail.lua", "coordinator/ui/components/imatrix.lua", "coordinator/ui/components/turbine.lua", "coordinator/session/api.lua", "coordinator/session/apisessions.lua"], "pocket": ["pocket/pocket.lua", "pocket/renderer.lua", "pocket/config.lua", "pocket/coreio.lua", "pocket/startup.lua", "pocket/ui/main.lua", "pocket/ui/style.lua", "pocket/ui/components/turbine_page.lua", "pocket/ui/components/reactor_page.lua", "pocket/ui/components/home_page.lua", "pocket/ui/components/unit_page.lua", "pocket/ui/components/boiler_page.lua", "pocket/ui/components/conn_waiting.lua"]}, "depends": {"reactor-plc": ["system", "common", "graphics"], "rtu": ["system", "common", "graphics"], "supervisor": ["system", "common"], "coordinator": ["system", "common", "graphics"], "pocket": ["system", "common", "graphics"]}, "sizes": {"manifest": 5530, "system": 1991, "common": 90635, "graphics": 126610, "lockbox": 100797, "reactor-plc": 95708, "rtu": 100971, "supervisor": 283160, "coordinator": 196014, "pocket": 36221}} \ No newline at end of file diff --git a/pocket/config.lua b/pocket/config.lua index cacd9f1..27e1489 100644 --- a/pocket/config.lua +++ b/pocket/config.lua @@ -17,5 +17,7 @@ config.LOG_PATH = "/log.txt" -- 0 = APPEND (adds to existing file on start) -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 +-- true to log verbose debug messages +config.LOG_DEBUG = false return config diff --git a/pocket/renderer.lua b/pocket/renderer.lua index 1ecf3ab..fa25bcd 100644 --- a/pocket/renderer.lua +++ b/pocket/renderer.lua @@ -70,9 +70,9 @@ end function renderer.ui_ready() return ui.display ~= nil end -- handle a mouse event ----@param event mouse_interaction +---@param event mouse_interaction|nil function renderer.handle_mouse(event) - if ui.display ~= nil then + if ui.display ~= nil and event ~= nil then ui.display.handle_mouse(event) end end diff --git a/pocket/startup.lua b/pocket/startup.lua index 8397437..552f67f 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -36,6 +36,7 @@ cfv.assert_type_num(config.COMMS_TIMEOUT) cfv.assert_min(config.COMMS_TIMEOUT, 2) cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_int(config.LOG_MODE) +cfv.assert_type_bool(config.LOG_DEBUG) assert(cfv.valid(), "bad config file: missing/invalid fields") @@ -43,7 +44,7 @@ assert(cfv.valid(), "bad config file: missing/invalid fields") -- log init ---------------------------------------- -log.init(config.LOG_PATH, config.LOG_MODE) +log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG) log.info("========================================") log.info("BOOTING pocket.startup " .. POCKET_VERSION) @@ -151,9 +152,9 @@ local function main() -- got a packet local packet = pocket_comms.parse_packet(param1, param2, param3, param4, param5) pocket_comms.handle_packet(packet) - elseif event == "mouse_click" then + elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then -- handle a monitor touch event - renderer.handle_mouse(core.events.touch(param1, param2, param3)) + renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) end -- check for termination request diff --git a/pocket/ui/components/conn_waiting.lua b/pocket/ui/components/conn_waiting.lua index cd08652..9bbbfc0 100644 --- a/pocket/ui/components/conn_waiting.lua +++ b/pocket/ui/components/conn_waiting.lua @@ -11,9 +11,9 @@ local TextBox = require("graphics.elements.textbox") local WaitingAnim = require("graphics.elements.animations.waiting") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local cpair = core.graphics.cpair +local cpair = core.cpair -- create a waiting view ---@param parent graphics_element parent diff --git a/pocket/ui/main.lua b/pocket/ui/main.lua index 18302a3..a1a8c24 100644 --- a/pocket/ui/main.lua +++ b/pocket/ui/main.lua @@ -22,9 +22,9 @@ local TextBox = require("graphics.elements.textbox") local Sidebar = require("graphics.elements.controls.sidebar") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local cpair = core.graphics.cpair +local cpair = core.cpair -- create new main view ---@param main graphics_element main displaybox diff --git a/pocket/ui/pages/boiler_page.lua b/pocket/ui/pages/boiler_page.lua index fd0eca1..ec13d59 100644 --- a/pocket/ui/pages/boiler_page.lua +++ b/pocket/ui/pages/boiler_page.lua @@ -5,9 +5,9 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") --- local cpair = core.graphics.cpair +-- local cpair = core.cpair -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -- new boiler page view ---@param root graphics_element parent diff --git a/pocket/ui/pages/home_page.lua b/pocket/ui/pages/home_page.lua index 5287cac..a31cae8 100644 --- a/pocket/ui/pages/home_page.lua +++ b/pocket/ui/pages/home_page.lua @@ -5,9 +5,9 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") --- local cpair = core.graphics.cpair +-- local cpair = core.cpair -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -- new home page view ---@param root graphics_element parent diff --git a/pocket/ui/pages/reactor_page.lua b/pocket/ui/pages/reactor_page.lua index 50b1939..c7a2e96 100644 --- a/pocket/ui/pages/reactor_page.lua +++ b/pocket/ui/pages/reactor_page.lua @@ -5,9 +5,9 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") --- local cpair = core.graphics.cpair +-- local cpair = core.cpair -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -- new reactor page view ---@param root graphics_element parent diff --git a/pocket/ui/pages/turbine_page.lua b/pocket/ui/pages/turbine_page.lua index 9fd7af5..527e419 100644 --- a/pocket/ui/pages/turbine_page.lua +++ b/pocket/ui/pages/turbine_page.lua @@ -5,9 +5,9 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") --- local cpair = core.graphics.cpair +-- local cpair = core.cpair -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -- new turbine page view ---@param root graphics_element parent diff --git a/pocket/ui/pages/unit_page.lua b/pocket/ui/pages/unit_page.lua index 2e24df3..a0718e6 100644 --- a/pocket/ui/pages/unit_page.lua +++ b/pocket/ui/pages/unit_page.lua @@ -5,9 +5,9 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") --- local cpair = core.graphics.cpair +-- local cpair = core.cpair -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -- new unit page view ---@param root graphics_element parent diff --git a/pocket/ui/style.lua b/pocket/ui/style.lua index b9a09fc..2fb7526 100644 --- a/pocket/ui/style.lua +++ b/pocket/ui/style.lua @@ -6,7 +6,7 @@ local core = require("graphics.core") local style = {} -local cpair = core.graphics.cpair +local cpair = core.cpair -- GLOBAL -- diff --git a/reactor-plc/config.lua b/reactor-plc/config.lua index e479ede..e402bbb 100644 --- a/reactor-plc/config.lua +++ b/reactor-plc/config.lua @@ -24,5 +24,7 @@ config.LOG_PATH = "/log.txt" -- 0 = APPEND (adds to existing file on start) -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 +-- true to log verbose debug messages +config.LOG_DEBUG = false return config diff --git a/reactor-plc/panel/front_panel.lua b/reactor-plc/panel/front_panel.lua index 70c80ef..c000a3d 100644 --- a/reactor-plc/panel/front_panel.lua +++ b/reactor-plc/panel/front_panel.lua @@ -22,10 +22,10 @@ local LED = require("graphics.elements.indicators.led") local LEDPair = require("graphics.elements.indicators.ledpair") local RGBLED = require("graphics.elements.indicators.ledrgb") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local cpair = core.graphics.cpair -local border = core.graphics.border +local cpair = core.cpair +local border = core.border -- create new main view ---@param panel graphics_element main displaybox diff --git a/reactor-plc/panel/style.lua b/reactor-plc/panel/style.lua index 31039d4..996453c 100644 --- a/reactor-plc/panel/style.lua +++ b/reactor-plc/panel/style.lua @@ -6,7 +6,7 @@ local core = require("graphics.core") local style = {} -local cpair = core.graphics.cpair +local cpair = core.cpair -- GLOBAL -- diff --git a/reactor-plc/renderer.lua b/reactor-plc/renderer.lua index 0700ebe..9830751 100644 --- a/reactor-plc/renderer.lua +++ b/reactor-plc/renderer.lua @@ -70,9 +70,9 @@ end function renderer.ui_ready() return ui.display ~= nil end -- handle a mouse event ----@param event mouse_interaction +---@param event mouse_interaction|nil function renderer.handle_mouse(event) - if ui.display ~= nil then + if ui.display ~= nil and event ~= nil then ui.display.handle_mouse(event) end end diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index bd40fce..183fbf3 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.1.17" +local R_PLC_VERSION = "v1.2.0" local println = util.println local println_ts = util.println_ts @@ -38,6 +38,7 @@ cfv.assert_type_num(config.COMMS_TIMEOUT) cfv.assert_min(config.COMMS_TIMEOUT, 2) cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_int(config.LOG_MODE) +cfv.assert_type_bool(config.LOG_DEBUG) assert(cfv.valid(), "bad config file: missing/invalid fields") @@ -54,7 +55,7 @@ end -- log init ---------------------------------------- -log.init(config.LOG_PATH, config.LOG_MODE) +log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG) log.info("========================================") log.info("BOOTING reactor-plc.startup " .. R_PLC_VERSION) diff --git a/reactor-plc/threads.lua b/reactor-plc/threads.lua index 8470430..8dbea6d 100644 --- a/reactor-plc/threads.lua +++ b/reactor-plc/threads.lua @@ -257,9 +257,9 @@ function threads.thread__main(smem, init) -- update indicators databus.tx_hw_status(plc_state) - elseif event == "mouse_click" then - -- handle a monitor touch event - renderer.handle_mouse(core.events.click(param1, param2, param3)) + elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then + -- handle a mouse event + renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) elseif event == "clock_start" then -- start loop clock loop_clock.start() diff --git a/rtu/config.lua b/rtu/config.lua index 96b26ee..1b96bec 100644 --- a/rtu/config.lua +++ b/rtu/config.lua @@ -17,6 +17,8 @@ config.LOG_PATH = "/log.txt" -- 0 = APPEND (adds to existing file on start) -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 +-- true to log verbose debug messages +config.LOG_DEBUG = false -- RTU peripheral devices (named: side/network device name) config.RTU_DEVICES = { diff --git a/rtu/panel/front_panel.lua b/rtu/panel/front_panel.lua index f6014da..8dcaa1f 100644 --- a/rtu/panel/front_panel.lua +++ b/rtu/panel/front_panel.lua @@ -16,9 +16,9 @@ local TextBox = require("graphics.elements.textbox") local LED = require("graphics.elements.indicators.led") local RGBLED = require("graphics.elements.indicators.ledrgb") -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local TEXT_ALIGN = core.TEXT_ALIGN -local cpair = core.graphics.cpair +local cpair = core.cpair local UNIT_TYPE_LABELS = { "UNKNOWN", diff --git a/rtu/panel/style.lua b/rtu/panel/style.lua index 31039d4..996453c 100644 --- a/rtu/panel/style.lua +++ b/rtu/panel/style.lua @@ -6,7 +6,7 @@ local core = require("graphics.core") local style = {} -local cpair = core.graphics.cpair +local cpair = core.cpair -- GLOBAL -- diff --git a/rtu/renderer.lua b/rtu/renderer.lua index f79f19e..490959c 100644 --- a/rtu/renderer.lua +++ b/rtu/renderer.lua @@ -71,9 +71,9 @@ end function renderer.ui_ready() return ui.display ~= nil end -- handle a mouse event ----@param event mouse_interaction +---@param event mouse_interaction|nil function renderer.handle_mouse(event) - if ui.display ~= nil then + if ui.display ~= nil and event ~= nil then ui.display.handle_mouse(event) end end diff --git a/rtu/startup.lua b/rtu/startup.lua index 97832fa..215b4a9 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -28,7 +28,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "v1.0.6" +local RTU_VERSION = "v1.1.1" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE @@ -49,6 +49,7 @@ cfv.assert_type_num(config.COMMS_TIMEOUT) cfv.assert_min(config.COMMS_TIMEOUT, 2) cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_int(config.LOG_MODE) +cfv.assert_type_bool(config.LOG_DEBUG) cfv.assert_type_table(config.RTU_DEVICES) cfv.assert_type_table(config.RTU_REDSTONE) assert(cfv.valid(), "bad config file: missing/invalid fields") @@ -57,7 +58,7 @@ assert(cfv.valid(), "bad config file: missing/invalid fields") -- log init ---------------------------------------- -log.init(config.LOG_PATH, config.LOG_MODE) +log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG) log.info("========================================") log.info("BOOTING rtu.startup " .. RTU_VERSION) diff --git a/rtu/threads.lua b/rtu/threads.lua index 9dac9f1..046525c 100644 --- a/rtu/threads.lua +++ b/rtu/threads.lua @@ -229,9 +229,9 @@ function threads.thread__main(smem) end end end - elseif event == "mouse_click" then - -- handle a monitor touch event - renderer.handle_mouse(core.events.click(param1, param2, param3)) + elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then + -- handle a mouse event + renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) end -- check for termination request diff --git a/scada-common/log.lua b/scada-common/log.lua index 40baef2..7e2d3fd 100644 --- a/scada-common/log.lua +++ b/scada-common/log.lua @@ -15,12 +15,10 @@ local MODE = { log.MODE = MODE --- whether to log debug messages or not -local LOG_DEBUG = true - -local log_sys = { +local logger = { path = "/log.txt", mode = MODE.APPEND, + debug = false, file = nil, dmesg_out = nil } @@ -41,8 +39,8 @@ local function _log(msg) -- attempt to write log local status, result = pcall(function () - log_sys.file.writeLine(stamped) - log_sys.file.flush() + logger.file.writeLine(stamped) + logger.file.flush() end) -- if we don't have space, we need to create a new log file @@ -57,18 +55,18 @@ local function _log(msg) end end - if out_of_space or (free_space(log_sys.path) < 100) then + if out_of_space or (free_space(logger.path) < 100) then -- delete the old log file before opening a new one - log_sys.file.close() - fs.delete(log_sys.path) + logger.file.close() + fs.delete(logger.path) -- re-init logger and pass dmesg_out so that it doesn't change - log.init(log_sys.path, log_sys.mode, log_sys.dmesg_out) + log.init(logger.path, logger.mode, logger.debug, logger.dmesg_out) -- leave a message - log_sys.file.writeLine(time_stamp .. "recycled log file") - log_sys.file.writeLine(stamped) - log_sys.file.flush() + logger.file.writeLine(time_stamp .. "recycled log file") + logger.file.writeLine(stamped) + logger.file.flush() end end @@ -78,33 +76,35 @@ end -- initialize logger ---@param path string file path ----@param write_mode MODE +---@param write_mode MODE file write mode +---@param include_debug boolean whether or not to include debug logs ---@param dmesg_redirect? table terminal/window to direct dmesg to -function log.init(path, write_mode, dmesg_redirect) - log_sys.path = path - log_sys.mode = write_mode +function log.init(path, write_mode, include_debug, dmesg_redirect) + logger.path = path + logger.mode = write_mode + logger.debug = include_debug - if log_sys.mode == MODE.APPEND then - log_sys.file = fs.open(path, "a") + if logger.mode == MODE.APPEND then + logger.file = fs.open(path, "a") else - log_sys.file = fs.open(path, "w") + logger.file = fs.open(path, "w") end if dmesg_redirect then - log_sys.dmesg_out = dmesg_redirect + logger.dmesg_out = dmesg_redirect else - log_sys.dmesg_out = term.current() + logger.dmesg_out = term.current() end end -- close the log file handle function log.close() - log_sys.file.close() + logger.file.close() end -- direct dmesg output to a monitor/window ---@param window table window or terminal reference -function log.direct_dmesg(window) log_sys.dmesg_out = window end +function log.direct_dmesg(window) logger.dmesg_out = window end -- dmesg style logging for boot because I like linux-y things ---@param msg string message @@ -120,7 +120,7 @@ function log.dmesg(msg, tag, tag_color) tag = util.strval(tag) local t_stamp = string.format("%12.2f", os.clock()) - local out = log_sys.dmesg_out + local out = logger.dmesg_out if out ~= nil then local out_w, out_h = out.getSize() @@ -216,7 +216,7 @@ end function log.dmesg_working(msg, tag, tag_color) local ts_coord = log.dmesg(msg, tag, tag_color) - local out = log_sys.dmesg_out + local out = logger.dmesg_out local width = (ts_coord.x2 - ts_coord.x1) + 1 if out ~= nil then @@ -275,7 +275,7 @@ end ---@param msg string message ---@param trace? boolean include file trace function log.debug(msg, trace) - if LOG_DEBUG then + if logger.debug then local dbg_info = "" if trace then diff --git a/scada-common/types.lua b/scada-common/types.lua index 9beb1e6..8df01c1 100644 --- a/scada-common/types.lua +++ b/scada-common/types.lua @@ -39,6 +39,10 @@ function types.new_radiation_reading(r, u) return { radiation = r, unit = u } en ---@return radiation_reading function types.new_zero_radiation_reading() return { radiation = 0, unit = "nSv" } end +---@class coordinate_2d +---@field x integer +---@field y integer + ---@class coordinate ---@field x integer ---@field y integer diff --git a/scada-common/util.lua b/scada-common/util.lua index e13f9fc..063143c 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -113,10 +113,13 @@ end ---@return table lines function util.strwrap(str, limit) return cc_strings.wrap(str, limit) end +-- luacheck: no unused args + -- concatenation with built-in to string ---@nodiscard ---@vararg any ---@return string +---@diagnostic disable-next-line: unused-vararg function util.concat(...) local str = "" for _, v in ipairs(arg) do str = str .. util.strval(v) end @@ -130,10 +133,13 @@ util.c = util.concat ---@nodiscard ---@param format string ---@vararg any +---@diagnostic disable-next-line: unused-vararg function util.sprintf(format, ...) return string.format(format, table.unpack(arg)) end +-- luacheck: unused args + -- format a number string with commas as the thousands separator
-- subtracts from spaces at the start if present for each comma used ---@nodiscard diff --git a/supervisor/config.lua b/supervisor/config.lua index 0aa26d8..b716d38 100644 --- a/supervisor/config.lua +++ b/supervisor/config.lua @@ -28,5 +28,7 @@ config.LOG_PATH = "/log.txt" -- 0 = APPEND (adds to existing file on start) -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 +-- true to log verbose debug messages +config.LOG_DEBUG = false return config diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index 5d0e5c2..b853575 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -119,26 +119,31 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili if unit_advert.reactor > 0 then local target_unit = self.fac_units[unit_advert.reactor] ---@type reactor_unit + -- unit RTUs if u_type == RTU_UNIT_TYPE.REDSTONE then -- redstone unit = svrs_redstone.new(id, i, unit_advert, self.modbus_q) if type(unit) ~= "nil" then target_unit.add_redstone(unit) end elseif u_type == RTU_UNIT_TYPE.BOILER_VALVE then - -- boiler (Mekanism 10.1+) + -- boiler unit = svrs_boilerv.new(id, i, unit_advert, self.modbus_q) if type(unit) ~= "nil" then target_unit.add_boiler(unit) end elseif u_type == RTU_UNIT_TYPE.TURBINE_VALVE then - -- turbine (Mekanism 10.1+) + -- turbine unit = svrs_turbinev.new(id, i, unit_advert, self.modbus_q) if type(unit) ~= "nil" then target_unit.add_turbine(unit) end elseif u_type == RTU_UNIT_TYPE.ENV_DETECTOR then -- environment detector unit = svrs_envd.new(id, i, unit_advert, self.modbus_q) if type(unit) ~= "nil" then target_unit.add_envd(unit) end + elseif u_type == RTU_UNIT_TYPE.VIRTUAL then + -- skip virtual units + log.debug(util.c(log_header, "skipping virtual RTU unit #", i)) else log.error(util.c(log_header, "bad advertisement: encountered unsupported reactor-specific RTU type ", type_string)) end else + -- facility RTUs if u_type == RTU_UNIT_TYPE.REDSTONE then -- redstone unit = svrs_redstone.new(id, i, unit_advert, self.modbus_q) @@ -157,6 +162,9 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili -- environment detector unit = svrs_envd.new(id, i, unit_advert, self.modbus_q) if type(unit) ~= "nil" then facility.add_envd(unit) end + elseif u_type == RTU_UNIT_TYPE.VIRTUAL then + -- skip virtual units + log.debug(util.c(log_header, "skipping virtual RTU unit #", i)) else log.error(util.c(log_header, "bad advertisement: encountered unsupported reactor-independent RTU type ", type_string)) end @@ -165,7 +173,7 @@ function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facili if unit ~= nil then table.insert(self.units, unit) - else + elseif u_type ~= RTU_UNIT_TYPE.VIRTUAL then _reset_config() log.error(util.c(log_header, "bad advertisement: error occured while creating a unit (type is ", type_string, ")")) break diff --git a/supervisor/session/rtu/redstone.lua b/supervisor/session/rtu/redstone.lua index bc7b81d..25b7284 100644 --- a/supervisor/session/rtu/redstone.lua +++ b/supervisor/session/rtu/redstone.lua @@ -120,9 +120,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) local io_f = { ---@nodiscard read = function () return rsio.digital_is_active(port, self.phy_io.digital_in[port].phy) end, - ---@param active boolean ----@diagnostic disable-next-line: unused-local - write = function (active) end + write = function () end } self.db.io[port] = io_f @@ -155,9 +153,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) ---@nodiscard ---@return integer read = function () return self.phy_io.analog_in[port].phy end, - ---@param value integer ----@diagnostic disable-next-line: unused-local - write = function (value) end + write = function () end } self.db.io[port] = io_f diff --git a/supervisor/session/rtu/unit_session.lua b/supervisor/session/rtu/unit_session.lua index 700f9b1..711fc75 100644 --- a/supervisor/session/rtu/unit_session.lua +++ b/supervisor/session/rtu/unit_session.lua @@ -166,6 +166,8 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t -- PUBLIC TEMPLATE FUNCTIONS -- +-- luacheck: no unused args + -- handle a packet ---@param m_pkt modbus_frame ---@diagnostic disable-next-line: unused-local @@ -180,6 +182,8 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t log.debug("template unit_session.update() called", true) end +-- luacheck: unused args + -- invalidate build cache function public.invalidate_cache() log.debug("template unit_session.invalidate_cache() called", true) diff --git a/supervisor/startup.lua b/supervisor/startup.lua index c104e1b..57888e5 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -45,6 +45,7 @@ cfv.assert_type_int(config.NUM_REACTORS) cfv.assert_type_table(config.REACTOR_COOLING) cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_int(config.LOG_MODE) +cfv.assert_type_bool(config.LOG_DEBUG) assert(cfv.valid(), "bad config file: missing/invalid fields") @@ -66,7 +67,7 @@ end -- log init ---------------------------------------- -log.init(config.LOG_PATH, config.LOG_MODE) +log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG) log.info("========================================") log.info("BOOTING supervisor.startup " .. SUPERVISOR_VERSION) diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 076f0ac..4fba85c 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -16,7 +16,7 @@ local println = function (str) end -- supervisory controller communications ---@nodiscard ----@param version string supervisor version +---@param _version string supervisor version ---@param num_reactors integer number of reactors ---@param cooling_conf table cooling configuration table ---@param modem table modem device @@ -24,7 +24,7 @@ local println = function (str) end ---@param svctl_listen integer listening port for supervisor access ---@param range integer trusted device connection range ---@diagnostic disable-next-line: unused-local -function supervisor.comms(version, num_reactors, cooling_conf, modem, dev_listen, svctl_listen, range) +function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_listen, svctl_listen, range) local self = { last_est_acks = {} } diff --git a/test/modbustest.lua b/test/modbustest.lua index 40d2b2f..b521e7b 100644 --- a/test/modbustest.lua +++ b/test/modbustest.lua @@ -63,6 +63,7 @@ mbt.test_error__check_request(MODBUS_EXCODE.NEG_ACKNOWLEDGE) println("PASS") print("99 {1,2}: ") +---@diagnostic disable-next-line: param-type-mismatch mbt.pkt_set(99, {1, 2}) mbt.test_error__check_request(MODBUS_EXCODE.ILLEGAL_FUNCTION) println("PASS")