From 4710fa7ceef41bb5fee01254b5032fe2ab9c950a Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 31 Jan 2024 14:10:03 -0500 Subject: [PATCH 01/43] #309 WIP coordinator configurator --- configure.lua | 4 +- coordinator/configure.lua | 967 ++++++++++++++++++++++++++++++++++++ coordinator/coordinator.lua | 4 +- startup.lua | 2 +- 4 files changed, 972 insertions(+), 5 deletions(-) create mode 100644 coordinator/configure.lua diff --git a/configure.lua b/configure.lua index ab0b64e..69d7f0f 100644 --- a/configure.lua +++ b/configure.lua @@ -6,8 +6,8 @@ elseif fs.exists("rtu/configure.lua") then require("rtu.configure").configure() elseif fs.exists("supervisor/configure.lua") then require("supervisor.configure").configure() -elseif fs.exists("coordinator/startup.lua") then - print("CONFIGURE> coordinator configurator not yet implemented (use 'edit coordinator/config.lua' to configure)") +elseif fs.exists("coordinator/configure.lua") then + require("coordinator.configure").configure() elseif fs.exists("pocket/startup.lua") then print("CONFIGURE> pocket configurator not yet implemented (use 'edit pocket/config.lua' to configure)") else diff --git a/coordinator/configure.lua b/coordinator/configure.lua new file mode 100644 index 0000000..13f104e --- /dev/null +++ b/coordinator/configure.lua @@ -0,0 +1,967 @@ +-- +-- Configuration GUI +-- + +local comms = require("scada-common.comms") +local log = require("scada-common.log") +local network = require("scada-common.network") +local ppm = require("scada-common.ppm") +local tcd = require("scada-common.tcd") +local util = require("scada-common.util") + +local core = require("graphics.core") + +local DisplayBox = require("graphics.elements.displaybox") +local Div = require("graphics.elements.div") +local ListBox = require("graphics.elements.listbox") +local MultiPane = require("graphics.elements.multipane") +local TextBox = require("graphics.elements.textbox") + +local CheckBox = require("graphics.elements.controls.checkbox") +local PushButton = require("graphics.elements.controls.push_button") +local Radio2D = require("graphics.elements.controls.radio_2d") +local RadioButton = require("graphics.elements.controls.radio_button") + +local NumberField = require("graphics.elements.form.number_field") +local TextField = require("graphics.elements.form.text_field") + +local println = util.println +local tri = util.trinary + +local PROTOCOL = comms.PROTOCOL +local DEVICE_TYPE = comms.DEVICE_TYPE +local ESTABLISH_ACK = comms.ESTABLISH_ACK +local MGMT_TYPE = comms.MGMT_TYPE + +local cpair = core.cpair + +local LEFT = core.ALIGN.LEFT +local CENTER = core.ALIGN.CENTER +local RIGHT = core.ALIGN.RIGHT + +-- changes to the config data/format to let the user know +local changes = {} + +---@class crd_configurator +local configurator = {} + +local style = {} + +style.root = cpair(colors.black, colors.lightGray) +style.header = cpair(colors.white, colors.gray) + +style.colors = { + { c = colors.red, hex = 0xdf4949 }, + { c = colors.orange, hex = 0xffb659 }, + { c = colors.yellow, hex = 0xfffc79 }, + { c = colors.lime, hex = 0x80ff80 }, + { c = colors.green, hex = 0x4aee8a }, + { c = colors.cyan, hex = 0x34bac8 }, + { c = colors.lightBlue, hex = 0x6cc0f2 }, + { c = colors.blue, hex = 0x0096ff }, + { c = colors.purple, hex = 0xb156ee }, + { c = colors.pink, hex = 0xf26ba2 }, + { c = colors.magenta, hex = 0xf9488a }, + { c = colors.lightGray, hex = 0xcacaca }, + { c = colors.gray, hex = 0x575757 } +} + +local bw_fg_bg = cpair(colors.black, colors.white) +local g_lg_fg_bg = cpair(colors.gray, colors.lightGray) +local nav_fg_bg = bw_fg_bg +local btn_act_fg_bg = cpair(colors.white, colors.gray) + +local tool_ctl = { + nic = nil, ---@type nic + net_listen = false, + sv_addr = comms.BROADCAST, + sv_seq_num = 0, + sv_fac_conf = nil, ---@type facility_conf + + ask_config = false, + has_config = false, + viewing_config = false, + importing_legacy = false, + + view_cfg = nil, ---@type graphics_element + settings_apply = nil, ---@type graphics_element + + gen_summary = nil, ---@type function + show_current_cfg = nil, ---@type function + load_legacy = nil, ---@type function + + show_auth_key = nil, ---@type function + show_key_btn = nil, ---@type graphics_element + auth_key_textbox = nil, ---@type graphics_element + auth_key_value = "", + + sv_connect = nil, ---@type function + sv_conn_button = nil, ---@type graphics_element + sv_conn_status = nil, ---@type graphics_element + sv_conn_detail = nil, ---@type graphics_element + sv_skip = nil, ---@type graphics_element + sv_next = nil, ---@type graphics_element + + cooling_elems = {}, + tank_elems = {}, + + vis_ftanks = {}, + vis_utanks = {} +} + +---@class crd_config +local tmp_cfg = { + UnitCount = 1, + SpeakerVolume = 1.0, + Time24Hour = true, + DisableFlowView = false, + Displays = {}, + SVR_Channel = nil, ---@type integer + CRD_Channel = nil, ---@type integer + PKT_Channel = nil, ---@type integer + SVR_Timeout = nil, ---@type number + API_Timeout = nil, ---@type number + TrustedRange = nil, ---@type number + AuthKey = nil, ---@type string|nil + LogMode = 0, + LogPath = "", + LogDebug = false, +} + +---@class crd_config +local ini_cfg = {} +---@class crd_config +local settings_cfg = {} + +-- all settings fields, their nice names, and their default values +local fields = { + { "UnitCount", "Number of Reactors", 1 }, + { "SpeakerVolume", "Speaker Volume", 1.0 }, + { "Use24HourTime", "Use 24-hour Time Format", true }, + { "DisableFlowView", "Don't Use Flow", {} }, + { "SVR_Channel", "SVR Channel", 16240 }, + { "CRD_Channel", "CRD Channel", 16243 }, + { "PKT_Channel", "PKT Channel", 16244 }, + { "CRD_Timeout", "CRD Connection Timeout", 5 }, + { "API_Timeout", "API Connection Timeout", 5 }, + { "TrustedRange", "Trusted Range", 0 }, + { "AuthKey", "Facility Auth Key" , ""}, + { "LogMode", "Log Mode", log.MODE.APPEND }, + { "LogPath", "Log Path", "/log.txt" }, + { "LogDebug","Log Debug Messages", false } +} + +-- send an management packet to the supervisor +---@param msg_type MGMT_TYPE +---@param msg table +local function send_sv(msg_type, msg) + local s_pkt = comms.scada_packet() + local pkt = comms.mgmt_packet() + + pkt.make(msg_type, msg) + s_pkt.make(tool_ctl.sv_addr, tool_ctl.sv_seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) + + tool_ctl.nic.transmit(tmp_cfg.SVR_Channel, tmp_cfg.CRD_Channel, s_pkt) + tool_ctl.sv_seq_num = tool_ctl.sv_seq_num + 1 +end + +-- handle an establish message from the supervisor +---@param packet mgmt_frame +local function handle_packet(packet) + local error_msg = nil + + if packet.scada_frame.local_channel() ~= tmp_cfg.CRD_Channel then + error_msg = "Error: unknown receive channel." + elseif packet.scada_frame.remote_channel() == tmp_cfg.SVR_Channel and packet.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then + if packet.type == MGMT_TYPE.ESTABLISH then + -- connection with supervisor established + if packet.length == 2 then + local est_ack = packet.data[1] + local config = packet.data[2] + + if est_ack == ESTABLISH_ACK.ALLOW then + if type(config) == "table" and #config == 2 then + tool_ctl.sv_fac_conf = { num_units = config[1], cooling = config[2] } + tool_ctl.sv_addr = packet.scada_frame.src_addr() + send_sv(MGMT_TYPE.CLOSE, {}) + else + error_msg = "Error: invalid cooling configuration supervisor." + end + else + error_msg = "Error: invalid allow reply length from supervisor." + end + elseif packet.length == 1 then + local est_ack = packet.data[1] + + if est_ack == ESTABLISH_ACK.DENY then + error_msg = "Error: supervisor connection denied." + elseif est_ack == ESTABLISH_ACK.COLLISION then + error_msg = "Error: a coordinator is already/still connected. Please try again." + elseif est_ack == ESTABLISH_ACK.BAD_VERSION then + error_msg = "Error: coordinator comms version does not match supervisor comms version." + else + error_msg = "Error: invalid reply from supervisor." + end + else + error_msg = "Error: invalid reply length from supervisor." + end + else + error_msg = "Error: didn't get an establish reply from supervisor." + end + end + + tool_ctl.net_listen = false + + if error_msg then + tool_ctl.sv_conn_status.set_value("") + tool_ctl.sv_conn_detail.set_value(error_msg) + tool_ctl.sv_conn_button.enable() + else + tool_ctl.sv_conn_status.set_value("Connected!") + tool_ctl.sv_conn_detail.set_value("Data received successfully, press 'Next' to continue.") + tool_ctl.sv_skip.hide() + tool_ctl.sv_next.show() + end +end + +-- handle supervisor connection failure +local function handle_timeout() + tool_ctl.net_listen = false + tool_ctl.sv_conn_button.enable() + tool_ctl.sv_conn_status.set_value("Timed out.") + tool_ctl.sv_conn_detail.set_value("Supervisor did not reply. Ensure startup app is running on the supervisor.") +end + +-- load data from the settings file +---@param target crd_config +---@param raw boolean? true to not use default values +local function load_settings(target, raw) + for _, v in pairs(fields) do settings.unset(v[1]) end + + local loaded = settings.load("/coordinator.settings") + + for _, v in pairs(fields) do target[v[1]] = settings.get(v[1], tri(raw, nil, v[3])) end + + return loaded +end + +-- create the config view +---@param display graphics_element +local function config_view(display) +---@diagnostic disable-next-line: undefined-field + local function exit() os.queueEvent("terminate") end + + TextBox{parent=display,y=1,text="Coordinator Configurator",alignment=CENTER,height=1,fg_bg=style.header} + + local root_pane_div = Div{parent=display,x=1,y=2} + + local main_page = Div{parent=root_pane_div,x=1,y=1} + local net_cfg = Div{parent=root_pane_div,x=1,y=1} + local fac_cfg = Div{parent=root_pane_div,x=1,y=1} + local mon_cfg = Div{parent=root_pane_div,x=1,y=1} + local crd_cfg = Div{parent=root_pane_div,x=1,y=1} + local log_cfg = Div{parent=root_pane_div,x=1,y=1} + local summary = Div{parent=root_pane_div,x=1,y=1} + local changelog = Div{parent=root_pane_div,x=1,y=1} + local import_err = Div{parent=root_pane_div,x=1,y=1} + + local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,crd_cfg,log_cfg,summary,changelog,import_err}} + + -- MAIN PAGE + + local y_start = 5 + + TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Coordinator configurator! Please select one of the following options."} + + if tool_ctl.ask_config then + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)} + y_start = y_start + 5 + end + + local function view_config() + tool_ctl.viewing_config = true + tool_ctl.gen_summary(settings_cfg) + tool_ctl.settings_apply.hide(true) + main_pane.set_value(5) + end + + if fs.exists("/supervisor/config.lua") then + PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg} + y_start = y_start + 2 + end + + PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} + tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + + if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end + + PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} + PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#region NET CONFIG + + local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49} + local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49} + local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=49} + local net_c_4 = Div{parent=net_cfg,x=2,y=4,width=49} + + local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}} + + TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)} + + TextBox{parent=net_c_1,x=1,y=1,height=1,text="Please set the network channels below."} + TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 3 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_1,x=1,y=8,height=1,width=18,text="Supervisor Channel"} + local svr_chan = NumberField{parent=net_c_1,x=21,y=8,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg} + TextBox{parent=net_c_1,x=29,y=8,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_1,x=1,y=10,height=1,width=19,text="Coordinator Channel"} + local crd_chan = NumberField{parent=net_c_1,x=21,y=10,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg} + TextBox{parent=net_c_1,x=29,y=10,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_1,x=1,y=12,height=1,width=14,text="Pocket Channel"} + local pkt_chan = NumberField{parent=net_c_1,x=21,y=12,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg} + TextBox{parent=net_c_1,x=29,y=12,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg} + + local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_channels() + local svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value()) + if svr_c ~= nil and crd_c ~= nil and pkt_c ~= nil then + tmp_cfg.SVR_Channel, tmp_cfg.CRD_Channel, tmp_cfg.PKT_Channel = svr_c, crd_c, pkt_c + net_pane.set_value(2) + chan_err.hide(true) + else chan_err.show() end + end + + PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_2,x=1,y=1,height=1,text="Please set the connection timeouts below."} + TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally should not need to modify these. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection. The default for all is 5 seconds.",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_2,x=1,y=8,height=1,width=19,text="Supervisor Timeout"} + local svr_timeout = NumberField{parent=net_c_2,x=20,y=8,width=7,default=ini_cfg.SVR_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg} + + TextBox{parent=net_c_2,x=1,y=10,height=1,width=14,text="Pocket Timeout"} + local pkt_timeout = NumberField{parent=net_c_2,x=20,y=10,width=7,default=ini_cfg.PKT_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg} + + TextBox{parent=net_c_2,x=28,y=8,height=4,width=7,text="seconds\n\nseconds",fg_bg=g_lg_fg_bg} + + local ct_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_timeouts() + local svr_cto, pkt_cto = tonumber(svr_timeout.get_value()), tonumber(pkt_timeout.get_value()) + if svr_cto ~= nil and pkt_cto ~= nil then + tmp_cfg.SVR_Timeout, tmp_cfg.PKT_Timeout = svr_cto, pkt_cto + net_pane.set_value(3) + ct_err.hide(true) + else ct_err.show() end + end + + PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_3,x=1,y=1,height=1,text="Please set the trusted range below."} + TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg} + TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg} + + local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg} + + local tr_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_tr() + local range_val = tonumber(range.get_value()) + if range_val ~= nil then + tmp_cfg.TrustedRange = range_val + comms.set_trusted_range(range_val) + net_pane.set_value(4) + tr_err.hide(true) + else tr_err.show() end + end + + PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."} + TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_4,x=1,y=11,height=1,text="Facility Auth Key"} + local key, _, censor = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg} + + local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end + + local hide_key = CheckBox{parent=net_c_4,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key} + + hide_key.set_value(true) + censor_key(true) + + local key_err = TextBox{parent=net_c_4,x=8,y=14,height=1,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_auth() + local v = key.get_value() + if string.len(v) == 0 or string.len(v) >= 8 then + tmp_cfg.AuthKey = key.get_value() + main_pane.set_value(3) + key_err.hide(true) + + -- init mac for supervisor connection + if string.len(v) >= 8 then network.init_mac(tmp_cfg.AuthKey) end + + -- prep supervisor connection screen + tool_ctl.sv_conn_button.enable() + tool_ctl.sv_conn_status.set_value("") + tool_ctl.sv_conn_detail.set_value("") + tool_ctl.sv_next.hide() + tool_ctl.sv_skip.show() + else key_err.show() end + end + + PushButton{parent=net_c_4,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_4,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#endregion + + -- FACILITY CONFIG + + local fac_c_1 = Div{parent=fac_cfg,x=2,y=4,width=49} + local fac_c_2 = Div{parent=fac_cfg,x=2,y=4,width=49} + + local fac_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={fac_c_1,fac_c_2}} + + TextBox{parent=fac_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)} + + TextBox{parent=fac_c_1,x=1,y=1,height=4,text="This tool can attempt to connect to your supervisor computer. This would load facility information in order to get the unit count and aid monitor setup."} + TextBox{parent=fac_c_1,x=1,y=6,height=2,text="The supervisor startup app must be running and fully configured on your supervisor computer."} + + tool_ctl.sv_conn_status = TextBox{parent=fac_c_1,x=11,y=9,height=1,text=""} + tool_ctl.sv_conn_detail = TextBox{parent=fac_c_1,x=1,y=11,height=2,text=""} + + tool_ctl.sv_conn_button = PushButton{parent=fac_c_1,x=1,y=9,text="Connect",min_width=9,callback=function()tool_ctl.sv_connect()end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + + function tool_ctl.sv_connect() + tool_ctl.sv_conn_button.disable() + tool_ctl.sv_conn_detail.set_value("") + + local modem = ppm.get_wireless_modem() + if modem == nil then + tool_ctl.sv_conn_status.set_value("Please connect an ender/wireless modem.") + else + tool_ctl.sv_conn_status.set_value("Modem found, connecting...") + if tool_ctl.nic == nil then tool_ctl.nic = network.nic(modem) end + + tool_ctl.nic.closeAll() + tool_ctl.nic.open(tmp_cfg.CRD_Channel) + + tool_ctl.sv_addr = comms.BROADCAST + tool_ctl.sv_seq_num = 0 + tool_ctl.net_listen = true + + send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.CRD }) + + tcd.dispatch_unique(8, handle_timeout) + end + end + + PushButton{parent=fac_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + tool_ctl.sv_skip = PushButton{parent=fac_c_1,x=44,y=14,text="Skip \x1a",callback=function()fac_pane.set_value(3)end,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} + tool_ctl.sv_next = PushButton{parent=fac_c_1,x=44,y=14,text="Next \x1a",callback=function()fac_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,hidden=true} + + TextBox{parent=fac_c_2,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."} + local num_units = NumberField{parent=fac_c_2,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg} + TextBox{parent=fac_c_2,x=7,y=5,height=1,text="reactors"} + TextBox{parent=fac_c_2,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg} + + local nu_error = TextBox{parent=fac_c_2,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_num_units() + local count = tonumber(num_units.get_value()) + if count ~= nil and count > 0 and count < 5 then + nu_error.hide(true) + tmp_cfg.UnitCount = count + main_pane.set_value(3) + else nu_error.show() end + end + + PushButton{parent=fac_c_2,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=fac_c_2,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + -- MONITOR CONFIG + + local mon_c_1 = Div{parent=mon_cfg,x=2,y=4,width=49} + + local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1}} + + TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.lime)} + + TextBox{parent=mon_c_1,x=1,y=1,height=3,text="Your configuration requires the following monitors:"} + + local nu_error = TextBox{parent=mon_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_num_units() + -- local count = tonumber(num_units.get_value()) + -- if count ~= nil and count > 0 and count < 5 then + -- nu_error.hide(true) + -- tmp_cfg.UnitCount = count + + -- local confs = tool_ctl.cooling_elems + -- if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end + -- if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end + -- if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end + + -- crd_pane.set_value(2) + -- else nu_error.show() end + end + + PushButton{parent=mon_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + -- COORDINATOR CONFIG + + local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49} + local crd_c_2 = Div{parent=crd_cfg,x=2,y=4,width=49} + local crd_c_3 = Div{parent=crd_cfg,x=2,y=4,width=49} + local crd_c_4 = Div{parent=crd_cfg,x=2,y=4,width=49} + local crd_c_5 = Div{parent=crd_cfg,x=2,y=4,width=49} + local crd_c_6 = Div{parent=crd_cfg,x=2,y=4,width=49} + + local crd_pane = MultiPane{parent=crd_cfg,x=1,y=4,panes={crd_c_1}} + + TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator Configuration",fg_bg=cpair(colors.black,colors.lime)} + + TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."} + local num_units = NumberField{parent=crd_c_1,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg} + TextBox{parent=crd_c_1,x=7,y=5,height=1,text="reactors"} + TextBox{parent=crd_c_1,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg} + + local nu_error = TextBox{parent=crd_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_num_units() + local count = tonumber(num_units.get_value()) + if count ~= nil and count > 0 and count < 5 then + nu_error.hide(true) + tmp_cfg.UnitCount = count + + local confs = tool_ctl.cooling_elems + if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end + if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end + if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end + + crd_pane.set_value(2) + else nu_error.show() end + end + + PushButton{parent=crd_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=crd_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + -- LOG CONFIG + + local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49} + + TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)} + + TextBox{parent=log_c_1,x=1,y=1,height=1,text="Please configure logging below."} + + TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"} + local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink} + + TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"} + local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg} + + local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)} + TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg} + + local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_log() + if path.get_value() ~= "" then + path_err.hide(true) + tmp_cfg.LogMode = mode.get_value() - 1 + tmp_cfg.LogPath = path.get_value() + tmp_cfg.LogDebug = en_dbg.get_value() + tool_ctl.gen_summary(tmp_cfg) + tool_ctl.viewing_config = false + tool_ctl.importing_legacy = false + tool_ctl.settings_apply.show() + main_pane.set_value(5) + else path_err.show() end + end + + PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + -- SUMMARY OF CHANGES + + local sum_c_1 = Div{parent=summary,x=2,y=4,width=49} + local sum_c_2 = Div{parent=summary,x=2,y=4,width=49} + local sum_c_3 = Div{parent=summary,x=2,y=4,width=49} + local sum_c_4 = Div{parent=summary,x=2,y=4,width=49} + + local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}} + + TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)} + + local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + + local function back_from_settings() + if tool_ctl.viewing_config or tool_ctl.importing_legacy then + main_pane.set_value(1) + tool_ctl.viewing_config = false + tool_ctl.importing_legacy = false + tool_ctl.settings_apply.show() + else + main_pane.set_value(4) + end + end + + ---@param element graphics_element + ---@param data any + local function try_set(element, data) + if data ~= nil then element.set_value(data) end + end + + local function save_and_continue() + for k, v in pairs(tmp_cfg) do settings.set(k, v) end + + if settings.save("supervisor.settings") then + load_settings(settings_cfg, true) + load_settings(ini_cfg) + + try_set(num_units, ini_cfg.UnitCount) + try_set(tank_mode, ini_cfg.FacilityTankMode) + try_set(svr_chan, ini_cfg.SVR_Channel) + try_set(plc_chan, ini_cfg.PLC_Channel) + try_set(rtu_chan, ini_cfg.RTU_Channel) + try_set(crd_chan, ini_cfg.CRD_Channel) + try_set(pkt_chan, ini_cfg.PKT_Channel) + try_set(plc_timeout, ini_cfg.PLC_Timeout) + try_set(rtu_timeout, ini_cfg.RTU_Timeout) + try_set(crd_timeout, ini_cfg.CRD_Timeout) + try_set(pkt_timeout, ini_cfg.PKT_Timeout) + try_set(range, ini_cfg.TrustedRange) + try_set(key, ini_cfg.AuthKey) + try_set(mode, ini_cfg.LogMode) + try_set(path, ini_cfg.LogPath) + try_set(en_dbg, ini_cfg.LogDebug) + + for i = 1, #ini_cfg.CoolingConfig do + local cfg, elems = ini_cfg.CoolingConfig[i], tool_ctl.cooling_elems[i] + try_set(elems.boilers, cfg.BoilerCount) + try_set(elems.turbines, cfg.TurbineCount) + try_set(elems.tank, cfg.TankConnection) + end + + for i = 1, #ini_cfg.FacilityTankDefs do + try_set(tool_ctl.tank_elems[i].tank_opt, ini_cfg.FacilityTankDefs[i]) + end + + en_fac_tanks.set_value(ini_cfg.FacilityTankMode > 0) + + tool_ctl.view_cfg.enable() + + if tool_ctl.importing_legacy then + tool_ctl.importing_legacy = false + sum_pane.set_value(3) + else + sum_pane.set_value(2) + end + else + sum_pane.set_value(4) + end + end + + PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_settings,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg} + + TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"} + + local function go_home() + main_pane.set_value(1) + svr_pane.set_value(1) + net_pane.set_value(1) + sum_pane.set_value(1) + end + + PushButton{parent=sum_c_2,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=sum_c_2,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} + + TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."} + + local function delete_legacy() + fs.delete("/supervisor/config.lua") + exit() + end + + PushButton{parent=sum_c_3,x=1,y=14,min_width=8,text="Cancel",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=sum_c_3,x=44,y=14,min_width=6,text="OK",callback=delete_legacy,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.gray)} + + TextBox{parent=sum_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."} + PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} + + -- CONFIG CHANGE LOG + + local cl = Div{parent=changelog,x=2,y=4,width=49} + + TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg} + + local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + + for _, change in ipairs(changes) do + TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg} + for _, v in ipairs(change[2]) do + local e = Div{parent=c_log,height=#util.strwrap(v,46)} + TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)} + TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)} + end + end + + PushButton{parent=cl,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + -- IMPORT ERROR + + local i_err = Div{parent=import_err,x=2,y=4,width=49} + + TextBox{parent=import_err,x=1,y=2,height=1,text=" Import Error",fg_bg=cpair(colors.black,colors.red)} + TextBox{parent=i_err,x=1,y=1,height=1,text="There is a problem with your config.lua file:"} + + local import_err_msg = TextBox{parent=i_err,x=1,y=3,height=6,text=""} + + PushButton{parent=i_err,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=i_err,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} + + -- set tool functions now that we have the elements + + -- load a legacy config file + function tool_ctl.load_legacy() + local config = require("supervisor.config") + + tmp_cfg.UnitCount = config.NUM_REACTORS + + if config.REACTOR_COOLING == nil or tmp_cfg.UnitCount ~= #config.REACTOR_COOLING then + import_err_msg.set_value("Cooling configuration table length must match the number of units.") + main_pane.set_value(7) + return + end + + for i = 1, tmp_cfg.UnitCount do + local cfg = config.REACTOR_COOLING[i] + + if type(cfg) ~= "table" then + import_err_msg.set_value("Cooling configuration for unit " .. i .. " must be a table.") + main_pane.set_value(7) + return + end + + tmp_cfg.CoolingConfig[i] = { BoilerCount = cfg.BOILERS or 0, TurbineCount = cfg.TURBINES or 1, TankConnection = cfg.TANK or false } + end + + tmp_cfg.FacilityTankMode = config.FAC_TANK_MODE + + if not (util.is_int(tmp_cfg.FacilityTankMode) and tmp_cfg.FacilityTankMode >= 0 and tmp_cfg.FacilityTankMode <= 8) then + import_err_msg.set_value("Invalid tank mode present in config. FAC_TANK_MODE must be a number 0 through 8.") + main_pane.set_value(7) + return + end + + if config.FAC_TANK_MODE > 0 then + if config.FAC_TANK_DEFS == nil or tmp_cfg.UnitCount ~= #config.FAC_TANK_DEFS then + import_err_msg.set_value("Facility tank definitions table length must match the number of units when using facility tanks.") + main_pane.set_value(7) + return + end + + for i = 1, tmp_cfg.UnitCount do + tmp_cfg.FacilityTankDefs[i] = config.FAC_TANK_DEFS[i] + end + else + tmp_cfg.FacilityTankMode = 0 + tmp_cfg.FacilityTankDefs = {} + end + + tmp_cfg.SVR_Channel = config.SVR_CHANNEL + tmp_cfg.PLC_Channel = config.PLC_CHANNEL + tmp_cfg.RTU_Channel = config.RTU_CHANNEL + tmp_cfg.CRD_Channel = config.CRD_CHANNEL + tmp_cfg.PKT_Channel = config.PKT_CHANNEL + + tmp_cfg.PLC_Timeout = config.PLC_TIMEOUT + tmp_cfg.RTU_Timeout = config.RTU_TIMEOUT + tmp_cfg.CRD_Timeout = config.CRD_TIMEOUT + tmp_cfg.PKT_Timeout = config.PKT_TIMEOUT + + tmp_cfg.TrustedRange = config.TRUSTED_RANGE + tmp_cfg.AuthKey = config.AUTH_KEY or "" + tmp_cfg.LogMode = config.LOG_MODE + tmp_cfg.LogPath = config.LOG_PATH + tmp_cfg.LogDebug = config.LOG_DEBUG or false + + tool_ctl.gen_summary(tmp_cfg) + sum_pane.set_value(1) + main_pane.set_value(5) + tool_ctl.importing_legacy = true + end + + -- expose the auth key on the summary page + function tool_ctl.show_auth_key() + tool_ctl.show_key_btn.disable() + tool_ctl.auth_key_textbox.set_value(tool_ctl.auth_key_value) + end + + -- generate the summary list + ---@param cfg svr_config + function tool_ctl.gen_summary(cfg) + setting_list.remove_all() + + local alternate = false + local inner_width = setting_list.get_width() - 1 + + tool_ctl.show_key_btn.enable() + tool_ctl.auth_key_value = cfg.AuthKey or "" -- to show auth key + + for i = 1, #fields do + local f = fields[i] + local height = 1 + local label_w = string.len(f[2]) + local val_max_w = (inner_width - label_w) + 1 + local raw = cfg[f[1]] + local val = util.strval(raw) + + if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) + elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") + elseif f[1] == "CoolingConfig" and cfg.CoolingConfig then + val = "" + + for idx = 1, #cfg.CoolingConfig do + local ccfg = cfg.CoolingConfig[idx] + local b_plural = util.trinary(ccfg.BoilerCount == 1, "", "s") + local t_plural = util.trinary(ccfg.TurbineCount == 1, "", "s") + local tank = util.trinary(ccfg.TankConnection, "has tank conn", "no tank conn") + val = val .. util.trinary(idx == 1, "", "\n") .. + util.sprintf(" \x07 unit %d - %d boiler%s, %d turbine%s, %s", idx, ccfg.BoilerCount, b_plural, ccfg.TurbineCount, t_plural, tank) + end + + if val == "" then val = "no facility tanks" end + elseif f[1] == "FacilityTankMode" and raw == 0 then val = "0 (n/a, unit mode)" + elseif f[1] == "FacilityTankDefs" and cfg.FacilityTankDefs then + val = "" + + for idx = 1, #cfg.FacilityTankDefs do + local t_mode = "not connected to a tank" + if cfg.FacilityTankDefs[idx] == 1 then + t_mode = "connected to its unit tank" + elseif cfg.FacilityTankDefs[idx] == 2 then + t_mode = "connected to a facility tank" + end + + val = val .. util.trinary(idx == 1, "", "\n") .. util.sprintf(" \x07 unit %d - %s", idx, t_mode) + end + + if val == "" then val = "no facility tanks" end + end + + if val == "nil" then val = "" end + + local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white)) + alternate = not alternate + + if string.len(val) > val_max_w then + local lines = util.strwrap(val, inner_width) + height = #lines + 1 + end + + local line = Div{parent=setting_list,height=height,fg_bg=c} + TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} + + local textbox + if height > 1 then + textbox = TextBox{parent=line,x=1,y=2,text=val,height=height-1,alignment=LEFT} + else + textbox = TextBox{parent=line,x=label_w+1,y=1,text=val,alignment=RIGHT} + end + + if f[1] == "AuthKey" then tool_ctl.auth_key_textbox = textbox end + end + end +end + +-- reset terminal screen +local function reset_term() + term.setTextColor(colors.white) + term.setBackgroundColor(colors.black) + term.clear() + term.setCursorPos(1, 1) +end + +-- run the supervisor configurator +---@param ask_config? boolean indicate if this is being called by the supervisor startup app due to an invalid configuration +function configurator.configure(ask_config) + tool_ctl.ask_config = ask_config == true + + load_settings(settings_cfg, true) + tool_ctl.has_config = load_settings(ini_cfg) + + reset_term() + + ppm.mount_all() + + -- set overridden colors + for i = 1, #style.colors do + term.setPaletteColor(style.colors[i].c, style.colors[i].hex) + end + + local status, error = pcall(function () + local display = DisplayBox{window=term.current(),fg_bg=style.root} + config_view(display) + + while true do + local event, param1, param2, param3, param4, param5 = util.pull_event() + + -- handle event + if event == "timer" then + tcd.handle(param1) + elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then + local m_e = core.events.new_mouse_event(event, param1, param2, param3) + if m_e then display.handle_mouse(m_e) end + elseif event == "char" or event == "key" or event == "key_up" then + local k_e = core.events.new_key_event(event, param1, param2) + if k_e then display.handle_key(k_e) end + elseif event == "paste" then + display.handle_paste(param1) + elseif event == "peripheral_detach" then + ppm.handle_unmount(param1) + elseif event == "peripheral" then + ppm.mount(param1) + elseif event == "modem_message" and tool_ctl.nic ~= nil and tool_ctl.net_listen then + local s_pkt = tool_ctl.nic.receive(param1, param2, param3, param4, param5) + + if s_pkt and s_pkt.protocol() == PROTOCOL.SCADA_MGMT then + local mgmt_pkt = comms.mgmt_packet() + if mgmt_pkt.decode(s_pkt) then + tcd.abort(handle_timeout) + handle_packet(mgmt_pkt.get()) + end + end + end + + if event == "terminate" then return end + end + end) + + -- restore colors + for i = 1, #style.colors do + local r, g, b = term.nativePaletteColor(style.colors[i].c) + term.setPaletteColor(style.colors[i].c, r, g, b) + end + + reset_term() + if not status then + println("configurator error: " .. error) + end + + return status, error +end + +return configurator diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 80c21b6..78c5f97 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -269,8 +269,6 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk comms.set_trusted_range(range) - -- PRIVATE FUNCTIONS -- - -- configure network channels nic.closeAll() nic.open(crd_channel) @@ -278,6 +276,8 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk -- link nic to apisessions apisessions.init(nic) + -- PRIVATE FUNCTIONS -- + -- send a packet to the supervisor ---@param msg_type MGMT_TYPE|CRDN_TYPE ---@param msg table diff --git a/startup.lua b/startup.lua index 7dd95f6..525402b 100644 --- a/startup.lua +++ b/startup.lua @@ -2,7 +2,7 @@ local util = require("scada-common.util") local println = util.println -local BOOTLOADER_VERSION = "0.5" +local BOOTLOADER_VERSION = "0.6" println("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION) println("BOOT> SCANNING FOR APPLICATIONS...") From 907f27baf8886f374b64fa12024a69f7fa2e0a3b Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 2 Feb 2024 23:01:51 +0000 Subject: [PATCH 02/43] #309 show data received from supervisor --- coordinator/configure.lua | 73 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 13f104e..59b0df4 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -76,7 +76,8 @@ local tool_ctl = { net_listen = false, sv_addr = comms.BROADCAST, sv_seq_num = 0, - sv_fac_conf = nil, ---@type facility_conf + sv_cool_conf = nil, ---@type table list of boiler & turbine counts + show_sv_cfg = nil, ---@type function ask_config = false, has_config = false, @@ -151,6 +152,8 @@ local fields = { { "LogDebug","Log Debug Messages", false } } +local function is_int_min_max(x, min, max) return util.is_int(x) and x >= min and x <= max end + -- send an management packet to the supervisor ---@param msg_type MGMT_TYPE ---@param msg table @@ -181,7 +184,26 @@ local function handle_packet(packet) if est_ack == ESTABLISH_ACK.ALLOW then if type(config) == "table" and #config == 2 then - tool_ctl.sv_fac_conf = { num_units = config[1], cooling = config[2] } + local count_ok = is_int_min_max(config[1], 1, 4) + local cool_ok = type(config[2]) == "table" and type(config[2].r_cool) == "table" and #config[2].r_cool == config[1] + + if count_ok and cool_ok then + tmp_cfg.UnitCount = config[1] + for i = 1, tmp_cfg.UnitCount do + local num_b = config[2].r_cool.BoilerCount + local num_t = config[2].r_cool.TurbineCount + tool_ctl.sv_cool_conf[i] = { num_b, num_t } + cool_ok = cool_ok and is_int_min_max(num_b, 0, 2) and is_int_min_max(num_t, 1, 3) + end + end + + if not count_ok then + error_msg = "Error: supervisor unit count out of range." + elseif not cool_ok then + error_msg = "Error: supervisor cooling configuration malformed." + tool_ctl.sv_cool_conf = nil + end + tool_ctl.sv_addr = packet.scada_frame.src_addr() send_sv(MGMT_TYPE.CLOSE, {}) else @@ -427,8 +449,9 @@ local function config_view(display) local fac_c_1 = Div{parent=fac_cfg,x=2,y=4,width=49} local fac_c_2 = Div{parent=fac_cfg,x=2,y=4,width=49} + local fac_c_3 = Div{parent=fac_cfg,x=2,y=4,width=49} - local fac_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={fac_c_1,fac_c_2}} + local fac_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3}} TextBox{parent=fac_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)} @@ -464,9 +487,21 @@ local function config_view(display) end end + local function sv_skip() + tcd.abort(handle_timeout) + tool_ctl.sv_fac_conf = nil + tool_ctl.net_listen = false + fac_pane.set_value(2) + end + + local function sv_next() + tool_ctl.show_sv_cfg() + fac_pane.set_value(3) + end + PushButton{parent=fac_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - tool_ctl.sv_skip = PushButton{parent=fac_c_1,x=44,y=14,text="Skip \x1a",callback=function()fac_pane.set_value(3)end,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} - tool_ctl.sv_next = PushButton{parent=fac_c_1,x=44,y=14,text="Next \x1a",callback=function()fac_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,hidden=true} + tool_ctl.sv_skip = PushButton{parent=fac_c_1,x=44,y=14,text="Skip \x1a",callback=sv_skip,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} + tool_ctl.sv_next = PushButton{parent=fac_c_1,x=44,y=14,text="Next \x1a",callback=sv_next,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,hidden=true} TextBox{parent=fac_c_2,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."} local num_units = NumberField{parent=fac_c_2,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg} @@ -487,6 +522,13 @@ local function config_view(display) PushButton{parent=fac_c_2,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=fac_c_2,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + TextBox{parent=fac_c_3,x=1,y=1,height=2,text="The following facility configuration was fetched from your supervisor computer."} + + local fac_config_list = ListBox{parent=fac_c_3,x=1,y=4,height=9,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + + PushButton{parent=fac_c_3,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=fac_c_3,x=44,y=14,text="Next \x1a",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + -- MONITOR CONFIG local mon_c_1 = Div{parent=mon_cfg,x=2,y=4,width=49} @@ -804,6 +846,27 @@ local function config_view(display) tool_ctl.importing_legacy = true end + -- show the facility's unit count and cooling configuration data + function tool_ctl.show_sv_cfg() + local conf = tool_ctl.sv_fac_conf + local r_cool = conf.cooling.r_cool + fac_config_list.remove_all() + + local str = util.sprintf("\x07 facility has %d reactor units", conf.num_units) + + local line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)} + TextBox{parent=line,text=str,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} + + for i = 1, conf.num_units do + local num_b = r_cool[i].BoilerCount + local num_t = r_cool[i].TurbineCount + str = util.sprintf("\x07 unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s")) + + local c_line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)} + TextBox{parent=c_line,text=str,width=10,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} + end + end + -- expose the auth key on the summary page function tool_ctl.show_auth_key() tool_ctl.show_key_btn.disable() From 34cbb6be39aafcd68569547395cf6ce11c7c0543 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 3 Feb 2024 01:48:56 -0500 Subject: [PATCH 03/43] bugfixes --- coordinator/configure.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 59b0df4..9064c03 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -189,9 +189,11 @@ local function handle_packet(packet) if count_ok and cool_ok then tmp_cfg.UnitCount = config[1] + tool_ctl.sv_cool_conf = {} + for i = 1, tmp_cfg.UnitCount do - local num_b = config[2].r_cool.BoilerCount - local num_t = config[2].r_cool.TurbineCount + local num_b = config[2].r_cool[i].BoilerCount + local num_t = config[2].r_cool[i].TurbineCount tool_ctl.sv_cool_conf[i] = { num_b, num_t } cool_ok = cool_ok and is_int_min_max(num_b, 0, 2) and is_int_min_max(num_t, 1, 3) end @@ -848,22 +850,20 @@ local function config_view(display) -- show the facility's unit count and cooling configuration data function tool_ctl.show_sv_cfg() - local conf = tool_ctl.sv_fac_conf - local r_cool = conf.cooling.r_cool + local conf = tool_ctl.sv_cool_conf fac_config_list.remove_all() - local str = util.sprintf("\x07 facility has %d reactor units", conf.num_units) + local str = util.sprintf("Facility has %d reactor units:", #conf) local line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)} TextBox{parent=line,text=str,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} - for i = 1, conf.num_units do - local num_b = r_cool[i].BoilerCount - local num_t = r_cool[i].TurbineCount - str = util.sprintf("\x07 unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s")) + for i = 1, #conf do + local num_b, num_t = conf[i][1], conf[i][2] + str = util.sprintf("\x07 Unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s")) local c_line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)} - TextBox{parent=c_line,text=str,width=10,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} + TextBox{parent=c_line,text=str,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} end end From 108cf75cadc9dab0bce236a5573f117378e6a893 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 14 Feb 2024 09:43:30 -0500 Subject: [PATCH 04/43] #309 coordinator monitor configuration --- coordinator/configure.lua | 323 +++++++++++++++++++++++++++++++++----- scada-common/util.lua | 10 ++ 2 files changed, 292 insertions(+), 41 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 9064c03..4246a0a 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -79,7 +79,7 @@ local tool_ctl = { sv_cool_conf = nil, ---@type table list of boiler & turbine counts show_sv_cfg = nil, ---@type function - ask_config = false, + start_fail = false, has_config = false, viewing_config = false, importing_legacy = false, @@ -103,11 +103,12 @@ local tool_ctl = { sv_skip = nil, ---@type graphics_element sv_next = nil, ---@type graphics_element - cooling_elems = {}, - tank_elems = {}, + update_mon_reqs = nil, ---@type function + gen_mon_list = function () end, + assign_monitor = nil, ---@type function - vis_ftanks = {}, - vis_utanks = {} + mon_iface = "", + mon_expect = {} } ---@class crd_config @@ -116,7 +117,9 @@ local tmp_cfg = { SpeakerVolume = 1.0, Time24Hour = true, DisableFlowView = false, - Displays = {}, + MainDisplay = nil, + FlowDisplay = nil, + UnitDisplays = {}, SVR_Channel = nil, ---@type integer CRD_Channel = nil, ---@type integer PKT_Channel = nil, ---@type integer @@ -177,7 +180,6 @@ local function handle_packet(packet) error_msg = "Error: unknown receive channel." elseif packet.scada_frame.remote_channel() == tmp_cfg.SVR_Channel and packet.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then if packet.type == MGMT_TYPE.ESTABLISH then - -- connection with supervisor established if packet.length == 2 then local est_ack = packet.data[1] local config = packet.data[2] @@ -297,7 +299,10 @@ local function config_view(display) TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Coordinator configurator! Please select one of the following options."} - if tool_ctl.ask_config then + if tool_ctl.start_fail == 2 then + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: There is a problem with your monitor configuration. You may have lost a monitor or their sizes may be incorrect. Please reconfigure monitors or correct their sizes.",fg_bg=cpair(colors.red,colors.lightGray)} + y_start = y_start + 5 + elseif tool_ctl.start_fail > 0 then TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)} y_start = y_start + 5 end @@ -309,10 +314,10 @@ local function config_view(display) main_pane.set_value(5) end - if fs.exists("/supervisor/config.lua") then - PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg} - y_start = y_start + 2 - end + -- if fs.exists("/coordinator/config.lua") then + -- PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg} + -- y_start = y_start + 2 + -- end PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} @@ -322,7 +327,7 @@ local function config_view(display) PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - --#region NET CONFIG + --#region Network Config local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49} local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49} @@ -447,7 +452,7 @@ local function config_view(display) --#endregion - -- FACILITY CONFIG + --#region Facility local fac_c_1 = Div{parent=fac_cfg,x=2,y=4,width=49} local fac_c_2 = Div{parent=fac_cfg,x=2,y=4,width=49} @@ -498,6 +503,7 @@ local function config_view(display) local function sv_next() tool_ctl.show_sv_cfg() + tool_ctl.update_mon_reqs() fac_pane.set_value(3) end @@ -509,6 +515,7 @@ local function config_view(display) local num_units = NumberField{parent=fac_c_2,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg} TextBox{parent=fac_c_2,x=7,y=5,height=1,text="reactors"} TextBox{parent=fac_c_2,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg} + TextBox{parent=fac_c_2,x=1,y=10,height=3,text="Since you skipped supervisor sync, the main monitor minimum height can't be determined precisely. It is marked with * on the next page.",fg_bg=g_lg_fg_bg} local nu_error = TextBox{parent=fac_c_2,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} @@ -517,7 +524,8 @@ local function config_view(display) if count ~= nil and count > 0 and count < 5 then nu_error.hide(true) tmp_cfg.UnitCount = count - main_pane.set_value(3) + tool_ctl.update_mon_reqs() + main_pane.set_value(4) else nu_error.show() end end @@ -529,39 +537,264 @@ local function config_view(display) local fac_config_list = ListBox{parent=fac_c_3,x=1,y=4,height=9,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} PushButton{parent=fac_c_3,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=fac_c_3,x=44,y=14,text="Next \x1a",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=fac_c_3,x=44,y=14,text="Next \x1a",callback=function()main_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- MONITOR CONFIG + --#endregion + + --#region Monitors local mon_c_1 = Div{parent=mon_cfg,x=2,y=4,width=49} + local mon_c_2 = Div{parent=mon_cfg,x=2,y=4,width=49} + local mon_c_3 = Div{parent=mon_cfg,x=2,y=4,width=49} - local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1}} + local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3}} TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.lime)} - TextBox{parent=mon_c_1,x=1,y=1,height=3,text="Your configuration requires the following monitors:"} + TextBox{parent=mon_c_1,x=1,y=1,height=5,text="Your configuration requires the following monitors. The main and flow monitors' heights are dependent on your unit count and cooling setup. If you manually entered the unit count, a * will be shown on potentially inaccurate calculations."} + local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} - local nu_error = TextBox{parent=mon_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + function tool_ctl.update_mon_reqs() + local plural = tmp_cfg.UnitCount > 1 - local function submit_num_units() - -- local count = tonumber(num_units.get_value()) - -- if count ~= nil and count > 0 and count < 5 then - -- nu_error.hide(true) - -- tmp_cfg.UnitCount = count + if tool_ctl.sv_cool_conf ~= nil then + local cnf = tool_ctl.sv_cool_conf - -- local confs = tool_ctl.cooling_elems - -- if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end - -- if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end - -- if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end + local row1_tall = cnf[1][1] > 1 or cnf[1][2] > 2 or (cnf[2] and (cnf[2][1] > 1 or cnf[2][2] > 2)) + local row1_short = (cnf[1][1] == 0 and cnf[1][2] == 1) and (cnf[2] == nil or (cnf[2][1] == 0 and cnf[2][2] == 1)) + local row2_tall = (cnf[3] and (cnf[3][1] > 1 or cnf[3][2] > 2)) or (cnf[4] and (cnf[4][1] > 1 or cnf[4][2] > 2)) + local row2_short = (cnf[3] == nil or (cnf[3][1] == 0 and cnf[3][2] == 1)) and (cnf[4] == nil or (cnf[4][1] == 0 and cnf[4][2] == 1)) - -- crd_pane.set_value(2) - -- else nu_error.show() end + if tmp_cfg.UnitCount <= 2 then + tool_ctl.main_mon_h = util.trinary(row1_tall, 5, 4) + else + if row1_tall or row2_tall then + tool_ctl.main_mon_h = util.trinary((row1_short and row2_tall) or (row1_tall and row2_short), 5, 6) + else tool_ctl.main_mon_h = 6 end + end + else + tool_ctl.main_mon_h = util.trinary(tmp_cfg.UnitCount <= 2, 4, 5) + end + + tool_ctl.flow_mon_h = 2 + tmp_cfg.UnitCount + + local asterisk = util.trinary(tool_ctl.sv_cool_conf == nil, "*", "") + local m_at_least = util.trinary(tool_ctl.main_mon_h < 6, "at least ", "") + local f_at_least = util.trinary(tool_ctl.flow_mon_h < 6, "at least ", "") + + mon_reqs.remove_all() + + TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a "..tmp_cfg.UnitCount.." Unit View Monitor"..util.trinary(plural,"s","")} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" "..util.trinary(plural,"each ","").."must be 4 blocks wide by 4 tall",fg_bg=cpair(colors.gray,colors.white)} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Main View Monitor"} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..m_at_least..tool_ctl.main_mon_h..asterisk.." tall",fg_bg=cpair(colors.gray,colors.white)} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Flow View Monitor"} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)} end - PushButton{parent=mon_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=mon_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=function()mon_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- COORDINATOR CONFIG + TextBox{parent=mon_c_2,x=1,y=1,height=5,text="Please configure your monitors below. You can go back to the prior page without losing progress to double check what you need. All of those monitors must be assigned before you can proceed."} + + local mon_list = ListBox{parent=mon_c_2,x=1,y=6,height=7,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + + local function to_block_size(w, h) + local width = math.floor((w - 15) / 21) + 1 + local height = math.floor((h - 10) / 14) + 1 + return width, height + end + + function tool_ctl.gen_mon_list() + mon_list.remove_all() + + local monitors = ppm.get_monitor_list() + for iface, device in pairs(monitors) do + local dev = device.dev + + dev.setTextScale(0.5) + dev.setTextColor(colors.white) + dev.setBackgroundColor(colors.black) + dev.clear() + dev.setCursorPos(1, 1) + dev.setTextColor(colors.magenta) + dev.write("This is monitor") + dev.setCursorPos(1, 2) + dev.setTextColor(colors.white) + dev.write(iface) + + local assignment = "Unused" + + if tmp_cfg.MainDisplay == iface then + assignment = "Main" + elseif tmp_cfg.FlowDisplay == iface then + assignment = "Flow" + else + for i = 1, tmp_cfg.UnitCount do + if tmp_cfg.UnitDisplays[i] == iface then + assignment = "Unit " .. i + break + end + end + end + + local line = Div{parent=mon_list,x=1,y=1,height=1} + + TextBox{parent=line,x=1,y=1,width=6,height=1,text=assignment,fg_bg=cpair(util.trinary(assignment=="Unused",colors.red,colors.blue),colors.white)} + TextBox{parent=line,x=8,y=1,height=1,text=iface} + + local w, h = to_block_size(dev.getSize()) + + local function unset_mon() + if tmp_cfg.MainDisplay == iface then + tmp_cfg.MainDisplay = nil + elseif tmp_cfg.FlowDisplay == iface then + tmp_cfg.FlowDisplay = nil + else + for i = 1, tmp_cfg.UnitCount do + if tmp_cfg.UnitDisplays[i] == iface then + tmp_cfg.UnitDisplays[i] = nil + break + end + end + end + + tool_ctl.gen_mon_list() + end + + TextBox{parent=line,x=33,y=1,width=4,height=1,text=w.."x"..h,fg_bg=cpair(colors.black,colors.white)} + PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.assign_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.lime),active_fg_bg=btn_act_fg_bg} + local unset = PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)} + + if assignment == "Unused" then unset.disable() end + end + end + + tool_ctl.gen_mon_list() + + local assign_err = TextBox{parent=mon_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_monitors() + if tmp_cfg.MainDisplay == nil then + assign_err.set_value("Please assign the main monitor.") + elseif tmp_cfg.FlowDisplay == nil then + assign_err.set_value("Please assign the flow monitor.") + ---@fixme this will have incorrect behavior if the unit count is reduced + elseif util.table_len(tmp_cfg.UnitDisplays) ~= tmp_cfg.UnitCount then + for i = 1, tmp_cfg.UnitCount do + if tmp_cfg.UnitDisplays[i] == nil then + assign_err.set_value("Please assign the unit " .. i .. " monitor.") + break + end + end + else + assign_err.hide(true) + main_pane.set_value(5) + return + end + + assign_err.show() + end + + PushButton{parent=mon_c_2,x=1,y=14,text="\x1b Back",callback=function()mon_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=mon_c_2,x=44,y=14,text="Next \x1a",callback=submit_monitors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + local mon_desc = TextBox{parent=mon_c_3,x=1,y=1,height=4,text=""} + + local mon_unit_l, mon_unit = nil, nil ---@type graphics_element, graphics_element + + local mon_warn = TextBox{parent=mon_c_3,x=1,y=11,height=2,text="That assignment doesn't match monitor dimensions. You'll need to resize the monitor for it to work.",fg_bg=cpair(colors.red,colors.lightGray)} + + local function on_assign(val) + if not util.table_contains(tool_ctl.mon_expect, val) then + mon_warn.show() + else mon_warn.hide(true) end + + if val == 3 then + mon_unit_l.show() + mon_unit.show() + mon_unit.set_value(0) + else + mon_unit_l.hide(true) + mon_unit.hide(true) + end + + mon_unit.set_max(tmp_cfg.UnitCount) + end + + TextBox{parent=mon_c_3,x=1,y=6,height=4,text="Assignment"} + local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + + mon_unit_l = TextBox{parent=mon_c_3,x=18,y=6,width=7,height=1,text="Unit ID"} + mon_unit = NumberField{parent=mon_c_3,x=18,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg} + + local mon_u_err = TextBox{parent=mon_c_3,x=8,y=14,height=1,width=35,text="Please provide a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function apply_monitor() + local type = mon_assign.get_value() + local u_id = tonumber(mon_unit.get_value()) + + if type == 1 then + tmp_cfg.MainDisplay = tool_ctl.mon_iface + elseif type == 2 then + tmp_cfg.FlowDisplay = tool_ctl.mon_iface + elseif u_id and u_id > 0 then + tmp_cfg.UnitDisplays[u_id] = tool_ctl.mon_iface + else + mon_u_err.show() + return + end + + tool_ctl.gen_mon_list() + mon_u_err.hide(true) + mon_pane.set_value(2) + end + + PushButton{parent=mon_c_3,x=1,y=14,text="\x1b Back",callback=function()mon_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=mon_c_3,x=43,y=14,min_width=7,text="Apply",callback=apply_monitor,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} + + ---@param iface string + ---@param device ppm_entry + function tool_ctl.assign_monitor(iface, device) + tool_ctl.mon_iface = iface + + local dev = device.dev + local w, h = to_block_size(dev.getSize()) + + local msg = "This size doesn't match a required screen. Please go back and resize it, or configure below at the risk of it not working." + + mon_assign.set_value(1) + tool_ctl.mon_expect = {} + + if w == 4 and h == 4 then + msg = "This could work as a unit display. Please configure below." + tool_ctl.mon_expect = { 3 } + mon_assign.set_value(3) + mon_unit.set_value(0) + elseif w == 8 then + if h >= tool_ctl.main_mon_h and h >= tool_ctl.flow_mon_h then + msg = "This could work as either your main monitor or flow monitor. Please configure below." + tool_ctl.mon_expect = { 1, 2 } + if tmp_cfg.MainDisplay then mon_assign.set_value(2) end + elseif h >= tool_ctl.main_mon_h then + msg = "This could work as your main monitor. Please configure below." + tool_ctl.mon_expect = { 1 } + elseif h >= tool_ctl.flow_mon_h then + msg = "This could work as your flow monitor. Please configure below." + tool_ctl.mon_expect = { 2 } + mon_assign.set_value(2) + end + end + + on_assign(mon_assign.get_value()) + + mon_desc.set_value(util.c("You have selected '", iface, "', which has a block size of ", w, " wide by ", h, " tall. ", msg)) + mon_pane.set_value(3) + end + + --#endregion + + --#region Coordinator General local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49} local crd_c_2 = Div{parent=crd_cfg,x=2,y=4,width=49} @@ -596,10 +829,12 @@ local function config_view(display) else nu_error.show() end end - PushButton{parent=crd_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=crd_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=crd_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- LOG CONFIG + --#endregion + + --#region Logging local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49} @@ -723,8 +958,10 @@ local function config_view(display) local function go_home() main_pane.set_value(1) - svr_pane.set_value(1) net_pane.set_value(1) + fac_pane.set_value(1) + mon_pane.set_value(1) + crd_pane.set_value(1) sum_pane.set_value(1) end @@ -958,10 +1195,10 @@ local function reset_term() term.setCursorPos(1, 1) end --- run the supervisor configurator ----@param ask_config? boolean indicate if this is being called by the supervisor startup app due to an invalid configuration -function configurator.configure(ask_config) - tool_ctl.ask_config = ask_config == true +-- run the coordinator configurator +---@param start_fail? integer indicate if this is being called by the coordinator startup app due to an invalid configuration +function configurator.configure(start_fail) + tool_ctl.start_fail = start_fail or 0 load_settings(settings_cfg, true) tool_ctl.has_config = load_settings(ini_cfg) @@ -995,8 +1232,12 @@ function configurator.configure(ask_config) display.handle_paste(param1) elseif event == "peripheral_detach" then ppm.handle_unmount(param1) + tool_ctl.gen_mon_list() elseif event == "peripheral" then ppm.mount(param1) + tool_ctl.gen_mon_list() + elseif event == "monitor_resize" then + tool_ctl.gen_mon_list() elseif event == "modem_message" and tool_ctl.nic ~= nil and tool_ctl.net_listen then local s_pkt = tool_ctl.nic.receive(param1, param2, param3, param4, param5) diff --git a/scada-common/util.lua b/scada-common/util.lua index 28974de..c067c62 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -348,6 +348,16 @@ function util.table_contains(t, element) return false end +-- count the length of a table, even if the values are not sequential or contain named keys +---@nodiscard +---@param t table +---@return integer length +function util.table_len(t) + local n = 0 + for _, _ in pairs(t) do n = n + 1 end + return n +end + --#endregion --#region MEKANISM POWER From d19794ae4fbb0c33b44791a04c6471686568494b Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 14 Feb 2024 10:24:27 -0500 Subject: [PATCH 05/43] #309 unassign unused monitors on unit count reduction and support autofilling fields when editing existing monitor configs --- coordinator/configure.lua | 264 +++++++++++++++++++++----------------- 1 file changed, 149 insertions(+), 115 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 4246a0a..fc12c87 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -105,7 +105,7 @@ local tool_ctl = { update_mon_reqs = nil, ---@type function gen_mon_list = function () end, - assign_monitor = nil, ---@type function + edit_monitor = nil, ---@type function mon_iface = "", mon_expect = {} @@ -592,8 +592,16 @@ local function config_view(display) TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)} end + local function next_from_reqs() + -- unassign unit monitors above the unit count + for i = tmp_cfg.UnitCount + 1, 4 do tmp_cfg.UnitDisplays[i] = nil end + + tool_ctl.gen_mon_list() + mon_pane.set_value(2) + end + PushButton{parent=mon_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=function()mon_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=next_from_reqs,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} TextBox{parent=mon_c_2,x=1,y=1,height=5,text="Please configure your monitors below. You can go back to the prior page without losing progress to double check what you need. All of those monitors must be assigned before you can proceed."} @@ -605,73 +613,6 @@ local function config_view(display) return width, height end - function tool_ctl.gen_mon_list() - mon_list.remove_all() - - local monitors = ppm.get_monitor_list() - for iface, device in pairs(monitors) do - local dev = device.dev - - dev.setTextScale(0.5) - dev.setTextColor(colors.white) - dev.setBackgroundColor(colors.black) - dev.clear() - dev.setCursorPos(1, 1) - dev.setTextColor(colors.magenta) - dev.write("This is monitor") - dev.setCursorPos(1, 2) - dev.setTextColor(colors.white) - dev.write(iface) - - local assignment = "Unused" - - if tmp_cfg.MainDisplay == iface then - assignment = "Main" - elseif tmp_cfg.FlowDisplay == iface then - assignment = "Flow" - else - for i = 1, tmp_cfg.UnitCount do - if tmp_cfg.UnitDisplays[i] == iface then - assignment = "Unit " .. i - break - end - end - end - - local line = Div{parent=mon_list,x=1,y=1,height=1} - - TextBox{parent=line,x=1,y=1,width=6,height=1,text=assignment,fg_bg=cpair(util.trinary(assignment=="Unused",colors.red,colors.blue),colors.white)} - TextBox{parent=line,x=8,y=1,height=1,text=iface} - - local w, h = to_block_size(dev.getSize()) - - local function unset_mon() - if tmp_cfg.MainDisplay == iface then - tmp_cfg.MainDisplay = nil - elseif tmp_cfg.FlowDisplay == iface then - tmp_cfg.FlowDisplay = nil - else - for i = 1, tmp_cfg.UnitCount do - if tmp_cfg.UnitDisplays[i] == iface then - tmp_cfg.UnitDisplays[i] = nil - break - end - end - end - - tool_ctl.gen_mon_list() - end - - TextBox{parent=line,x=33,y=1,width=4,height=1,text=w.."x"..h,fg_bg=cpair(colors.black,colors.white)} - PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.assign_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.lime),active_fg_bg=btn_act_fg_bg} - local unset = PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)} - - if assignment == "Unused" then unset.disable() end - end - end - - tool_ctl.gen_mon_list() - local assign_err = TextBox{parent=mon_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} local function submit_monitors() @@ -679,7 +620,6 @@ local function config_view(display) assign_err.set_value("Please assign the main monitor.") elseif tmp_cfg.FlowDisplay == nil then assign_err.set_value("Please assign the flow monitor.") - ---@fixme this will have incorrect behavior if the unit count is reduced elseif util.table_len(tmp_cfg.UnitDisplays) ~= tmp_cfg.UnitCount then for i = 1, tmp_cfg.UnitCount do if tmp_cfg.UnitDisplays[i] == nil then @@ -705,7 +645,8 @@ local function config_view(display) local mon_warn = TextBox{parent=mon_c_3,x=1,y=11,height=2,text="That assignment doesn't match monitor dimensions. You'll need to resize the monitor for it to work.",fg_bg=cpair(colors.red,colors.lightGray)} - local function on_assign(val) + ---@param val integer assignment type + local function on_assign_mon(val) if not util.table_contains(tool_ctl.mon_expect, val) then mon_warn.show() else mon_warn.hide(true) end @@ -713,33 +654,54 @@ local function config_view(display) if val == 3 then mon_unit_l.show() mon_unit.show() - mon_unit.set_value(0) else mon_unit_l.hide(true) mon_unit.hide(true) end + local value = mon_unit.get_value() mon_unit.set_max(tmp_cfg.UnitCount) + if value == "0" or value == nil then mon_unit.set_value(0) end end TextBox{parent=mon_c_3,x=1,y=6,height=4,text="Assignment"} - local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign_mon,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} mon_unit_l = TextBox{parent=mon_c_3,x=18,y=6,width=7,height=1,text="Unit ID"} mon_unit = NumberField{parent=mon_c_3,x=18,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg} local mon_u_err = TextBox{parent=mon_c_3,x=8,y=14,height=1,width=35,text="Please provide a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + -- purge all assignments for a given monitor + ---@param iface string + local function purge_assignments(iface) + if tmp_cfg.MainDisplay == iface then + tmp_cfg.MainDisplay = nil + elseif tmp_cfg.FlowDisplay == iface then + tmp_cfg.FlowDisplay = nil + else + for i = 1, tmp_cfg.UnitCount do + if tmp_cfg.UnitDisplays[i] == iface then + tmp_cfg.UnitDisplays[i] = nil + end + end + end + end + local function apply_monitor() + local iface = tool_ctl.mon_iface local type = mon_assign.get_value() local u_id = tonumber(mon_unit.get_value()) if type == 1 then - tmp_cfg.MainDisplay = tool_ctl.mon_iface + purge_assignments(iface) + tmp_cfg.MainDisplay = iface elseif type == 2 then - tmp_cfg.FlowDisplay = tool_ctl.mon_iface + purge_assignments(iface) + tmp_cfg.FlowDisplay = iface elseif u_id and u_id > 0 then - tmp_cfg.UnitDisplays[u_id] = tool_ctl.mon_iface + purge_assignments(iface) + tmp_cfg.UnitDisplays[u_id] = iface else mon_u_err.show() return @@ -753,45 +715,6 @@ local function config_view(display) PushButton{parent=mon_c_3,x=1,y=14,text="\x1b Back",callback=function()mon_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=mon_c_3,x=43,y=14,min_width=7,text="Apply",callback=apply_monitor,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} - ---@param iface string - ---@param device ppm_entry - function tool_ctl.assign_monitor(iface, device) - tool_ctl.mon_iface = iface - - local dev = device.dev - local w, h = to_block_size(dev.getSize()) - - local msg = "This size doesn't match a required screen. Please go back and resize it, or configure below at the risk of it not working." - - mon_assign.set_value(1) - tool_ctl.mon_expect = {} - - if w == 4 and h == 4 then - msg = "This could work as a unit display. Please configure below." - tool_ctl.mon_expect = { 3 } - mon_assign.set_value(3) - mon_unit.set_value(0) - elseif w == 8 then - if h >= tool_ctl.main_mon_h and h >= tool_ctl.flow_mon_h then - msg = "This could work as either your main monitor or flow monitor. Please configure below." - tool_ctl.mon_expect = { 1, 2 } - if tmp_cfg.MainDisplay then mon_assign.set_value(2) end - elseif h >= tool_ctl.main_mon_h then - msg = "This could work as your main monitor. Please configure below." - tool_ctl.mon_expect = { 1 } - elseif h >= tool_ctl.flow_mon_h then - msg = "This could work as your flow monitor. Please configure below." - tool_ctl.mon_expect = { 2 } - mon_assign.set_value(2) - end - end - - on_assign(mon_assign.get_value()) - - mon_desc.set_value(util.c("You have selected '", iface, "', which has a block size of ", w, " wide by ", h, " tall. ", msg)) - mon_pane.set_value(3) - end - --#endregion --#region Coordinator General @@ -1104,6 +1027,115 @@ local function config_view(display) end end + -- set/edit a monitor's assignment + ---@param iface string + ---@param device ppm_entry + function tool_ctl.edit_monitor(iface, device) + tool_ctl.mon_iface = iface + + local dev = device.dev + local w, h = to_block_size(dev.getSize()) + + local msg = "This size doesn't match a required screen. Please go back and resize it, or configure below at the risk of it not working." + + tool_ctl.mon_expect = {} + mon_assign.set_value(1) + mon_unit.set_value(0) + + if w == 4 and h == 4 then + msg = "This could work as a unit display. Please configure below." + tool_ctl.mon_expect = { 3 } + mon_assign.set_value(3) + elseif w == 8 then + if h >= tool_ctl.main_mon_h and h >= tool_ctl.flow_mon_h then + msg = "This could work as either your main monitor or flow monitor. Please configure below." + tool_ctl.mon_expect = { 1, 2 } + if tmp_cfg.MainDisplay then mon_assign.set_value(2) end + elseif h >= tool_ctl.main_mon_h then + msg = "This could work as your main monitor. Please configure below." + tool_ctl.mon_expect = { 1 } + elseif h >= tool_ctl.flow_mon_h then + msg = "This could work as your flow monitor. Please configure below." + tool_ctl.mon_expect = { 2 } + mon_assign.set_value(2) + end + end + + -- override if a config exists + if tmp_cfg.MainDisplay == iface then + mon_assign.set_value(1) + elseif tmp_cfg.FlowDisplay == iface then + mon_assign.set_value(2) + else + for i = 1, tmp_cfg.UnitCount do + if tmp_cfg.UnitDisplays[i] == iface then + mon_assign.set_value(3) + mon_unit.set_value(i) + break + end + end + end + + on_assign_mon(mon_assign.get_value()) + + mon_desc.set_value(util.c("You have selected '", iface, "', which has a block size of ", w, " wide by ", h, " tall. ", msg)) + mon_pane.set_value(3) + end + + -- generate the list of available monitors + function tool_ctl.gen_mon_list() + mon_list.remove_all() + + local monitors = ppm.get_monitor_list() + for iface, device in pairs(monitors) do + local dev = device.dev + + dev.setTextScale(0.5) + dev.setTextColor(colors.white) + dev.setBackgroundColor(colors.black) + dev.clear() + dev.setCursorPos(1, 1) + dev.setTextColor(colors.magenta) + dev.write("This is monitor") + dev.setCursorPos(1, 2) + dev.setTextColor(colors.white) + dev.write(iface) + + local assignment = "Unused" + + if tmp_cfg.MainDisplay == iface then + assignment = "Main" + elseif tmp_cfg.FlowDisplay == iface then + assignment = "Flow" + else + for i = 1, tmp_cfg.UnitCount do + if tmp_cfg.UnitDisplays[i] == iface then + assignment = "Unit " .. i + break + end + end + end + + local line = Div{parent=mon_list,x=1,y=1,height=1} + + TextBox{parent=line,x=1,y=1,width=6,height=1,text=assignment,fg_bg=cpair(util.trinary(assignment=="Unused",colors.red,colors.blue),colors.white)} + TextBox{parent=line,x=8,y=1,height=1,text=iface} + + local w, h = to_block_size(dev.getSize()) + + local function unset_mon() + purge_assignments(iface) + tool_ctl.gen_mon_list() + end + + TextBox{parent=line,x=33,y=1,width=4,height=1,text=w.."x"..h,fg_bg=cpair(colors.black,colors.white)} + PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.edit_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.lime),active_fg_bg=btn_act_fg_bg} + local unset = PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)} + + if assignment == "Unused" then unset.disable() end + end + end + -- expose the auth key on the summary page function tool_ctl.show_auth_key() tool_ctl.show_key_btn.disable() @@ -1216,6 +1248,8 @@ function configurator.configure(start_fail) local display = DisplayBox{window=term.current(),fg_bg=style.root} config_view(display) + tool_ctl.gen_mon_list() + while true do local event, param1, param2, param3, param4, param5 = util.pull_event() From 7374bb02d179a23f64563eba5dbdbb67a21ba1b8 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 14 Feb 2024 14:41:34 -0500 Subject: [PATCH 06/43] #309 speaker and time format configuration --- coordinator/configure.lua | 81 +++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index fc12c87..d26bc07 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -285,13 +285,14 @@ local function config_view(display) local net_cfg = Div{parent=root_pane_div,x=1,y=1} local fac_cfg = Div{parent=root_pane_div,x=1,y=1} local mon_cfg = Div{parent=root_pane_div,x=1,y=1} + local spkr_cfg = Div{parent=root_pane_div,x=1,y=1} local crd_cfg = Div{parent=root_pane_div,x=1,y=1} local log_cfg = Div{parent=root_pane_div,x=1,y=1} local summary = Div{parent=root_pane_div,x=1,y=1} local changelog = Div{parent=root_pane_div,x=1,y=1} local import_err = Div{parent=root_pane_div,x=1,y=1} - local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,crd_cfg,log_cfg,summary,changelog,import_err}} + local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,spkr_cfg,crd_cfg,log_cfg,summary,changelog,import_err}} -- MAIN PAGE @@ -327,7 +328,7 @@ local function config_view(display) PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - --#region Network Config + --#region Network local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49} local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49} @@ -549,7 +550,7 @@ local function config_view(display) local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3}} - TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.lime)} + TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.blue)} TextBox{parent=mon_c_1,x=1,y=1,height=5,text="Your configuration requires the following monitors. The main and flow monitors' heights are dependent on your unit count and cooling setup. If you manually entered the unit count, a * will be shown on potentially inaccurate calculations."} local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} @@ -665,7 +666,7 @@ local function config_view(display) end TextBox{parent=mon_c_3,x=1,y=6,height=4,text="Assignment"} - local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign_mon,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign_mon,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.blue} mon_unit_l = TextBox{parent=mon_c_3,x=18,y=6,width=7,height=1,text="Unit ID"} mon_unit = NumberField{parent=mon_c_3,x=18,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg} @@ -717,43 +718,55 @@ local function config_view(display) --#endregion - --#region Coordinator General + --#region Speaker + + local spkr_c = Div{parent=spkr_cfg,x=2,y=4,width=49} + + TextBox{parent=spkr_cfg,x=1,y=2,height=1,text=" Speaker Configuration",fg_bg=cpair(colors.black,colors.cyan)} + + TextBox{parent=spkr_c,x=1,y=1,height=2,text="The coordinator uses a speaker to play alarm sounds."} + TextBox{parent=spkr_c,x=1,y=4,height=3,text="You can change the speaker audio volume from the default. The range is 0.0 to 3.0, where 1.0 is standard volume."} + + local s_vol = NumberField{parent=spkr_c,x=1,y=8,width=9,max_chars=7,allow_decimal=true,default=ini_cfg.SpeakerVolume,min=0,max=3,fg_bg=bw_fg_bg} + + TextBox{parent=spkr_c,x=1,y=10,height=3,text="Note: alarm sine waves are at half scale so that multiple will be required to reach full scale.",fg_bg=g_lg_fg_bg} + + local s_vol_err = TextBox{parent=spkr_c,x=8,y=14,height=1,width=35,text="Please set a volume.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_vol() + local vol = tonumber(s_vol.get_value()) + if vol ~= nil then + s_vol_err.hide(true) + tmp_cfg.SpeakerVolume = vol + main_pane.set_value(6) + else s_vol_err.show() end + end + + PushButton{parent=spkr_c,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=spkr_c,x=44,y=14,text="Next \x1a",callback=submit_vol,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#endregion + + --#region Coordinator UI local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49} - local crd_c_2 = Div{parent=crd_cfg,x=2,y=4,width=49} - local crd_c_3 = Div{parent=crd_cfg,x=2,y=4,width=49} - local crd_c_4 = Div{parent=crd_cfg,x=2,y=4,width=49} - local crd_c_5 = Div{parent=crd_cfg,x=2,y=4,width=49} - local crd_c_6 = Div{parent=crd_cfg,x=2,y=4,width=49} local crd_pane = MultiPane{parent=crd_cfg,x=1,y=4,panes={crd_c_1}} - TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator Configuration",fg_bg=cpair(colors.black,colors.lime)} + TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator UI Configuration",fg_bg=cpair(colors.black,colors.lime)} - TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."} - local num_units = NumberField{parent=crd_c_1,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg} - TextBox{parent=crd_c_1,x=7,y=5,height=1,text="reactors"} - TextBox{parent=crd_c_1,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg} + TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Configure the UI interface options below if you wish to customize formats."} - local nu_error = TextBox{parent=crd_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + TextBox{parent=crd_c_1,x=1,y=4,height=1,text="Clock Time Format"} + local clock_fmt = RadioButton{parent=crd_c_1,x=1,y=5,default=util.trinary(ini_cfg.Time24Hour,1,2),options={"24-Hour","12-Hour"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} - local function submit_num_units() - local count = tonumber(num_units.get_value()) - if count ~= nil and count > 0 and count < 5 then - nu_error.hide(true) - tmp_cfg.UnitCount = count - - local confs = tool_ctl.cooling_elems - if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end - if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end - if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end - - crd_pane.set_value(2) - else nu_error.show() end + local function submit_ui_opts() + tmp_cfg.Time24Hour = clock_fmt.get_value() == 1 + main_pane.set_value(7) end - PushButton{parent=crd_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=crd_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=crd_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=crd_c_1,x=44,y=14,text="Next \x1a",callback=submit_ui_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} --#endregion @@ -786,11 +799,11 @@ local function config_view(display) tool_ctl.viewing_config = false tool_ctl.importing_legacy = false tool_ctl.settings_apply.show() - main_pane.set_value(5) + main_pane.set_value(8) else path_err.show() end end - PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} -- SUMMARY OF CHANGES @@ -1129,7 +1142,7 @@ local function config_view(display) end TextBox{parent=line,x=33,y=1,width=4,height=1,text=w.."x"..h,fg_bg=cpair(colors.black,colors.white)} - PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.edit_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.lime),active_fg_bg=btn_act_fg_bg} + PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.edit_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} local unset = PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)} if assignment == "Unused" then unset.disable() end From 5e9f03c900ef577336986ea9fa63f108e3bae030 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 17 Feb 2024 18:12:28 -0500 Subject: [PATCH 07/43] #418 fixed graphics bug with redraw --- graphics/core.lua | 2 +- graphics/element.lua | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/graphics/core.lua b/graphics/core.lua index 13f97ec..3e53b83 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -7,7 +7,7 @@ local flasher = require("graphics.flasher") local core = {} -core.version = "2.1.0" +core.version = "2.1.1" core.flasher = flasher core.events = events diff --git a/graphics/element.lua b/graphics/element.lua index 6debaf4..939efd6 100644 --- a/graphics/element.lua +++ b/graphics/element.lua @@ -843,9 +843,12 @@ function element.new(args, child_offset_x, child_offset_y) -- re-draw this element and all its children function public.redraw() + local bg, fg = protected.window.getBackgroundColor(), protected.window.getTextColor() protected.window.setBackgroundColor(protected.fg_bg.bkg) protected.window.setTextColor(protected.fg_bg.fgd) protected.window.clear() + protected.window.setBackgroundColor(bg) + protected.window.setTextColor(fg) protected.redraw() for _, child in pairs(protected.children) do child.get().redraw() end end From 6eccebbe39a3782a0cc0ceb0b541a4b0ad3f63ce Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 17 Feb 2024 18:16:21 -0500 Subject: [PATCH 08/43] #409 fixed positioning --- supervisor/configure.lua | 2 +- supervisor/startup.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/supervisor/configure.lua b/supervisor/configure.lua index 745c082..50d404b 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -331,7 +331,7 @@ local function config_view(display) TextBox{parent=div,x=1,y=1,width=33,height=1,text="Unit "..i.." will be connected to..."} TextBox{parent=div,x=6,y=2,width=3,height=1,text="..."} - local tank_opt = Radio2D{parent=div,x=10,y=2,rows=1,columns=2,default=val,options={"its own Unit Tank","a Facility Tank"},radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg} + local tank_opt = Radio2D{parent=div,x=9,y=2,rows=1,columns=2,default=val,options={"its own Unit Tank","a Facility Tank"},radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg} local no_tank = TextBox{parent=div,x=9,y=2,width=34,height=1,text="no tank (as you set two steps ago)",fg_bg=cpair(colors.gray,colors.lightGray),hidden=true} tool_ctl.tank_elems[i] = { div = div, tank_opt = tank_opt, no_tank = no_tank } diff --git a/supervisor/startup.lua b/supervisor/startup.lua index e4ab974..4e86a1e 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v1.2.5" +local SUPERVISOR_VERSION = "v1.2.6" local println = util.println local println_ts = util.println_ts From cafba6c67a4b459c3d9d501ec560e89d4b69f2a8 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 17 Feb 2024 18:38:36 -0500 Subject: [PATCH 09/43] #309 legacy options and general improvements --- coordinator/configure.lua | 73 ++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index d26bc07..edae838 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -70,6 +70,7 @@ local bw_fg_bg = cpair(colors.black, colors.white) local g_lg_fg_bg = cpair(colors.gray, colors.lightGray) local nav_fg_bg = bw_fg_bg local btn_act_fg_bg = cpair(colors.white, colors.gray) +local dis_fg_bg = cpair(colors.lightGray,colors.white) local tool_ctl = { nic = nil, ---@type nic @@ -103,6 +104,8 @@ local tool_ctl = { sv_skip = nil, ---@type graphics_element sv_next = nil, ---@type graphics_element + apply_mon = nil, ---@type graphics_element + update_mon_reqs = nil, ---@type function gen_mon_list = function () end, edit_monitor = nil, ---@type function @@ -140,6 +143,9 @@ local settings_cfg = {} -- all settings fields, their nice names, and their default values local fields = { { "UnitCount", "Number of Reactors", 1 }, + { "MainDisplay", "Main Monitor", nil }, + { "FlowDisplay", "Flow Monitor", nil }, + { "UnitDisplays", "Unit Monitors", {} }, { "SpeakerVolume", "Speaker Volume", 1.0 }, { "Use24HourTime", "Use 24-hour Time Format", true }, { "DisableFlowView", "Don't Use Flow", {} }, @@ -258,6 +264,17 @@ local function handle_timeout() tool_ctl.sv_conn_detail.set_value("Supervisor did not reply. Ensure startup app is running on the supervisor.") end +-- load tmp_cfg fields from ini_cfg fields for displays +local function preset_monitor_fields() + tmp_cfg.DisableFlowView = ini_cfg.DisableFlowView + + tmp_cfg.MainDisplay = ini_cfg.MainDisplay + tmp_cfg.FlowDisplay = ini_cfg.FlowDisplay + for i = 1, ini_cfg.UnitCount do + tmp_cfg.UnitDisplays[i] = ini_cfg.UnitDisplays[i] + end +end + -- load data from the settings file ---@param target crd_config ---@param raw boolean? true to not use default values @@ -294,7 +311,7 @@ local function config_view(display) local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,spkr_cfg,crd_cfg,log_cfg,summary,changelog,import_err}} - -- MAIN PAGE + -- Main Page local y_start = 5 @@ -321,7 +338,7 @@ local function config_view(display) -- end PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} - tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end @@ -445,6 +462,8 @@ local function config_view(display) tool_ctl.sv_conn_detail.set_value("") tool_ctl.sv_next.hide() tool_ctl.sv_skip.show() + tool_ctl.sv_skip.disable() + tcd.dispatch_unique(2, function () tool_ctl.sv_skip.enable() end) else key_err.show() end end @@ -469,7 +488,7 @@ local function config_view(display) tool_ctl.sv_conn_status = TextBox{parent=fac_c_1,x=11,y=9,height=1,text=""} tool_ctl.sv_conn_detail = TextBox{parent=fac_c_1,x=1,y=11,height=2,text=""} - tool_ctl.sv_conn_button = PushButton{parent=fac_c_1,x=1,y=9,text="Connect",min_width=9,callback=function()tool_ctl.sv_connect()end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + tool_ctl.sv_conn_button = PushButton{parent=fac_c_1,x=1,y=9,text="Connect",min_width=9,callback=function()tool_ctl.sv_connect()end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} function tool_ctl.sv_connect() tool_ctl.sv_conn_button.disable() @@ -509,7 +528,7 @@ local function config_view(display) end PushButton{parent=fac_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - tool_ctl.sv_skip = PushButton{parent=fac_c_1,x=44,y=14,text="Skip \x1a",callback=sv_skip,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} + tool_ctl.sv_skip = PushButton{parent=fac_c_1,x=44,y=14,text="Skip \x1a",callback=sv_skip,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} tool_ctl.sv_next = PushButton{parent=fac_c_1,x=44,y=14,text="Next \x1a",callback=sv_next,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,hidden=true} TextBox{parent=fac_c_2,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."} @@ -548,7 +567,7 @@ local function config_view(display) local mon_c_2 = Div{parent=mon_cfg,x=2,y=4,width=49} local mon_c_3 = Div{parent=mon_cfg,x=2,y=4,width=49} - local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3}} + local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3,mon_c_4}} TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.blue)} @@ -589,8 +608,10 @@ local function config_view(display) TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" "..util.trinary(plural,"each ","").."must be 4 blocks wide by 4 tall",fg_bg=cpair(colors.gray,colors.white)} TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Main View Monitor"} TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..m_at_least..tool_ctl.main_mon_h..asterisk.." tall",fg_bg=cpair(colors.gray,colors.white)} - TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Flow View Monitor"} - TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)} + if not tmp_cfg.DisableFlowView then + TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Flow View Monitor"} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)} + end end local function next_from_reqs() @@ -602,6 +623,7 @@ local function config_view(display) end PushButton{parent=mon_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=mon_c_1,x=8,y=14,text="Legacy Options",min_width=16,callback=function()mon_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=next_from_reqs,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} TextBox{parent=mon_c_2,x=1,y=1,height=5,text="Please configure your monitors below. You can go back to the prior page without losing progress to double check what you need. All of those monitors must be assigned before you can proceed."} @@ -619,7 +641,7 @@ local function config_view(display) local function submit_monitors() if tmp_cfg.MainDisplay == nil then assign_err.set_value("Please assign the main monitor.") - elseif tmp_cfg.FlowDisplay == nil then + elseif tmp_cfg.FlowDisplay == nil and not tmp_cfg.DisableFlowView then assign_err.set_value("Please assign the flow monitor.") elseif util.table_len(tmp_cfg.UnitDisplays) ~= tmp_cfg.UnitCount then for i = 1, tmp_cfg.UnitCount do @@ -644,13 +666,22 @@ local function config_view(display) local mon_unit_l, mon_unit = nil, nil ---@type graphics_element, graphics_element - local mon_warn = TextBox{parent=mon_c_3,x=1,y=11,height=2,text="That assignment doesn't match monitor dimensions. You'll need to resize the monitor for it to work.",fg_bg=cpair(colors.red,colors.lightGray)} + local mon_warn = TextBox{parent=mon_c_3,x=1,y=11,height=2,text="",fg_bg=cpair(colors.red,colors.lightGray)} ---@param val integer assignment type local function on_assign_mon(val) - if not util.table_contains(tool_ctl.mon_expect, val) then + if val == 2 and tmp_cfg.DisableFlowView then + tool_ctl.apply_mon.disable() + mon_warn.set_value("You disabled having a flow view monitor. It can't be set unless you go back and enable it.") mon_warn.show() - else mon_warn.hide(true) end + elseif not util.table_contains(tool_ctl.mon_expect, val) then + tool_ctl.apply_mon.disable() + mon_warn.set_value("That assignment doesn't match monitor dimensions. You'll need to resize the monitor for it to work.") + mon_warn.show() + else + tool_ctl.apply_mon.enable() + mon_warn.hide(true) + end if val == 3 then mon_unit_l.show() @@ -665,7 +696,7 @@ local function config_view(display) if value == "0" or value == nil then mon_unit.set_value(0) end end - TextBox{parent=mon_c_3,x=1,y=6,height=4,text="Assignment"} + TextBox{parent=mon_c_3,x=1,y=6,width=10,height=1,text="Assignment"} local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign_mon,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.blue} mon_unit_l = TextBox{parent=mon_c_3,x=18,y=6,width=7,height=1,text="Unit ID"} @@ -714,7 +745,20 @@ local function config_view(display) end PushButton{parent=mon_c_3,x=1,y=14,text="\x1b Back",callback=function()mon_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=mon_c_3,x=43,y=14,min_width=7,text="Apply",callback=apply_monitor,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} + tool_ctl.apply_mon = PushButton{parent=mon_c_3,x=43,y=14,min_width=7,text="Apply",callback=apply_monitor,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} + + TextBox{parent=mon_c_4,x=1,y=1,height=3,text="For legacy compatibility with facilities built without space for a flow monitor, you can disable the flow monitor requirement here."} + TextBox{parent=mon_c_4,x=1,y=5,height=3,text="Please be aware that THIS WILL BE REMOVED ON RELEASE. It will only be available for the remainder of the beta."} + + local dis_flow_view = CheckBox{parent=mon_c_4,x=1,y=9,default=ini_cfg.DisableFlowView,label="Disable Flow View Monitor",box_fg_bg=cpair(colors.blue,colors.black)} + + local function back_from_legacy() + tmp_cfg.DisableFlowView = dis_flow_view.get_value() + tool_ctl.update_mon_reqs() + mon_pane.set_value(1) + end + + PushButton{parent=mon_c_4,x=1,y=14,text="\x1b Back",callback=back_from_legacy,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} --#endregion @@ -1248,6 +1292,9 @@ function configurator.configure(start_fail) load_settings(settings_cfg, true) tool_ctl.has_config = load_settings(ini_cfg) + -- copy in some important values to start with + preset_monitor_fields() + reset_term() ppm.mount_all() From 2142c1b4f704ad818471c4b5749abe2f25716ea0 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 17 Feb 2024 18:39:02 -0500 Subject: [PATCH 10/43] updated license year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index e6824b5..7578961 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright © 2022 - 2023 Mikayla Fischler +Copyright © 2022 - 2024 Mikayla Fischler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From d6de9c266bfcf91074b4eb263c71fb736d6509d9 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 17 Feb 2024 22:39:50 -0500 Subject: [PATCH 11/43] #309 viewing and saving coordinator config --- coordinator/configure.lua | 113 ++++++++++++++------------------------ 1 file changed, 41 insertions(+), 72 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index edae838..ee5d4b2 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -147,12 +147,12 @@ local fields = { { "FlowDisplay", "Flow Monitor", nil }, { "UnitDisplays", "Unit Monitors", {} }, { "SpeakerVolume", "Speaker Volume", 1.0 }, - { "Use24HourTime", "Use 24-hour Time Format", true }, - { "DisableFlowView", "Don't Use Flow", {} }, + { "Time24Hour", "Use 24-hour Time Format", true }, + { "DisableFlowView", "Disable Flow Monitor (legacy, discouraged)", false }, { "SVR_Channel", "SVR Channel", 16240 }, { "CRD_Channel", "CRD Channel", 16243 }, { "PKT_Channel", "PKT Channel", 16244 }, - { "CRD_Timeout", "CRD Connection Timeout", 5 }, + { "SVR_Timeout", "Supervisor Connection Timeout", 5 }, { "API_Timeout", "API Connection Timeout", 5 }, { "TrustedRange", "Trusted Range", 0 }, { "AuthKey", "Facility Auth Key" , ""}, @@ -329,7 +329,7 @@ local function config_view(display) tool_ctl.viewing_config = true tool_ctl.gen_summary(settings_cfg) tool_ctl.settings_apply.hide(true) - main_pane.set_value(5) + main_pane.set_value(8) end -- if fs.exists("/coordinator/config.lua") then @@ -392,16 +392,16 @@ local function config_view(display) local svr_timeout = NumberField{parent=net_c_2,x=20,y=8,width=7,default=ini_cfg.SVR_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg} TextBox{parent=net_c_2,x=1,y=10,height=1,width=14,text="Pocket Timeout"} - local pkt_timeout = NumberField{parent=net_c_2,x=20,y=10,width=7,default=ini_cfg.PKT_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg} + local api_timeout = NumberField{parent=net_c_2,x=20,y=10,width=7,default=ini_cfg.API_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg} TextBox{parent=net_c_2,x=28,y=8,height=4,width=7,text="seconds\n\nseconds",fg_bg=g_lg_fg_bg} local ct_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} local function submit_timeouts() - local svr_cto, pkt_cto = tonumber(svr_timeout.get_value()), tonumber(pkt_timeout.get_value()) - if svr_cto ~= nil and pkt_cto ~= nil then - tmp_cfg.SVR_Timeout, tmp_cfg.PKT_Timeout = svr_cto, pkt_cto + local svr_cto, api_cto = tonumber(svr_timeout.get_value()), tonumber(api_timeout.get_value()) + if svr_cto ~= nil and api_cto ~= nil then + tmp_cfg.SVR_Timeout, tmp_cfg.API_Timeout = svr_cto, api_cto net_pane.set_value(3) ct_err.hide(true) else ct_err.show() end @@ -566,6 +566,7 @@ local function config_view(display) local mon_c_1 = Div{parent=mon_cfg,x=2,y=4,width=49} local mon_c_2 = Div{parent=mon_cfg,x=2,y=4,width=49} local mon_c_3 = Div{parent=mon_cfg,x=2,y=4,width=49} + local mon_c_4 = Div{parent=mon_cfg,x=2,y=4,width=49} local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3,mon_c_4}} @@ -850,7 +851,9 @@ local function config_view(display) PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- SUMMARY OF CHANGES + --#endregion + + --#region Summary and Saving local sum_c_1 = Div{parent=summary,x=2,y=4,width=49} local sum_c_2 = Div{parent=summary,x=2,y=4,width=49} @@ -863,14 +866,14 @@ local function config_view(display) local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} - local function back_from_settings() + local function back_from_summary() if tool_ctl.viewing_config or tool_ctl.importing_legacy then main_pane.set_value(1) tool_ctl.viewing_config = false tool_ctl.importing_legacy = false tool_ctl.settings_apply.show() else - main_pane.set_value(4) + main_pane.set_value(7) end end @@ -883,39 +886,28 @@ local function config_view(display) local function save_and_continue() for k, v in pairs(tmp_cfg) do settings.set(k, v) end - if settings.save("supervisor.settings") then + if settings.save("coordinator.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) - try_set(num_units, ini_cfg.UnitCount) - try_set(tank_mode, ini_cfg.FacilityTankMode) try_set(svr_chan, ini_cfg.SVR_Channel) - try_set(plc_chan, ini_cfg.PLC_Channel) - try_set(rtu_chan, ini_cfg.RTU_Channel) try_set(crd_chan, ini_cfg.CRD_Channel) try_set(pkt_chan, ini_cfg.PKT_Channel) - try_set(plc_timeout, ini_cfg.PLC_Timeout) - try_set(rtu_timeout, ini_cfg.RTU_Timeout) - try_set(crd_timeout, ini_cfg.CRD_Timeout) - try_set(pkt_timeout, ini_cfg.PKT_Timeout) + try_set(svr_timeout, ini_cfg.SVR_Timeout) + try_set(api_timeout, ini_cfg.API_Timeout) try_set(range, ini_cfg.TrustedRange) try_set(key, ini_cfg.AuthKey) + try_set(num_units, ini_cfg.UnitCount) + try_set(dis_flow_view, ini_cfg.DisableFlowView) + try_set(s_vol, ini_cfg.SpeakerVolume) + try_set(clock_fmt, util.trinary(ini_cfg.Time24Hour, 1, 2)) try_set(mode, ini_cfg.LogMode) try_set(path, ini_cfg.LogPath) try_set(en_dbg, ini_cfg.LogDebug) - for i = 1, #ini_cfg.CoolingConfig do - local cfg, elems = ini_cfg.CoolingConfig[i], tool_ctl.cooling_elems[i] - try_set(elems.boilers, cfg.BoilerCount) - try_set(elems.turbines, cfg.TurbineCount) - try_set(elems.tank, cfg.TankConnection) - end + preset_monitor_fields() - for i = 1, #ini_cfg.FacilityTankDefs do - try_set(tool_ctl.tank_elems[i].tank_opt, ini_cfg.FacilityTankDefs[i]) - end - - en_fac_tanks.set_value(ini_cfg.FacilityTankMode > 0) + tool_ctl.gen_mon_list() tool_ctl.view_cfg.enable() @@ -930,8 +922,8 @@ local function config_view(display) end end - PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_settings,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_summary,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg} TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"} @@ -948,10 +940,11 @@ local function config_view(display) PushButton{parent=sum_c_2,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=sum_c_2,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."} + TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua and coord.settings files will now be deleted, then the configurator will exit."} local function delete_legacy() - fs.delete("/supervisor/config.lua") + fs.delete("/coordinator/config.lua") + fs.delete("/coord.settings") exit() end @@ -962,7 +955,9 @@ local function config_view(display) PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - -- CONFIG CHANGE LOG + --#endregion + + -- Config Change Log local cl = Div{parent=changelog,x=2,y=4,width=49} @@ -997,9 +992,9 @@ local function config_view(display) -- load a legacy config file function tool_ctl.load_legacy() - local config = require("supervisor.config") + local config = require("coordinator.config") - tmp_cfg.UnitCount = config.NUM_REACTORS + tmp_cfg.UnitCount = config.NUM_UNITS if config.REACTOR_COOLING == nil or tmp_cfg.UnitCount ~= #config.REACTOR_COOLING then import_err_msg.set_value("Cooling configuration table length must match the number of units.") @@ -1043,15 +1038,11 @@ local function config_view(display) end tmp_cfg.SVR_Channel = config.SVR_CHANNEL - tmp_cfg.PLC_Channel = config.PLC_CHANNEL - tmp_cfg.RTU_Channel = config.RTU_CHANNEL tmp_cfg.CRD_Channel = config.CRD_CHANNEL tmp_cfg.PKT_Channel = config.PKT_CHANNEL - tmp_cfg.PLC_Timeout = config.PLC_TIMEOUT - tmp_cfg.RTU_Timeout = config.RTU_TIMEOUT - tmp_cfg.CRD_Timeout = config.CRD_TIMEOUT - tmp_cfg.PKT_Timeout = config.PKT_TIMEOUT + tmp_cfg.SVR_Timeout = config.SVR_TIMEOUT + tmp_cfg.API_Timeout = config.API_TIMEOUT tmp_cfg.TrustedRange = config.TRUSTED_RANGE tmp_cfg.AuthKey = config.AUTH_KEY or "" @@ -1200,7 +1191,7 @@ local function config_view(display) end -- generate the summary list - ---@param cfg svr_config + ---@param cfg crd_config function tool_ctl.gen_summary(cfg) setting_list.remove_all() @@ -1220,35 +1211,11 @@ local function config_view(display) if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") - elseif f[1] == "CoolingConfig" and cfg.CoolingConfig then + elseif f[1] == "UnitDisplays" then val = "" - - for idx = 1, #cfg.CoolingConfig do - local ccfg = cfg.CoolingConfig[idx] - local b_plural = util.trinary(ccfg.BoilerCount == 1, "", "s") - local t_plural = util.trinary(ccfg.TurbineCount == 1, "", "s") - local tank = util.trinary(ccfg.TankConnection, "has tank conn", "no tank conn") - val = val .. util.trinary(idx == 1, "", "\n") .. - util.sprintf(" \x07 unit %d - %d boiler%s, %d turbine%s, %s", idx, ccfg.BoilerCount, b_plural, ccfg.TurbineCount, t_plural, tank) + for idx = 1, #cfg.UnitDisplays do + val = val .. util.trinary(idx == 1, "", "\n") .. util.sprintf(" \x07 Unit %d - %s", idx, cfg.UnitDisplays[idx]) end - - if val == "" then val = "no facility tanks" end - elseif f[1] == "FacilityTankMode" and raw == 0 then val = "0 (n/a, unit mode)" - elseif f[1] == "FacilityTankDefs" and cfg.FacilityTankDefs then - val = "" - - for idx = 1, #cfg.FacilityTankDefs do - local t_mode = "not connected to a tank" - if cfg.FacilityTankDefs[idx] == 1 then - t_mode = "connected to its unit tank" - elseif cfg.FacilityTankDefs[idx] == 2 then - t_mode = "connected to a facility tank" - end - - val = val .. util.trinary(idx == 1, "", "\n") .. util.sprintf(" \x07 unit %d - %s", idx, t_mode) - end - - if val == "" then val = "no facility tanks" end end if val == "nil" then val = "" end @@ -1261,6 +1228,8 @@ local function config_view(display) height = #lines + 1 end + if f[1] == "UnitDisplays" and height == 1 then height = 2 end + local line = Div{parent=setting_list,height=height,fg_bg=c} TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} From 1fa8c03dff2a1820122754a633ba4ac2dc7113ed Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 00:56:36 -0500 Subject: [PATCH 12/43] #309 import legacy configs --- coordinator/configure.lua | 102 ++++++++++++++------------------------ coordinator/startup.lua | 2 +- 2 files changed, 37 insertions(+), 67 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index ee5d4b2..7446477 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -19,7 +19,6 @@ local TextBox = require("graphics.elements.textbox") local CheckBox = require("graphics.elements.controls.checkbox") local PushButton = require("graphics.elements.controls.push_button") -local Radio2D = require("graphics.elements.controls.radio_2d") local RadioButton = require("graphics.elements.controls.radio_button") local NumberField = require("graphics.elements.form.number_field") @@ -307,9 +306,8 @@ local function config_view(display) local log_cfg = Div{parent=root_pane_div,x=1,y=1} local summary = Div{parent=root_pane_div,x=1,y=1} local changelog = Div{parent=root_pane_div,x=1,y=1} - local import_err = Div{parent=root_pane_div,x=1,y=1} - local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,spkr_cfg,crd_cfg,log_cfg,summary,changelog,import_err}} + local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,spkr_cfg,crd_cfg,log_cfg,summary,changelog}} -- Main Page @@ -332,10 +330,10 @@ local function config_view(display) main_pane.set_value(8) end - -- if fs.exists("/coordinator/config.lua") then - -- PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg} - -- y_start = y_start + 2 - -- end + if fs.exists("/coordinator/config.lua") then + PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg} + y_start = y_start + 2 + end PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} @@ -976,83 +974,55 @@ local function config_view(display) PushButton{parent=cl,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- IMPORT ERROR - - local i_err = Div{parent=import_err,x=2,y=4,width=49} - - TextBox{parent=import_err,x=1,y=2,height=1,text=" Import Error",fg_bg=cpair(colors.black,colors.red)} - TextBox{parent=i_err,x=1,y=1,height=1,text="There is a problem with your config.lua file:"} - - local import_err_msg = TextBox{parent=i_err,x=1,y=3,height=6,text=""} - - PushButton{parent=i_err,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=i_err,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - -- set tool functions now that we have the elements -- load a legacy config file function tool_ctl.load_legacy() local config = require("coordinator.config") - tmp_cfg.UnitCount = config.NUM_UNITS - - if config.REACTOR_COOLING == nil or tmp_cfg.UnitCount ~= #config.REACTOR_COOLING then - import_err_msg.set_value("Cooling configuration table length must match the number of units.") - main_pane.set_value(7) - return - end - - for i = 1, tmp_cfg.UnitCount do - local cfg = config.REACTOR_COOLING[i] - - if type(cfg) ~= "table" then - import_err_msg.set_value("Cooling configuration for unit " .. i .. " must be a table.") - main_pane.set_value(7) - return - end - - tmp_cfg.CoolingConfig[i] = { BoilerCount = cfg.BOILERS or 0, TurbineCount = cfg.TURBINES or 1, TankConnection = cfg.TANK or false } - end - - tmp_cfg.FacilityTankMode = config.FAC_TANK_MODE - - if not (util.is_int(tmp_cfg.FacilityTankMode) and tmp_cfg.FacilityTankMode >= 0 and tmp_cfg.FacilityTankMode <= 8) then - import_err_msg.set_value("Invalid tank mode present in config. FAC_TANK_MODE must be a number 0 through 8.") - main_pane.set_value(7) - return - end - - if config.FAC_TANK_MODE > 0 then - if config.FAC_TANK_DEFS == nil or tmp_cfg.UnitCount ~= #config.FAC_TANK_DEFS then - import_err_msg.set_value("Facility tank definitions table length must match the number of units when using facility tanks.") - main_pane.set_value(7) - return - end - - for i = 1, tmp_cfg.UnitCount do - tmp_cfg.FacilityTankDefs[i] = config.FAC_TANK_DEFS[i] - end - else - tmp_cfg.FacilityTankMode = 0 - tmp_cfg.FacilityTankDefs = {} - end - tmp_cfg.SVR_Channel = config.SVR_CHANNEL tmp_cfg.CRD_Channel = config.CRD_CHANNEL tmp_cfg.PKT_Channel = config.PKT_CHANNEL - - tmp_cfg.SVR_Timeout = config.SVR_TIMEOUT + tmp_cfg.SVR_Timeout = config.SV_TIMEOUT tmp_cfg.API_Timeout = config.API_TIMEOUT - tmp_cfg.TrustedRange = config.TRUSTED_RANGE tmp_cfg.AuthKey = config.AUTH_KEY or "" + + tmp_cfg.UnitCount = config.NUM_UNITS + tmp_cfg.DisableFlowView = config.DISABLE_FLOW_VIEW + tmp_cfg.SpeakerVolume = config.SOUNDER_VOLUME + tmp_cfg.Time24Hour = config.TIME_24_HOUR + tmp_cfg.LogMode = config.LOG_MODE tmp_cfg.LogPath = config.LOG_PATH tmp_cfg.LogDebug = config.LOG_DEBUG or false + settings.load("/coord.settings") + + tmp_cfg.MainDisplay = settings.get("PRIMARY_DISPLAY") + tmp_cfg.FlowDisplay = settings.get("FLOW_DISPLAY") + tmp_cfg.UnitDisplays = settings.get("UNIT_DISPLAYS", {}) + + if settings.get("ControlStates") == nil then + local ctrl_states = { + WasteModes = settings.get("WASTE_MODES"), + PriorityGroups = settings.get("PRIORITY_GROUPS"), + Process = settings.get("PROCESS") + } + + settings.set("ControlStates", ctrl_states) + end + + settings.unset("PRIMARY_DISPLAY") + settings.unset("FLOW_DISPLAY") + settings.unset("UNIT_DISPLAYS") + settings.unset("WASTE_MODES") + settings.unset("PRIORITY_GROUPS") + settings.unset("PROCESS") + tool_ctl.gen_summary(tmp_cfg) sum_pane.set_value(1) - main_pane.set_value(5) + main_pane.set_value(8) tool_ctl.importing_legacy = true end diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 3a52029..2f56527 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder") local apisessions = require("coordinator.session.apisessions") -local COORDINATOR_VERSION = "v1.1.0" +local COORDINATOR_VERSION = "v1.2.0" local println = util.println local println_ts = util.println_ts From 3e83c8e2c6099fcee740eb9576d29987b66d6660 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 13:00:18 -0500 Subject: [PATCH 13/43] #309 moved monitor block size to ppm and fixed size estimation for monitor requirements --- coordinator/configure.lua | 13 ++++--------- scada-common/ppm.lua | 13 +++++++++++++ scada-common/util.lua | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 7446477..88267cc 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -587,9 +587,10 @@ local function config_view(display) if tmp_cfg.UnitCount <= 2 then tool_ctl.main_mon_h = util.trinary(row1_tall, 5, 4) else + -- is only one tall and the other short, or are both tall? -> 5 or 6; are neither tall? -> 5 if row1_tall or row2_tall then tool_ctl.main_mon_h = util.trinary((row1_short and row2_tall) or (row1_tall and row2_short), 5, 6) - else tool_ctl.main_mon_h = 6 end + else tool_ctl.main_mon_h = 5 end end else tool_ctl.main_mon_h = util.trinary(tmp_cfg.UnitCount <= 2, 4, 5) @@ -629,12 +630,6 @@ local function config_view(display) local mon_list = ListBox{parent=mon_c_2,x=1,y=6,height=7,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} - local function to_block_size(w, h) - local width = math.floor((w - 15) / 21) + 1 - local height = math.floor((h - 10) / 14) + 1 - return width, height - end - local assign_err = TextBox{parent=mon_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} local function submit_monitors() @@ -1052,7 +1047,7 @@ local function config_view(display) tool_ctl.mon_iface = iface local dev = device.dev - local w, h = to_block_size(dev.getSize()) + local w, h = ppm.monitor_block_size(dev.getSize()) local msg = "This size doesn't match a required screen. Please go back and resize it, or configure below at the risk of it not working." @@ -1139,7 +1134,7 @@ local function config_view(display) TextBox{parent=line,x=1,y=1,width=6,height=1,text=assignment,fg_bg=cpair(util.trinary(assignment=="Unused",colors.red,colors.blue),colors.white)} TextBox{parent=line,x=8,y=1,height=1,text=iface} - local w, h = to_block_size(dev.getSize()) + local w, h = ppm.monitor_block_size(dev.getSize()) local function unset_mon() purge_assignments(iface) diff --git a/scada-common/ppm.lua b/scada-common/ppm.lua index d54fec8..37a2ecb 100644 --- a/scada-common/ppm.lua +++ b/scada-common/ppm.lua @@ -421,4 +421,17 @@ function ppm.get_monitor_list() return list end +-- HELPER FUNCTIONS + +-- get the block size of a monitor given its width and height at a text scale of 0.5 +---@nodiscard +---@param w integer character width +---@param h integer character width +---@return integer block_width, integer block_height +function ppm.monitor_block_size(w, h) + local width = math.floor((w - 15) / 21) + 1 + local height = math.floor((h - 10) / 14) + 1 + return width, height +end + return ppm diff --git a/scada-common/util.lua b/scada-common/util.lua index c067c62..f407e36 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -22,7 +22,7 @@ local t_pack = table.pack local util = {} -- scada-common version -util.version = "1.1.12" +util.version = "1.1.13" util.TICK_TIME_S = 0.05 util.TICK_TIME_MS = 50 From 36b12d5dea1f15c9db6288973afb771421b4a2f5 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 15:21:00 -0500 Subject: [PATCH 14/43] #309 integrated new configuration into coordinator --- coordinator/configure.lua | 13 +- coordinator/coordinator.lua | 290 +++++++++++++++--------------------- coordinator/renderer.lua | 34 +---- coordinator/startup.lua | 79 ++++------ coordinator/ui/dialog.lua | 52 ------- 5 files changed, 161 insertions(+), 307 deletions(-) delete mode 100644 coordinator/ui/dialog.lua diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 88267cc..c0d3acd 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -80,6 +80,7 @@ local tool_ctl = { show_sv_cfg = nil, ---@type function start_fail = false, + fail_message = "", has_config = false, viewing_config = false, importing_legacy = false, @@ -316,7 +317,8 @@ local function config_view(display) TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Coordinator configurator! Please select one of the following options."} if tool_ctl.start_fail == 2 then - TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: There is a problem with your monitor configuration. You may have lost a monitor or their sizes may be incorrect. Please reconfigure monitors or correct their sizes.",fg_bg=cpair(colors.red,colors.lightGray)} + local msg = util.c("Notice: There is a problem with your monitor configuration. ", tool_ctl.fail_message, " Please reconfigure monitors or correct their sizes.") + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text=msg,fg_bg=cpair(colors.red,colors.lightGray)} y_start = y_start + 5 elseif tool_ctl.start_fail > 0 then TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)} @@ -1218,10 +1220,13 @@ local function reset_term() term.setCursorPos(1, 1) end --- run the coordinator configurator ----@param start_fail? integer indicate if this is being called by the coordinator startup app due to an invalid configuration -function configurator.configure(start_fail) +-- run the coordinator configurator
+-- start_fail of 0 is OK (not expected, default if not provided), 1 is bad config, 2 is bad monitor config +---@param start_fail? 0|1|2 indicate if this is being called by the startup app due to an invalid configuration +---@param message? any string message to display on a start_fail of 2 +function configurator.configure(start_fail, message) tool_ctl.start_fail = start_fail or 0 + tool_ctl.fail_message = util.trinary(type(message) == "string", message, "") load_settings(settings_cfg, true) tool_ctl.has_config = load_settings(ini_cfg) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 78c5f97..402827c 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -9,11 +9,6 @@ local process = require("coordinator.process") local apisessions = require("coordinator.session.apisessions") -local dialog = require("coordinator.ui.dialog") - -local print = util.print -local println = util.println - local PROTOCOL = comms.PROTOCOL local DEVICE_TYPE = comms.DEVICE_TYPE local ESTABLISH_ACK = comms.ESTABLISH_ACK @@ -26,32 +21,76 @@ local LINK_TIMEOUT = 60.0 local coordinator = {} --- request the user to select a monitor ----@nodiscard ----@param names table available monitors ----@return boolean|string|nil -local function ask_monitor(names) - println("available monitors:") - for i = 1, #names do - print(" " .. names[i]) - end - println("") - println("select a monitor or type c to cancel") +---@type crd_config +local config = {} - local iface = dialog.ask_options(names, "c") +coordinator.config = config - if iface ~= false and iface ~= nil then - util.filter_table(names, function (x) return x ~= iface end) +-- load the coordinator configuration
+-- status of 0 is OK, 1 is bad config, 2 is bad monitor config +---@return 0|1|2 status, nil|monitors_struct|string monitors +function coordinator.load_config() + if not settings.load("/coordinator.settings") then return 1 end + + config.UnitCount = settings.get("UnitCount") + config.SpeakerVolume = settings.get("SpeakerVolume") + config.Time24Hour = settings.get("Time24Hour") + + config.DisableFlowView = settings.get("DisableFlowView") + config.MainDisplay = settings.get("MainDisplay") + config.FlowDisplay = settings.get("FlowDisplay") + config.UnitDisplays = settings.get("UnitDisplays") + + config.SVR_Channel = settings.get("SVR_Channel") + config.CRD_Channel = settings.get("CRD_Channel") + config.PKT_Channel = settings.get("PKT_Channel") + config.SVR_Timeout = settings.get("SVR_Timeout") + config.API_Timeout = settings.get("API_Timeout") + config.TrustedRange = settings.get("TrustedRange") + config.AuthKey = settings.get("AuthKey") + + config.LogMode = settings.get("LogMode") + config.LogPath = settings.get("LogPath") + config.LogDebug = settings.get("LogDebug") + + local cfv = util.new_validator() + + cfv.assert_type_int(config.UnitCount) + cfv.assert_range(config.UnitCount, 1, 4) + cfv.assert_type_bool(config.Time24Hour) + + cfv.assert_type_bool(config.DisableFlowView) + cfv.assert_type_table(config.UnitDisplays) + + cfv.assert_type_num(config.SpeakerVolume) + cfv.assert_min(config.SpeakerVolume, 0.0) + cfv.assert_max(config.SpeakerVolume, 3.0) + + cfv.assert_channel(config.SVR_Channel) + cfv.assert_channel(config.CRD_Channel) + cfv.assert_channel(config.PKT_Channel) + + cfv.assert_type_num(config.SVR_Timeout) + cfv.assert_min(config.SVR_Timeout, 2) + cfv.assert_type_num(config.API_Timeout) + cfv.assert_min(config.API_Timeout, 2) + + cfv.assert_type_num(config.TrustedRange) + cfv.assert_min(config.TrustedRange, 0) + cfv.assert_type_str(config.AuthKey) + + if type(config.AuthKey) == "string" then + local len = string.len(config.AuthKey) + cfv.assert_eq(len == 0 or len >= 8, true) end - return iface -end + cfv.assert_type_int(config.LogMode) + cfv.assert_range(config.LogMode, 0, 1) + cfv.assert_type_str(config.LogPath) + cfv.assert_type_bool(config.LogDebug) + + -- Monitor Setup --- configure monitor layout ----@param num_units integer number of units expected ----@param disable_flow_view boolean disable flow view (legacy) ----@return boolean success, monitors_struct? monitors -function coordinator.configure_monitors(num_units, disable_flow_view) ---@class monitors_struct local monitors = { primary = nil, ---@type table|nil @@ -62,146 +101,70 @@ function coordinator.configure_monitors(num_units, disable_flow_view) unit_name_map = {} } - local monitors_avail = ppm.get_monitor_list() - local names = {} - local available = {} + local mon_cfv = util.new_validator() -- get all interface names - for iface, _ in pairs(monitors_avail) do - table.insert(names, iface) - table.insert(available, iface) - end + local names = {} + for iface, _ in pairs(ppm.get_monitor_list()) do table.insert(names, iface) end - -- we need a certain number of monitors (1 per unit + 1 primary display + 1 flow display) - local num_displays_needed = num_units + util.trinary(disable_flow_view, 1, 2) - if #names < num_displays_needed then - local message = "not enough monitors connected (need " .. num_displays_needed .. ")" - println(message) - log.warning(message) - return false - end + local function setup_monitors() + mon_cfv.assert_type_str(config.MainDisplay) + if not config.DisableFlowView then mon_cfv.assert_type_str(config.FlowDisplay) end + mon_cfv.assert_eq(#config.UnitDisplays, config.UnitCount) - -- attempt to load settings - if not settings.load("/coord.settings") then - log.warning("configure_monitors(): failed to load coordinator settings file (may not exist yet)") - else - local _primary = settings.get("PRIMARY_DISPLAY") - local _flow = settings.get("FLOW_DISPLAY") - local _unitd = settings.get("UNIT_DISPLAYS") + if mon_cfv.valid() then + mon_cfv.assert(util.table_contains(names, config.MainDisplay)) - -- filter out already assigned monitors - util.filter_table(available, function (x) return x ~= _primary end) - util.filter_table(available, function (x) return x ~= _flow end) - if type(_unitd) == "table" then - util.filter_table(available, function (x) return not util.table_contains(_unitd, x) end) - end - end + if not mon_cfv.valid() then return 2, "Main monitor is not connected." end - --------------------- - -- PRIMARY DISPLAY -- - --------------------- + monitors.primary = ppm.get_periph(config.MainDisplay) + monitors.primary_name = config.MainDisplay - local iface_primary_display = settings.get("PRIMARY_DISPLAY") ---@type boolean|string|nil + local w, h = ppm.monitor_block_size(monitors.primary.getSize()) + mon_cfv.assert(w == 8) - if not util.table_contains(names, iface_primary_display) then - println("primary display is not connected") - local response = dialog.ask_y_n("would you like to change it", true) - if response == false then return false end - iface_primary_display = nil - end + if not mon_cfv.valid() then return 2, "Main monitor width is incorrect." end - while iface_primary_display == nil and #available > 0 do - iface_primary_display = ask_monitor(available) - end + if not config.DisableFlowView then + mon_cfv.assert(util.table_contains(names, config.FlowDisplay)) - if type(iface_primary_display) ~= "string" then return false end + if not mon_cfv.valid() then return 2, "Flow monitor is not connected." end - settings.set("PRIMARY_DISPLAY", iface_primary_display) - util.filter_table(available, function (x) return x ~= iface_primary_display end) + monitors.flow = ppm.get_periph(config.FlowDisplay) + monitors.flow_name = config.FlowDisplay - monitors.primary = ppm.get_periph(iface_primary_display) - monitors.primary_name = iface_primary_display + w, h = ppm.monitor_block_size(monitors.flow.getSize()) + mon_cfv.assert(w == 8) - -------------------------- - -- FLOW MONITOR DISPLAY -- - -------------------------- - - if not disable_flow_view then - local iface_flow_display = settings.get("FLOW_DISPLAY") ---@type boolean|string|nil - - if not util.table_contains(names, iface_flow_display) then - println("flow monitor display is not connected") - local response = dialog.ask_y_n("would you like to change it", true) - if response == false then return false end - iface_flow_display = nil - end - - while iface_flow_display == nil and #available > 0 do - iface_flow_display = ask_monitor(available) - end - - if type(iface_flow_display) ~= "string" then return false end - - settings.set("FLOW_DISPLAY", iface_flow_display) - util.filter_table(available, function (x) return x ~= iface_flow_display end) - - monitors.flow = ppm.get_periph(iface_flow_display) - monitors.flow_name = iface_flow_display - end - - ------------------- - -- UNIT DISPLAYS -- - ------------------- - - local unit_displays = settings.get("UNIT_DISPLAYS") - - if unit_displays == nil then - unit_displays = {} - for i = 1, num_units do - local display = nil - - while display == nil and #available > 0 do - println("please select monitor for unit #" .. i) - display = ask_monitor(available) + if not mon_cfv.valid() then return 2, "Flow monitor width is incorrect." end end - if display == false then return false end + for i = 1, config.UnitCount do + local display = config.UnitDisplays[i] - unit_displays[i] = display - end - else - -- make sure all displays are connected - for i = 1, num_units do - local display = unit_displays[i] + mon_cfv.assert_type_str(display) + mon_cfv.assert(util.table_contains(names, display)) - if not util.table_contains(names, display) then - println("unit #" .. i .. " display is not connected") - local response = dialog.ask_y_n("would you like to change it", true) - if response == false then return false end - display = nil + if not mon_cfv.valid() then return 2, "Unit " .. i .. " monitor is not connected." end + + monitors.unit_displays[i] = ppm.get_periph(display) + monitors.unit_name_map[i] = display + + w, h = ppm.monitor_block_size(monitors.unit_displays[i].getSize()) + mon_cfv.assert(w == 4 and h == 4) + + if not mon_cfv.valid() then return 2, "Unit " .. i .. " monitor size is incorrect." end end - - while display == nil and #available > 0 do - display = ask_monitor(available) - end - - if display == false then return false end - - unit_displays[i] = display - end + else return 2, "Monitor configuration invalid." end end - settings.set("UNIT_DISPLAYS", unit_displays) - if not settings.save("/coord.settings") then - log.warning("configure_monitors(): failed to save coordinator settings file") - end + if cfv.valid() then + local ok, result, message = pcall(setup_monitors) + assert(ok, util.c("fatal error while trying to verify monitors: ", result)) + if result == 2 then return 2, message end + else return 1 end - for i = 1, #unit_displays do - monitors.unit_displays[i] = ppm.get_periph(unit_displays[i]) - monitors.unit_name_map[i] = unit_displays[i] - end - - return true, monitors + return 0, monitors end -- dmesg print wrapper @@ -246,13 +209,8 @@ end ---@nodiscard ---@param version string coordinator version ---@param nic nic network interface device ----@param num_units integer number of configured units for number of monitors, checked against SV ----@param crd_channel integer port of configured supervisor ----@param svr_channel integer listening port for supervisor replys ----@param pkt_channel integer listening port for pocket API ----@param range integer trusted device connection range ---@param sv_watchdog watchdog -function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pkt_channel, range, sv_watchdog) +function coordinator.comms(version, nic, sv_watchdog) local self = { sv_linked = false, sv_addr = comms.BROADCAST, @@ -267,11 +225,11 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk est_task_done = nil } - comms.set_trusted_range(range) + comms.set_trusted_range(config.TrustedRange) -- configure network channels nic.closeAll() - nic.open(crd_channel) + nic.open(config.CRD_Channel) -- link nic to apisessions apisessions.init(nic) @@ -296,7 +254,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk pkt.make(msg_type, msg) s_pkt.make(self.sv_addr, self.sv_seq_num, protocol, pkt.raw_sendable()) - nic.transmit(svr_channel, crd_channel, s_pkt) + nic.transmit(config.SVR_Channel, config.CRD_Channel, s_pkt) self.sv_seq_num = self.sv_seq_num + 1 end @@ -310,7 +268,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk m_pkt.make(MGMT_TYPE.ESTABLISH, { ack }) s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - nic.transmit(pkt_channel, crd_channel, s_pkt) + nic.transmit(config.PKT_Channel, config.CRD_Channel, s_pkt) self.last_api_est_acks[packet.src_addr()] = ack end @@ -343,7 +301,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk self.est_last = self.est_start self.est_tick_waiting, self.est_task_done = - coordinator.log_comms_connecting("attempting to connect to configured supervisor on channel " .. svr_channel) + coordinator.log_comms_connecting("attempting to connect to configured supervisor on channel " .. config.SVR_Channel) _send_establish() else @@ -356,7 +314,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk if abort then coordinator.log_comms("supervisor connection attempt cancelled by user") elseif self.sv_config_err then - coordinator.log_comms("supervisor cooling configuration invalid, check supervisor config file") + coordinator.log_comms("supervisor unit count does not match coordinator unit count, check configs") elseif not self.sv_linked then if self.last_est_ack == ESTABLISH_ACK.DENY then coordinator.log_comms("supervisor connection attempt denied") @@ -371,7 +329,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk ok = false elseif self.sv_config_err then - coordinator.log_comms("supervisor cooling configuration invalid, check supervisor config file") + coordinator.log_comms("supervisor unit count does not match coordinator unit count, check configs") ok = false elseif (util.time_s() - self.est_last) > 1.0 then _send_establish() @@ -464,9 +422,9 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk local src_addr = packet.scada_frame.src_addr() local protocol = packet.scada_frame.protocol() - if l_chan ~= crd_channel then + if l_chan ~= config.CRD_Channel then log.debug("received packet on unconfigured channel " .. l_chan, true) - elseif r_chan == pkt_channel then + elseif r_chan == config.PKT_Channel then if not self.sv_linked then log.debug("discarding pocket API packet before linked to supervisor") elseif protocol == PROTOCOL.SCADA_CRDN then @@ -526,7 +484,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk else log.debug("illegal packet type " .. protocol .. " on pocket channel", true) end - elseif r_chan == svr_channel then + elseif r_chan == config.SVR_Channel then -- check sequence number if self.sv_r_seq_num == nil then self.sv_r_seq_num = packet.scada_frame.seq_num() @@ -699,22 +657,22 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk -- connection with supervisor established if packet.length == 2 then local est_ack = packet.data[1] - local config = packet.data[2] + local sv_config = packet.data[2] if est_ack == ESTABLISH_ACK.ALLOW then -- reset to disconnected before validating iocontrol.fp_link_state(types.PANEL_LINK_STATE.DISCONNECTED) - if type(config) == "table" and #config == 2 then + if type(sv_config) == "table" and #sv_config == 2 then -- get configuration ---@class facility_conf local conf = { - num_units = config[1], ---@type integer - cooling = config[2] ---@type sv_cooling_conf + num_units = sv_config[1], ---@type integer + cooling = sv_config[2] ---@type sv_cooling_conf } - if conf.num_units == num_units then + if conf.num_units == config.UnitCount then -- init io controller iocontrol.init(conf, public) diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index 5aadbb2..f9c793d 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -4,6 +4,7 @@ local log = require("scada-common.log") local util = require("scada-common.util") +local ppm = require("scada-common.ppm") local iocontrol = require("coordinator.iocontrol") @@ -93,39 +94,6 @@ function renderer.init_displays() end end --- check main display width ----@nodiscard ----@return boolean width_okay -function renderer.validate_main_display_width() - local w, _ = engine.monitors.primary.getSize() - return w == 164 -end - --- check flow display width ----@nodiscard ----@return boolean width_okay -function renderer.validate_flow_display_width() - local w, _ = engine.monitors.flow.getSize() - return w == 164 -end - --- check display sizes ----@nodiscard ----@return boolean valid all unit display dimensions OK -function renderer.validate_unit_display_sizes() - local valid = true - - for id, monitor in ipairs(engine.monitors.unit_displays) do - local w, h = monitor.getSize() - if w ~= 79 or h ~= 52 then - log.warning(util.c("RENDERER: unit ", id, " display resolution not 79 wide by 52 tall: ", w, ", ", h)) - valid = false - end - end - - return valid -end - -- initialize the dmesg output window function renderer.init_dmesg() local disp_x, disp_y = engine.monitors.primary.getSize() diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 2f56527..4301bac 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -14,7 +14,7 @@ local util = require("scada-common.util") local core = require("graphics.core") -local config = require("coordinator.config") +local configure = require("coordinator.configure") local coordinator = require("coordinator.coordinator") local iocontrol = require("coordinator.iocontrol") local renderer = require("coordinator.renderer") @@ -34,32 +34,34 @@ local log_comms = coordinator.log_comms local log_crypto = coordinator.log_crypto ---------------------------------------- --- config validation +-- get configuration ---------------------------------------- -local cfv = util.new_validator() +-- mount connected devices (required for monitor setup) +ppm.mount_all() -cfv.assert_channel(config.SVR_CHANNEL) -cfv.assert_channel(config.CRD_CHANNEL) -cfv.assert_channel(config.PKT_CHANNEL) -cfv.assert_type_int(config.TRUSTED_RANGE) -cfv.assert_type_num(config.SV_TIMEOUT) -cfv.assert_min(config.SV_TIMEOUT, 2) -cfv.assert_type_num(config.API_TIMEOUT) -cfv.assert_min(config.API_TIMEOUT, 2) -cfv.assert_type_int(config.NUM_UNITS) -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) +local loaded, monitors = coordinator.load_config() +if loaded ~= 0 then + -- try to reconfigure (user action) + local success, error = configure.configure(loaded, monitors) + if success then + loaded, monitors = coordinator.load_config() + assert(loaded == 0, util.trinary(loaded == 1, "failed to load valid configuration", "monitor configuration invalid")) + else + assert(success, "coordinator configuration error: " .. error) + end +end -assert(cfv.valid(), "bad config file: missing/invalid fields") +-- passed checks, good now +---@cast monitors monitors_struct + +local config = coordinator.config ---------------------------------------- -- log init ---------------------------------------- -log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG == true) +log.init(config.LogPath, config.LogMode, config.LogDebug) log.info("========================================") log.info("BOOTING coordinator.startup " .. COORDINATOR_VERSION) @@ -77,39 +79,13 @@ local function main() -- system startup ---------------------------------------- - -- mount connected devices - ppm.mount_all() - -- report versions/init fp PSIL iocontrol.init_fp(COORDINATOR_VERSION, comms.version) - -- setup monitors - local configured, monitors = coordinator.configure_monitors(config.NUM_UNITS, config.DISABLE_FLOW_VIEW == true) - if not configured or monitors == nil then - println("startup> monitor setup failed") - log.fatal("monitor configuration failed") - return - end - -- init renderer - renderer.legacy_disable_flow_view(config.DISABLE_FLOW_VIEW == true) + renderer.legacy_disable_flow_view(config.DisableFlowView) renderer.set_displays(monitors) renderer.init_displays() - - if not renderer.validate_main_display_width() then - println("startup> main display must be 8 blocks wide") - log.fatal("main display not wide enough") - return - elseif (config.DISABLE_FLOW_VIEW ~= true) and not renderer.validate_flow_display_width() then - println("startup> flow display must be 8 blocks wide") - log.fatal("flow display not wide enough") - return - elseif not renderer.validate_unit_display_sizes() then - println("startup> one or more unit display dimensions incorrect; they must be 4x4 blocks") - log.fatal("unit display dimensions incorrect") - return - end - renderer.init_dmesg() -- lets get started! @@ -132,7 +108,7 @@ local function main() else local sounder_start = util.time_ms() log_boot("annunciator alarm speaker connected") - sounder.init(speaker, config.SOUNDER_VOLUME) + sounder.init(speaker, config.SpeakerVolume) log_boot("tone generation took " .. (util.time_ms() - sounder_start) .. "ms") log_sys("annunciator alarm configured") iocontrol.fp_has_speaker(true) @@ -143,8 +119,8 @@ local function main() ---------------------------------------- -- message authentication init - if type(config.AUTH_KEY) == "string" then - local init_time = network.init_mac(config.AUTH_KEY) + if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then + local init_time = network.init_mac(config.AuthKey) log_crypto("HMAC init took " .. init_time .. "ms") end @@ -161,14 +137,13 @@ local function main() end -- create connection watchdog - local conn_watchdog = util.new_watchdog(config.SV_TIMEOUT) + local conn_watchdog = util.new_watchdog(config.SVR_Timeout) conn_watchdog.cancel() log.debug("startup> conn watchdog created") -- create network interface then setup comms local nic = network.nic(modem) - local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, config.NUM_UNITS, config.CRD_CHANNEL, - config.SVR_CHANNEL, config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog) + local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, conn_watchdog) log.debug("startup> comms init") log_comms("comms initialized") @@ -214,7 +189,7 @@ local function main() local link_failed = false local ui_ok = true - local date_format = util.trinary(config.TIME_24_HOUR, "%X \x04 %A, %B %d %Y", "%r \x04 %A, %B %d %Y") + local date_format = util.trinary(config.Time24Hour, "%X \x04 %A, %B %d %Y", "%r \x04 %A, %B %d %Y") -- start clock loop_clock.start() diff --git a/coordinator/ui/dialog.lua b/coordinator/ui/dialog.lua deleted file mode 100644 index 676ae2b..0000000 --- a/coordinator/ui/dialog.lua +++ /dev/null @@ -1,52 +0,0 @@ -local completion = require("cc.completion") - -local util = require("scada-common.util") - -local print = util.print - -local dialog = {} - --- ask the user yes or no ----@nodiscard ----@param question string ----@param default boolean ----@return boolean|nil -function dialog.ask_y_n(question, default) - print(question) - - if default == true then - print(" (Y/n)? ") - else - print(" (y/N)? ") - end - - local response = read(nil, nil) - - if response == "" then - return default - elseif response == "Y" or response == "y" then - return true - elseif response == "N" or response == "n" then - return false - else - return nil - end -end - --- ask the user for an input within a set of options ----@nodiscard ----@param options table ----@param cancel string ----@return boolean|string|nil -function dialog.ask_options(options, cancel) - print("> ") - local response = read(nil, nil, function(text) return completion.choice(text, options) end) - - if response == cancel then return false end - - if util.table_contains(options, response) then - return response - else return nil end -end - -return dialog From 1984b63837b83b36b3129ad2dfa2d8e715c0a5e4 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 15:25:30 -0500 Subject: [PATCH 15/43] additional config validations --- reactor-plc/plc.lua | 4 ++++ reactor-plc/startup.lua | 2 +- rtu/rtu.lua | 4 ++++ rtu/startup.lua | 2 +- supervisor/supervisor.lua | 2 ++ 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 7287d3d..c5e6be3 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -37,14 +37,17 @@ function plc.load_config() config.Networked = settings.get("Networked") config.UnitID = settings.get("UnitID") + config.EmerCoolEnable = settings.get("EmerCoolEnable") config.EmerCoolSide = settings.get("EmerCoolSide") config.EmerCoolColor = settings.get("EmerCoolColor") + config.SVR_Channel = settings.get("SVR_Channel") config.PLC_Channel = settings.get("PLC_Channel") config.ConnTimeout = settings.get("ConnTimeout") config.TrustedRange = settings.get("TrustedRange") config.AuthKey = settings.get("AuthKey") + config.LogMode = settings.get("LogMode") config.LogPath = settings.get("LogPath") config.LogDebug = settings.get("LogDebug") @@ -71,6 +74,7 @@ function plc.load_config() end cfv.assert_type_int(config.LogMode) + cfv.assert_range(config.LogMode, 0, 1) cfv.assert_type_str(config.LogPath) cfv.assert_type_bool(config.LogDebug) diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 1e066d0..c79909a 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.6.9" +local R_PLC_VERSION = "v1.6.10" local println = util.println local println_ts = util.println_ts diff --git a/rtu/rtu.lua b/rtu/rtu.lua index b76ce30..da25523 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -41,6 +41,9 @@ function rtu.load_config() local cfv = util.new_validator() cfv.assert_type_num(config.SpeakerVolume) + cfv.assert_min(config.SpeakerVolume, 0.0) + cfv.assert_max(config.SpeakerVolume, 3.0) + cfv.assert_channel(config.SVR_Channel) cfv.assert_channel(config.RTU_Channel) cfv.assert_type_num(config.ConnTimeout) @@ -55,6 +58,7 @@ function rtu.load_config() end cfv.assert_type_int(config.LogMode) + cfv.assert_range(config.LogMode, 0, 1) cfv.assert_type_str(config.LogPath) cfv.assert_type_bool(config.LogDebug) diff --git a/rtu/startup.lua b/rtu/startup.lua index 0a3753d..2d50f31 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -31,7 +31,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.7.11" +local RTU_VERSION = "v1.7.12" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 755893f..7b2b87e 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -69,6 +69,7 @@ function supervisor.load_config() cfv.assert_min(config.PKT_Timeout, 2) cfv.assert_type_num(config.TrustedRange) + cfv.assert_min(config.TrustedRange, 0) if type(config.AuthKey) == "string" then local len = string.len(config.AuthKey) @@ -76,6 +77,7 @@ function supervisor.load_config() end cfv.assert_type_int(config.LogMode) + cfv.assert_range(config.LogMode, 0, 1) cfv.assert_type_str(config.LogPath) cfv.assert_type_bool(config.LogDebug) From 56e69e3a29708c7755901fb4f184a1091e73c7fd Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 15:30:18 -0500 Subject: [PATCH 16/43] luacheck fixes --- coordinator/coordinator.lua | 6 ++++-- coordinator/renderer.lua | 2 -- scada-common/ppm.lua | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 402827c..4df3ad1 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -113,6 +113,8 @@ function coordinator.load_config() mon_cfv.assert_eq(#config.UnitDisplays, config.UnitCount) if mon_cfv.valid() then + local w, h, _ + mon_cfv.assert(util.table_contains(names, config.MainDisplay)) if not mon_cfv.valid() then return 2, "Main monitor is not connected." end @@ -120,7 +122,7 @@ function coordinator.load_config() monitors.primary = ppm.get_periph(config.MainDisplay) monitors.primary_name = config.MainDisplay - local w, h = ppm.monitor_block_size(monitors.primary.getSize()) + w, _ = ppm.monitor_block_size(monitors.primary.getSize()) mon_cfv.assert(w == 8) if not mon_cfv.valid() then return 2, "Main monitor width is incorrect." end @@ -133,7 +135,7 @@ function coordinator.load_config() monitors.flow = ppm.get_periph(config.FlowDisplay) monitors.flow_name = config.FlowDisplay - w, h = ppm.monitor_block_size(monitors.flow.getSize()) + w, _ = ppm.monitor_block_size(monitors.flow.getSize()) mon_cfv.assert(w == 8) if not mon_cfv.valid() then return 2, "Flow monitor width is incorrect." end diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index f9c793d..07282c4 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -3,8 +3,6 @@ -- local log = require("scada-common.log") -local util = require("scada-common.util") -local ppm = require("scada-common.ppm") local iocontrol = require("coordinator.iocontrol") diff --git a/scada-common/ppm.lua b/scada-common/ppm.lua index 37a2ecb..116264b 100644 --- a/scada-common/ppm.lua +++ b/scada-common/ppm.lua @@ -427,7 +427,7 @@ end ---@nodiscard ---@param w integer character width ---@param h integer character width ----@return integer block_width, integer block_height +---@return integer block_width, integer block_height function ppm.monitor_block_size(w, h) local width = math.floor((w - 15) / 21) + 1 local height = math.floor((h - 10) / 14) + 1 From 827953c0a1e916fdfb9a973adcce64244303be42 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 15:31:45 -0500 Subject: [PATCH 17/43] more luacheck fixes --- coordinator/coordinator.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 4df3ad1..3ad2ce0 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -365,10 +365,10 @@ function coordinator.comms(version, nic, sv_watchdog) end -- send the auto process control configuration with a start command - ---@param config coord_auto_config configuration - function public.send_auto_start(config) + ---@param auto_cfg coord_auto_config configuration + function public.send_auto_start(auto_cfg) _send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.FAC_CMD, { - FAC_COMMAND.START, config.mode, config.burn_target, config.charge_target, config.gen_target, config.limits + FAC_COMMAND.START, auto_cfg.mode, auto_cfg.burn_target, auto_cfg.charge_target, auto_cfg.gen_target, auto_cfg.limits }) end From 1a9892b29117bd38b19647ab11d7a67a22bd7647 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 16:49:39 -0500 Subject: [PATCH 18/43] cleanup and optimizations --- coordinator/coordinator.lua | 37 ++++++++++++++----------------------- rtu/rtu.lua | 3 +-- scada-common/ppm.lua | 2 +- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 3ad2ce0..6be3b73 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -28,7 +28,7 @@ coordinator.config = config -- load the coordinator configuration
-- status of 0 is OK, 1 is bad config, 2 is bad monitor config ----@return 0|1|2 status, nil|monitors_struct|string monitors +---@return 0|1|2 status, nil|monitors_struct|string monitors (or error message) function coordinator.load_config() if not settings.load("/coordinator.settings") then return 1 end @@ -63,8 +63,7 @@ function coordinator.load_config() cfv.assert_type_table(config.UnitDisplays) cfv.assert_type_num(config.SpeakerVolume) - cfv.assert_min(config.SpeakerVolume, 0.0) - cfv.assert_max(config.SpeakerVolume, 3.0) + cfv.assert_range(config.SpeakerVolume, 0, 3) cfv.assert_channel(config.SVR_Channel) cfv.assert_channel(config.CRD_Channel) @@ -115,47 +114,39 @@ function coordinator.load_config() if mon_cfv.valid() then local w, h, _ - mon_cfv.assert(util.table_contains(names, config.MainDisplay)) - - if not mon_cfv.valid() then return 2, "Main monitor is not connected." end + if not util.table_contains(names, config.MainDisplay) then + return 2, "Main monitor is not connected." + end monitors.primary = ppm.get_periph(config.MainDisplay) monitors.primary_name = config.MainDisplay w, _ = ppm.monitor_block_size(monitors.primary.getSize()) - mon_cfv.assert(w == 8) - - if not mon_cfv.valid() then return 2, "Main monitor width is incorrect." end + if w ~= 8 then return 2, "Main monitor width is incorrect." end if not config.DisableFlowView then - mon_cfv.assert(util.table_contains(names, config.FlowDisplay)) - - if not mon_cfv.valid() then return 2, "Flow monitor is not connected." end + if not util.table_contains(names, config.FlowDisplay) then + return 2, "Flow monitor is not connected." + end monitors.flow = ppm.get_periph(config.FlowDisplay) monitors.flow_name = config.FlowDisplay w, _ = ppm.monitor_block_size(monitors.flow.getSize()) - mon_cfv.assert(w == 8) - - if not mon_cfv.valid() then return 2, "Flow monitor width is incorrect." end + if w ~= 8 then return 2, "Flow monitor width is incorrect." end end for i = 1, config.UnitCount do local display = config.UnitDisplays[i] - - mon_cfv.assert_type_str(display) - mon_cfv.assert(util.table_contains(names, display)) - - if not mon_cfv.valid() then return 2, "Unit " .. i .. " monitor is not connected." end + if type(display) ~= "string" or not util.table_contains(names, display) then + return 2, "Unit " .. i .. " monitor is not connected." + end monitors.unit_displays[i] = ppm.get_periph(display) monitors.unit_name_map[i] = display w, h = ppm.monitor_block_size(monitors.unit_displays[i].getSize()) - mon_cfv.assert(w == 4 and h == 4) - - if not mon_cfv.valid() then return 2, "Unit " .. i .. " monitor size is incorrect." end + if w ~= 4 or h ~= 4 then return 2, "Unit " .. i .. " monitor size is incorrect." end end else return 2, "Monitor configuration invalid." end end diff --git a/rtu/rtu.lua b/rtu/rtu.lua index da25523..6c2b01f 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -41,8 +41,7 @@ function rtu.load_config() local cfv = util.new_validator() cfv.assert_type_num(config.SpeakerVolume) - cfv.assert_min(config.SpeakerVolume, 0.0) - cfv.assert_max(config.SpeakerVolume, 3.0) + cfv.assert_range(config.SpeakerVolume, 0, 3) cfv.assert_channel(config.SVR_Channel) cfv.assert_channel(config.RTU_Channel) diff --git a/scada-common/ppm.lua b/scada-common/ppm.lua index 116264b..1f032c6 100644 --- a/scada-common/ppm.lua +++ b/scada-common/ppm.lua @@ -426,7 +426,7 @@ end -- get the block size of a monitor given its width and height at a text scale of 0.5 ---@nodiscard ---@param w integer character width ----@param h integer character width +---@param h integer character height ---@return integer block_width, integer block_height function ppm.monitor_block_size(w, h) local width = math.floor((w - 15) / 21) + 1 From 20bff48cfd5afb05ecd4d936a67e503738dd4e11 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 20:21:07 -0500 Subject: [PATCH 19/43] #309 cleanup, fixes, optimizations --- coordinator/configure.lua | 170 +++++++++++++++++++------------------- reactor-plc/startup.lua | 2 +- rtu/startup.lua | 2 +- scada-common/ppm.lua | 10 +-- supervisor/configure.lua | 4 +- supervisor/startup.lua | 2 +- 6 files changed, 94 insertions(+), 96 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index c0d3acd..440e9fe 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -79,7 +79,7 @@ local tool_ctl = { sv_cool_conf = nil, ---@type table list of boiler & turbine counts show_sv_cfg = nil, ---@type function - start_fail = false, + start_fail = 0, fail_message = "", has_config = false, viewing_config = false, @@ -120,8 +120,8 @@ local tmp_cfg = { SpeakerVolume = 1.0, Time24Hour = true, DisableFlowView = false, - MainDisplay = nil, - FlowDisplay = nil, + MainDisplay = nil, ---@type string + FlowDisplay = nil, ---@type string UnitDisplays = {}, SVR_Channel = nil, ---@type integer CRD_Channel = nil, ---@type integer @@ -161,9 +161,13 @@ local fields = { { "LogDebug","Log Debug Messages", false } } +-- check if a value is an integer within a range (inclusive) +---@param x integer +---@param min integer +---@param max integer local function is_int_min_max(x, min, max) return util.is_int(x) and x >= min and x <= max end --- send an management packet to the supervisor +-- send a management packet to the supervisor ---@param msg_type MGMT_TYPE ---@param msg table local function send_sv(msg_type, msg) @@ -490,30 +494,6 @@ local function config_view(display) tool_ctl.sv_conn_button = PushButton{parent=fac_c_1,x=1,y=9,text="Connect",min_width=9,callback=function()tool_ctl.sv_connect()end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} - function tool_ctl.sv_connect() - tool_ctl.sv_conn_button.disable() - tool_ctl.sv_conn_detail.set_value("") - - local modem = ppm.get_wireless_modem() - if modem == nil then - tool_ctl.sv_conn_status.set_value("Please connect an ender/wireless modem.") - else - tool_ctl.sv_conn_status.set_value("Modem found, connecting...") - if tool_ctl.nic == nil then tool_ctl.nic = network.nic(modem) end - - tool_ctl.nic.closeAll() - tool_ctl.nic.open(tmp_cfg.CRD_Channel) - - tool_ctl.sv_addr = comms.BROADCAST - tool_ctl.sv_seq_num = 0 - tool_ctl.net_listen = true - - send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.CRD }) - - tcd.dispatch_unique(8, handle_timeout) - end - end - local function sv_skip() tcd.abort(handle_timeout) tool_ctl.sv_fac_conf = nil @@ -575,47 +555,6 @@ local function config_view(display) TextBox{parent=mon_c_1,x=1,y=1,height=5,text="Your configuration requires the following monitors. The main and flow monitors' heights are dependent on your unit count and cooling setup. If you manually entered the unit count, a * will be shown on potentially inaccurate calculations."} local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} - function tool_ctl.update_mon_reqs() - local plural = tmp_cfg.UnitCount > 1 - - if tool_ctl.sv_cool_conf ~= nil then - local cnf = tool_ctl.sv_cool_conf - - local row1_tall = cnf[1][1] > 1 or cnf[1][2] > 2 or (cnf[2] and (cnf[2][1] > 1 or cnf[2][2] > 2)) - local row1_short = (cnf[1][1] == 0 and cnf[1][2] == 1) and (cnf[2] == nil or (cnf[2][1] == 0 and cnf[2][2] == 1)) - local row2_tall = (cnf[3] and (cnf[3][1] > 1 or cnf[3][2] > 2)) or (cnf[4] and (cnf[4][1] > 1 or cnf[4][2] > 2)) - local row2_short = (cnf[3] == nil or (cnf[3][1] == 0 and cnf[3][2] == 1)) and (cnf[4] == nil or (cnf[4][1] == 0 and cnf[4][2] == 1)) - - if tmp_cfg.UnitCount <= 2 then - tool_ctl.main_mon_h = util.trinary(row1_tall, 5, 4) - else - -- is only one tall and the other short, or are both tall? -> 5 or 6; are neither tall? -> 5 - if row1_tall or row2_tall then - tool_ctl.main_mon_h = util.trinary((row1_short and row2_tall) or (row1_tall and row2_short), 5, 6) - else tool_ctl.main_mon_h = 5 end - end - else - tool_ctl.main_mon_h = util.trinary(tmp_cfg.UnitCount <= 2, 4, 5) - end - - tool_ctl.flow_mon_h = 2 + tmp_cfg.UnitCount - - local asterisk = util.trinary(tool_ctl.sv_cool_conf == nil, "*", "") - local m_at_least = util.trinary(tool_ctl.main_mon_h < 6, "at least ", "") - local f_at_least = util.trinary(tool_ctl.flow_mon_h < 6, "at least ", "") - - mon_reqs.remove_all() - - TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a "..tmp_cfg.UnitCount.." Unit View Monitor"..util.trinary(plural,"s","")} - TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" "..util.trinary(plural,"each ","").."must be 4 blocks wide by 4 tall",fg_bg=cpair(colors.gray,colors.white)} - TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Main View Monitor"} - TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..m_at_least..tool_ctl.main_mon_h..asterisk.." tall",fg_bg=cpair(colors.gray,colors.white)} - if not tmp_cfg.DisableFlowView then - TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Flow View Monitor"} - TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)} - end - end - local function next_from_reqs() -- unassign unit monitors above the unit count for i = tmp_cfg.UnitCount + 1, 4 do tmp_cfg.UnitDisplays[i] = nil end @@ -672,7 +611,7 @@ local function config_view(display) mon_warn.show() elseif not util.table_contains(tool_ctl.mon_expect, val) then tool_ctl.apply_mon.disable() - mon_warn.set_value("That assignment doesn't match monitor dimensions. You'll need to resize the monitor for it to work.") + mon_warn.set_value("That assignment doesn't fit monitor dimensions. You'll need to resize the monitor for it to work.") mon_warn.show() else tool_ctl.apply_mon.enable() @@ -709,9 +648,7 @@ local function config_view(display) tmp_cfg.FlowDisplay = nil else for i = 1, tmp_cfg.UnitCount do - if tmp_cfg.UnitDisplays[i] == iface then - tmp_cfg.UnitDisplays[i] = nil - end + if tmp_cfg.UnitDisplays[i] == iface then tmp_cfg.UnitDisplays[i] = nil end end end end @@ -744,7 +681,7 @@ local function config_view(display) tool_ctl.apply_mon = PushButton{parent=mon_c_3,x=43,y=14,min_width=7,text="Apply",callback=apply_monitor,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} TextBox{parent=mon_c_4,x=1,y=1,height=3,text="For legacy compatibility with facilities built without space for a flow monitor, you can disable the flow monitor requirement here."} - TextBox{parent=mon_c_4,x=1,y=5,height=3,text="Please be aware that THIS WILL BE REMOVED ON RELEASE. It will only be available for the remainder of the beta."} + TextBox{parent=mon_c_4,x=1,y=5,height=3,text="Please be aware that THIS OPTION WILL BE REMOVED ON RELEASE. Disabling it will only be available for the remainder of the beta."} local dis_flow_view = CheckBox{parent=mon_c_4,x=1,y=9,default=ini_cfg.DisableFlowView,label="Disable Flow View Monitor",box_fg_bg=cpair(colors.blue,colors.black)} @@ -1023,22 +960,85 @@ local function config_view(display) tool_ctl.importing_legacy = true end + -- attempt a connection to the supervisor to get cooling info + function tool_ctl.sv_connect() + tool_ctl.sv_conn_button.disable() + tool_ctl.sv_conn_detail.set_value("") + + local modem = ppm.get_wireless_modem() + if modem == nil then + tool_ctl.sv_conn_status.set_value("Please connect an ender/wireless modem.") + else + tool_ctl.sv_conn_status.set_value("Modem found, connecting...") + if tool_ctl.nic == nil then tool_ctl.nic = network.nic(modem) end + + tool_ctl.nic.closeAll() + tool_ctl.nic.open(tmp_cfg.CRD_Channel) + + tool_ctl.sv_addr = comms.BROADCAST + tool_ctl.sv_seq_num = 0 + tool_ctl.net_listen = true + + send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.CRD }) + + tcd.dispatch_unique(8, handle_timeout) + end + end + -- show the facility's unit count and cooling configuration data function tool_ctl.show_sv_cfg() local conf = tool_ctl.sv_cool_conf fac_config_list.remove_all() local str = util.sprintf("Facility has %d reactor units:", #conf) - - local line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)} - TextBox{parent=line,text=str,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} + TextBox{parent=fac_config_list,height=1,text=str,fg_bg=cpair(colors.gray,colors.white)} for i = 1, #conf do local num_b, num_t = conf[i][1], conf[i][2] str = util.sprintf("\x07 Unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s")) + TextBox{parent=fac_config_list,height=1,text=str,fg_bg=cpair(colors.gray,colors.white)} + end + end - local c_line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)} - TextBox{parent=c_line,text=str,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} + -- update list of monitor requirements + function tool_ctl.update_mon_reqs() + local plural = tmp_cfg.UnitCount > 1 + + if tool_ctl.sv_cool_conf ~= nil then + local cnf = tool_ctl.sv_cool_conf + + local row1_tall = cnf[1][1] > 1 or cnf[1][2] > 2 or (cnf[2] and (cnf[2][1] > 1 or cnf[2][2] > 2)) + local row1_short = (cnf[1][1] == 0 and cnf[1][2] == 1) and (cnf[2] == nil or (cnf[2][1] == 0 and cnf[2][2] == 1)) + local row2_tall = (cnf[3] and (cnf[3][1] > 1 or cnf[3][2] > 2)) or (cnf[4] and (cnf[4][1] > 1 or cnf[4][2] > 2)) + local row2_short = (cnf[3] == nil or (cnf[3][1] == 0 and cnf[3][2] == 1)) and (cnf[4] == nil or (cnf[4][1] == 0 and cnf[4][2] == 1)) + + if tmp_cfg.UnitCount <= 2 then + tool_ctl.main_mon_h = util.trinary(row1_tall, 5, 4) + else + -- is only one tall and the other short, or are both tall? -> 5 or 6; are neither tall? -> 5 + if row1_tall or row2_tall then + tool_ctl.main_mon_h = util.trinary((row1_short and row2_tall) or (row1_tall and row2_short), 5, 6) + else tool_ctl.main_mon_h = 5 end + end + else + tool_ctl.main_mon_h = util.trinary(tmp_cfg.UnitCount <= 2, 4, 5) + end + + tool_ctl.flow_mon_h = 2 + tmp_cfg.UnitCount + + local asterisk = util.trinary(tool_ctl.sv_cool_conf == nil, "*", "") + local m_at_least = util.trinary(tool_ctl.main_mon_h < 6, "at least ", "") + local f_at_least = util.trinary(tool_ctl.flow_mon_h < 6, "at least ", "") + + mon_reqs.remove_all() + + TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a "..tmp_cfg.UnitCount.." Unit View Monitor"..util.trinary(plural,"s","")} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" "..util.trinary(plural,"each ","").."must be 4 blocks wide by 4 tall",fg_bg=cpair(colors.gray,colors.white)} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Main View Monitor"} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..m_at_least..tool_ctl.main_mon_h..asterisk.." tall",fg_bg=cpair(colors.gray,colors.white)} + if not tmp_cfg.DisableFlowView then + TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Flow View Monitor"} + TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)} end end @@ -1178,7 +1178,7 @@ local function config_view(display) if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") - elseif f[1] == "UnitDisplays" then + elseif f[1] == "UnitDisplays" and type(cfg.UnitDisplays) == "table" then val = "" for idx = 1, #cfg.UnitDisplays do val = val .. util.trinary(idx == 1, "", "\n") .. util.sprintf(" \x07 Unit %d - %s", idx, cfg.UnitDisplays[idx]) @@ -1195,7 +1195,7 @@ local function config_view(display) height = #lines + 1 end - if f[1] == "UnitDisplays" and height == 1 then height = 2 end + if (f[1] == "UnitDisplays") and (height == 1) and (val ~= "") then height = 2 end local line = Div{parent=setting_list,height=height,fg_bg=c} TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} @@ -1221,11 +1221,11 @@ local function reset_term() end -- run the coordinator configurator
--- start_fail of 0 is OK (not expected, default if not provided), 1 is bad config, 2 is bad monitor config ----@param start_fail? 0|1|2 indicate if this is being called by the startup app due to an invalid configuration +-- start_fail of 0 is OK (default if not provided), 1 is bad config, 2 is bad monitor config +---@param start_code? 0|1|2 indicate error state when called from the startup app ---@param message? any string message to display on a start_fail of 2 -function configurator.configure(start_fail, message) - tool_ctl.start_fail = start_fail or 0 +function configurator.configure(start_code, message) + tool_ctl.start_fail = start_code or 0 tool_ctl.fail_message = util.trinary(type(message) == "string", message, "") load_settings(settings_cfg, true) diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index c79909a..d73037d 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -31,7 +31,7 @@ if not plc.load_config() then -- try to reconfigure (user action) local success, error = configure.configure(true) if success then - assert(plc.load_config(), "failed to load valid reactor PLC configuration") + assert(plc.load_config(), "failed to load valid configuration") else assert(success, "reactor PLC configuration error: " .. error) end diff --git a/rtu/startup.lua b/rtu/startup.lua index 2d50f31..e8e85fc 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -47,7 +47,7 @@ if not rtu.load_config() then -- try to reconfigure (user action) local success, error = configure.configure(true) if success then - assert(rtu.load_config(), "failed to load valid RTU configuration") + assert(rtu.load_config(), "failed to load valid configuration") else assert(success, "RTU configuration error: " .. error) end diff --git a/scada-common/ppm.lua b/scada-common/ppm.lua index 1f032c6..df64b68 100644 --- a/scada-common/ppm.lua +++ b/scada-common/ppm.lua @@ -425,13 +425,11 @@ end -- get the block size of a monitor given its width and height at a text scale of 0.5 ---@nodiscard ----@param w integer character width ----@param h integer character height +---@param width integer character width +---@param height integer character height ---@return integer block_width, integer block_height -function ppm.monitor_block_size(w, h) - local width = math.floor((w - 15) / 21) + 1 - local height = math.floor((h - 10) / 14) + 1 - return width, height +function ppm.monitor_block_size(width, height) + return math.floor((width - 15) / 21) + 1, math.floor((height - 10) / 14) + 1 end return ppm diff --git a/supervisor/configure.lua b/supervisor/configure.lua index 50d404b..b8c4e00 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -968,7 +968,7 @@ local function config_view(display) if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") - elseif f[1] == "CoolingConfig" and cfg.CoolingConfig then + elseif f[1] == "CoolingConfig" and type(cfg.CoolingConfig) == "table" then val = "" for idx = 1, #cfg.CoolingConfig do @@ -982,7 +982,7 @@ local function config_view(display) if val == "" then val = "no facility tanks" end elseif f[1] == "FacilityTankMode" and raw == 0 then val = "0 (n/a, unit mode)" - elseif f[1] == "FacilityTankDefs" and cfg.FacilityTankDefs then + elseif f[1] == "FacilityTankDefs" and type(cfg.FacilityTankDefs) == "table" then val = "" for idx = 1, #cfg.FacilityTankDefs do diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 4e86a1e..a983ee4 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -34,7 +34,7 @@ if not supervisor.load_config() then -- try to reconfigure (user action) local success, error = configure.configure(true) if success then - assert(supervisor.load_config(), "failed to load valid supervisor configuration") + assert(supervisor.load_config(), "failed to load valid configuration") else assert(success, "supervisor configuration error: " .. error) end From e416faf3130fcb745167b1f72ab668f2790fa2ca Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 20:47:37 -0500 Subject: [PATCH 20/43] #309 integrated process control with new settings file --- coordinator/configure.lua | 8 +- coordinator/process.lua | 156 ++++++++++++++++++-------------------- 2 files changed, 78 insertions(+), 86 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 440e9fe..3a97b34 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -939,9 +939,9 @@ local function config_view(display) if settings.get("ControlStates") == nil then local ctrl_states = { - WasteModes = settings.get("WASTE_MODES"), - PriorityGroups = settings.get("PRIORITY_GROUPS"), - Process = settings.get("PROCESS") + process = settings.get("PROCESS"), + waste_modes = settings.get("WASTE_MODES"), + priority_groups = settings.get("PRIORITY_GROUPS"), } settings.set("ControlStates", ctrl_states) @@ -950,9 +950,9 @@ local function config_view(display) settings.unset("PRIMARY_DISPLAY") settings.unset("FLOW_DISPLAY") settings.unset("UNIT_DISPLAYS") + settings.unset("PROCESS") settings.unset("WASTE_MODES") settings.unset("PRIORITY_GROUPS") - settings.unset("PROCESS") tool_ctl.gen_summary(tmp_cfg) sum_pane.set_value(1) diff --git a/coordinator/process.lua b/coordinator/process.lua index 5551c93..581fcd9 100644 --- a/coordinator/process.lua +++ b/coordinator/process.lua @@ -19,15 +19,20 @@ local process = {} local self = { io = nil, ---@type ioctl comms = nil, ---@type coord_comms - ---@class coord_auto_config - config = { - mode = PROCESS.INACTIVE, - burn_target = 0.0, - charge_target = 0.0, - gen_target = 0.0, - limits = {}, - waste_product = PRODUCT.PLUTONIUM, - pu_fallback = false + ---@class coord_control_states + control_states = { + ---@class coord_auto_config + process = { + mode = PROCESS.INACTIVE, + burn_target = 0.0, + charge_target = 0.0, + gen_target = 0.0, + limits = {}, + waste_product = PRODUCT.PLUTONIUM, + pu_fallback = false + }, + waste_modes = {}, + priority_groups = {} } } @@ -42,63 +47,64 @@ function process.init(iocontrol, coord_comms) self.io = iocontrol self.comms = coord_comms + local ctl_proc = self.control_states.process + for i = 1, self.io.facility.num_units do - self.config.limits[i] = 0.1 + ctl_proc.limits[i] = 0.1 end - -- load settings - if not settings.load("/coord.settings") then - log.error("process.init(): failed to load coordinator settings file") - end + local ctrl_states = settings.get("ControlStates", {}) + local config = ctrl_states.process ---@type coord_auto_config -- facility auto control configuration - local config = settings.get("PROCESS") ---@type coord_auto_config|nil if type(config) == "table" then - self.config.mode = config.mode - self.config.burn_target = config.burn_target - self.config.charge_target = config.charge_target - self.config.gen_target = config.gen_target - self.config.limits = config.limits - self.config.waste_product = config.waste_product - self.config.pu_fallback = config.pu_fallback + ctl_proc.mode = config.mode + ctl_proc.burn_target = config.burn_target + ctl_proc.charge_target = config.charge_target + ctl_proc.gen_target = config.gen_target + ctl_proc.limits = config.limits + ctl_proc.waste_product = config.waste_product + ctl_proc.pu_fallback = config.pu_fallback - self.io.facility.ps.publish("process_mode", self.config.mode) - self.io.facility.ps.publish("process_burn_target", self.config.burn_target) - self.io.facility.ps.publish("process_charge_target", self.config.charge_target) - self.io.facility.ps.publish("process_gen_target", self.config.gen_target) - self.io.facility.ps.publish("process_waste_product", self.config.waste_product) - self.io.facility.ps.publish("process_pu_fallback", self.config.pu_fallback) + self.io.facility.ps.publish("process_mode", ctl_proc.mode) + self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target) + self.io.facility.ps.publish("process_charge_target", ctl_proc.charge_target) + self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target) + self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product) + self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback) - for id = 1, math.min(#self.config.limits, self.io.facility.num_units) do + for id = 1, math.min(#ctl_proc.limits, self.io.facility.num_units) do local unit = self.io.units[id] ---@type ioctl_unit - unit.unit_ps.publish("burn_limit", self.config.limits[id]) + unit.unit_ps.publish("burn_limit", ctl_proc.limits[id]) end - log.info("PROCESS: loaded auto control settings from coord.settings") + log.info("PROCESS: loaded auto control settings") -- notify supervisor of auto waste config - self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, self.config.waste_product) - self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, self.config.pu_fallback) + self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, ctl_proc.waste_product) + self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, ctl_proc.pu_fallback) end -- unit waste states - local waste_modes = settings.get("WASTE_MODES") ---@type table|nil + local waste_modes = ctrl_states.waste_modes ---@type table|nil if type(waste_modes) == "table" then for id, mode in pairs(waste_modes) do + self.control_states.waste_modes[id] = mode self.comms.send_unit_command(UNIT_COMMAND.SET_WASTE, id, mode) end - log.info("PROCESS: loaded unit waste mode settings from coord.settings") + log.info("PROCESS: loaded unit waste mode settings") end -- unit priority groups - local prio_groups = settings.get("PRIORITY_GROUPS") ---@type table|nil + local prio_groups = ctrl_states.priority_groups ---@type table|nil if type(prio_groups) == "table" then for id, group in pairs(prio_groups) do + self.control_states.priority_groups[id] = group self.comms.send_unit_command(UNIT_COMMAND.SET_GROUP, id, group) end - log.info("PROCESS: loaded priority groups settings from coord.settings") + log.info("PROCESS: loaded priority groups settings") end end @@ -155,15 +161,10 @@ function process.set_unit_waste(id, mode) self.comms.send_unit_command(UNIT_COMMAND.SET_WASTE, id, mode) log.debug(util.c("PROCESS: UNIT[", id, "] SET WASTE ", mode)) - local waste_mode = settings.get("WASTE_MODES") ---@type table|nil + self.control_states.waste_modes[id] = mode + settings.set("ControlStates", self.control_states) - if type(waste_mode) ~= "table" then waste_mode = {} end - - waste_mode[id] = mode - - settings.set("WASTE_MODES", waste_mode) - - if not settings.save("/coord.settings") then + if not settings.save("/coordinator.settings") then log.error("process.set_unit_waste(): failed to save coordinator settings file") end end @@ -198,15 +199,10 @@ function process.set_group(unit_id, group_id) self.comms.send_unit_command(UNIT_COMMAND.SET_GROUP, unit_id, group_id) log.debug(util.c("PROCESS: UNIT[", unit_id, "] SET GROUP ", group_id)) - local prio_groups = settings.get("PRIORITY_GROUPS") ---@type table|nil + self.control_states.priority_groups[unit_id] = group_id + settings.set("ControlStates", self.control_states) - if type(prio_groups) ~= "table" then prio_groups = {} end - - prio_groups[unit_id] = group_id - - settings.set("PRIORITY_GROUPS", prio_groups) - - if not settings.save("/coord.settings") then + if not settings.save("/coordinator.settings") then log.error("process.set_group(): failed to save coordinator settings file") end end @@ -217,20 +213,14 @@ end -- write auto process control to config file local function _write_auto_config() - -- attempt to load settings - if not settings.load("/coord.settings") then - log.warning("process._write_auto_config(): failed to load coordinator settings file") - end - -- save config - settings.set("PROCESS", self.config) - local saved = settings.save("/coord.settings") - + settings.set("ControlStates", self.control_states) + local saved = settings.save("/coordinator.settings") if not saved then log.warning("process._write_auto_config(): failed to save coordinator settings file") end - return not not saved + return saved end -- stop automatic process control @@ -241,7 +231,7 @@ end -- start automatic process control function process.start_auto() - self.comms.send_auto_start(self.config) + self.comms.send_auto_start(self.control_states.process) log.debug("PROCESS: START AUTO CTL") end @@ -253,7 +243,7 @@ function process.set_process_waste(product) log.debug(util.c("PROCESS: SET WASTE ", product)) -- update config table and save - self.config.waste_product = product + self.control_states.process.waste_product = product _write_auto_config() end @@ -265,7 +255,7 @@ function process.set_pu_fallback(enabled) log.debug(util.c("PROCESS: SET PU FALLBACK ", enabled)) -- update config table and save - self.config.pu_fallback = enabled + self.control_states.process.pu_fallback = enabled _write_auto_config() end @@ -279,11 +269,12 @@ function process.save(mode, burn_target, charge_target, gen_target, limits) log.debug("PROCESS: SAVE") -- update config table - self.config.mode = mode - self.config.burn_target = burn_target - self.config.charge_target = charge_target - self.config.gen_target = gen_target - self.config.limits = limits + local ctl_proc = self.control_states.process + ctl_proc.mode = mode + ctl_proc.burn_target = burn_target + ctl_proc.charge_target = charge_target + ctl_proc.gen_target = gen_target + ctl_proc.limits = limits -- save config self.io.facility.save_cfg_ack(_write_auto_config()) @@ -294,22 +285,23 @@ end function process.start_ack_handle(response) local ack = response[1] - self.config.mode = response[2] - self.config.burn_target = response[3] - self.config.charge_target = response[4] - self.config.gen_target = response[5] + local ctl_proc = self.control_states.process + ctl_proc.mode = response[2] + ctl_proc.burn_target = response[3] + ctl_proc.charge_target = response[4] + ctl_proc.gen_target = response[5] for i = 1, math.min(#response[6], self.io.facility.num_units) do - self.config.limits[i] = response[6][i] + ctl_proc.limits[i] = response[6][i] local unit = self.io.units[i] ---@type ioctl_unit - unit.unit_ps.publish("burn_limit", self.config.limits[i]) + unit.unit_ps.publish("burn_limit", ctl_proc.limits[i]) end - self.io.facility.ps.publish("process_mode", self.config.mode) - self.io.facility.ps.publish("process_burn_target", self.config.burn_target) - self.io.facility.ps.publish("process_charge_target", self.config.charge_target) - self.io.facility.ps.publish("process_gen_target", self.config.gen_target) + self.io.facility.ps.publish("process_mode", ctl_proc.mode) + self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target) + self.io.facility.ps.publish("process_charge_target", ctl_proc.charge_target) + self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target) self.io.facility.start_ack(ack) end @@ -317,14 +309,14 @@ end -- record waste product state after attempting to change it ---@param response WASTE_PRODUCT supervisor waste product state function process.waste_ack_handle(response) - self.config.waste_product = response + self.control_states.process.waste_product = response self.io.facility.ps.publish("process_waste_product", response) end -- record plutonium fallback state after attempting to change it ---@param response boolean supervisor plutonium fallback state function process.pu_fb_ack_handle(response) - self.config.pu_fallback = response + self.control_states.process.pu_fallback = response self.io.facility.ps.publish("process_pu_fallback", response) end From 195f59178f87f963b5a0d87d20710a1521bb2e52 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 21:24:30 -0500 Subject: [PATCH 21/43] #309 bugfix to apisessions still using old config --- coordinator/configure.lua | 6 ++++++ coordinator/coordinator.lua | 4 ++-- coordinator/session/apisessions.lua | 22 +++++++++++++++------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 3a97b34..44b0b19 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -937,6 +937,12 @@ local function config_view(display) tmp_cfg.FlowDisplay = settings.get("FLOW_DISPLAY") tmp_cfg.UnitDisplays = settings.get("UNIT_DISPLAYS", {}) + -- if there are extra monitor entries, delete them now + -- not doing so will cause the app to fail to start + if is_int_min_max(tmp_cfg.UnitCount, 1, 4) then + for i = tmp_cfg.UnitCount + 1, 4 do tmp_cfg.UnitDisplays[i] = nil end + end + if settings.get("ControlStates") == nil then local ctrl_states = { process = settings.get("PROCESS"), diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 6be3b73..635de90 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -224,8 +224,8 @@ function coordinator.comms(version, nic, sv_watchdog) nic.closeAll() nic.open(config.CRD_Channel) - -- link nic to apisessions - apisessions.init(nic) + -- pass config to apisessions + apisessions.init(nic, config.CRD_Channel, config.PKT_Channel, config.API_Timeout) -- PRIVATE FUNCTIONS -- diff --git a/coordinator/session/apisessions.lua b/coordinator/session/apisessions.lua index 6e8c771..b4e1a30 100644 --- a/coordinator/session/apisessions.lua +++ b/coordinator/session/apisessions.lua @@ -3,7 +3,6 @@ local log = require("scada-common.log") local mqueue = require("scada-common.mqueue") local util = require("scada-common.util") -local config = require("coordinator.config") local iocontrol = require("coordinator.iocontrol") local pocket = require("coordinator.session.pocket") @@ -11,7 +10,10 @@ local pocket = require("coordinator.session.pocket") local apisessions = {} local self = { - nic = nil, + nic = nil, ---@type nic + crd_channel = nil, ---@type integer + pkt_channel = nil, ---@type integer + api_timeout = nil, ---@type number next_id = 0, sessions = {} } @@ -32,7 +34,7 @@ local function _api_handle_outq(session) if msg ~= nil then if msg.qtype == mqueue.TYPE.PACKET then -- handle a packet to be sent - self.nic.transmit(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message) + self.nic.transmit(self.pkt_channel, self.crd_channel, msg.message) elseif msg.qtype == mqueue.TYPE.COMMAND then -- handle instruction/notification elseif msg.qtype == mqueue.TYPE.DATA then @@ -59,7 +61,7 @@ local function _shutdown(session) while session.out_queue.ready() do local msg = session.out_queue.pop() if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then - self.nic.transmit(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message) + self.nic.transmit(self.pkt_channel, self.crd_channel, msg.message) end end @@ -69,9 +71,15 @@ end -- PUBLIC FUNCTIONS -- -- initialize apisessions ----@param nic nic -function apisessions.init(nic) +---@param nic nic network interface +---@param crd_channel integer coordinator channel +---@param pkt_channel integer pocket channel +---@param api_timeout number api session timeout +function apisessions.init(nic, crd_channel, pkt_channel, api_timeout) self.nic = nic + self.crd_channel = crd_channel + self.pkt_channel = pkt_channel + self.api_timeout = api_timeout end -- find a session by remote port @@ -103,7 +111,7 @@ function apisessions.establish_session(source_addr, version) local id = self.next_id - pkt_s.instance = pocket.new_session(id, source_addr, pkt_s.in_queue, pkt_s.out_queue, config.API_TIMEOUT) + pkt_s.instance = pocket.new_session(id, source_addr, pkt_s.in_queue, pkt_s.out_queue, self.api_timeout) table.insert(self.sessions, pkt_s) local mt = { From ca559482869a791bd396340011c9f63203a77585 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 21:25:21 -0500 Subject: [PATCH 22/43] #309 remove legacy config.lua --- coordinator/config.lua | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 coordinator/config.lua diff --git a/coordinator/config.lua b/coordinator/config.lua deleted file mode 100644 index bdf01e2..0000000 --- a/coordinator/config.lua +++ /dev/null @@ -1,41 +0,0 @@ -local config = {} - --- supervisor comms channel -config.SVR_CHANNEL = 16240 --- coordinator comms channel -config.CRD_CHANNEL = 16243 --- pocket comms channel -config.PKT_CHANNEL = 16244 --- max trusted modem message distance (0 to disable check) -config.TRUSTED_RANGE = 0 --- time in seconds (>= 2) before assuming a remote device is no longer active -config.SV_TIMEOUT = 5 -config.API_TIMEOUT = 5 --- facility authentication key (do NOT use one of your passwords) --- this enables verifying that messages are authentic --- all devices on the same network must use the same key --- config.AUTH_KEY = "SCADAfacility123" - --- expected number of reactor units, used only to require that number of unit monitors -config.NUM_UNITS = 4 - --- 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 - --- disable flow view (for legacy layouts) -config.DISABLE_FLOW_VIEW = false - --- 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 --- true to log verbose debug messages -config.LOG_DEBUG = false - -return config From 1cf7375311352c213da324c8b463011e63121319 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 21:34:25 -0500 Subject: [PATCH 23/43] #309 cleanup --- coordinator/coordinator.lua | 2 +- coordinator/session/apisessions.lua | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 635de90..61ee56f 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -225,7 +225,7 @@ function coordinator.comms(version, nic, sv_watchdog) nic.open(config.CRD_Channel) -- pass config to apisessions - apisessions.init(nic, config.CRD_Channel, config.PKT_Channel, config.API_Timeout) + apisessions.init(nic, config) -- PRIVATE FUNCTIONS -- diff --git a/coordinator/session/apisessions.lua b/coordinator/session/apisessions.lua index b4e1a30..516b91b 100644 --- a/coordinator/session/apisessions.lua +++ b/coordinator/session/apisessions.lua @@ -10,10 +10,8 @@ local pocket = require("coordinator.session.pocket") local apisessions = {} local self = { - nic = nil, ---@type nic - crd_channel = nil, ---@type integer - pkt_channel = nil, ---@type integer - api_timeout = nil, ---@type number + nic = nil, ---@type nic + config = nil, ---@type crd_config next_id = 0, sessions = {} } @@ -34,7 +32,7 @@ local function _api_handle_outq(session) if msg ~= nil then if msg.qtype == mqueue.TYPE.PACKET then -- handle a packet to be sent - self.nic.transmit(self.pkt_channel, self.crd_channel, msg.message) + self.nic.transmit(self.config.PKT_Channel, self.config.CRD_Channel, msg.message) elseif msg.qtype == mqueue.TYPE.COMMAND then -- handle instruction/notification elseif msg.qtype == mqueue.TYPE.DATA then @@ -61,7 +59,7 @@ local function _shutdown(session) while session.out_queue.ready() do local msg = session.out_queue.pop() if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then - self.nic.transmit(self.pkt_channel, self.crd_channel, msg.message) + self.nic.transmit(self.config.PKT_Channel, self.config.CRD_Channel, msg.message) end end @@ -72,14 +70,10 @@ end -- initialize apisessions ---@param nic nic network interface ----@param crd_channel integer coordinator channel ----@param pkt_channel integer pocket channel ----@param api_timeout number api session timeout -function apisessions.init(nic, crd_channel, pkt_channel, api_timeout) +---@param config crd_config coordinator config +function apisessions.init(nic, config) self.nic = nic - self.crd_channel = crd_channel - self.pkt_channel = pkt_channel - self.api_timeout = api_timeout + self.config = config end -- find a session by remote port @@ -111,7 +105,7 @@ function apisessions.establish_session(source_addr, version) local id = self.next_id - pkt_s.instance = pocket.new_session(id, source_addr, pkt_s.in_queue, pkt_s.out_queue, self.api_timeout) + pkt_s.instance = pocket.new_session(id, source_addr, pkt_s.in_queue, pkt_s.out_queue, self.config.API_Timeout) table.insert(self.sessions, pkt_s) local mt = { From 6d0e777e6808ff14338cdee57a570d08cd2d7b3e Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 18 Feb 2024 21:40:25 -0500 Subject: [PATCH 24/43] updated copyright and removed coordinator from list mentioning config.lua --- ccmsi.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ccmsi.lua b/ccmsi.lua index 9b9a98c..327018d 100644 --- a/ccmsi.lua +++ b/ccmsi.lua @@ -1,7 +1,7 @@ --[[ CC-MEK-SCADA Installer Utility -Copyright (c) 2023 Mikayla Fischler +Copyright (c) 2023 - 2024 Mikayla Fischler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, @@ -18,7 +18,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. local function println(message) print(tostring(message)) end local function print(message) term.write(tostring(message)) end -local CCMSI_VERSION = "v1.12a" +local CCMSI_VERSION = "v1.12b" local install_dir = "/.install-cache" local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/" @@ -345,7 +345,7 @@ elseif mode == "install" or mode == "update" then if mode == "install" then println("Installing " .. app .. " files...") elseif mode == "update" then - if app == "coordinator" or app == "pocket" then println("Updating " .. app .. " files... (keeping old config.lua)") + if app == "pocket" then println("Updating " .. app .. " files... (keeping old config.lua)") else println("Updating " .. app .. " files...") end end white() From 02906ae7072f3acd5787f0a6a98aec1d6970f8b5 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 12:49:26 -0500 Subject: [PATCH 25/43] add FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6a9136a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: mikayla_f From 823711357744026c1e10e667736ff02f81b26538 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 13:49:50 -0500 Subject: [PATCH 26/43] #408 pocket configurator --- pocket/configure.lua | 577 +++++++++++++++++++++++++++++++++++++++++++ pocket/startup.lua | 2 +- 2 files changed, 578 insertions(+), 1 deletion(-) create mode 100644 pocket/configure.lua diff --git a/pocket/configure.lua b/pocket/configure.lua new file mode 100644 index 0000000..d7087b9 --- /dev/null +++ b/pocket/configure.lua @@ -0,0 +1,577 @@ +-- +-- Configuration GUI +-- + +local log = require("scada-common.log") +local tcd = require("scada-common.tcd") +local util = require("scada-common.util") + +local core = require("graphics.core") + +local DisplayBox = require("graphics.elements.displaybox") +local Div = require("graphics.elements.div") +local ListBox = require("graphics.elements.listbox") +local MultiPane = require("graphics.elements.multipane") +local TextBox = require("graphics.elements.textbox") + +local CheckBox = require("graphics.elements.controls.checkbox") +local PushButton = require("graphics.elements.controls.push_button") +local RadioButton = require("graphics.elements.controls.radio_button") + +local NumberField = require("graphics.elements.form.number_field") +local TextField = require("graphics.elements.form.text_field") + +local println = util.println +local tri = util.trinary + +local cpair = core.cpair + +local LEFT = core.ALIGN.LEFT +local CENTER = core.ALIGN.CENTER +local RIGHT = core.ALIGN.RIGHT + +-- changes to the config data/format to let the user know +local changes = {} + +---@class pkt_configurator +local configurator = {} + +local style = {} + +style.root = cpair(colors.black, colors.lightGray) +style.header = cpair(colors.white, colors.gray) + +style.colors = { + { c = colors.red, hex = 0xdf4949 }, + { c = colors.orange, hex = 0xffb659 }, + { c = colors.yellow, hex = 0xfffc79 }, + { c = colors.lime, hex = 0x80ff80 }, + { c = colors.green, hex = 0x4aee8a }, + { c = colors.cyan, hex = 0x34bac8 }, + { c = colors.lightBlue, hex = 0x6cc0f2 }, + { c = colors.blue, hex = 0x0096ff }, + { c = colors.purple, hex = 0xb156ee }, + { c = colors.pink, hex = 0xf26ba2 }, + { c = colors.magenta, hex = 0xf9488a }, + { c = colors.lightGray, hex = 0xcacaca }, + { c = colors.gray, hex = 0x575757 } +} + +local bw_fg_bg = cpair(colors.black, colors.white) +local g_lg_fg_bg = cpair(colors.gray, colors.lightGray) +local nav_fg_bg = bw_fg_bg +local btn_act_fg_bg = cpair(colors.white, colors.gray) +local dis_fg_bg = cpair(colors.lightGray,colors.white) + +local tool_ctl = { + ask_config = false, + has_config = false, + viewing_config = false, + importing_legacy = false, + + view_cfg = nil, ---@type graphics_element + settings_apply = nil, ---@type graphics_element + + set_networked = nil, ---@type function + bundled_emcool = nil, ---@type function + gen_summary = nil, ---@type function + show_current_cfg = nil, ---@type function + load_legacy = nil, ---@type function + + show_auth_key = nil, ---@type function + show_key_btn = nil, ---@type graphics_element + auth_key_textbox = nil, ---@type graphics_element + auth_key_value = "" +} + +---@class pkt_config +local tmp_cfg = { + SVR_Channel = nil, ---@type integer + CRD_Channel = nil, ---@type integer + PKT_Channel = nil, ---@type integer + ConnTimeout = nil, ---@type number + TrustedRange = nil, ---@type number + AuthKey = nil, ---@type string|nil + LogMode = 0, + LogPath = "", + LogDebug = false, +} + +---@class pkt_config +local ini_cfg = {} +---@class pkt_config +local settings_cfg = {} + +-- all settings fields, their nice names, and their default values +local fields = { + { "SVR_Channel", "SVR Channel", 16240 }, + { "CRD_Channel", "CRD Channel", 16243 }, + { "PKT_Channel", "PKT Channel", 16244 }, + { "ConnTimeout", "Connection Timeout", 5 }, + { "TrustedRange", "Trusted Range", 0 }, + { "AuthKey", "Facility Auth Key" , ""}, + { "LogMode", "Log Mode", log.MODE.APPEND }, + { "LogPath", "Log Path", "/log.txt" }, + { "LogDebug","Log Debug Messages", false } +} + +-- load data from the settings file +---@param target pkt_config +---@param raw boolean? true to not use default values +local function load_settings(target, raw) + for _, v in pairs(fields) do settings.unset(v[1]) end + + local loaded = settings.load("/pocket.settings") + + for _, v in pairs(fields) do target[v[1]] = settings.get(v[1], tri(raw, nil, v[3])) end + + return loaded +end + +-- create the config view +---@param display graphics_element +local function config_view(display) +---@diagnostic disable-next-line: undefined-field + local function exit() os.queueEvent("terminate") end + + TextBox{parent=display,y=1,text="Pocket Configurator",alignment=CENTER,height=1,fg_bg=style.header} + + local root_pane_div = Div{parent=display,x=1,y=2} + + local main_page = Div{parent=root_pane_div,x=1,y=1} + local net_cfg = Div{parent=root_pane_div,x=1,y=1} + local log_cfg = Div{parent=root_pane_div,x=1,y=1} + local summary = Div{parent=root_pane_div,x=1,y=1} + local changelog = Div{parent=root_pane_div,x=1,y=1} + + local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,log_cfg,summary,changelog}} + + -- Main Page + + local y_start = 7 + + TextBox{parent=main_page,x=2,y=2,height=4,text="Welcome to the Pocket configurator! Please select one of the following options."} + + if tool_ctl.ask_config then + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Please configure before starting up.",fg_bg=cpair(colors.red,colors.lightGray)} + y_start = y_start + 3 + end + + local function view_config() + tool_ctl.viewing_config = true + tool_ctl.gen_summary(settings_cfg) + tool_ctl.settings_apply.hide(true) + main_pane.set_value(4) + end + + if fs.exists("/pocket/config.lua") then + PushButton{parent=main_page,x=2,y=y_start,min_width=22,text="Import Legacy Config",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg} + y_start = y_start + 2 + end + + PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} + tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} + + if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end + + PushButton{parent=main_page,x=2,y=18,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} + PushButton{parent=main_page,x=14,y=18,min_width=12,text="Change Log",callback=function()main_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#region Network + + local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=24} + local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=24} + local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=24} + local net_c_4 = Div{parent=net_cfg,x=2,y=4,width=24} + + local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}} + + TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)} + + TextBox{parent=net_c_1,x=1,y=1,height=1,text="Set network channels."} + TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the named channels must be the same within a particular SCADA network.",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_1,x=1,y=8,height=1,width=18,text="Supervisor Channel"} + local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg} + TextBox{parent=net_c_1,x=9,y=9,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_1,x=1,y=10,height=1,width=19,text="Coordinator Channel"} + local crd_chan = NumberField{parent=net_c_1,x=1,y=11,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg} + TextBox{parent=net_c_1,x=9,y=11,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_1,x=1,y=12,height=1,width=14,text="Pocket Channel"} + local pkt_chan = NumberField{parent=net_c_1,x=1,y=13,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg} + TextBox{parent=net_c_1,x=9,y=13,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg} + + local chan_err = TextBox{parent=net_c_1,x=1,y=14,height=1,width=24,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_channels() + local svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value()) + if svr_c ~= nil and crd_c ~= nil and pkt_c ~= nil then + tmp_cfg.SVR_Channel, tmp_cfg.CRD_Channel, tmp_cfg.PKT_Channel = svr_c, crd_c, pkt_c + net_pane.set_value(2) + chan_err.hide(true) + else chan_err.show() end + end + + PushButton{parent=net_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_1,x=19,y=15,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_2,x=1,y=1,height=1,text="Set connection timeout."} + TextBox{parent=net_c_2,x=1,y=3,height=7,text="You generally should not need to modify this. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_2,x=1,y=11,height=1,width=19,text="Connection Timeout"} + local timeout = NumberField{parent=net_c_2,x=1,y=12,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg} + + TextBox{parent=net_c_2,x=9,y=12,height=2,text="seconds\n(default 5)",fg_bg=g_lg_fg_bg} + + local ct_err = TextBox{parent=net_c_2,x=1,y=14,height=1,width=24,text="Please set timeout.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_timeouts() + local timeout_val = tonumber(timeout.get_value()) + if timeout_val ~= nil then + tmp_cfg.ConnTimeout = timeout_val + net_pane.set_value(3) + ct_err.hide(true) + else ct_err.show() end + end + + PushButton{parent=net_c_2,x=1,y=15,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_2,x=19,y=15,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_3,x=1,y=1,height=1,text="Set the trusted range."} + TextBox{parent=net_c_3,x=1,y=3,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many blocks away.",fg_bg=g_lg_fg_bg} + TextBox{parent=net_c_3,x=1,y=8,height=4,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg} + + local range = NumberField{parent=net_c_3,x=1,y=13,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg} + + local tr_err = TextBox{parent=net_c_3,x=1,y=14,height=1,width=24,text="Set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_tr() + local range_val = tonumber(range.get_value()) + if range_val ~= nil then + tmp_cfg.TrustedRange = range_val + net_pane.set_value(4) + tr_err.hide(true) + else tr_err.show() end + end + + PushButton{parent=net_c_3,x=1,y=15,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_3,x=19,y=15,text="Next \x1a",callback=submit_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_4,x=1,y=1,height=4,text="Optionally, set the facility authentication key. Do NOT use one of your passwords."} + TextBox{parent=net_c_4,x=1,y=6,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers.",fg_bg=g_lg_fg_bg} + + TextBox{parent=net_c_4,x=1,y=12,height=1,text="Facility Auth Key"} + local key, _, censor = TextField{parent=net_c_4,x=1,y=13,max_len=64,value=ini_cfg.AuthKey,width=24,height=1,fg_bg=bw_fg_bg} + + local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end + + local hide_key = CheckBox{parent=net_c_4,x=8,y=15,label="Hide Key",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key} + + hide_key.set_value(true) + censor_key(true) + + local key_err = TextBox{parent=net_c_4,x=1,y=14,height=1,width=24,text="Length must be > 7.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_auth() + local v = key.get_value() + if string.len(v) == 0 or string.len(v) >= 8 then + tmp_cfg.AuthKey = key.get_value() + main_pane.set_value(3) + key_err.hide(true) + else key_err.show() end + end + + PushButton{parent=net_c_4,x=1,y=15,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_4,x=19,y=15,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#endregion + + --#region Logging + + local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=24} + + TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)} + + TextBox{parent=log_c_1,x=1,y=1,height=1,text="Configure logging below."} + + TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"} + local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink} + + TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"} + local path = TextField{parent=log_c_1,x=1,y=8,width=24,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg} + + local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)} + TextBox{parent=log_c_1,x=3,y=11,height=4,text="This results in much larger log files. Use only as needed.",fg_bg=g_lg_fg_bg} + + local path_err = TextBox{parent=log_c_1,x=1,y=14,height=1,width=24,text="Provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_log() + if path.get_value() ~= "" then + path_err.hide(true) + tmp_cfg.LogMode = mode.get_value() - 1 + tmp_cfg.LogPath = path.get_value() + tmp_cfg.LogDebug = en_dbg.get_value() + tool_ctl.gen_summary(tmp_cfg) + tool_ctl.viewing_config = false + tool_ctl.importing_legacy = false + tool_ctl.settings_apply.show() + main_pane.set_value(4) + else path_err.show() end + end + + PushButton{parent=log_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=log_c_1,x=19,y=15,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#endregion + + --#region Summary and Saving + + local sum_c_1 = Div{parent=summary,x=2,y=4,width=24} + local sum_c_2 = Div{parent=summary,x=2,y=4,width=24} + local sum_c_3 = Div{parent=summary,x=2,y=4,width=24} + local sum_c_4 = Div{parent=summary,x=2,y=4,width=24} + + local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}} + + TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)} + + local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=11,width=24,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + + local function back_from_summary() + if tool_ctl.viewing_config or tool_ctl.importing_legacy then + main_pane.set_value(1) + tool_ctl.viewing_config = false + tool_ctl.importing_legacy = false + tool_ctl.settings_apply.show() + else + main_pane.set_value(3) + end + end + + ---@param element graphics_element + ---@param data any + local function try_set(element, data) + if data ~= nil then element.set_value(data) end + end + + local function save_and_continue() + for k, v in pairs(tmp_cfg) do settings.set(k, v) end + + if settings.save("pocket.settings") then + load_settings(settings_cfg, true) + load_settings(ini_cfg) + + try_set(svr_chan, ini_cfg.SVR_Channel) + try_set(crd_chan, ini_cfg.CRD_Channel) + try_set(pkt_chan, ini_cfg.PKT_Channel) + try_set(timeout, ini_cfg.ConnTimeout) + try_set(range, ini_cfg.TrustedRange) + try_set(key, ini_cfg.AuthKey) + try_set(mode, ini_cfg.LogMode) + try_set(path, ini_cfg.LogPath) + try_set(en_dbg, ini_cfg.LogDebug) + + tool_ctl.view_cfg.enable() + + if tool_ctl.importing_legacy then + tool_ctl.importing_legacy = false + sum_pane.set_value(3) + else + sum_pane.set_value(2) + end + else + sum_pane.set_value(4) + end + end + + PushButton{parent=sum_c_1,x=1,y=15,text="\x1b Back",callback=back_from_summary,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=1,y=13,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} + tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=18,y=15,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg} + + TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"} + + local function go_home() + main_pane.set_value(1) + net_pane.set_value(1) + sum_pane.set_value(1) + end + + PushButton{parent=sum_c_2,x=1,y=15,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=sum_c_2,x=19,y=15,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} + + TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."} + + local function delete_legacy() + fs.delete("/pocket/config.lua") + exit() + end + + PushButton{parent=sum_c_3,x=1,y=15,min_width=8,text="Cancel",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=sum_c_3,x=19,y=15,min_width=6,text="OK",callback=delete_legacy,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.gray)} + + TextBox{parent=sum_c_4,x=1,y=1,height=8,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."} + PushButton{parent=sum_c_4,x=1,y=15,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=sum_c_4,x=19,y=15,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} + + --#endregion + + -- Config Change Log + + local cl = Div{parent=changelog,x=2,y=4,width=24} + + TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg} + + local c_log = ListBox{parent=cl,x=1,y=1,height=13,width=24,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + + for _, change in ipairs(changes) do + TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg} + for _, v in ipairs(change[2]) do + local e = Div{parent=c_log,height=#util.strwrap(v,21)} + TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)} + TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)} + end + end + + PushButton{parent=cl,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + -- set tool functions now that we have the elements + + -- load a legacy config file + function tool_ctl.load_legacy() + local config = require("pocket.config") + + tmp_cfg.SVR_Channel = config.SVR_CHANNEL + tmp_cfg.CRD_Channel = config.CRD_CHANNEL + tmp_cfg.PKT_Channel = config.PKT_CHANNEL + tmp_cfg.ConnTimeout = config.COMMS_TIMEOUT + tmp_cfg.TrustedRange = config.TRUSTED_RANGE + tmp_cfg.AuthKey = config.AUTH_KEY or "" + + tmp_cfg.LogMode = config.LOG_MODE + tmp_cfg.LogPath = config.LOG_PATH + tmp_cfg.LogDebug = config.LOG_DEBUG or false + + tool_ctl.gen_summary(tmp_cfg) + sum_pane.set_value(1) + main_pane.set_value(4) + tool_ctl.importing_legacy = true + end + + -- expose the auth key on the summary page + function tool_ctl.show_auth_key() + tool_ctl.show_key_btn.disable() + tool_ctl.auth_key_textbox.set_value(tool_ctl.auth_key_value) + end + + -- generate the summary list + ---@param cfg pkt_config + function tool_ctl.gen_summary(cfg) + setting_list.remove_all() + + local alternate = false + local inner_width = setting_list.get_width() - 1 + + tool_ctl.show_key_btn.enable() + tool_ctl.auth_key_value = cfg.AuthKey or "" -- to show auth key + + for i = 1, #fields do + local f = fields[i] + local height = 1 + local label_w = string.len(f[2]) + local val_max_w = (inner_width - label_w) - 1 + local raw = cfg[f[1]] + local val = util.strval(raw) + + if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) + elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") + end + + if val == "nil" then val = "" end + + local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white)) + alternate = not alternate + + if string.len(val) > val_max_w then + local lines = util.strwrap(val, inner_width) + height = #lines + 1 + end + + local line = Div{parent=setting_list,height=height,fg_bg=c} + TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)} + + local textbox + if height > 1 then + textbox = TextBox{parent=line,x=1,y=2,text=val,height=height-1,alignment=LEFT} + else + textbox = TextBox{parent=line,x=label_w+1,y=1,text=val,alignment=RIGHT} + end + + if f[1] == "AuthKey" then tool_ctl.auth_key_textbox = textbox end + end + end +end + +-- reset terminal screen +local function reset_term() + term.setTextColor(colors.white) + term.setBackgroundColor(colors.black) + term.clear() + term.setCursorPos(1, 1) +end + +-- run the pcoket configurator +---@param ask_config? boolean indicate if this is being called by the startup app due to an invalid configuration +function configurator.configure(ask_config) + tool_ctl.ask_config = ask_config == true + + load_settings(settings_cfg, true) + tool_ctl.has_config = load_settings(ini_cfg) + + reset_term() + + -- set overridden colors + for i = 1, #style.colors do + term.setPaletteColor(style.colors[i].c, style.colors[i].hex) + end + + local status, error = pcall(function () + local display = DisplayBox{window=term.current(),fg_bg=style.root} + config_view(display) + + while true do + local event, param1, param2, param3 = util.pull_event() + + -- handle event + if event == "timer" then + tcd.handle(param1) + elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then + local m_e = core.events.new_mouse_event(event, param1, param2, param3) + if m_e then display.handle_mouse(m_e) end + elseif event == "char" or event == "key" or event == "key_up" then + local k_e = core.events.new_key_event(event, param1, param2) + if k_e then display.handle_key(k_e) end + elseif event == "paste" then + display.handle_paste(param1) + end + + if event == "terminate" then return end + end + end) + + -- restore colors + for i = 1, #style.colors do + local r, g, b = term.nativePaletteColor(style.colors[i].c) + term.setPaletteColor(style.colors[i].c, r, g, b) + end + + reset_term() + if not status then + println("configurator error: " .. error) + end + + return status, error +end + +return configurator diff --git a/pocket/startup.lua b/pocket/startup.lua index 4929955..b9334b5 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -18,7 +18,7 @@ local iocontrol = require("pocket.iocontrol") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "v0.6.3-alpha" +local POCKET_VERSION = "v0.7.0-alpha" local println = util.println local println_ts = util.println_ts From 53bb36ce8d06897a039aa996bb3fef0eb7b6d0a6 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 13:50:03 -0500 Subject: [PATCH 27/43] #145 fixed change log page on coordinator --- coordinator/configure.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 44b0b19..106335c 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -347,7 +347,7 @@ local function config_view(display) if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} - PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(9)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} --#region Network From bb062cf397d5fc7c6c3ede7487ae1499d85a77c6 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 13:50:38 -0500 Subject: [PATCH 28/43] #408 added pocket configure to configure launcher --- configure.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.lua b/configure.lua index 69d7f0f..49ae2a8 100644 --- a/configure.lua +++ b/configure.lua @@ -8,8 +8,8 @@ elseif fs.exists("supervisor/configure.lua") then require("supervisor.configure").configure() elseif fs.exists("coordinator/configure.lua") then require("coordinator.configure").configure() -elseif fs.exists("pocket/startup.lua") then - print("CONFIGURE> pocket configurator not yet implemented (use 'edit pocket/config.lua' to configure)") +elseif fs.exists("pocket/configure.lua") then + require("pocket.configure").configure() else print("CONFIGURE> NO CONFIGURATOR FOUND") print("CONFIGURE> EXIT") From bbc64c8dc2165a81a03f3c0533736095dccbdc67 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 13:54:23 -0500 Subject: [PATCH 29/43] #145 fixed oversized listboxes --- coordinator/configure.lua | 10 +++++----- coordinator/startup.lua | 2 +- reactor-plc/configure.lua | 4 ++-- reactor-plc/startup.lua | 2 +- rtu/configure.lua | 16 ++++++++-------- rtu/startup.lua | 2 +- supervisor/configure.lua | 4 ++-- supervisor/startup.lua | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 106335c..080d5aa 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -534,7 +534,7 @@ local function config_view(display) TextBox{parent=fac_c_3,x=1,y=1,height=2,text="The following facility configuration was fetched from your supervisor computer."} - local fac_config_list = ListBox{parent=fac_c_3,x=1,y=4,height=9,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local fac_config_list = ListBox{parent=fac_c_3,x=1,y=4,height=9,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} PushButton{parent=fac_c_3,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=fac_c_3,x=44,y=14,text="Next \x1a",callback=function()main_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} @@ -553,7 +553,7 @@ local function config_view(display) TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.blue)} TextBox{parent=mon_c_1,x=1,y=1,height=5,text="Your configuration requires the following monitors. The main and flow monitors' heights are dependent on your unit count and cooling setup. If you manually entered the unit count, a * will be shown on potentially inaccurate calculations."} - local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function next_from_reqs() -- unassign unit monitors above the unit count @@ -569,7 +569,7 @@ local function config_view(display) TextBox{parent=mon_c_2,x=1,y=1,height=5,text="Please configure your monitors below. You can go back to the prior page without losing progress to double check what you need. All of those monitors must be assigned before you can proceed."} - local mon_list = ListBox{parent=mon_c_2,x=1,y=6,height=7,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local mon_list = ListBox{parent=mon_c_2,x=1,y=6,height=7,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local assign_err = TextBox{parent=mon_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} @@ -796,7 +796,7 @@ local function config_view(display) TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)} - local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function back_from_summary() if tool_ctl.viewing_config or tool_ctl.importing_legacy then @@ -895,7 +895,7 @@ local function config_view(display) TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg} - local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} for _, change in ipairs(changes) do TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg} diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 4301bac..a16077a 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder") local apisessions = require("coordinator.session.apisessions") -local COORDINATOR_VERSION = "v1.2.0" +local COORDINATOR_VERSION = "v1.2.1" local println = util.println local println_ts = util.println_ts diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index fbefb97..62f7d85 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -441,7 +441,7 @@ local function config_view(display) TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)} - local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function back_from_settings() if tool_ctl.viewing_config or tool_ctl.importing_legacy then @@ -531,7 +531,7 @@ local function config_view(display) TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg} - local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} for _, change in ipairs(changes) do TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg} diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index d73037d..e667e46 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.6.10" +local R_PLC_VERSION = "v1.6.11" local println = util.println local println_ts = util.println_ts diff --git a/rtu/configure.lua b/rtu/configure.lua index c1ab9db..d84cdc7 100644 --- a/rtu/configure.lua +++ b/rtu/configure.lua @@ -508,7 +508,7 @@ local function config_view(display) TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)} - local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function back_from_settings() if tool_ctl.viewing_config or tool_ctl.importing_legacy then @@ -578,13 +578,13 @@ local function config_view(display) tool_ctl.settings_confirm.hide() TextBox{parent=sum_c_2,x=1,y=1,height=1,text="The following peripherals will be imported:"} - local peri_import_list = ListBox{parent=sum_c_2,x=1,y=3,height=10,width=51,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local peri_import_list = ListBox{parent=sum_c_2,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} PushButton{parent=sum_c_2,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=sum_c_2,x=41,y=14,min_width=9,text="Confirm",callback=function()sum_pane.set_value(3)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg} TextBox{parent=sum_c_3,x=1,y=1,height=1,text="The following redstone entries will be imported:"} - local rs_import_list = ListBox{parent=sum_c_3,x=1,y=3,height=10,width=51,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local rs_import_list = ListBox{parent=sum_c_3,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} PushButton{parent=sum_c_3,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=sum_c_3,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg} @@ -620,7 +620,7 @@ local function config_view(display) TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg} - local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} for _, change in ipairs(changes) do TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg} @@ -649,7 +649,7 @@ local function config_view(display) TextBox{parent=peri_cfg,x=1,y=2,height=1,text=" Peripheral Connections",fg_bg=cpair(colors.black,colors.purple)} - local peri_list = ListBox{parent=peri_c_1,x=1,y=1,height=12,width=51,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local peri_list = ListBox{parent=peri_c_1,x=1,y=1,height=12,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function peri_revert() tmp_cfg.Peripherals = deep_copy_peri(ini_cfg.Peripherals) @@ -675,7 +675,7 @@ local function config_view(display) TextBox{parent=peri_c_2,x=1,y=1,height=1,text="Select one of the below devices to use."} - tool_ctl.ppm_devs = ListBox{parent=peri_c_2,x=1,y=3,height=10,width=51,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + tool_ctl.ppm_devs = ListBox{parent=peri_c_2,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} PushButton{parent=peri_c_2,x=1,y=14,text="\x1b Back",callback=function()peri_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=peri_c_2,x=8,y=14,min_width=10,text="Manual +",callback=function()peri_pane.set_value(3)end,fg_bg=cpair(colors.black,colors.orange),active_fg_bg=btn_act_fg_bg} @@ -963,7 +963,7 @@ local function config_view(display) TextBox{parent=rs_cfg,x=1,y=2,height=1,text=" Redstone Connections",fg_bg=cpair(colors.black,colors.red)} TextBox{parent=rs_c_1,x=1,y=1,height=1,text=" port side/color unit/facility",fg_bg=g_lg_fg_bg} - local rs_list = ListBox{parent=rs_c_1,x=1,y=2,height=11,width=51,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local rs_list = ListBox{parent=rs_c_1,x=1,y=2,height=11,width=49,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function rs_revert() tmp_cfg.Redstone = deep_copy_rs(ini_cfg.Redstone) @@ -992,7 +992,7 @@ local function config_view(display) TextBox{parent=rs_c_2,x=1,y=1,height=1,text="Select one of the below ports to use."} - local rs_ports = ListBox{parent=rs_c_2,x=1,y=3,height=10,width=51,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local rs_ports = ListBox{parent=rs_c_2,x=1,y=3,height=10,width=49,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function new_rs(port) if (rsio.get_io_dir(port) == rsio.IO_DIR.IN) then diff --git a/rtu/startup.lua b/rtu/startup.lua index e8e85fc..d58639c 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -31,7 +31,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.7.12" +local RTU_VERSION = "v1.7.13" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE diff --git a/supervisor/configure.lua b/supervisor/configure.lua index b8c4e00..357f733 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -739,7 +739,7 @@ local function config_view(display) TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)} - local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} local function back_from_settings() if tool_ctl.viewing_config or tool_ctl.importing_legacy then @@ -844,7 +844,7 @@ local function config_view(display) TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg} - local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} + local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)} for _, change in ipairs(changes) do TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg} diff --git a/supervisor/startup.lua b/supervisor/startup.lua index a983ee4..080e318 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v1.2.6" +local SUPERVISOR_VERSION = "v1.2.7" local println = util.println local println_ts = util.println_ts From a330249c7ef0768ab5bbfe6bb48286d789ed41c8 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 14:07:26 -0500 Subject: [PATCH 30/43] #408 integrate new settings with pocket --- pocket/config.lua | 27 ------------------- pocket/pocket.lua | 64 +++++++++++++++++++++++++++++++++++++--------- pocket/startup.lua | 38 +++++++++++++-------------- 3 files changed, 70 insertions(+), 59 deletions(-) delete mode 100644 pocket/config.lua diff --git a/pocket/config.lua b/pocket/config.lua deleted file mode 100644 index 72625f4..0000000 --- a/pocket/config.lua +++ /dev/null @@ -1,27 +0,0 @@ -local config = {} - --- supervisor comms channel -config.SVR_CHANNEL = 16240 --- coordinator comms channel -config.CRD_CHANNEL = 16243 --- pocket comms channel -config.PKT_CHANNEL = 16244 --- max trusted modem message distance (0 to disable check) -config.TRUSTED_RANGE = 0 --- time in seconds (>= 2) before assuming a remote device is no longer active -config.COMMS_TIMEOUT = 5 --- facility authentication key (do NOT use one of your passwords) --- this enables verifying that messages are authentic --- all devices on the same network must use the same key --- config.AUTH_KEY = "SCADAfacility123" - --- 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 --- true to log verbose debug messages -config.LOG_DEBUG = false - -return config diff --git a/pocket/pocket.lua b/pocket/pocket.lua index b2da51d..0bec6ba 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -13,17 +13,57 @@ local LINK_STATE = iocontrol.LINK_STATE local pocket = {} +---@type pkt_config +local config = {} + +pocket.config = config + +-- load the pocket configuration +function pocket.load_config() + if not settings.load("/pocket.settings") then return false end + + config.SVR_Channel = settings.get("SVR_Channel") + config.CRD_Channel = settings.get("CRD_Channel") + config.PKT_Channel = settings.get("PKT_Channel") + config.ConnTimeout = settings.get("ConnTimeout") + config.TrustedRange = settings.get("TrustedRange") + config.AuthKey = settings.get("AuthKey") + + config.LogMode = settings.get("LogMode") + config.LogPath = settings.get("LogPath") + config.LogDebug = settings.get("LogDebug") + + local cfv = util.new_validator() + + cfv.assert_channel(config.SVR_Channel) + cfv.assert_channel(config.CRD_Channel) + cfv.assert_channel(config.PKT_Channel) + cfv.assert_type_num(config.ConnTimeout) + cfv.assert_min(config.ConnTimeout, 2) + cfv.assert_type_num(config.TrustedRange) + cfv.assert_min(config.TrustedRange, 0) + cfv.assert_type_str(config.AuthKey) + + if type(config.AuthKey) == "string" then + local len = string.len(config.AuthKey) + cfv.assert(len == 0 or len >= 8) + end + + cfv.assert_type_int(config.LogMode) + cfv.assert_range(config.LogMode, 0, 1) + cfv.assert_type_str(config.LogPath) + cfv.assert_type_bool(config.LogDebug) + + return cfv.valid() +end + -- pocket coordinator + supervisor communications ---@nodiscard ---@param version string pocket version ---@param nic nic network interface device ----@param pkt_channel integer pocket comms channel ----@param svr_channel integer supervisor access channel ----@param crd_channel integer coordinator access channel ----@param range integer trusted device connection range ---@param sv_watchdog watchdog ---@param api_watchdog watchdog -function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range, sv_watchdog, api_watchdog) +function pocket.comms(version, nic, sv_watchdog, api_watchdog) local self = { sv = { linked = false, @@ -42,13 +82,13 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range establish_delay_counter = 0 } - comms.set_trusted_range(range) + comms.set_trusted_range(config.TrustedRange) -- PRIVATE FUNCTIONS -- -- configure network channels nic.closeAll() - nic.open(pkt_channel) + nic.open(config.PKT_Channel) -- send a management packet to the supervisor ---@param msg_type MGMT_TYPE @@ -60,7 +100,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range pkt.make(msg_type, msg) s_pkt.make(self.sv.addr, self.sv.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) - nic.transmit(svr_channel, pkt_channel, s_pkt) + nic.transmit(config.SVR_Channel, config.PKT_Channel, s_pkt) self.sv.seq_num = self.sv.seq_num + 1 end @@ -74,7 +114,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range pkt.make(msg_type, msg) s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) - nic.transmit(crd_channel, pkt_channel, s_pkt) + nic.transmit(config.CRD_Channel, config.PKT_Channel, s_pkt) self.api.seq_num = self.api.seq_num + 1 end @@ -217,9 +257,9 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range local protocol = packet.scada_frame.protocol() local src_addr = packet.scada_frame.src_addr() - if l_chan ~= pkt_channel then + if l_chan ~= config.PKT_Channel then log.debug("received packet on unconfigured channel " .. l_chan, true) - elseif r_chan == crd_channel then + elseif r_chan == config.CRD_Channel then -- check sequence number if self.api.r_seq_num == nil then self.api.r_seq_num = packet.scada_frame.seq_num() @@ -308,7 +348,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range else log.debug("illegal packet type " .. protocol .. " from coordinator", true) end - elseif r_chan == svr_channel then + elseif r_chan == config.SVR_Channel then -- check sequence number if self.sv.r_seq_num == nil then self.sv.r_seq_num = packet.scada_frame.seq_num() diff --git a/pocket/startup.lua b/pocket/startup.lua index b9334b5..13ac7fd 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -13,7 +13,7 @@ local util = require("scada-common.util") local core = require("graphics.core") -local config = require("pocket.config") +local configure = require("pocket.configure") local iocontrol = require("pocket.iocontrol") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") @@ -24,27 +24,26 @@ local println = util.println local println_ts = util.println_ts ---------------------------------------- --- config validation +-- get configuration ---------------------------------------- -local cfv = util.new_validator() +if not pocket.load_config() then + -- try to reconfigure (user action) + local success, error = configure.configure(true) + if success then + assert(pocket.load_config(), "failed to load valid configuration") + else + assert(success, "pocket configuration error: " .. error) + end +end -cfv.assert_channel(config.SVR_CHANNEL) -cfv.assert_channel(config.CRD_CHANNEL) -cfv.assert_channel(config.PKT_CHANNEL) -cfv.assert_type_int(config.TRUSTED_RANGE) -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) - -assert(cfv.valid(), "bad config file: missing/invalid fields") +local config = pocket.config ---------------------------------------- -- log init ---------------------------------------- -log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG == true) +log.init(config.LogPath, config.LogMode, config.LogDebug) log.info("========================================") log.info("BOOTING pocket.startup " .. POCKET_VERSION) @@ -69,8 +68,8 @@ local function main() ---------------------------------------- -- message authentication init - if type(config.AUTH_KEY) == "string" then - network.init_mac(config.AUTH_KEY) + if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then + network.init_mac(config.AuthKey) end iocontrol.report_link_state(iocontrol.LINK_STATE.UNLINKED) @@ -85,8 +84,8 @@ local function main() -- create connection watchdogs local conn_wd = { - sv = util.new_watchdog(config.COMMS_TIMEOUT), - api = util.new_watchdog(config.COMMS_TIMEOUT) + sv = util.new_watchdog(config.ConnTimeout), + api = util.new_watchdog(config.ConnTimeout) } conn_wd.sv.cancel() @@ -96,8 +95,7 @@ local function main() -- create network interface then setup comms local nic = network.nic(modem) - local pocket_comms = pocket.comms(POCKET_VERSION, nic, config.PKT_CHANNEL, config.SVR_CHANNEL, - config.CRD_CHANNEL, config.TRUSTED_RANGE, conn_wd.sv, conn_wd.api) + local pocket_comms = pocket.comms(POCKET_VERSION, nic, conn_wd.sv, conn_wd.api) log.debug("startup> comms init") -- base loop clock (2Hz, 10 ticks) From 76f21e925b69eb693da443d66364be76a3906c64 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 14:18:23 -0500 Subject: [PATCH 31/43] #145 removed unneeded references to config.lua files + some minification --- ccmsi.lua | 121 +++++++++++++++++++++++++----------------------------- 1 file changed, 57 insertions(+), 64 deletions(-) diff --git a/ccmsi.lua b/ccmsi.lua index 327018d..29045f8 100644 --- a/ccmsi.lua +++ b/ccmsi.lua @@ -18,7 +18,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. local function println(message) print(tostring(message)) end local function print(message) term.write(tostring(message)) end -local CCMSI_VERSION = "v1.12b" +local CCMSI_VERSION = "v1.13" local install_dir = "/.install-cache" local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/" @@ -26,7 +26,7 @@ local repo_path = "http://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada local opts = { ... } local mode, app, target -local install_manifest = manifest_path .. "main/install_manifest.json" +local install_manifest = manifest_path.."main/install_manifest.json" local function red() term.setTextColor(colors.red) end local function orange() term.setTextColor(colors.orange) end @@ -59,17 +59,17 @@ local function ask_y_n(question, default) end -- print out a white + blue text message -local function pkg_message(message, package) white();print(message .. " ");blue();println(package);white() end +local function pkg_message(message, package) white();print(message.." ");blue();println(package);white() end -- indicate actions to be taken based on package differences for installs/updates local function show_pkg_change(name, v) if v.v_local ~= nil then if v.v_local ~= v.v_remote then - print("[" .. name .. "] updating ");blue();print(v.v_local);white();print(" \xbb ");blue();println(v.v_remote);white() + print("["..name.."] updating ");blue();print(v.v_local);white();print(" \xbb ");blue();println(v.v_remote);white() elseif mode == "install" then - pkg_message("[" .. name .. "] reinstalling", v.v_local) + pkg_message("["..name.."] reinstalling", v.v_local) end - else pkg_message("[" .. name .. "] new install of", v.v_remote) end + else pkg_message("["..name.."] new install of", v.v_remote) end return v.v_local ~= v.v_remote end @@ -90,7 +90,7 @@ local function get_remote_manifest() local response, error = http.get(install_manifest) if response == nil then orange();println("Failed to get installation manifest from GitHub, cannot update or install.") - red();println("HTTP error: " .. error);white() + red();println("HTTP error: "..error);white() return false, {} end @@ -155,13 +155,13 @@ local function _clean_dir(dir, tree) if tree == nil then tree = {} end local ls = fs.list(dir) for _, val in pairs(ls) do - local path = dir .. "/" .. val + local path = dir.."/"..val if fs.isDir(path) then _clean_dir(path, tree[val]) - if #fs.list(path) == 0 then fs.delete(path);println("deleted " .. path) end - elseif (not _in_array(val, tree)) and (val ~= "config.lua" ) then ---@fixme remove condition after migration to settings files + if #fs.list(path) == 0 then fs.delete(path);println("deleted "..path) end + elseif (not _in_array(val, tree)) and (val ~= "config.lua" ) then fs.delete(path) - println("deleted " .. path) + println("deleted "..path) end end end @@ -177,13 +177,13 @@ local function clean(manifest) local ls = fs.list("/") for _, val in pairs(ls) do if fs.isDriveRoot(val) then - yellow();println("skipped mount '" .. val .. "'") + yellow();println("skipped mount '"..val.."'") elseif fs.isDir(val) then - if tree[val] ~= nil then lgray();_clean_dir("/" .. val, tree[val]) - else white(); if ask_y_n("delete the unused directory '" .. val .. "'") then lgray();_clean_dir("/" .. val) end end - if #fs.list(val) == 0 then fs.delete(val);lgray();println("deleted empty directory '" .. val .. "'") end + if tree[val] ~= nil then lgray();_clean_dir("/"..val, tree[val]) + else white(); if ask_y_n("delete the unused directory '"..val.."'") then lgray();_clean_dir("/"..val) end end + if #fs.list(val) == 0 then fs.delete(val);lgray();println("deleted empty directory '"..val.."'") end elseif not _in_array(val, tree) and (string.find(val, ".settings") == nil) then - white();if ask_y_n("delete the unused file '" .. val .. "'") then fs.delete(val);lgray();println("deleted " .. val) end + white();if ask_y_n("delete the unused file '"..val.."'") then fs.delete(val);lgray();println("deleted "..val) end end end @@ -192,7 +192,7 @@ end -- get and validate command line options -println("-- CC Mekanism SCADA Installer " .. CCMSI_VERSION .. " --") +println("-- CC Mekanism SCADA Installer "..CCMSI_VERSION.." --") if #opts == 0 or opts[1] == "help" then println("usage: ccmsi ") @@ -202,8 +202,8 @@ if #opts == 0 or opts[1] == "help" then yellow() println(" ccmsi check for target") lgray() - println(" install - fresh install, overwrites config.lua") - println(" update - update files EXCEPT for config.lua") + println(" install - fresh install") + println(" update - update files") println(" uninstall - delete files INCLUDING config/logs") white();println("");lgray() println(" reactor-plc - reactor PLC firmware") @@ -239,8 +239,8 @@ else end -- set paths - install_manifest = manifest_path .. target .. "/install_manifest.json" - repo_path = repo_path .. target .. "/" + install_manifest = manifest_path..target.."/install_manifest.json" + repo_path = repo_path..target.."/" end -- run selected mode @@ -260,7 +260,7 @@ if mode == "check" then -- list all versions for key, value in pairs(manifest.versions) do term.setTextColor(colors.purple) - print(string.format("%-14s", "[" .. key .. "]")) + print(string.format("%-14s", "["..key.."]")) if key == "installer" or (local_ok and (local_manifest.versions[key] ~= nil)) then blue();print(local_manifest.versions[key]) if value ~= local_manifest.versions[key] then @@ -315,10 +315,10 @@ elseif mode == "install" or mode == "update" then if not update_installer then yellow();println("A different version of the installer is available, it is recommended to update to it.");white() end if update_installer or ask_y_n("Would you like to update now") then lgray();println("GET ccmsi.lua") - local dl, err = http.get(repo_path .. "ccmsi.lua") + local dl, err = http.get(repo_path.."ccmsi.lua") if dl == nil then - red();println("HTTP Error " .. err) + red();println("HTTP Error "..err) println("Installer download failed.");white() else local handle = fs.open(debug.getinfo(1, "S").source:sub(2), "w") -- this file, regardless of name or location @@ -342,13 +342,8 @@ elseif mode == "install" or mode == "update" then ver.lockbox.v_remote = manifest.versions.lockbox green() - if mode == "install" then - println("Installing " .. app .. " files...") - elseif mode == "update" then - if app == "pocket" then println("Updating " .. app .. " files... (keeping old config.lua)") - else println("Updating " .. app .. " files...") end - end - white() + if mode == "install" then println("Installing ") else println("Updating ") end + println(app.." files...");white() ver.boot.changed = show_pkg_change("bootldr", ver.boot) ver.common.changed = show_pkg_change("common", ver.common) @@ -374,7 +369,6 @@ elseif mode == "install" or mode == "update" then local file_list = manifest.files local size_list = manifest.sizes local dependencies = manifest.depends[app] - local config_file = app .. "/config.lua" table.insert(dependencies, app) @@ -421,15 +415,15 @@ elseif mode == "install" or mode == "update" then local files = file_list[dependency] for _, file in pairs(files) do - println("GET " .. file) - local dl, err = http.get(repo_path .. file) + println("GET "..file) + local dl, err = http.get(repo_path..file) if dl == nil then - red();println("HTTP Error " .. err) + red();println("HTTP Error "..err) success = false break else - local handle = fs.open(install_dir .. "/" .. file, "w") + local handle = fs.open(install_dir.."/"..file, "w") handle.write(dl.readAll()) handle.close() end @@ -448,11 +442,9 @@ elseif mode == "install" or mode == "update" then local files = file_list[dependency] for _, file in pairs(files) do - if mode == "install" or file ~= config_file then - local temp_file = install_dir .. "/" .. file - if fs.exists(file) then fs.delete(file) end - fs.move(temp_file, file) - end + local temp_file = install_dir.."/"..file + if fs.exists(file) then fs.delete(file) end + fs.move(temp_file, file) end end end @@ -485,19 +477,17 @@ elseif mode == "install" or mode == "update" then local files = file_list[dependency] for _, file in pairs(files) do - if mode == "install" or file ~= config_file then - println("GET " .. file) - local dl, err = http.get(repo_path .. file) + println("GET "..file) + local dl, err = http.get(repo_path..file) - if dl == nil then - red();println("HTTP Error " .. err) - success = false - break - else - local handle = fs.open("/" .. file, "w") - handle.write(dl.readAll()) - handle.close() - end + if dl == nil then + red();println("HTTP Error "..err) + success = false + break + else + local handle = fs.open("/"..file, "w") + handle.write(dl.readAll()) + handle.close() end end end @@ -527,11 +517,11 @@ elseif mode == "uninstall" then end if manifest.versions[app] == nil then - red();println("Error: '" .. app .. "' is not installed.") + red();println("Error: '"..app.."' is not installed.") return end - orange();println("Uninstalling all " .. app .. " files...") + orange();println("Uninstalling all "..app.." files...") -- ask for confirmation if not ask_y_n("Continue", false) then return end @@ -546,16 +536,16 @@ elseif mode == "uninstall" then -- delete log file local log_deleted = false - local settings_file = app .. ".settings" - local legacy_config_file = app .. "/config.lua" + local settings_file = app..".settings" + local legacy_config_file = app.."/config.lua" lgray() if fs.exists(legacy_config_file) then log_deleted = pcall(function () - local config = require(app .. ".config") + local config = require(app..".config") if fs.exists(config.LOG_PATH) then fs.delete(config.LOG_PATH) - println("deleted log file " .. config.LOG_PATH) + println("deleted log file "..config.LOG_PATH) end end) elseif fs.exists(settings_file) and settings.load(settings_file) then @@ -563,7 +553,7 @@ elseif mode == "uninstall" then if log ~= nil and fs.exists(log) then log_deleted = true fs.delete(log) - println("deleted log file " .. log) + println("deleted log file "..log) end end @@ -577,7 +567,7 @@ elseif mode == "uninstall" then for _, dependency in pairs(dependencies) do local files = file_list[dependency] for _, file in pairs(files) do - if fs.exists(file) then fs.delete(file);println("deleted " .. file) end + if fs.exists(file) then fs.delete(file);println("deleted "..file) end end local folder = files[1] @@ -588,13 +578,16 @@ elseif mode == "uninstall" then if fs.isDir(folder) then fs.delete(folder) - println("deleted directory " .. folder) + println("deleted directory "..folder) end end + if fs.exists(legacy_config_file) then + fs.delete(legacy_config_file);println("deleted "..legacy_config_file) + end + if fs.exists(settings_file) then - fs.delete(settings_file) - println("deleted " .. settings_file) + fs.delete(settings_file);println("deleted "..settings_file) end fs.delete("install_manifest.json") From f112746e12b2ab0a8e793ac60ae1505113ee6ef8 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 14:27:02 -0500 Subject: [PATCH 32/43] #408 increment bootloader version for pocket configurator, minification --- configure.lua | 15 +++++---------- startup.lua | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/configure.lua b/configure.lua index 49ae2a8..ce6ed40 100644 --- a/configure.lua +++ b/configure.lua @@ -1,15 +1,10 @@ print("CONFIGURE> SCANNING FOR CONFIGURATOR...") -if fs.exists("reactor-plc/configure.lua") then - require("reactor-plc.configure").configure() -elseif fs.exists("rtu/configure.lua") then - require("rtu.configure").configure() -elseif fs.exists("supervisor/configure.lua") then - require("supervisor.configure").configure() -elseif fs.exists("coordinator/configure.lua") then - require("coordinator.configure").configure() -elseif fs.exists("pocket/configure.lua") then - require("pocket.configure").configure() +if fs.exists("reactor-plc/configure.lua") then require("reactor-plc.configure").configure() +elseif fs.exists("rtu/configure.lua") then require("rtu.configure").configure() +elseif fs.exists("supervisor/configure.lua") then require("supervisor.configure").configure() +elseif fs.exists("coordinator/configure.lua") then require("coordinator.configure").configure() +elseif fs.exists("pocket/configure.lua") then require("pocket.configure").configure() else print("CONFIGURE> NO CONFIGURATOR FOUND") print("CONFIGURE> EXIT") diff --git a/startup.lua b/startup.lua index 525402b..811f510 100644 --- a/startup.lua +++ b/startup.lua @@ -2,7 +2,7 @@ local util = require("scada-common.util") local println = util.println -local BOOTLOADER_VERSION = "0.6" +local BOOTLOADER_VERSION = "1.0" println("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION) println("BOOT> SCANNING FOR APPLICATIONS...") From 8ac46faf36e6af600bd57aca8b1670fb9c663fdc Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 18:56:24 -0500 Subject: [PATCH 33/43] set text scales before checking monitor dimensions --- coordinator/coordinator.lua | 15 ++++++++++++--- coordinator/startup.lua | 3 +++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 61ee56f..ae2ab77 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -121,8 +121,11 @@ function coordinator.load_config() monitors.primary = ppm.get_periph(config.MainDisplay) monitors.primary_name = config.MainDisplay + monitors.primary.setTextScale(0.5) w, _ = ppm.monitor_block_size(monitors.primary.getSize()) - if w ~= 8 then return 2, "Main monitor width is incorrect." end + if w ~= 8 then + return 2, util.c("Main monitor width is incorrect (was ", w, ", must be 8).") + end if not config.DisableFlowView then if not util.table_contains(names, config.FlowDisplay) then @@ -132,8 +135,11 @@ function coordinator.load_config() monitors.flow = ppm.get_periph(config.FlowDisplay) monitors.flow_name = config.FlowDisplay + monitors.flow.setTextScale(0.5) w, _ = ppm.monitor_block_size(monitors.flow.getSize()) - if w ~= 8 then return 2, "Flow monitor width is incorrect." end + if w ~= 8 then + return 2, util.c("Flow monitor width is incorrect (was ", w, ", must be 8).") + end end for i = 1, config.UnitCount do @@ -145,8 +151,11 @@ function coordinator.load_config() monitors.unit_displays[i] = ppm.get_periph(display) monitors.unit_name_map[i] = display + monitors.unit_displays[i].setTextScale(0.5) w, h = ppm.monitor_block_size(monitors.unit_displays[i].getSize()) - if w ~= 4 or h ~= 4 then return 2, "Unit " .. i .. " monitor size is incorrect." end + if w ~= 4 or h ~= 4 then + return 2, util.c("Unit ", i, " monitor size is incorrect (was ", w, " by ", h,", must be 4 by 4).") + end end else return 2, "Monitor configuration invalid." end end diff --git a/coordinator/startup.lua b/coordinator/startup.lua index a16077a..5db4f90 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -79,6 +79,9 @@ local function main() -- system startup ---------------------------------------- + -- re-mount devices now that logging is ready + ppm.mount_all() + -- report versions/init fp PSIL iocontrol.init_fp(COORDINATOR_VERSION, comms.version) From 126d6eb163c4f0e84b3d54977d64f29650afedfc Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 19:28:12 -0500 Subject: [PATCH 34/43] #424 fixed key derivation init --- scada-common/network.lua | 12 ++++++------ scada-common/util.lua | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scada-common/network.lua b/scada-common/network.lua index d9fa83f..742f418 100644 --- a/scada-common/network.lua +++ b/scada-common/network.lua @@ -7,7 +7,7 @@ local log = require("scada-common.log") local util = require("scada-common.util") local md5 = require("lockbox.digest.md5") -local sha256 = require("lockbox.digest.sha2_256") +local sha1 = require("lockbox.digest.sha1") local pbkdf2 = require("lockbox.kdf.pbkdf2") local hmac = require("lockbox.mac.hmac") local stream = require("lockbox.util.stream") @@ -31,12 +31,12 @@ function network.init_mac(passkey) local key_deriv = pbkdf2() -- setup PBKDF2 - key_deriv.setPassword(passkey) + key_deriv.setPRF(hmac().setBlockSize(64).setDigest(sha1)) + key_deriv.setBlockLen(20) + key_deriv.setDKeyLen(20) + key_deriv.setIterations(256) key_deriv.setSalt("pepper") - key_deriv.setIterations(32) - key_deriv.setBlockLen(8) - key_deriv.setDKeyLen(16) - key_deriv.setPRF(hmac().setBlockSize(64).setDigest(sha256)) + key_deriv.setPassword(passkey) key_deriv.finish() c_eng.key = array.fromHex(key_deriv.asHex()) diff --git a/scada-common/util.lua b/scada-common/util.lua index f407e36..10b4202 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -22,7 +22,7 @@ local t_pack = table.pack local util = {} -- scada-common version -util.version = "1.1.13" +util.version = "1.1.14" util.TICK_TIME_S = 0.05 util.TICK_TIME_MS = 50 From 440b724798acb47c740ab78d69648ec06ec8679c Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 19:33:08 -0500 Subject: [PATCH 35/43] #424 tick comms version up --- scada-common/comms.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scada-common/comms.lua b/scada-common/comms.lua index edcb7c3..eaa7ec4 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -17,7 +17,7 @@ local max_distance = nil local comms = {} -- protocol/data version (protocol/data independent changes tracked by util.lua version) -comms.version = "2.4.3" +comms.version = "2.4.4" ---@enum PROTOCOL local PROTOCOL = { From c47fa5433f4e1bc9785304406e530b7f87d29c7c Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 19:36:27 -0500 Subject: [PATCH 36/43] #408 improvements to pocket configurator --- pocket/configure.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pocket/configure.lua b/pocket/configure.lua index d7087b9..9ac13f0 100644 --- a/pocket/configure.lua +++ b/pocket/configure.lua @@ -169,7 +169,7 @@ local function config_view(display) y_start = y_start + 2 end - PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} + PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure Device",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg} tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg} if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end @@ -267,6 +267,9 @@ local function config_view(display) local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end + -- declare back first so tabbing makes sense visually + PushButton{parent=net_c_4,x=1,y=15,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + local hide_key = CheckBox{parent=net_c_4,x=8,y=15,label="Hide Key",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key} hide_key.set_value(true) @@ -283,7 +286,6 @@ local function config_view(display) else key_err.show() end end - PushButton{parent=net_c_4,x=1,y=15,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=net_c_4,x=19,y=15,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} --#endregion @@ -401,7 +403,7 @@ local function config_view(display) PushButton{parent=sum_c_2,x=1,y=15,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=sum_c_2,x=19,y=15,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."} + TextBox{parent=sum_c_3,x=1,y=1,height=4,text="The old config.lua file will now be deleted, then the configurator will exit."} local function delete_legacy() fs.delete("/pocket/config.lua") From 96db709ced9eeba8b9a4c82ea2e760f42f8abc6e Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 20:24:30 -0500 Subject: [PATCH 37/43] ccmsi print fix --- ccmsi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccmsi.lua b/ccmsi.lua index 29045f8..432bbef 100644 --- a/ccmsi.lua +++ b/ccmsi.lua @@ -342,7 +342,7 @@ elseif mode == "install" or mode == "update" then ver.lockbox.v_remote = manifest.versions.lockbox green() - if mode == "install" then println("Installing ") else println("Updating ") end + if mode == "install" then print("Installing ") else print("Updating ") end println(app.." files...");white() ver.boot.changed = show_pkg_change("bootldr", ver.boot) From baaef862ab5ed082cadefa794155cc49fbfa61fc Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 20:26:05 -0500 Subject: [PATCH 38/43] #145 coordinator configurator enhancements --- coordinator/configure.lua | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 080d5aa..0fc68ff 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -453,20 +453,22 @@ local function config_view(display) local function submit_auth() local v = key.get_value() if string.len(v) == 0 or string.len(v) >= 8 then + -- prep supervisor connection screen + tool_ctl.sv_next.hide() + tool_ctl.sv_skip.disable() + tool_ctl.sv_skip.show() + tool_ctl.sv_conn_button.enable() + tool_ctl.sv_conn_status.set_value("") + tool_ctl.sv_conn_detail.set_value("") + tmp_cfg.AuthKey = key.get_value() - main_pane.set_value(3) key_err.hide(true) -- init mac for supervisor connection if string.len(v) >= 8 then network.init_mac(tmp_cfg.AuthKey) end - -- prep supervisor connection screen - tool_ctl.sv_conn_button.enable() - tool_ctl.sv_conn_status.set_value("") - tool_ctl.sv_conn_detail.set_value("") - tool_ctl.sv_next.hide() - tool_ctl.sv_skip.show() - tool_ctl.sv_skip.disable() + main_pane.set_value(3) + tcd.dispatch_unique(2, function () tool_ctl.sv_skip.enable() end) else key_err.show() end end @@ -818,7 +820,7 @@ local function config_view(display) local function save_and_continue() for k, v in pairs(tmp_cfg) do settings.set(k, v) end - if settings.save("coordinator.settings") then + if settings.save("/coordinator.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) From 788cef8f8614bc2959e72c75e8e2a452fb187328 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 20:32:33 -0500 Subject: [PATCH 39/43] comment cleanup and absolute paths while saving --- pocket/configure.lua | 5 ++--- reactor-plc/configure.lua | 24 ++++++++++++++++-------- rtu/configure.lua | 24 ++++++++++++------------ supervisor/configure.lua | 26 +++++++++++++++++--------- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/pocket/configure.lua b/pocket/configure.lua index 9ac13f0..6c0f728 100644 --- a/pocket/configure.lua +++ b/pocket/configure.lua @@ -361,7 +361,7 @@ local function config_view(display) local function save_and_continue() for k, v in pairs(tmp_cfg) do settings.set(k, v) end - if settings.save("pocket.settings") then + if settings.save("/pocket.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) @@ -487,8 +487,7 @@ local function config_view(display) local val = util.strval(raw) if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) - elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") - end + elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") end if val == "nil" then val = "" end diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index 62f7d85..db9162d 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -181,7 +181,7 @@ local function config_view(display) local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,summary,changelog}} - -- MAIN PAGE + -- Main Page local y_start = 5 @@ -212,7 +212,7 @@ local function config_view(display) PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- PLC CONFIG + --#region PLC local plc_c_1 = Div{parent=plc_cfg,x=2,y=4,width=49} local plc_c_2 = Div{parent=plc_cfg,x=2,y=4,width=49} @@ -290,7 +290,9 @@ local function config_view(display) PushButton{parent=plc_c_4,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=plc_c_4,x=44,y=14,text="Next \x1a",callback=submit_emcool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- NET CONFIG + --#endregion + + --#region Network local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49} local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49} @@ -390,7 +392,9 @@ local function config_view(display) PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- LOG CONFIG + --#endregion + + --#region Logging local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49} @@ -430,7 +434,9 @@ local function config_view(display) PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=back_from_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- SUMMARY OF CHANGES + --#endregion + + --#region Summary and Saving local sum_c_1 = Div{parent=summary,x=2,y=4,width=49} local sum_c_2 = Div{parent=summary,x=2,y=4,width=49} @@ -463,7 +469,7 @@ local function config_view(display) local function save_and_continue() for k, v in pairs(tmp_cfg) do settings.set(k, v) end - if settings.save("reactor-plc.settings") then + if settings.save("/reactor-plc.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) @@ -525,7 +531,9 @@ local function config_view(display) PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - -- CONFIG CHANGE LOG + --#endregion + + -- Config Change Log local cl = Div{parent=changelog,x=2,y=4,width=49} @@ -646,7 +654,7 @@ local function reset_term() end -- run the reactor PLC configurator ----@param ask_config? boolean indicate if this is being called by the PLC startup app due to an invalid configuration +---@param ask_config? boolean indicate if this is being called by the startup app due to an invalid configuration function configurator.configure(ask_config) tool_ctl.ask_config = ask_config == true diff --git a/rtu/configure.lua b/rtu/configure.lua index d84cdc7..131623b 100644 --- a/rtu/configure.lua +++ b/rtu/configure.lua @@ -273,7 +273,7 @@ local function config_view(display) local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,spkr_cfg,net_cfg,log_cfg,summary,changelog,peri_cfg,rs_cfg}} - --#region MAIN PAGE + --#region Main Page local y_start = 2 @@ -324,7 +324,7 @@ local function config_view(display) --#endregion - --#region SPEAKER CONFIG + --#region Speakers local spkr_c = Div{parent=spkr_cfg,x=2,y=4,width=49} @@ -353,7 +353,7 @@ local function config_view(display) --#endregion - --#region NET CONFIG + --#region Network local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49} local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49} @@ -455,7 +455,7 @@ local function config_view(display) --#endregion - --#region LOG CONFIG + --#region Logging local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49} @@ -494,7 +494,7 @@ local function config_view(display) --#endregion - --#region SUMMARY OF CHANGES + --#region Summary and Saving local sum_c_1 = Div{parent=summary,x=2,y=4,width=49} local sum_c_2 = Div{parent=summary,x=2,y=4,width=49} @@ -539,7 +539,7 @@ local function config_view(display) if settings.get("Peripherals") == nil then settings.set("Peripherals", {}) end if settings.get("Redstone") == nil then settings.set("Redstone", {}) end - if settings.save("rtu.settings") then + if settings.save("/rtu.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) @@ -614,7 +614,7 @@ local function config_view(display) --#endregion - --#region CONFIG CHANGE LOG + --#region Config Change Log local cl = Div{parent=changelog,x=2,y=4,width=49} @@ -635,7 +635,7 @@ local function config_view(display) --#endregion - --#region DEVICES + --#region Peripherals local peri_c_1 = Div{parent=peri_cfg,x=2,y=4,width=49} local peri_c_2 = Div{parent=peri_cfg,x=2,y=4,width=49} @@ -659,7 +659,7 @@ local function config_view(display) local function peri_apply() settings.set("Peripherals", tmp_cfg.Peripherals) - if settings.save("rtu.settings") then + if settings.save("/rtu.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) peri_pane.set_value(5) @@ -949,7 +949,7 @@ local function config_view(display) --#endregion - --#region REDSTONE + --#region Redstone local rs_c_1 = Div{parent=rs_cfg,x=2,y=4,width=49} local rs_c_2 = Div{parent=rs_cfg,x=2,y=4,width=49} @@ -973,7 +973,7 @@ local function config_view(display) local function rs_apply() settings.set("Redstone", tmp_cfg.Redstone) - if settings.save("rtu.settings") then + if settings.save("/rtu.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) rs_pane.set_value(4) @@ -1466,7 +1466,7 @@ local function reset_term() end -- run the RTU gateway configurator ----@param ask_config? boolean indicate if this is being called by the RTU startup app due to an invalid configuration +---@param ask_config? boolean indicate if this is being called by the startup app due to an invalid configuration function configurator.configure(ask_config) tool_ctl.ask_config = ask_config == true diff --git a/supervisor/configure.lua b/supervisor/configure.lua index 357f733..34398b6 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -170,7 +170,7 @@ local function config_view(display) local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,svr_cfg,net_cfg,log_cfg,summary,changelog,import_err}} - -- MAIN PAGE + -- Main Page local y_start = 5 @@ -201,7 +201,7 @@ local function config_view(display) PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- SUPERVISOR CONFIG + --#region Facility local svr_c_1 = Div{parent=svr_cfg,x=2,y=4,width=49} local svr_c_2 = Div{parent=svr_cfg,x=2,y=4,width=49} @@ -564,7 +564,9 @@ local function config_view(display) PushButton{parent=svr_c_6,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- NET CONFIG + --#endregion + + --#region Network local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49} local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49} @@ -692,7 +694,9 @@ local function config_view(display) PushButton{parent=net_c_4,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=net_c_4,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- LOG CONFIG + --#endregion + + --#region Logging local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49} @@ -728,7 +732,9 @@ local function config_view(display) PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- SUMMARY OF CHANGES + --#endregion + + --#region Summary and Saving local sum_c_1 = Div{parent=summary,x=2,y=4,width=49} local sum_c_2 = Div{parent=summary,x=2,y=4,width=49} @@ -761,7 +767,7 @@ local function config_view(display) local function save_and_continue() for k, v in pairs(tmp_cfg) do settings.set(k, v) end - if settings.save("supervisor.settings") then + if settings.save("/supervisor.settings") then load_settings(settings_cfg, true) load_settings(ini_cfg) @@ -838,7 +844,9 @@ local function config_view(display) PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - -- CONFIG CHANGE LOG + --#endregion + + -- Config Change Log local cl = Div{parent=changelog,x=2,y=4,width=49} @@ -857,7 +865,7 @@ local function config_view(display) PushButton{parent=cl,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - -- IMPORT ERROR + -- Import Error local i_err = Div{parent=import_err,x=2,y=4,width=49} @@ -1033,7 +1041,7 @@ local function reset_term() end -- run the supervisor configurator ----@param ask_config? boolean indicate if this is being called by the supervisor startup app due to an invalid configuration +---@param ask_config? boolean indicate if this is being called by the startup app due to an invalid configuration function configurator.configure(ask_config) tool_ctl.ask_config = ask_config == true From 158cc39b80e6ec1274f55e43e65754e802c175ee Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 19 Feb 2024 20:40:05 -0500 Subject: [PATCH 40/43] #421 remove 'latest' branch --- .github/workflows/check.yml | 2 -- .github/workflows/manifest.yml | 18 +----------------- ccmsi.lua | 6 +++--- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 8524d55..975bb73 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -5,12 +5,10 @@ on: push: branches: - main - - latest - devel pull_request: branches: - main - - latest - devel jobs: check: diff --git a/.github/workflows/manifest.yml b/.github/workflows/manifest.yml index a631058..efafe3d 100644 --- a/.github/workflows/manifest.yml +++ b/.github/workflows/manifest.yml @@ -6,7 +6,6 @@ on: push: branches: - main - - latest - devel # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages @@ -43,7 +42,7 @@ jobs: - name: Create outputs folders if: success() || failure() shell: bash - run: mkdir deploy; mkdir deploy/manifests; mkdir deploy/manifests/main deploy/manifests/latest deploy/manifests/devel + run: mkdir deploy; mkdir deploy/manifests; mkdir deploy/manifests/main deploy/manifests/devel - name: Generate manifest and shields for main branch id: manifest-main if: ${{ (success() || failure()) && steps.checkout-main.outcome == 'success' }} @@ -51,21 +50,6 @@ jobs: - name: Save main's manifest if: ${{ (success() || failure()) && steps.manifest-main.outcome == 'success' }} run: mv install_manifest.json deploy/manifests/main - # Generate manifest for latest branch - - name: Checkout latest - id: checkout-latest - if: success() || failure() - uses: actions/checkout@v3 - with: - ref: 'latest' - clean: false - - name: Generate manifest for latest - id: manifest-latest - if: ${{ (success() || failure()) && steps.checkout-latest.outcome == 'success' }} - run: python imgen.py - - name: Save latest's manifest - if: ${{ (success() || failure()) && steps.manifest-latest.outcome == 'success' }} - run: mv install_manifest.json deploy/manifests/latest # Generate manifest for devel branch - name: Checkout devel id: checkout-devel diff --git a/ccmsi.lua b/ccmsi.lua index 432bbef..cc4a5d0 100644 --- a/ccmsi.lua +++ b/ccmsi.lua @@ -18,7 +18,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. local function println(message) print(tostring(message)) end local function print(message) term.write(tostring(message)) end -local CCMSI_VERSION = "v1.13" +local CCMSI_VERSION = "v1.14" local install_dir = "/.install-cache" local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/" @@ -213,7 +213,7 @@ if #opts == 0 or opts[1] == "help" then println(" pocket - pocket application") println(" installer - ccmsi installer (update only)") white();println("") - lgray();println(" main (default) | latest | devel");white() + lgray();println(" main (default) | devel");white() return else mode = get_opt(opts[1], { "check", "install", "update", "uninstall" }) @@ -233,7 +233,7 @@ else -- determine target if mode == "check" then target = opts[2] else target = opts[3] end - if (target ~= "main") and (target ~= "latest") and (target ~= "devel") then + if (target ~= "main") and (target ~= "devel") then if (target and target ~= "") then yellow();println("Unknown target, defaulting to 'main'");white() end target = "main" end From 910509d76402b18aeb804e527b2c8d913d39e9f1 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 20 Feb 2024 19:33:14 -0500 Subject: [PATCH 41/43] coordinator configurator bugfixes --- coordinator/configure.lua | 4 ++-- coordinator/startup.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 0fc68ff..7abb267 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -498,7 +498,7 @@ local function config_view(display) local function sv_skip() tcd.abort(handle_timeout) - tool_ctl.sv_fac_conf = nil + tool_ctl.sv_cool_conf = nil tool_ctl.net_listen = false fac_pane.set_value(2) end @@ -998,7 +998,7 @@ local function config_view(display) local conf = tool_ctl.sv_cool_conf fac_config_list.remove_all() - local str = util.sprintf("Facility has %d reactor units:", #conf) + local str = util.sprintf("Facility has %d reactor unit%s:", #conf, util.trinary(#conf==1,"","s")) TextBox{parent=fac_config_list,height=1,text=str,fg_bg=cpair(colors.gray,colors.white)} for i = 1, #conf do diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 5db4f90..5831b8a 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder") local apisessions = require("coordinator.session.apisessions") -local COORDINATOR_VERSION = "v1.2.1" +local COORDINATOR_VERSION = "v1.2.2" local println = util.println local println_ts = util.println_ts From 96691d773ae266516c40dd4218614b7d8c9c30b6 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 21 Feb 2024 12:58:49 -0500 Subject: [PATCH 42/43] supervisor debug log messages and #427 fix --- supervisor/facility.lua | 12 +++++++----- supervisor/startup.lua | 2 +- supervisor/supervisor.lua | 5 ++--- supervisor/unit.lua | 14 ++++++++------ 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/supervisor/facility.lua b/supervisor/facility.lua index ce98d1f..94f93b3 100644 --- a/supervisor/facility.lua +++ b/supervisor/facility.lua @@ -337,7 +337,7 @@ function facility.new(num_reactors, cooling_conf) if state_changed then self.saturated = false - log.debug("FAC: state changed from " .. PROCESS_NAMES[self.last_mode + 1] .. " to " .. PROCESS_NAMES[self.mode + 1]) + log.debug(util.c("FAC: state changed from ", PROCESS_NAMES[self.last_mode + 1], " to ", PROCESS_NAMES[self.mode + 1])) if (self.last_mode == PROCESS.INACTIVE) or (self.last_mode == PROCESS.GEN_RATE_FAULT_IDLE) then self.start_fail = START_STATUS.OK @@ -375,6 +375,8 @@ function facility.new(num_reactors, cooling_conf) end end + log.debug(util.c("FAC: computed a max combined burn rate of ", self.max_burn_combined, "mB/t")) + if blade_count == nil then -- no units log.warning("FAC: cannot start process control with 0 units assigned") @@ -436,7 +438,7 @@ function facility.new(num_reactors, cooling_conf) self.saturated = true self.status_text = { "MONITORED MODE", "running reactors at limit" } - log.info(util.c("FAC: MAX_BURN process mode started")) + log.info("FAC: MAX_BURN process mode started") end _allocate_burn_rate(self.max_burn_combined, true) @@ -445,7 +447,7 @@ function facility.new(num_reactors, cooling_conf) if state_changed then self.time_start = now self.status_text = { "BURN RATE MODE", "running" } - log.info(util.c("FAC: BURN_RATE process mode started")) + log.info("FAC: BURN_RATE process mode started") end local unallocated = _allocate_burn_rate(self.burn_target, true) @@ -459,7 +461,7 @@ function facility.new(num_reactors, cooling_conf) self.accumulator = 0 self.status_text = { "CHARGE MODE", "running control loop" } - log.info(util.c("FAC: CHARGE mode starting PID control")) + log.info("FAC: CHARGE mode starting PID control") elseif self.last_update ~= charge_update then -- convert to kFE to make constants not microscopic local error = util.round((self.charge_setpoint - avg_charge) / 1000) / 1000 @@ -614,7 +616,7 @@ function facility.new(num_reactors, cooling_conf) astatus.matrix_fill = (db.tanks.energy_fill >= ALARM_LIMS.CHARGE_HIGH) or (astatus.matrix_fill and db.tanks.energy_fill > ALARM_LIMS.CHARGE_RE_ENABLE) if was_fill and not astatus.matrix_fill then - log.info("FAC: charge state of induction matrix entered acceptable range <= " .. (ALARM_LIMS.CHARGE_RE_ENABLE * 100) .. "%") + log.info(util.c("FAC: charge state of induction matrix entered acceptable range <= ", ALARM_LIMS.CHARGE_RE_ENABLE * 100, "%")) end -- check for critical unit alarms diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 080e318..69633fa 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v1.2.7" +local SUPERVISOR_VERSION = "v1.2.8" local println = util.println local println_ts = util.println_ts diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 7b2b87e..27bf22c 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -199,9 +199,8 @@ function supervisor.comms(_version, nic, fp_ok) -- pass the packet onto the session handler session.in_queue.push_packet(packet) else - -- unknown session, force a re-link - log.debug("PLC_ESTABLISH: no session but not an establish, forcing relink") - _send_establish(packet.scada_frame, ESTABLISH_ACK.DENY) + -- any other packet should be session related, discard it + log.debug("discarding RPLC packet without a known session") end elseif protocol == PROTOCOL.SCADA_MGMT then ---@cast packet mgmt_frame diff --git a/supervisor/unit.lua b/supervisor/unit.lua index 645f4e9..a751339 100644 --- a/supervisor/unit.lua +++ b/supervisor/unit.lua @@ -564,6 +564,7 @@ function unit.new(reactor_id, num_boilers, num_turbines) function public.auto_engage() self.auto_engaged = true if self.plc_i ~= nil then + log.debug(util.c("UNIT ", self.r_id, ": engaged auto control")) self.plc_i.auto_lock(true) end end @@ -572,6 +573,7 @@ function unit.new(reactor_id, num_boilers, num_turbines) function public.auto_disengage() self.auto_engaged = false if self.plc_i ~= nil then + log.debug(util.c("UNIT ", self.r_id, ": disengaged auto control")) self.plc_i.auto_lock(false) self.db.control.br100 = 0 end @@ -582,12 +584,12 @@ function unit.new(reactor_id, num_boilers, num_turbines) ---@nodiscard ---@return integer lim_br100 function public.auto_get_effective_limit() - if (not self.db.control.ready) or self.db.control.degraded or self.plc_cache.rps_trip then - self.db.control.br100 = 0 + local ctrl = self.db.control + if (not ctrl.ready) or ctrl.degraded or self.plc_cache.rps_trip then + log.debug(util.c("UNIT ", self.r_id, ": effective limit is zero! ready[", ctrl.ready, "] degraded[", ctrl.degraded, "] rps_trip[", self.plc_cache.rps_trip, "]")) + ctrl.br100 = 0 return 0 - else - return self.db.control.lim_br100 - end + else return ctrl.lim_br100 end end -- set the automatic burn rate based on the last set burn rate in 100ths @@ -595,8 +597,8 @@ function unit.new(reactor_id, num_boilers, num_turbines) function public.auto_commit_br100(ramp) if self.auto_engaged then if self.plc_i ~= nil then + log.debug(util.c("UNIT ", self.r_id, ": commit br100 of ", self.db.control.br100, " with ramp set to ", ramp)) self.plc_i.auto_set_burn(self.db.control.br100 / 100, ramp) - if ramp then self.ramp_target_br100 = self.db.control.br100 end end end From 10d0a9763ac78319d163c36a816bd2aa1f5794ea Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 21 Feb 2024 12:59:48 -0500 Subject: [PATCH 43/43] disabled a verbose log message --- supervisor/unit.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervisor/unit.lua b/supervisor/unit.lua index a751339..8d38222 100644 --- a/supervisor/unit.lua +++ b/supervisor/unit.lua @@ -586,7 +586,7 @@ function unit.new(reactor_id, num_boilers, num_turbines) function public.auto_get_effective_limit() local ctrl = self.db.control if (not ctrl.ready) or ctrl.degraded or self.plc_cache.rps_trip then - log.debug(util.c("UNIT ", self.r_id, ": effective limit is zero! ready[", ctrl.ready, "] degraded[", ctrl.degraded, "] rps_trip[", self.plc_cache.rps_trip, "]")) + -- log.debug(util.c("UNIT ", self.r_id, ": effective limit is zero! ready[", ctrl.ready, "] degraded[", ctrl.degraded, "] rps_trip[", self.plc_cache.rps_trip, "]")) ctrl.br100 = 0 return 0 else return ctrl.lim_br100 end