diff --git a/coordinator/config.lua b/coordinator/config.lua index ccd8b04..983c7d9 100644 --- a/coordinator/config.lua +++ b/coordinator/config.lua @@ -6,19 +6,27 @@ config.SCADA_SV_PORT = 16100 config.SCADA_SV_LISTEN = 16101 -- listen port for SCADA coordinator API access config.SCADA_API_LISTEN = 16200 + -- expected number of reactor units, used only to require that number of unit monitors config.NUM_UNITS = 4 + -- graphics color config.RECOLOR = true + -- alarm sounder volume (0.0 to 3.0, 1.0 being standard max volume, this is the option given to to speaker.play()) -- note: alarm sine waves are at half saturation, so that multiple will be required to reach full scale config.SOUNDER_VOLUME = 1.0 + +-- true for 24 hour time on main view screen +config.TIME_24_HOUR = true + -- log path config.LOG_PATH = "/log.txt" -- log mode -- 0 = APPEND (adds to existing file on start) -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 + -- crypto config config.SECURE = true -- must be common between all devices diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 5662cd6..9513b55 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -1,14 +1,12 @@ -local comms = require("scada-common.comms") -local log = require("scada-common.log") -local ppm = require("scada-common.ppm") -local util = require("scada-common.util") +local comms = require("scada-common.comms") +local log = require("scada-common.log") +local ppm = require("scada-common.ppm") +local util = require("scada-common.util") local apisessions = require("coordinator.apisessions") local iocontrol = require("coordinator.iocontrol") -local dialog = require("coordinator.ui.dialog") - -local coordinator = {} +local dialog = require("coordinator.ui.dialog") local print = util.print local println = util.println @@ -22,6 +20,8 @@ local SCADA_MGMT_TYPES = comms.SCADA_MGMT_TYPES local SCADA_CRDN_TYPES = comms.SCADA_CRDN_TYPES local CRDN_COMMANDS = comms.CRDN_COMMANDS +local coordinator = {} + -- request the user to select a monitor ---@param names table available monitors ---@return boolean|string|nil @@ -496,6 +496,8 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa -- log.debug("coord RTT = " .. trip_time .. "ms") + iocontrol.get_db().facility.ps.publish("sv_ping", trip_time) + _send_keep_alive_ack(timestamp) else log.debug("SCADA keep alive packet length mismatch") diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 76e10cc..08adbc2 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -18,6 +18,7 @@ local io = {} -- initialize the coordinator IO controller ---@param conf facility_conf configuration ---@param comms coord_comms comms reference +---@diagnostic disable-next-line: redefined-local function iocontrol.init(conf, comms) io.facility = { scram = false, diff --git a/coordinator/startup.lua b/coordinator/startup.lua index db8c426..efc900e 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -15,10 +15,11 @@ local core = require("graphics.core") local apisessions = require("coordinator.apisessions") local config = require("coordinator.config") local coordinator = require("coordinator.coordinator") +local iocontrol = require("coordinator.iocontrol") local renderer = require("coordinator.renderer") local sounder = require("coordinator.sounder") -local COORDINATOR_VERSION = "beta-v0.7.5" +local COORDINATOR_VERSION = "beta-v0.7.6" local print = util.print local println = util.println @@ -43,6 +44,7 @@ cfv.assert_port(config.SCADA_API_LISTEN) cfv.assert_type_int(config.NUM_UNITS) cfv.assert_type_bool(config.RECOLOR) 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.SECURE) @@ -199,6 +201,8 @@ local function main() -- main event loop ---------------------------------------- + local date_format = util.trinary(config.TIME_24_HOUR, "%X \x04 %A, %B %d %Y", "%r \x04 %A, %B %d %Y") + local no_modem = false if ui_ok then @@ -284,6 +288,9 @@ local function main() -- free any closed sessions --apisessions.free_all_closed() + -- update date and time string for main display + iocontrol.get_db().facility.ps.publish("date_time", os.date(date_format)) + loop_clock.start() elseif conn_watchdog.is_timer(param1) then -- supervisor watchdog timeout diff --git a/coordinator/ui/layout/main_view.lua b/coordinator/ui/layout/main_view.lua index b5fd744..16c114d 100644 --- a/coordinator/ui/layout/main_view.lua +++ b/coordinator/ui/layout/main_view.lua @@ -19,6 +19,8 @@ local TextBox = require("graphics.elements.textbox") local PushButton = require("graphics.elements.controls.push_button") local SwitchButton = require("graphics.elements.controls.switch_button") +local DataIndicator = require("graphics.elements.indicators.data") + local TEXT_ALIGN = core.graphics.TEXT_ALIGN local cpair = core.graphics.cpair @@ -26,42 +28,49 @@ local cpair = core.graphics.cpair -- create new main view ---@param monitor table main viewscreen local function init(monitor) + local facility = iocontrol.get_db().facility + local units = iocontrol.get_db().units + local main = DisplayBox{window=monitor,fg_bg=style.root} -- window header message - local header = TextBox{parent=main,text="Nuclear Generation Facility SCADA Coordinator",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} + local header = TextBox{parent=main,y=1,text="Nuclear Generation Facility SCADA Coordinator",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} + local ping = DataIndicator{parent=main,x=1,y=1,label="SVTT",format="%d",value=0,unit="ms",lu_colors=cpair(colors.lightGray, colors.white),width=12,fg_bg=style.header} + -- max length example: "01:23:45 AM - Wednesday, September 28 2022" + local datetime = TextBox{parent=main,x=(header.width()-42),y=1,text="",alignment=TEXT_ALIGN.RIGHT,width=42,height=1,fg_bg=style.header} - local db = iocontrol.get_db() + facility.ps.subscribe("sv_ping", ping.update) + facility.ps.subscribe("date_time", datetime.set_value) local uo_1, uo_2, uo_3, uo_4 ---@type graphics_element local cnc_y_start = 3 -- unit overviews - if db.facility.num_units >= 1 then - uo_1 = unit_overview(main, 2, 3, db.units[1]) + if facility.num_units >= 1 then + uo_1 = unit_overview(main, 2, 3, units[1]) cnc_y_start = cnc_y_start + uo_1.height() + 1 end - if db.facility.num_units >= 2 then - uo_2 = unit_overview(main, 84, 3, db.units[2]) + if facility.num_units >= 2 then + uo_2 = unit_overview(main, 84, 3, units[2]) end - if db.facility.num_units >= 3 then + if facility.num_units >= 3 then -- base offset 3, spacing 1, max height of units 1 and 2 local row_2_offset = 3 + 1 + math.max(uo_1.height(), uo_2.height()) - uo_3 = unit_overview(main, 2, row_2_offset, db.units[3]) + uo_3 = unit_overview(main, 2, row_2_offset, units[3]) cnc_y_start = cnc_y_start + uo_3.height() + 1 - if db.facility.num_units == 4 then - uo_4 = unit_overview(main, 84, row_2_offset, db.units[4]) + if facility.num_units == 4 then + uo_4 = unit_overview(main, 84, row_2_offset, units[4]) end end -- command & control - TextBox{parent=main,y=cnc_y_start,text=util.strrep("\x8c", header.width()),alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} + TextBox{parent=main,y=cnc_y_start,text=util.strrep("\x8c", header.width()),alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.lightGray,colors.gray)} -- testing ---@fixme remove test code diff --git a/graphics/elements/indicators/data.lua b/graphics/elements/indicators/data.lua index 5ebc9cb..d19fab0 100644 --- a/graphics/elements/indicators/data.lua +++ b/graphics/elements/indicators/data.lua @@ -42,17 +42,26 @@ local function data(args) e.window.setCursorPos(1, 1) e.window.write(args.label) - local data_start = string.len(args.label) + 2 - if string.len(args.label) == 0 then data_start = 1 end + local label_len = string.len(args.label) + local data_start = 1 + local clear_width = args.width + + if label_len > 0 then + data_start = data_start + (label_len + 1) + clear_width = args.width - (label_len + 1) + end -- on state change ---@param value any new value function e.on_update(value) e.value = value - local data_str = util.sprintf(args.format, value) + -- clear old data and label + e.window.setCursorPos(data_start, 1) + e.window.write(util.spaces(clear_width)) -- write data + local data_str = util.sprintf(args.format, value) e.window.setCursorPos(data_start, 1) e.window.setTextColor(e.fg_bg.fgd) if args.commas then