From 3762e9dcedecff3bab5945f50561d98c34c4ba64 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 16 Jul 2024 21:03:52 +0000 Subject: [PATCH 01/21] #524 fix tank layout render reset --- supervisor/configure.lua | 109 ++++++++++++++++++++------------------- supervisor/startup.lua | 2 +- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/supervisor/configure.lua b/supervisor/configure.lua index 33e20c3..eedc476 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -453,15 +453,16 @@ local function config_view(display) vis_unit_list.set_value(u_text) + local vis_ftanks = tool_ctl.vis_ftanks local next_idx = 1 if is_ft(1) then next_idx = 2 if (mode == 1 and (is_ft(2) or is_ft(3) or is_ft(4))) or (mode == 2 and (is_ft(2) or is_ft(3))) or ((mode == 3 or mode == 5) and is_ft(2)) then - tool_ctl.vis_ftanks[1].pipe_direct.set_value("\x8c\x8c\x8c\x9c\x8c") + vis_ftanks[1].pipe_direct.set_value("\x8c\x8c\x8c\x9c\x8c") else - tool_ctl.vis_ftanks[1].pipe_direct.set_value(string.rep("\x8c",5)) + vis_ftanks[1].pipe_direct.set_value(string.rep("\x8c",5)) end end @@ -469,108 +470,108 @@ local function config_view(display) local _2_46_need_chain = (mode == 4 and (is_ft(3) or is_ft(4))) or (mode == 6 and is_ft(3)) if is_ft(2) then - tool_ctl.vis_ftanks[2].label.set_value("Tank F" .. next_idx) + vis_ftanks[2].label.set_value("Tank F" .. next_idx) if (mode < 4 or mode == 5) and is_ft(1) then - tool_ctl.vis_ftanks[2].label.hide(true) - tool_ctl.vis_ftanks[2].pipe_direct.hide(true) + vis_ftanks[2].label.hide(true) + vis_ftanks[2].pipe_direct.hide(true) if _2_12_need_passt then - tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x9d") + vis_ftanks[2].pipe_chain.set_value("\x95\n\x9d") else - tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x8d") + vis_ftanks[2].pipe_chain.set_value("\x95\n\x8d") end - tool_ctl.vis_ftanks[2].pipe_chain.show() + vis_ftanks[2].pipe_chain.show() else - tool_ctl.vis_ftanks[2].label.show() + vis_ftanks[2].label.show() next_idx = next_idx + 1 - tool_ctl.vis_ftanks[2].pipe_chain.hide(true) + vis_ftanks[2].pipe_chain.hide(true) if _2_12_need_passt or _2_46_need_chain then - tool_ctl.vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x9c") + vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x9c") else - tool_ctl.vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x8c") + vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x8c") end - tool_ctl.vis_ftanks[2].pipe_direct.show() + vis_ftanks[2].pipe_direct.show() end - tool_ctl.vis_ftanks[2].line.show() + vis_ftanks[2].line.show() elseif is_ft(1) and _2_12_need_passt then - tool_ctl.vis_ftanks[2].label.hide(true) - tool_ctl.vis_ftanks[2].pipe_direct.hide(true) - tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x95") - tool_ctl.vis_ftanks[2].pipe_chain.show() - tool_ctl.vis_ftanks[2].line.show() + vis_ftanks[2].label.hide(true) + vis_ftanks[2].pipe_direct.hide(true) + vis_ftanks[2].pipe_chain.set_value("\x95\n\x95") + vis_ftanks[2].pipe_chain.show() + vis_ftanks[2].line.show() else - tool_ctl.vis_ftanks[2].line.hide(true) + vis_ftanks[2].line.hide(true) end if is_ft(3) then - tool_ctl.vis_ftanks[3].label.set_value("Tank F" .. next_idx) + vis_ftanks[3].label.set_value("Tank F" .. next_idx) if (mode < 3 and (is_ft(1) or is_ft(2))) or ((mode == 4 or mode == 6) and is_ft(2)) then - tool_ctl.vis_ftanks[3].label.hide(true) - tool_ctl.vis_ftanks[3].pipe_direct.hide(true) + vis_ftanks[3].label.hide(true) + vis_ftanks[3].pipe_direct.hide(true) if (mode == 1 or mode == 4) and is_ft(4) then - tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x9d") + vis_ftanks[3].pipe_chain.set_value("\x95\n\x9d") else - tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x8d") + vis_ftanks[3].pipe_chain.set_value("\x95\n\x8d") end - tool_ctl.vis_ftanks[3].pipe_chain.show() + vis_ftanks[3].pipe_chain.show() else - tool_ctl.vis_ftanks[3].label.show() + vis_ftanks[3].label.show() next_idx = next_idx + 1 - tool_ctl.vis_ftanks[3].pipe_chain.hide(true) + vis_ftanks[3].pipe_chain.hide(true) if (mode == 1 or mode == 3 or mode == 4 or mode == 7) and is_ft(4) then - tool_ctl.vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x9c") + vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x9c") else - tool_ctl.vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x8c") + vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x8c") end - tool_ctl.vis_ftanks[3].pipe_direct.show() + vis_ftanks[3].pipe_direct.show() end - tool_ctl.vis_ftanks[3].line.show() + vis_ftanks[3].line.show() elseif (mode == 1 and is_ft(4) and (is_ft(1) or is_ft(2))) or (mode == 4 and is_ft(2) and is_ft(4)) then - tool_ctl.vis_ftanks[3].label.hide(true) - tool_ctl.vis_ftanks[3].pipe_direct.hide(true) - tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x95") - tool_ctl.vis_ftanks[3].pipe_chain.show() - tool_ctl.vis_ftanks[3].line.show() + vis_ftanks[3].label.hide(true) + vis_ftanks[3].pipe_direct.hide(true) + vis_ftanks[3].pipe_chain.set_value("\x95\n\x95") + vis_ftanks[3].pipe_chain.show() + vis_ftanks[3].line.show() else - tool_ctl.vis_ftanks[3].line.hide(true) + vis_ftanks[3].line.hide(true) end if is_ft(4) then - tool_ctl.vis_ftanks[4].label.set_value("Tank F" .. next_idx) + vis_ftanks[4].label.set_value("Tank F" .. next_idx) if (mode == 1 and (is_ft(1) or is_ft(2) or is_ft(3))) or ((mode == 3 or mode == 7) and is_ft(3)) or (mode == 4 and (is_ft(2) or is_ft(3))) then - tool_ctl.vis_ftanks[4].label.hide(true) - tool_ctl.vis_ftanks[4].pipe_direct.hide(true) - tool_ctl.vis_ftanks[4].pipe_chain.show() + vis_ftanks[4].label.hide(true) + vis_ftanks[4].pipe_direct.hide(true) + vis_ftanks[4].pipe_chain.show() else - tool_ctl.vis_ftanks[4].label.show() - tool_ctl.vis_ftanks[4].pipe_chain.hide(true) - tool_ctl.vis_ftanks[4].pipe_direct.show() + vis_ftanks[4].label.show() + vis_ftanks[4].pipe_chain.hide(true) + vis_ftanks[4].pipe_direct.show() end - tool_ctl.vis_ftanks[4].line.show() + vis_ftanks[4].line.show() else - tool_ctl.vis_ftanks[4].line.hide(true) + vis_ftanks[4].line.hide(true) end end + local function change_mode(mode) + tmp_cfg.FacilityTankMode = mode + tool_ctl.vis_draw(mode) + end + local tank_modes = { "Mode 1", "Mode 2", "Mode 3", "Mode 4", "Mode 5", "Mode 6", "Mode 7", "Mode 8" } - local tank_mode = RadioButton{parent=svr_c_5,x=1,y=4,callback=tool_ctl.vis_draw,default=math.max(1,ini_cfg.FacilityTankMode),options=tank_modes,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow} + local tank_mode = RadioButton{parent=svr_c_5,x=1,y=4,callback=change_mode,default=math.max(1,ini_cfg.FacilityTankMode),options=tank_modes,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow} --#endregion - local function submit_mode() - tmp_cfg.FacilityTankMode = tank_mode.get_value() - svr_pane.set_value(7) - end - PushButton{parent=svr_c_5,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=svr_c_5,x=44,y=14,text="Next \x1a",callback=submit_mode,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=svr_c_5,x=44,y=14,text="Next \x1a",callback=function()svr_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=svr_c_5,x=8,y=14,min_width=7,text="About",callback=function()svr_pane.set_value(6)end,fg_bg=cpair(colors.black,colors.lightBlue),active_fg_bg=btn_act_fg_bg} diff --git a/supervisor/startup.lua b/supervisor/startup.lua index edc60f1..885dd09 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.4.0" +local SUPERVISOR_VERSION = "v1.4.1" local println = util.println local println_ts = util.println_ts From 715765d442d8145c0ef8e1b3da2db6ff0047b349 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Tue, 16 Jul 2024 18:07:37 -0400 Subject: [PATCH 02/21] #512 increased clarity of peripheral assignments --- rtu/configure.lua | 67 +++++++++++++---------------------------------- rtu/startup.lua | 2 +- 2 files changed, 19 insertions(+), 50 deletions(-) diff --git a/rtu/configure.lua b/rtu/configure.lua index e34315c..66dd817 100644 --- a/rtu/configure.lua +++ b/rtu/configure.lua @@ -158,7 +158,6 @@ local tool_ctl = { p_idx = nil, ---@type graphics_element p_unit = nil, ---@type graphics_element p_assign_btn = nil, ---@type graphics_element - p_assign_end = nil, ---@type graphics_element p_desc = nil, ---@type graphics_element p_desc_ext = nil, ---@type graphics_element p_err = nil, ---@type graphics_element @@ -828,53 +827,35 @@ local function config_view(display) tool_ctl.p_name_msg.set_value("Configuring peripheral on '" .. name .. "':") tool_ctl.p_desc_ext.set_value("") - if type == "boilerValve" then - tool_ctl.p_prompt.set_value("This is the # boiler for reactor unit # .") - tool_ctl.p_idx.show() - tool_ctl.p_idx.redraw() + local function reposition(prompt, idx_x, idx_max, unit_x, unit_y, desc_y) + tool_ctl.p_prompt.set_value(prompt) + tool_ctl.p_idx.reposition(idx_x, 4) tool_ctl.p_idx.enable() - tool_ctl.p_idx.set_max(2) - tool_ctl.p_unit.reposition(44, 4) + tool_ctl.p_idx.set_max(idx_max) + tool_ctl.p_idx.show() + tool_ctl.p_unit.reposition(unit_x, unit_y) tool_ctl.p_unit.enable() tool_ctl.p_unit.show() + tool_ctl.p_desc.reposition(1, desc_y) + end + + if type == "boilerValve" then + reposition("This is reactor unit # 's # boiler.", 31, 2, 23, 4, 7) tool_ctl.p_assign_btn.hide(true) - tool_ctl.p_assign_end.hide(true) - tool_ctl.p_desc.reposition(1, 7) tool_ctl.p_desc.set_value("Each unit can have at most 2 boilers. Boiler #1 shows up first on the main display, followed by boiler #2 below it. These numberings are independent of which RTU they are connected to. For example, one RTU can have boiler #1 and another can have #2, but both cannot have #1.") elseif type == "turbineValve" then - tool_ctl.p_prompt.set_value("This is the # turbine for reactor unit # .") - tool_ctl.p_idx.show() - tool_ctl.p_idx.redraw() - tool_ctl.p_idx.enable() - tool_ctl.p_idx.set_max(3) - tool_ctl.p_unit.reposition(45, 4) - tool_ctl.p_unit.enable() - tool_ctl.p_unit.show() + reposition("This is reactor unit # 's # turbine.", 31, 3, 23, 4, 7) tool_ctl.p_assign_btn.hide(true) - tool_ctl.p_assign_end.hide(true) - tool_ctl.p_desc.reposition(1, 7) tool_ctl.p_desc.set_value("Each unit can have at most 3 turbines. Turbine #1 shows up first on the main display, followed by #2 then #3 below it. These numberings are independent of which RTU they are connected to. For example, one RTU can have turbine #1 and another can have #2, but both cannot have #1.") elseif type == "solarNeutronActivator" then + reposition("This SNA is for reactor unit # .", 46, 1, 31, 4, 7) tool_ctl.p_idx.hide() - tool_ctl.p_prompt.set_value("This SNA is for reactor unit # .") - tool_ctl.p_unit.reposition(31, 4) - tool_ctl.p_unit.enable() - tool_ctl.p_unit.show() tool_ctl.p_assign_btn.hide(true) - tool_ctl.p_assign_end.hide(true) tool_ctl.p_desc_ext.set_value("Before adding lots of SNAs: multiply the \"PEAK\" rate on the flow monitor (after connecting at least 1 SNA) by 10 to get the mB/t of waste that they can process. Enough SNAs to provide 2x to 3x of your max burn rate should be a good margin to catch up after night or cloudy weather. Too many devices (such as SNAs) on one RTU can cause lag.") elseif type == "dynamicValve" then - tool_ctl.p_prompt.set_value("This is the # dynamic tank for...") + reposition("This is the below system's # dynamic tank.", 29, 4, 17, 6, 8) tool_ctl.p_assign_btn.show() tool_ctl.p_assign_btn.redraw() - tool_ctl.p_assign_end.show() - tool_ctl.p_assign_end.redraw() - tool_ctl.p_idx.show() - tool_ctl.p_idx.redraw() - tool_ctl.p_idx.set_max(4) - tool_ctl.p_unit.reposition(18, 6) - tool_ctl.p_unit.enable() - tool_ctl.p_unit.show() if tool_ctl.p_assign_btn.get_value() == 1 then tool_ctl.p_idx.enable() @@ -885,22 +866,12 @@ local function config_view(display) tool_ctl.p_unit.enable() end - tool_ctl.p_desc.reposition(1, 8) tool_ctl.p_desc.set_value("Each reactor unit can have at most 1 tank and the facility can have at most 4. Each facility tank must have a unique # 1 through 4, regardless of where it is connected. Only a total of 4 tanks can be displayed on the flow monitor.") elseif type == "environmentDetector" then - tool_ctl.p_prompt.set_value("This is the # environment detector for...") + reposition("This is the below system's # env. detector.", 29, 99, 17, 6, 8) tool_ctl.p_assign_btn.show() tool_ctl.p_assign_btn.redraw() - tool_ctl.p_assign_end.show() - tool_ctl.p_assign_end.redraw() - tool_ctl.p_idx.show() - tool_ctl.p_idx.redraw() - tool_ctl.p_idx.set_max(99) - tool_ctl.p_unit.reposition(18, 6) - tool_ctl.p_unit.enable() - tool_ctl.p_unit.show() if tool_ctl.p_assign_btn.get_value() == 1 then tool_ctl.p_unit.disable() else tool_ctl.p_unit.enable() end - tool_ctl.p_desc.reposition(1, 8) tool_ctl.p_desc.set_value("You can connect more than one environment detector for a particular unit or the facility. In that case, the maximum radiation reading from those assigned to that particular unit or the facility will be used for alarms and display.") elseif type == "inductionPort" or type == "spsPort" then local dev = tri(type == "inductionPort", "induction matrix", "SPS") @@ -908,7 +879,6 @@ local function config_view(display) tool_ctl.p_unit.hide(true) tool_ctl.p_prompt.set_value("This is the " .. dev .. " for the facility.") tool_ctl.p_assign_btn.hide(true) - tool_ctl.p_assign_end.hide(true) tool_ctl.p_desc.reposition(1, 7) tool_ctl.p_desc.set_value("There can only be one of these devices per SCADA network, so it will be assigned as the sole " .. dev .. " for the facility. There must only be one of these across all the RTUs you have.") else @@ -965,11 +935,10 @@ local function config_view(display) tool_ctl.p_name_msg = TextBox{parent=peri_c_4,x=1,y=1,height=2,text=""} tool_ctl.p_prompt = TextBox{parent=peri_c_4,x=1,y=4,height=2,text=""} - tool_ctl.p_idx = NumberField{parent=peri_c_4,x=14,y=4,width=4,max_chars=2,min=1,max=2,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} - tool_ctl.p_assign_btn = RadioButton{parent=peri_c_4,x=1,y=5,default=1,options={"the facility.","a unit. (unit #"},callback=function(v)tool_ctl.p_assign(v)end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.purple} - tool_ctl.p_assign_end = TextBox{parent=peri_c_4,x=22,y=6,height=6,width=1,text=")"} + tool_ctl.p_idx = NumberField{parent=peri_c_4,x=31,y=4,width=4,max_chars=2,min=1,max=2,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + tool_ctl.p_assign_btn = RadioButton{parent=peri_c_4,x=1,y=5,default=1,options={"the facility","reactor unit #"},callback=function(v)tool_ctl.p_assign(v)end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.purple} - tool_ctl.p_unit = NumberField{parent=peri_c_4,x=44,y=4,width=4,max_chars=2,min=1,max=4,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + tool_ctl.p_unit = NumberField{parent=peri_c_4,x=23,y=4,width=4,max_chars=2,min=1,max=4,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} tool_ctl.p_unit.disable() function tool_ctl.p_assign(opt) diff --git a/rtu/startup.lua b/rtu/startup.lua index c5000ea..e0f1616 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.10.1" +local RTU_VERSION = "v1.10.2" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE From 3afc1e6cfa1744638f403644d94f8634a9aeb605 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sat, 20 Jul 2024 18:14:59 +0000 Subject: [PATCH 03/21] #512 rtu help text updates --- rtu/configure.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtu/configure.lua b/rtu/configure.lua index 66dd817..8d20899 100644 --- a/rtu/configure.lua +++ b/rtu/configure.lua @@ -842,11 +842,11 @@ local function config_view(display) if type == "boilerValve" then reposition("This is reactor unit # 's # boiler.", 31, 2, 23, 4, 7) tool_ctl.p_assign_btn.hide(true) - tool_ctl.p_desc.set_value("Each unit can have at most 2 boilers. Boiler #1 shows up first on the main display, followed by boiler #2 below it. These numberings are independent of which RTU they are connected to. For example, one RTU can have boiler #1 and another can have #2, but both cannot have #1.") + tool_ctl.p_desc.set_value("Each unit can have at most 2 boilers. Boiler #1 shows up first on the main display, followed by boiler #2 below it. The numberings are per unit (unit 1 and unit 2 would both have a boiler #1 if each had one boiler) and can be split amongst multiple RTUs (one has #1, another has #2).") elseif type == "turbineValve" then reposition("This is reactor unit # 's # turbine.", 31, 3, 23, 4, 7) tool_ctl.p_assign_btn.hide(true) - tool_ctl.p_desc.set_value("Each unit can have at most 3 turbines. Turbine #1 shows up first on the main display, followed by #2 then #3 below it. These numberings are independent of which RTU they are connected to. For example, one RTU can have turbine #1 and another can have #2, but both cannot have #1.") + tool_ctl.p_desc.set_value("Each unit can have at most 3 turbines. Turbine #1 shows up first on the main display, followed by #2 then #3 below it. The numberings are per unit (unit 1 and unit 2 would both have a turbine #1) and can be split amongst multiple RTUs (one has #1, another has #2).") elseif type == "solarNeutronActivator" then reposition("This SNA is for reactor unit # .", 46, 1, 31, 4, 7) tool_ctl.p_idx.hide() From fb5f3b94740f558f3b37c5db3b95e3a995478c25 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sat, 20 Jul 2024 18:17:36 +0000 Subject: [PATCH 04/21] #363 work on PLC self-check --- reactor-plc/configure.lua | 57 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index 582a2da..fd454dd 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -3,6 +3,7 @@ -- local log = require("scada-common.log") +local ppm = require("scada-common.ppm") local rsio = require("scada-common.rsio") local tcd = require("scada-common.tcd") local util = require("scada-common.util") @@ -67,6 +68,7 @@ local tool_ctl = { jumped_to_color = false, view_cfg = nil, ---@type graphics_element + self_check = nil, ---@type graphics_element color_cfg = nil, ---@type graphics_element color_next = nil, ---@type graphics_element color_apply = nil, ---@type graphics_element @@ -179,10 +181,11 @@ local function config_view(display) local clr_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 check_sys = Div{parent=root_pane_div,x=1,y=1} - local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,clr_cfg,summary,changelog}} + local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,clr_cfg,summary,changelog,check_sys}} - -- Main Page + --#region Main Page local y_start = 5 @@ -216,14 +219,18 @@ local function config_view(display) 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} + tool_ctl.self_check = PushButton{parent=main_page,x=10,y=17,min_width=12,text="Self-Check",callback=function()main_pane.set_value(8)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.orange,colors.white)} tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} if not tool_ctl.has_config then tool_ctl.view_cfg.disable() + tool_ctl.self_check.disable() tool_ctl.color_cfg.disable() end + --#endregion + --#region PLC local plc_c_1 = Div{parent=plc_cfg,x=2,y=4,width=49} @@ -656,7 +663,7 @@ local function config_view(display) --#endregion - -- Config Change Log + --#region Config Change Log local cl = Div{parent=changelog,x=2,y=4,width=49} @@ -675,6 +682,50 @@ 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} + --#endregion + + --#region Self-Check + + local sc = Div{parent=check_sys,x=2,y=4,width=49} + + TextBox{parent=check_sys,x=1,y=2,text=" Reactor PLC Self-Check",fg_bg=bw_fg_bg} + + local sc_log = ListBox{parent=sc,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 check_msg(msg, success, fail_msg) + local e = TextBox{parent=sc_log,text=msg,fg_bg=bw_fg_bg} + TextBox{parent=sc_log,x=e.get_x()+e.get_width(),y=e.get_y(),text=tri(success,"PASS","FAIL"),fg_bg=tri(success,cpair(colors.green,colors._INHERIT),cpair(colors.red,colors._INHERIT))} + return success + end + + local function self_check() + sc_log.remove_all() + ppm.mount_all() + + local reactor = ppm.get_fission_reactor() + + if not check_msg("> check wireless/ender modem...", ppm.get_wireless_modem() ~= nil) then + TextBox{parent=sc_log,x=3,text="you must connect an ender or wireless modem to the reactor PLC",fg_bg=cpair(colors.gray,colors.white)} + end + + if not check_msg("> check fission reactor present...", reactor ~= nil) then + TextBox{parent=sc_log,x=3,text="please connect the reactor PLC to the reactor's fission reactor logic adapter",fg_bg=cpair(colors.gray,colors.white)} + end + + if not check_msg("> check fission reactor formed...", reactor and reactor.isFormed()) then + TextBox{parent=sc_log,x=3,text="ensure the fission reactor multiblock is formed",fg_bg=cpair(colors.gray,colors.white)} + end + + TextBox{parent=sc_log,text="attempting connection to the supervisor...",fg_bg=cpair(colors.gray,colors.white)} + + if not check_msg("> check supervisor connection...", reactor and reactor.isFormed()) then + TextBox{parent=sc_log,x=3,text="ensure the fission reactor multiblock is formed",fg_bg=cpair(colors.gray,colors.white)} + end + end + + PushButton{parent=sc,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=sc,x=1,y=40,min_width=10,text="Run Test",callback=self_check,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + -- set tool functions now that we have the elements function tool_ctl.set_networked(enable) From fd06730e46ca2392d22adce3407ff2eccda9a37c Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 22 Jul 2024 23:44:12 -0400 Subject: [PATCH 05/21] #363 PLC configurator self check WIP --- reactor-plc/configure.lua | 181 +++++++++++++++++++++++++++++++++----- reactor-plc/startup.lua | 2 +- 2 files changed, 158 insertions(+), 25 deletions(-) diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index fd454dd..e5c465d 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -2,7 +2,9 @@ -- 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 rsio = require("scada-common.rsio") local tcd = require("scada-common.tcd") @@ -30,6 +32,11 @@ local IndLight = require("graphics.elements.indicators.light") 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 @@ -58,14 +65,21 @@ 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 btn_dis_fg_bg = cpair(colors.lightGray, colors.white) ---@class _plc_cfg_tool_ctl local tool_ctl = { + nic = nil, ---@type nic + net_listen = false, + sv_addr = comms.BROADCAST, + sv_seq_num = util.time_ms() * 10, + ask_config = false, has_config = false, viewing_config = false, importing_legacy = false, jumped_to_color = false, + self_check_pass = true, view_cfg = nil, ---@type graphics_element self_check = nil, ---@type graphics_element @@ -73,12 +87,14 @@ local tool_ctl = { color_next = nil, ---@type graphics_element color_apply = nil, ---@type graphics_element settings_apply = nil, ---@type graphics_element + run_test_btn = 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 + self_check_msg = nil, ---@type function show_auth_key = nil, ---@type function show_key_btn = nil, ---@type graphics_element @@ -129,6 +145,68 @@ local fields = { { "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD } } +-- send a 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(settings_cfg.SVR_Channel, settings_cfg.PLC_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() ~= settings_cfg.PLC_Channel then + error_msg = "error: unknown receive channel" + elseif packet.scada_frame.remote_channel() == settings_cfg.SVR_Channel and packet.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then + if packet.type == MGMT_TYPE.ESTABLISH then + if packet.length == 1 then + local est_ack = packet.data[1] + + if est_ack== ESTABLISH_ACK.ALLOW then + tool_ctl.self_check_msg(nil, true, "") + tool_ctl.sv_addr = packet.scada_frame.src_addr() + send_sv(MGMT_TYPE.CLOSE, {}) + elseif est_ack == ESTABLISH_ACK.DENY then + error_msg = "error: supervisor connection denied" + elseif est_ack == ESTABLISH_ACK.COLLISION then + error_msg = "another reactor PLC is connected with this reactor unit ID" + elseif est_ack == ESTABLISH_ACK.BAD_VERSION then + error_msg = "reactor PLC comms version does not match supervisor comms version, make sure both devices are up-to-date (ccmsi update ...)" + 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 + tool_ctl.run_test_btn.enable() + + if error_msg then + tool_ctl.self_check_msg(nil, false, error_msg) + end +end + +-- handle supervisor connection failure +local function handle_timeout() + tool_ctl.net_listen = false + tool_ctl.run_test_btn.enable() + tool_ctl.self_check_msg(nil, false, "make sure your supervisor is running, your channels are correct, trusted ranges are set properly (if enabled), facility keys match (if set), and if you are using wireless modems rather than ender modems, that your devices are close together in the same dimension") +end + local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" } local side_options_map = { "top", "bottom", "left", "right", "front", "back" } local color_options = { "Red", "Orange", "Yellow", "Lime", "Green", "Cyan", "Light Blue", "Blue", "Purple", "Magenta", "Pink", "White", "Light Gray", "Gray", "Black", "Brown" } @@ -209,7 +287,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=btn_dis_fg_bg} local function jump_color() tool_ctl.jumped_to_color = true @@ -220,7 +298,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} tool_ctl.self_check = PushButton{parent=main_page,x=10,y=17,min_width=12,text="Self-Check",callback=function()main_pane.set_value(8)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.orange,colors.white)} - tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)} + tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg} PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} if not tool_ctl.has_config then @@ -618,6 +696,8 @@ local function config_view(display) try_set(c_mode, ini_cfg.ColorMode) tool_ctl.view_cfg.enable() + tool_ctl.self_check.enable() + tool_ctl.color_cfg.enable() if tool_ctl.importing_legacy then tool_ctl.importing_legacy = false @@ -631,7 +711,7 @@ local function config_view(display) 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.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=btn_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,text="Settings saved!"} @@ -692,39 +772,82 @@ local function config_view(display) local sc_log = ListBox{parent=sc,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 check_msg(msg, success, fail_msg) - local e = TextBox{parent=sc_log,text=msg,fg_bg=bw_fg_bg} - TextBox{parent=sc_log,x=e.get_x()+e.get_width(),y=e.get_y(),text=tri(success,"PASS","FAIL"),fg_bg=tri(success,cpair(colors.green,colors._INHERIT),cpair(colors.red,colors._INHERIT))} - return success + local last_check = { nil, nil } + + function tool_ctl.self_check_msg(msg, success, fail_msg) + if type(msg) == "string" then + last_check[1] = Div{parent=sc_log,height=1} + local e = TextBox{parent=last_check[1],text=msg,fg_bg=bw_fg_bg} + last_check[2] = e.get_x()+string.len(msg) + end + + if type(fail_msg) == "string" then + TextBox{parent=last_check[1],x=last_check[2],y=1,text=tri(success,"PASS","FAIL"),fg_bg=tri(success,cpair(colors.green,colors._INHERIT),cpair(colors.red,colors._INHERIT))} + + if not success then + local fail = Div{parent=sc_log,height=#util.strwrap(fail_msg, 46)} + TextBox{parent=fail,x=3,text=fail_msg,fg_bg=cpair(colors.gray,colors.white)} + end + + tool_ctl.self_check_pass = tool_ctl.self_check_pass and success + end end local function self_check() + tool_ctl.run_test_btn.disable() + sc_log.remove_all() ppm.mount_all() + tool_ctl.self_check_pass = true + + local modem = ppm.get_wireless_modem() local reactor = ppm.get_fission_reactor() - if not check_msg("> check wireless/ender modem...", ppm.get_wireless_modem() ~= nil) then - TextBox{parent=sc_log,x=3,text="you must connect an ender or wireless modem to the reactor PLC",fg_bg=cpair(colors.gray,colors.white)} + tool_ctl.self_check_msg("> check wireless/ender modem connected...", modem ~= nil, "you must connect an ender or wireless modem to the reactor PLC") + tool_ctl.self_check_msg("> check fission reactor connected...", reactor ~= nil, "please connect the reactor PLC to the reactor's fission reactor logic adapter") + tool_ctl.self_check_msg("> check fission reactor formed...") + -- this consumes events, but that is fine here + tool_ctl.self_check_msg(nil, reactor and reactor.isFormed(), "ensure the fission reactor multiblock is formed") + + if modem then + tool_ctl.self_check_msg("> check supervisor connection...") + + tool_ctl.nic = network.nic(modem) + + tool_ctl.nic.closeAll() + tool_ctl.nic.open(settings_cfg.PLC_Channel) + + tool_ctl.sv_addr = comms.BROADCAST + tool_ctl.net_listen = true + + send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.PLC, settings_cfg.UnitID }) + + tcd.dispatch_unique(8, handle_timeout) + else + tool_ctl.run_test_btn.enable() end - if not check_msg("> check fission reactor present...", reactor ~= nil) then - TextBox{parent=sc_log,x=3,text="please connect the reactor PLC to the reactor's fission reactor logic adapter",fg_bg=cpair(colors.gray,colors.white)} - end - - if not check_msg("> check fission reactor formed...", reactor and reactor.isFormed()) then - TextBox{parent=sc_log,x=3,text="ensure the fission reactor multiblock is formed",fg_bg=cpair(colors.gray,colors.white)} - end - - TextBox{parent=sc_log,text="attempting connection to the supervisor...",fg_bg=cpair(colors.gray,colors.white)} - - if not check_msg("> check supervisor connection...", reactor and reactor.isFormed()) then - TextBox{parent=sc_log,x=3,text="ensure the fission reactor multiblock is formed",fg_bg=cpair(colors.gray,colors.white)} + if tool_ctl.self_check_pass then + TextBox{parent=sc_log,text="> all tests passed!",fg_bg=cpair(colors.blue,colors._INHERIT)} + TextBox{parent=sc_log,text=""} + local more = Div{parent=sc_log,height=3,fg_bg=cpair(colors.gray,colors._INHERIT)} + TextBox{parent=more,text="if you still have a problem:"} + TextBox{parent=more,text="- check the wiki on GitHub"} + TextBox{parent=more,text="- ask for help on GitHub discussions or Discord"} end end - PushButton{parent=sc,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=sc,x=1,y=40,min_width=10,text="Run Test",callback=self_check,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + local function exit_self_check() + tcd.abort(handle_timeout) + tool_ctl.net_listen = false + tool_ctl.run_test_btn.enable() + sc_log.remove_all() + main_pane.set_value(1) + end + + PushButton{parent=sc,x=1,y=14,text="\x1b Back",callback=exit_self_check,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + tool_ctl.run_test_btn = PushButton{parent=sc,x=40,y=14,min_width=10,text="Run Test",callback=self_check,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg} -- set tool functions now that we have the elements @@ -853,7 +976,7 @@ function configurator.configure(ask_config) config_view(display) while true do - local event, param1, param2, param3 = util.pull_event() + local event, param1, param2, param3, param4, param5 = util.pull_event() -- handle event if event == "timer" then @@ -866,6 +989,16 @@ function configurator.configure(ask_config) if k_e then display.handle_key(k_e) end elseif event == "paste" then display.handle_paste(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 diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 6f6a27b..8293538 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.8.0" +local R_PLC_VERSION = "v1.8.1" local println = util.println local println_ts = util.println_ts From 1358d952690f395696c52b7e1df52c8e0aaf9224 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 22 Jul 2024 23:44:34 -0400 Subject: [PATCH 06/21] cc strings infinite loop mitigation --- scada-common/util.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scada-common/util.lua b/scada-common/util.lua index 6871baa..9d36e3a 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -24,7 +24,7 @@ local t_pack = table.pack local util = {} -- scada-common version -util.version = "1.4.0" +util.version = "1.4.1" util.TICK_TIME_S = 0.05 util.TICK_TIME_MS = 50 @@ -113,9 +113,12 @@ end -- wrap a string into a table of lines ---@nodiscard ---@param str string ----@param limit integer line limit +---@param limit integer line limit, must be greater than 0 ---@return table lines -function util.strwrap(str, limit) return cc_strings.wrap(str, limit) end +function util.strwrap(str, limit) + assert(limit > 0, "util.strwrap() limit not greater than 0") + return cc_strings.wrap(str, limit) +end -- concatenation with built-in to string ---@nodiscard From b61867be3c01aff660346bcaabc361d60d75ca45 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 22 Jul 2024 23:44:56 -0400 Subject: [PATCH 07/21] updated RTU configurator change log --- rtu/configure.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rtu/configure.lua b/rtu/configure.lua index 8d20899..4cb3ef0 100644 --- a/rtu/configure.lua +++ b/rtu/configure.lua @@ -84,7 +84,8 @@ assert(#PORT_DSGN == rsio.NUM_PORTS) local changes = { { "v1.7.9", { "ConnTimeout can now have a fractional part" } }, { "v1.7.15", { "Added front panel UI theme", "Added color accessibility modes" } }, - { "v1.9.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } } + { "v1.9.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } }, + { "v1.10.2", { "Re-organized peripheral configuration UI, resulting in some input fields being re-ordered" } } } ---@class rtu_rs_definition From 03bbf8a891d0a3374a91baf82a88fb3e42f195c9 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 22 Jul 2024 23:45:25 -0400 Subject: [PATCH 08/21] updated coordinator configurator connection sequence number logic to match new system --- coordinator/configure.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index b16a43d..8c764d9 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -70,7 +70,7 @@ local tool_ctl = { nic = nil, ---@type nic net_listen = false, sv_addr = comms.BROADCAST, - sv_seq_num = 0, + sv_seq_num = util.time_ms() * 10, sv_cool_conf = nil, ---@type table list of boiler & turbine counts show_sv_cfg = nil, ---@type function @@ -1122,7 +1122,6 @@ local function config_view(display) 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 }) From 3406d12681e0f29731347864011c85456cd26255 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 24 Jul 2024 22:42:14 -0400 Subject: [PATCH 09/21] #363 check config --- reactor-plc/configure.lua | 9 +++++-- reactor-plc/plc.lua | 54 ++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index e5c465d..cf7a612 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -10,6 +10,8 @@ local rsio = require("scada-common.rsio") local tcd = require("scada-common.tcd") local util = require("scada-common.util") +local plc = require("reactor-plc.plc") + local core = require("graphics.core") local themes = require("graphics.themes") @@ -270,7 +272,7 @@ local function config_view(display) TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Reactor PLC 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)} + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had 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 @@ -803,6 +805,7 @@ local function config_view(display) local modem = ppm.get_wireless_modem() local reactor = ppm.get_fission_reactor() + local valid_cfg = plc.validate_config(settings_cfg) tool_ctl.self_check_msg("> check wireless/ender modem connected...", modem ~= nil, "you must connect an ender or wireless modem to the reactor PLC") tool_ctl.self_check_msg("> check fission reactor connected...", reactor ~= nil, "please connect the reactor PLC to the reactor's fission reactor logic adapter") @@ -810,7 +813,9 @@ local function config_view(display) -- this consumes events, but that is fine here tool_ctl.self_check_msg(nil, reactor and reactor.isFormed(), "ensure the fission reactor multiblock is formed") - if modem then + tool_ctl.self_check_msg("> check configuration...", valid_cfg, "go through Configure System again and apply settings to repair any corrupted or missing settings") + + if valid_cfg and modem then tool_ctl.self_check_msg("> check supervisor connection...") tool_ctl.nic = network.nic(modem) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index c89974f..5300bd9 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -57,41 +57,47 @@ function plc.load_config() config.FrontPanelTheme = settings.get("FrontPanelTheme") config.ColorMode = settings.get("ColorMode") + return plc.validate_config(config) +end + +-- validate a PLC configuration +---@param cfg plc_config +function plc.validate_config(cfg) local cfv = util.new_validator() - cfv.assert_type_bool(config.Networked) - cfv.assert_type_int(config.UnitID) - cfv.assert_type_bool(config.EmerCoolEnable) + cfv.assert_type_bool(cfg.Networked) + cfv.assert_type_int(cfg.UnitID) + cfv.assert_type_bool(cfg.EmerCoolEnable) - if config.Networked == true then - cfv.assert_channel(config.SVR_Channel) - cfv.assert_channel(config.PLC_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 cfg.Networked == true then + cfv.assert_channel(cfg.SVR_Channel) + cfv.assert_channel(cfg.PLC_Channel) + cfv.assert_type_num(cfg.ConnTimeout) + cfv.assert_min(cfg.ConnTimeout, 2) + cfv.assert_type_num(cfg.TrustedRange) + cfv.assert_min(cfg.TrustedRange, 0) + cfv.assert_type_str(cfg.AuthKey) - if type(config.AuthKey) == "string" then - local len = string.len(config.AuthKey) + if type(cfg.AuthKey) == "string" then + local len = string.len(cfg.AuthKey) cfv.assert(len == 0 or len >= 8) end 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) + cfv.assert_type_int(cfg.LogMode) + cfv.assert_range(cfg.LogMode, 0, 1) + cfv.assert_type_str(cfg.LogPath) + cfv.assert_type_bool(cfg.LogDebug) - cfv.assert_type_int(config.FrontPanelTheme) - cfv.assert_range(config.FrontPanelTheme, 1, 2) - cfv.assert_type_int(config.ColorMode) - cfv.assert_range(config.ColorMode, 1, themes.COLOR_MODE.NUM_MODES) + cfv.assert_type_int(cfg.FrontPanelTheme) + cfv.assert_range(cfg.FrontPanelTheme, 1, 2) + cfv.assert_type_int(cfg.ColorMode) + cfv.assert_range(cfg.ColorMode, 1, themes.COLOR_MODE.NUM_MODES) -- check emergency coolant configuration if enabled - if config.EmerCoolEnable then - cfv.assert_eq(rsio.is_valid_side(config.EmerCoolSide), true) - cfv.assert_eq(config.EmerCoolColor == nil or rsio.is_color(config.EmerCoolColor), true) + if cfg.EmerCoolEnable then + cfv.assert_eq(rsio.is_valid_side(cfg.EmerCoolSide), true) + cfv.assert_eq(cfg.EmerCoolColor == nil or rsio.is_color(cfg.EmerCoolColor), true) end return cfv.valid() From ec107929bce79c4889a94210e6ad8dc8d402329b Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sat, 27 Jul 2024 00:27:38 +0000 Subject: [PATCH 10/21] #528 reactor PLC configurator cleanup --- reactor-plc/config/check.lua | 230 ++++++++++ reactor-plc/config/system.lua | 618 ++++++++++++++++++++++++++ reactor-plc/configure.lua | 794 ++-------------------------------- 3 files changed, 882 insertions(+), 760 deletions(-) create mode 100644 reactor-plc/config/check.lua create mode 100644 reactor-plc/config/system.lua diff --git a/reactor-plc/config/check.lua b/reactor-plc/config/check.lua new file mode 100644 index 0000000..de1d296 --- /dev/null +++ b/reactor-plc/config/check.lua @@ -0,0 +1,230 @@ +local comms = require("scada-common.comms") +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 plc = require("reactor-plc.plc") + +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +local ListBox = require("graphics.elements.listbox") +local TextBox = require("graphics.elements.textbox") + +local PushButton = require("graphics.elements.controls.push_button") + +local tri = util.trinary + +local cpair = core.cpair + +local PROTOCOL = comms.PROTOCOL +local DEVICE_TYPE = comms.DEVICE_TYPE +local ESTABLISH_ACK = comms.ESTABLISH_ACK +local MGMT_TYPE = comms.MGMT_TYPE + +local self = { + nic = nil, ---@type nic + net_listen = false, + sv_addr = comms.BROADCAST, + sv_seq_num = util.time_ms() * 10, + + self_check_pass = true, + + settings = nil, ---@type plc_config + + run_test_btn = nil, ---@type graphics_element + self_check_msg = nil ---@type function +} + +-- send a 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(self.sv_addr, self.sv_seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) + + self.nic.transmit(self.settings.SVR_Channel, self.settings.PLC_Channel, s_pkt) + self.sv_seq_num = self.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() ~= self.settings.PLC_Channel then + error_msg = "error: unknown receive channel" + elseif packet.scada_frame.remote_channel() == self.settings.SVR_Channel and packet.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then + if packet.type == MGMT_TYPE.ESTABLISH then + if packet.length == 1 then + local est_ack = packet.data[1] + + if est_ack== ESTABLISH_ACK.ALLOW then + self.self_check_msg(nil, true, "") + self.sv_addr = packet.scada_frame.src_addr() + send_sv(MGMT_TYPE.CLOSE, {}) + elseif est_ack == ESTABLISH_ACK.DENY then + error_msg = "error: supervisor connection denied" + elseif est_ack == ESTABLISH_ACK.COLLISION then + error_msg = "another reactor PLC is connected with this reactor unit ID" + elseif est_ack == ESTABLISH_ACK.BAD_VERSION then + error_msg = "reactor PLC comms version does not match supervisor comms version, make sure both devices are up-to-date (ccmsi update ...)" + 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 + + self.net_listen = false + self.run_test_btn.enable() + + if error_msg then + self.self_check_msg(nil, false, error_msg) + end +end + +-- handle supervisor connection failure +local function handle_timeout() + self.net_listen = false + self.run_test_btn.enable() + self.self_check_msg(nil, false, "make sure your supervisor is running, your channels are correct, trusted ranges are set properly (if enabled), facility keys match (if set), and if you are using wireless modems rather than ender modems, that your devices are close together in the same dimension") +end + +-- execute the self-check +---@param sc_log graphics_element +local function self_check(sc_log) + self.run_test_btn.disable() + + sc_log.remove_all() + ppm.mount_all() + + self.self_check_pass = true + + local modem = ppm.get_wireless_modem() + local reactor = ppm.get_fission_reactor() + local valid_cfg = plc.validate_config(self.settings) + + self.self_check_msg("> check wireless/ender modem connected...", modem ~= nil, "you must connect an ender or wireless modem to the reactor PLC") + self.self_check_msg("> check fission reactor connected...", reactor ~= nil, "please connect the reactor PLC to the reactor's fission reactor logic adapter") + self.self_check_msg("> check fission reactor formed...") + -- this consumes events, but that is fine here + self.self_check_msg(nil, reactor and reactor.isFormed(), "ensure the fission reactor multiblock is formed") + + self.self_check_msg("> check configuration...", valid_cfg, "go through Configure System again and apply settings to repair any corrupted or missing settings") + + if valid_cfg and modem then + self.self_check_msg("> check supervisor connection...") + + self.nic = network.nic(modem) + + self.nic.closeAll() + self.nic.open(self.settings.PLC_Channel) + + self.sv_addr = comms.BROADCAST + self.net_listen = true + + send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.PLC, self.settings.UnitID }) + + tcd.dispatch_unique(8, handle_timeout) + else + self.run_test_btn.enable() + end + + if self.self_check_pass then + TextBox{parent=sc_log,text="> all tests passed!",fg_bg=cpair(colors.blue,colors._INHERIT)} + TextBox{parent=sc_log,text=""} + local more = Div{parent=sc_log,height=3,fg_bg=cpair(colors.gray,colors._INHERIT)} + TextBox{parent=more,text="if you still have a problem:"} + TextBox{parent=more,text="- check the wiki on GitHub"} + TextBox{parent=more,text="- ask for help on GitHub discussions or Discord"} + end +end + +-- exit self check back home +---@param sc_log graphics_element +---@param main_pane graphics_element +local function exit_self_check(sc_log, main_pane) + tcd.abort(handle_timeout) + self.net_listen = false + self.run_test_btn.enable() + sc_log.remove_all() + main_pane.set_value(1) +end + +local check = {} + +-- create the self-check view +---@param main_pane graphics_element +---@param settings_cfg plc_config +---@param check_sys graphics_element +---@param style table +function check.create(main_pane, settings_cfg, check_sys, style) + local bw_fg_bg = style.bw_fg_bg + local g_lg_fg_bg = style.g_lg_fg_bg + local nav_fg_bg = style.nav_fg_bg + local btn_act_fg_bg = style.btn_act_fg_bg + local btn_dis_fg_bg = style.btn_dis_fg_bg + + self.settings = settings_cfg + + local sc = Div{parent=check_sys,x=2,y=4,width=49} + + TextBox{parent=check_sys,x=1,y=2,text=" Reactor PLC Self-Check",fg_bg=bw_fg_bg} + + local sc_log = ListBox{parent=sc,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 last_check = { nil, nil } + + function self.self_check_msg(msg, success, fail_msg) + if type(msg) == "string" then + last_check[1] = Div{parent=sc_log,height=1} + local e = TextBox{parent=last_check[1],text=msg,fg_bg=bw_fg_bg} + last_check[2] = e.get_x()+string.len(msg) + end + + if type(fail_msg) == "string" then + TextBox{parent=last_check[1],x=last_check[2],y=1,text=tri(success,"PASS","FAIL"),fg_bg=tri(success,cpair(colors.green,colors._INHERIT),cpair(colors.red,colors._INHERIT))} + + if not success then + local fail = Div{parent=sc_log,height=#util.strwrap(fail_msg, 46)} + TextBox{parent=fail,x=3,text=fail_msg,fg_bg=cpair(colors.gray,colors.white)} + end + + self.self_check_pass = self.self_check_pass and success + end + end + + PushButton{parent=sc,x=1,y=14,text="\x1b Back",callback=function()exit_self_check(sc_log,main_pane)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + self.run_test_btn = PushButton{parent=sc,x=40,y=14,min_width=10,text="Run Test",callback=function()self_check(sc_log)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg} +end + +-- handle incoming modem messages +---@param side string +---@param sender integer +---@param reply_to integer +---@param message any +---@param distance integer +function check.receive_sv(side, sender, reply_to, message, distance) + if self.nic ~= nil and self.net_listen then + local s_pkt = self.nic.receive(side, sender, reply_to, message, distance) + + 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 +end + +return check diff --git a/reactor-plc/config/system.lua b/reactor-plc/config/system.lua new file mode 100644 index 0000000..49630b0 --- /dev/null +++ b/reactor-plc/config/system.lua @@ -0,0 +1,618 @@ +local log = require("scada-common.log") +local rsio = require("scada-common.rsio") +local util = require("scada-common.util") + +local core = require("graphics.core") +local themes = require("graphics.themes") + +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 IndLight = require("graphics.elements.indicators.light") + +local cpair = core.cpair + +local LEFT = core.ALIGN.LEFT +local RIGHT = core.ALIGN.RIGHT + +local self = { + importing_legacy = false, + + set_networked = nil, ---@type function + bundled_emcool = 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 = "" +} + +local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" } +local side_options_map = { "top", "bottom", "left", "right", "front", "back" } +local color_options = { "Red", "Orange", "Yellow", "Lime", "Green", "Cyan", "Light Blue", "Blue", "Purple", "Magenta", "Pink", "White", "Light Gray", "Gray", "Black", "Brown" } +local color_options_map = { colors.red, colors.orange, colors.yellow, colors.lime, colors.green, colors.cyan, colors.lightBlue, colors.blue, colors.purple, colors.magenta, colors.pink, colors.white, colors.lightGray, colors.gray, colors.black, colors.brown } + +-- convert text representation to index +---@param side string +local function side_to_idx(side) + for k, v in ipairs(side_options_map) do + if v == side then return k end + end +end + +-- convert color to index +---@param color color +local function color_to_idx(color) + for k, v in ipairs(color_options_map) do + if v == color then return k end + end +end + +local system = {} + +-- create the system configuration view +---@param tool_ctl _plc_cfg_tool_ctl +---@param main_pane graphics_element +---@param settings table +---@param divs table +---@param style table +---@param exit function +function system.create(tool_ctl, main_pane, settings, divs, style, exit) + ---@type plc_config, plc_config, plc_config, table, function + local settings_cfg, ini_cfg, tmp_cfg, fields, load_settings = table.unpack(settings) + + ---@type graphics_element, graphics_element, graphics_element, graphics_element, graphics_element + local plc_cfg, net_cfg, log_cfg, clr_cfg, summary = table.unpack(divs) + + local bw_fg_bg = style.bw_fg_bg + local g_lg_fg_bg = style.g_lg_fg_bg + local nav_fg_bg = style.nav_fg_bg + local btn_act_fg_bg = style.btn_act_fg_bg + local btn_dis_fg_bg = style.btn_dis_fg_bg + + --#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} + local plc_c_3 = Div{parent=plc_cfg,x=2,y=4,width=49} + local plc_c_4 = Div{parent=plc_cfg,x=2,y=4,width=49} + + local plc_pane = MultiPane{parent=plc_cfg,x=1,y=4,panes={plc_c_1,plc_c_2,plc_c_3,plc_c_4}} + + TextBox{parent=plc_cfg,x=1,y=2,text=" PLC Configuration",fg_bg=cpair(colors.black,colors.orange)} + + TextBox{parent=plc_c_1,x=1,y=1,text="Would you like to set this PLC as networked?"} + TextBox{parent=plc_c_1,x=1,y=3,height=4,text="If you have a supervisor, select the box. You will later be prompted to select the network configuration. If you instead want to use this as a standalone safety system, don't select the box.",fg_bg=g_lg_fg_bg} + + local networked = CheckBox{parent=plc_c_1,x=1,y=8,label="Networked",default=ini_cfg.Networked,box_fg_bg=cpair(colors.orange,colors.black)} + + local function submit_networked() + self.set_networked(networked.get_value()) + plc_pane.set_value(2) + end + + PushButton{parent=plc_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=plc_c_1,x=44,y=14,text="Next \x1a",callback=submit_networked,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=plc_c_2,x=1,y=1,text="Please enter the reactor unit ID for this PLC."} + TextBox{parent=plc_c_2,x=1,y=3,height=3,text="If this is a networked PLC, currently only IDs 1 through 4 are acceptable.",fg_bg=g_lg_fg_bg} + + TextBox{parent=plc_c_2,x=1,y=6,text="Unit #"} + local u_id = NumberField{parent=plc_c_2,x=7,y=6,width=5,max_chars=3,default=ini_cfg.UnitID,min=1,fg_bg=bw_fg_bg} + + local u_id_err = TextBox{parent=plc_c_2,x=8,y=14,width=35,text="Please set a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + function self.set_networked(enable) + tmp_cfg.Networked = enable + if enable then u_id.set_max(4) else u_id.set_max(999) end + end + + local function submit_id() + local unit_id = tonumber(u_id.get_value()) + if unit_id ~= nil then + u_id_err.hide(true) + tmp_cfg.UnitID = unit_id + plc_pane.set_value(3) + else u_id_err.show() end + end + + PushButton{parent=plc_c_2,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=plc_c_2,x=44,y=14,text="Next \x1a",callback=submit_id,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=plc_c_3,x=1,y=1,height=4,text="When networked, the supervisor takes care of emergency coolant via RTUs. However, you can configure independent emergency coolant via the PLC."} + TextBox{parent=plc_c_3,x=1,y=6,height=5,text="This independent control can be used with or without a supervisor. To configure, you would next select the interface of the redstone output connected to one or more mekanism pipes.",fg_bg=g_lg_fg_bg} + + local en_em_cool = CheckBox{parent=plc_c_3,x=1,y=11,label="Enable PLC Emergency Coolant Control",default=ini_cfg.EmerCoolEnable,box_fg_bg=cpair(colors.orange,colors.black)} + + local function next_from_plc() + if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(4) end + end + + local function submit_en_emcool() + tmp_cfg.EmerCoolEnable = en_em_cool.get_value() + if tmp_cfg.EmerCoolEnable then plc_pane.set_value(4) else next_from_plc() end + end + + PushButton{parent=plc_c_3,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=plc_c_3,x=44,y=14,text="Next \x1a",callback=submit_en_emcool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=plc_c_4,x=1,y=1,text="Emergency Coolant Redstone Output Side"} + local side = Radio2D{parent=plc_c_4,x=1,y=2,rows=2,columns=3,default=side_to_idx(ini_cfg.EmerCoolSide),options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.orange} + + TextBox{parent=plc_c_4,x=1,y=5,text="Bundled Redstone Configuration"} + local bundled = CheckBox{parent=plc_c_4,x=1,y=6,label="Is Bundled?",default=ini_cfg.EmerCoolColor~=nil,box_fg_bg=cpair(colors.orange,colors.black),callback=function(v)self.bundled_emcool(v)end} + local color = Radio2D{parent=plc_c_4,x=1,y=8,rows=4,columns=4,default=color_to_idx(ini_cfg.EmerCoolColor),options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg} + if ini_cfg.EmerCoolColor == nil then color.disable() end + + function self.bundled_emcool(en) if en then color.enable() else color.disable() end end + + local function submit_emcool() + tmp_cfg.EmerCoolSide = side_options_map[side.get_value()] + tmp_cfg.EmerCoolColor = util.trinary(bundled.get_value(), color_options_map[color.get_value()], nil) + next_from_plc() + end + + 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} + + --#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} + local net_c_3 = 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}} + + TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)} + + TextBox{parent=net_c_1,x=1,y=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 2 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,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=11,text="PLC Channel"} + local plc_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg} + TextBox{parent=net_c_1,x=9,y=12,height=4,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg} + + local chan_err = TextBox{parent=net_c_1,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_channels() + local svr_c = tonumber(svr_chan.get_value()) + local plc_c = tonumber(plc_chan.get_value()) + if svr_c ~= nil and plc_c ~= nil then + tmp_cfg.SVR_Channel = svr_c + tmp_cfg.PLC_Channel = plc_c + net_pane.set_value(2) + chan_err.hide(true) + elseif svr_c == nil then + chan_err.set_value("Please set the supervisor channel.") + chan_err.show() + else + chan_err.set_value("Please set the PLC channel.") + chan_err.show() + end + end + + PushButton{parent=net_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} + 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,text="Connection Timeout"} + local timeout = NumberField{parent=net_c_2,x=1,y=2,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=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg} + TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can 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=8,text="Trusted Range"} + local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg} + TextBox{parent=net_c_2,x=1,y=10,height=4,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} + + local p2_err = TextBox{parent=net_c_2,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} + + local function submit_ct_tr() + local timeout_val = tonumber(timeout.get_value()) + local range_val = tonumber(range.get_value()) + if timeout_val ~= nil and range_val ~= nil then + tmp_cfg.ConnTimeout = timeout_val + tmp_cfg.TrustedRange = range_val + net_pane.set_value(3) + p2_err.hide(true) + elseif timeout_val == nil then + p2_err.set_value("Please set the connection timeout.") + p2_err.show() + else + p2_err.set_value("Please set the trusted range.") + p2_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_ct_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=net_c_3,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_3,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_3,x=1,y=11,text="Facility Auth Key"} + local key, _, censor = TextField{parent=net_c_3,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_3,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_3,x=8,y=14,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(4) + key_err.hide(true) + else key_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_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=49} + + TextBox{parent=log_cfg,x=1,y=2,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)} + + TextBox{parent=log_c_1,x=1,y=1,text="Please configure logging below."} + + TextBox{parent=log_c_1,x=1,y=3,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,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,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.color_apply.hide(true) + tool_ctl.color_next.show() + main_pane.set_value(5) + else path_err.show() end + end + + local function back_from_log() + if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(2) end + end + + 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} + + --#endregion + + --#region Color Options + + local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49} + local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49} + local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49} + local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49} + + local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}} + + TextBox{parent=clr_cfg,x=1,y=2,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)} + + TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."} + TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg} + + TextBox{parent=clr_c_1,x=1,y=7,text="Front Panel Theme"} + local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta} + + TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."} + + TextBox{parent=clr_c_2,x=21,y=7,text="Preview"} + local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)} + _ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)} + _ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)} + local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true} + local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true} + + local function recolor(value) + local c = themes.smooth_stone.color_modes[value] + + if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then + b_off.hide() + g_off.show() + else + g_off.hide() + b_off.show() + end + + if #c == 0 then + for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end + else + term.setPaletteColor(colors.green, c[1].hex) + term.setPaletteColor(colors.yellow, c[2].hex) + term.setPaletteColor(colors.red, c[3].hex) + end + end + + TextBox{parent=clr_c_2,x=1,y=7,width=10,text="Color Mode"} + local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta} + + TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg} + + PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + local function back_from_colors() + main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 4)) + tool_ctl.jumped_to_color = false + recolor(1) + end + + local function show_access() + clr_pane.set_value(2) + recolor(c_mode.get_value()) + end + + local function submit_colors() + tmp_cfg.FrontPanelTheme = fp_theme.get_value() + tmp_cfg.ColorMode = c_mode.get_value() + + if tool_ctl.jumped_to_color then + settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme) + settings.set("ColorMode", tmp_cfg.ColorMode) + + if settings.save("/reactor-plc.settings") then + load_settings(settings_cfg, true) + load_settings(ini_cfg) + clr_pane.set_value(3) + else + clr_pane.set_value(4) + end + else + tool_ctl.gen_summary(tmp_cfg) + tool_ctl.viewing_config = false + self.importing_legacy = false + tool_ctl.settings_apply.show() + main_pane.set_value(6) + end + end + + PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg} + + tool_ctl.color_apply.hide(true) + + local function c_go_home() + main_pane.set_value(1) + clr_pane.set_value(1) + end + + TextBox{parent=clr_c_3,x=1,y=1,text="Settings saved!"} + PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} + PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + TextBox{parent=clr_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=clr_c_4,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} + PushButton{parent=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,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=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,text=" Summary",fg_bg=cpair(colors.black,colors.green)} + + 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 self.importing_legacy then + main_pane.set_value(1) + tool_ctl.viewing_config = false + self.importing_legacy = false + tool_ctl.settings_apply.show() + else + main_pane.set_value(5) + 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("/reactor-plc.settings") then + load_settings(settings_cfg, true) + load_settings(ini_cfg) + + try_set(networked, ini_cfg.Networked) + try_set(u_id, ini_cfg.UnitID) + try_set(en_em_cool, ini_cfg.EmerCoolEnable) + try_set(side, side_to_idx(ini_cfg.EmerCoolSide)) + try_set(bundled, ini_cfg.EmerCoolColor ~= nil) + if ini_cfg.EmerCoolColor ~= nil then try_set(color, color_to_idx(ini_cfg.EmerCoolColor)) end + try_set(svr_chan, ini_cfg.SVR_Channel) + try_set(plc_chan, ini_cfg.PLC_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) + try_set(fp_theme, ini_cfg.FrontPanelTheme) + try_set(c_mode, ini_cfg.ColorMode) + + tool_ctl.view_cfg.enable() + tool_ctl.self_check.enable() + tool_ctl.color_cfg.enable() + + if self.importing_legacy then + self.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} + self.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()self.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_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,text="Settings saved!"} + + local function go_home() + main_pane.set_value(1) + plc_pane.set_value(1) + net_pane.set_value(1) + clr_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("/reactor-plc/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)} + + --#endregion + + --#region Tool Functions + + -- load a legacy config file + function tool_ctl.load_legacy() + local config = require("reactor-plc.config") + + tmp_cfg.Networked = config.NETWORKED + tmp_cfg.UnitID = config.REACTOR_ID + tmp_cfg.EmerCoolEnable = type(config.EMERGENCY_COOL) == "table" + + if tmp_cfg.EmerCoolEnable then + tmp_cfg.EmerCoolSide = config.EMERGENCY_COOL.side + tmp_cfg.EmerCoolColor = config.EMERGENCY_COOL.color + else + tmp_cfg.EmerCoolSide = nil + tmp_cfg.EmerCoolColor = nil + end + + tmp_cfg.SVR_Channel = config.SVR_CHANNEL + tmp_cfg.PLC_Channel = config.PLC_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(6) + self.importing_legacy = true + end + + -- expose the auth key on the summary page + function self.show_auth_key() + self.show_key_btn.disable() + self.auth_key_textbox.set_value(self.auth_key_value) + end + + -- generate the summary list + ---@param cfg plc_config + function tool_ctl.gen_summary(cfg) + setting_list.remove_all() + + local alternate = false + local inner_width = setting_list.get_width() - 1 + + if cfg.AuthKey then self.show_key_btn.enable() else self.show_key_btn.disable() end + self.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" and raw then val = string.rep("*", string.len(val)) + elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") + elseif f[1] == "EmerCoolColor" and raw ~= nil then val = rsio.color_name(raw) + elseif f[1] == "FrontPanelTheme" then + val = util.strval(themes.fp_theme_name(raw)) + elseif f[1] == "ColorMode" then + val = util.strval(themes.color_mode_name(raw)) + 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 self.auth_key_textbox = textbox end + end + end + + --#endregion +end + +return system \ No newline at end of file diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index cf7a612..fbcd888 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -2,48 +2,30 @@ -- 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 rsio = require("scada-common.rsio") -local tcd = require("scada-common.tcd") -local util = require("scada-common.util") +local log = require("scada-common.log") +local tcd = require("scada-common.tcd") +local util = require("scada-common.util") -local plc = require("reactor-plc.plc") +local check = require("reactor-plc.config.check") +local system = require("reactor-plc.config.system") -local core = require("graphics.core") -local themes = require("graphics.themes") +local core = require("graphics.core") +local themes = require("graphics.themes") -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 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 IndLight = require("graphics.elements.indicators.light") +local PushButton = require("graphics.elements.controls.push_button") 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 = { @@ -58,30 +40,23 @@ local configurator = {} local style = {} -style.root = cpair(colors.black, colors.lightGray) -style.header = cpair(colors.white, colors.gray) +style.root = cpair(colors.black, colors.lightGray) +style.header = cpair(colors.white, colors.gray) -style.colors = themes.smooth_stone.colors +style.colors = themes.smooth_stone.colors -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 btn_dis_fg_bg = cpair(colors.lightGray, colors.white) +style.bw_fg_bg = cpair(colors.black, colors.white) +style.g_lg_fg_bg = cpair(colors.gray, colors.lightGray) +style.nav_fg_bg = style.bw_fg_bg +style.btn_act_fg_bg = cpair(colors.white, colors.gray) +style.btn_dis_fg_bg = cpair(colors.lightGray, colors.white) ---@class _plc_cfg_tool_ctl local tool_ctl = { - nic = nil, ---@type nic - net_listen = false, - sv_addr = comms.BROADCAST, - sv_seq_num = util.time_ms() * 10, - ask_config = false, has_config = false, viewing_config = false, - importing_legacy = false, jumped_to_color = false, - self_check_pass = true, view_cfg = nil, ---@type graphics_element self_check = nil, ---@type graphics_element @@ -89,19 +64,9 @@ local tool_ctl = { color_next = nil, ---@type graphics_element color_apply = nil, ---@type graphics_element settings_apply = nil, ---@type graphics_element - run_test_btn = 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 - self_check_msg = 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 plc_config @@ -147,89 +112,6 @@ local fields = { { "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD } } --- send a 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(settings_cfg.SVR_Channel, settings_cfg.PLC_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() ~= settings_cfg.PLC_Channel then - error_msg = "error: unknown receive channel" - elseif packet.scada_frame.remote_channel() == settings_cfg.SVR_Channel and packet.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then - if packet.type == MGMT_TYPE.ESTABLISH then - if packet.length == 1 then - local est_ack = packet.data[1] - - if est_ack== ESTABLISH_ACK.ALLOW then - tool_ctl.self_check_msg(nil, true, "") - tool_ctl.sv_addr = packet.scada_frame.src_addr() - send_sv(MGMT_TYPE.CLOSE, {}) - elseif est_ack == ESTABLISH_ACK.DENY then - error_msg = "error: supervisor connection denied" - elseif est_ack == ESTABLISH_ACK.COLLISION then - error_msg = "another reactor PLC is connected with this reactor unit ID" - elseif est_ack == ESTABLISH_ACK.BAD_VERSION then - error_msg = "reactor PLC comms version does not match supervisor comms version, make sure both devices are up-to-date (ccmsi update ...)" - 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 - tool_ctl.run_test_btn.enable() - - if error_msg then - tool_ctl.self_check_msg(nil, false, error_msg) - end -end - --- handle supervisor connection failure -local function handle_timeout() - tool_ctl.net_listen = false - tool_ctl.run_test_btn.enable() - tool_ctl.self_check_msg(nil, false, "make sure your supervisor is running, your channels are correct, trusted ranges are set properly (if enabled), facility keys match (if set), and if you are using wireless modems rather than ender modems, that your devices are close together in the same dimension") -end - -local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" } -local side_options_map = { "top", "bottom", "left", "right", "front", "back" } -local color_options = { "Red", "Orange", "Yellow", "Lime", "Green", "Cyan", "Light Blue", "Blue", "Purple", "Magenta", "Pink", "White", "Light Gray", "Gray", "Black", "Brown" } -local color_options_map = { colors.red, colors.orange, colors.yellow, colors.lime, colors.green, colors.cyan, colors.lightBlue, colors.blue, colors.purple, colors.magenta, colors.pink, colors.white, colors.lightGray, colors.gray, colors.black, colors.brown } - --- convert text representation to index ----@param side string -local function side_to_idx(side) - for k, v in ipairs(side_options_map) do - if v == side then return k end - end -end - --- convert color to index ----@param color color -local function color_to_idx(color) - for k, v in ipairs(color_options_map) do - if v == color then return k end - end -end - -- load data from the settings file ---@param target plc_config ---@param raw boolean? true to not use default values @@ -246,6 +128,11 @@ end -- create the config view ---@param display graphics_element local function config_view(display) + local bw_fg_bg = style.bw_fg_bg + local g_lg_fg_bg = style.g_lg_fg_bg + local nav_fg_bg = style.nav_fg_bg + local btn_act_fg_bg = style.btn_act_fg_bg + local btn_dis_fg_bg = style.btn_dis_fg_bg ---@diagnostic disable-next-line: undefined-field local function exit() os.queueEvent("terminate") end @@ -311,437 +198,12 @@ local function config_view(display) --#endregion - --#region PLC + --#region System Configuration - 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} - local plc_c_3 = Div{parent=plc_cfg,x=2,y=4,width=49} - local plc_c_4 = Div{parent=plc_cfg,x=2,y=4,width=49} + local settings = { settings_cfg, ini_cfg, tmp_cfg, fields, load_settings } + local divs = { plc_cfg, net_cfg, log_cfg, clr_cfg, summary } - local plc_pane = MultiPane{parent=plc_cfg,x=1,y=4,panes={plc_c_1,plc_c_2,plc_c_3,plc_c_4}} - - TextBox{parent=plc_cfg,x=1,y=2,text=" PLC Configuration",fg_bg=cpair(colors.black,colors.orange)} - - TextBox{parent=plc_c_1,x=1,y=1,text="Would you like to set this PLC as networked?"} - TextBox{parent=plc_c_1,x=1,y=3,height=4,text="If you have a supervisor, select the box. You will later be prompted to select the network configuration. If you instead want to use this as a standalone safety system, don't select the box.",fg_bg=g_lg_fg_bg} - - local networked = CheckBox{parent=plc_c_1,x=1,y=8,label="Networked",default=ini_cfg.Networked,box_fg_bg=cpair(colors.orange,colors.black)} - - local function submit_networked() - tool_ctl.set_networked(networked.get_value()) - plc_pane.set_value(2) - end - - PushButton{parent=plc_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=plc_c_1,x=44,y=14,text="Next \x1a",callback=submit_networked,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - - TextBox{parent=plc_c_2,x=1,y=1,text="Please enter the reactor unit ID for this PLC."} - TextBox{parent=plc_c_2,x=1,y=3,height=3,text="If this is a networked PLC, currently only IDs 1 through 4 are acceptable.",fg_bg=g_lg_fg_bg} - - TextBox{parent=plc_c_2,x=1,y=6,text="Unit #"} - local u_id = NumberField{parent=plc_c_2,x=7,y=6,width=5,max_chars=3,default=ini_cfg.UnitID,min=1,fg_bg=bw_fg_bg} - - local u_id_err = TextBox{parent=plc_c_2,x=8,y=14,width=35,text="Please set a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} - - local function submit_id() - local unit_id = tonumber(u_id.get_value()) - if unit_id ~= nil then - u_id_err.hide(true) - tmp_cfg.UnitID = unit_id - plc_pane.set_value(3) - else u_id_err.show() end - end - - PushButton{parent=plc_c_2,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=plc_c_2,x=44,y=14,text="Next \x1a",callback=submit_id,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - - TextBox{parent=plc_c_3,x=1,y=1,height=4,text="When networked, the supervisor takes care of emergency coolant via RTUs. However, you can configure independent emergency coolant via the PLC."} - TextBox{parent=plc_c_3,x=1,y=6,height=5,text="This independent control can be used with or without a supervisor. To configure, you would next select the interface of the redstone output connected to one or more mekanism pipes.",fg_bg=g_lg_fg_bg} - - local en_em_cool = CheckBox{parent=plc_c_3,x=1,y=11,label="Enable PLC Emergency Coolant Control",default=ini_cfg.EmerCoolEnable,box_fg_bg=cpair(colors.orange,colors.black)} - - local function next_from_plc() - if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(4) end - end - - local function submit_en_emcool() - tmp_cfg.EmerCoolEnable = en_em_cool.get_value() - if tmp_cfg.EmerCoolEnable then plc_pane.set_value(4) else next_from_plc() end - end - - PushButton{parent=plc_c_3,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=plc_c_3,x=44,y=14,text="Next \x1a",callback=submit_en_emcool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - - TextBox{parent=plc_c_4,x=1,y=1,text="Emergency Coolant Redstone Output Side"} - local side = Radio2D{parent=plc_c_4,x=1,y=2,rows=2,columns=3,default=side_to_idx(ini_cfg.EmerCoolSide),options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.orange} - - TextBox{parent=plc_c_4,x=1,y=5,text="Bundled Redstone Configuration"} - local bundled = CheckBox{parent=plc_c_4,x=1,y=6,label="Is Bundled?",default=ini_cfg.EmerCoolColor~=nil,box_fg_bg=cpair(colors.orange,colors.black),callback=function(v)tool_ctl.bundled_emcool(v)end} - local color = Radio2D{parent=plc_c_4,x=1,y=8,rows=4,columns=4,default=color_to_idx(ini_cfg.EmerCoolColor),options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg} - if ini_cfg.EmerCoolColor == nil then color.disable() end - - local function submit_emcool() - tmp_cfg.EmerCoolSide = side_options_map[side.get_value()] - tmp_cfg.EmerCoolColor = util.trinary(bundled.get_value(), color_options_map[color.get_value()], nil) - next_from_plc() - end - - 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} - - --#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} - local net_c_3 = 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}} - - TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)} - - TextBox{parent=net_c_1,x=1,y=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 2 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,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=11,text="PLC Channel"} - local plc_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg} - TextBox{parent=net_c_1,x=9,y=12,height=4,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg} - - local chan_err = TextBox{parent=net_c_1,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} - - local function submit_channels() - local svr_c = tonumber(svr_chan.get_value()) - local plc_c = tonumber(plc_chan.get_value()) - if svr_c ~= nil and plc_c ~= nil then - tmp_cfg.SVR_Channel = svr_c - tmp_cfg.PLC_Channel = plc_c - net_pane.set_value(2) - chan_err.hide(true) - elseif svr_c == nil then - chan_err.set_value("Please set the supervisor channel.") - chan_err.show() - else - chan_err.set_value("Please set the PLC channel.") - chan_err.show() - end - end - - PushButton{parent=net_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} - 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,text="Connection Timeout"} - local timeout = NumberField{parent=net_c_2,x=1,y=2,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=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg} - TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can 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=8,text="Trusted Range"} - local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg} - TextBox{parent=net_c_2,x=1,y=10,height=4,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} - - local p2_err = TextBox{parent=net_c_2,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} - - local function submit_ct_tr() - local timeout_val = tonumber(timeout.get_value()) - local range_val = tonumber(range.get_value()) - if timeout_val ~= nil and range_val ~= nil then - tmp_cfg.ConnTimeout = timeout_val - tmp_cfg.TrustedRange = range_val - net_pane.set_value(3) - p2_err.hide(true) - elseif timeout_val == nil then - p2_err.set_value("Please set the connection timeout.") - p2_err.show() - else - p2_err.set_value("Please set the trusted range.") - p2_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_ct_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - - TextBox{parent=net_c_3,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_3,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_3,x=1,y=11,text="Facility Auth Key"} - local key, _, censor = TextField{parent=net_c_3,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_3,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_3,x=8,y=14,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(4) - key_err.hide(true) - else key_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_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=49} - - TextBox{parent=log_cfg,x=1,y=2,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)} - - TextBox{parent=log_c_1,x=1,y=1,text="Please configure logging below."} - - TextBox{parent=log_c_1,x=1,y=3,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,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,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.color_apply.hide(true) - tool_ctl.color_next.show() - main_pane.set_value(5) - else path_err.show() end - end - - local function back_from_log() - if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(2) end - end - - 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} - - --#endregion - - --#region Color Options - - local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49} - local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49} - local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49} - local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49} - - local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}} - - TextBox{parent=clr_cfg,x=1,y=2,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)} - - TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."} - TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg} - - TextBox{parent=clr_c_1,x=1,y=7,text="Front Panel Theme"} - local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta} - - TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."} - - TextBox{parent=clr_c_2,x=21,y=7,text="Preview"} - local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)} - _ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)} - _ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)} - local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true} - local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true} - - local function recolor(value) - local c = themes.smooth_stone.color_modes[value] - - if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then - b_off.hide() - g_off.show() - else - g_off.hide() - b_off.show() - end - - if #c == 0 then - for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end - else - term.setPaletteColor(colors.green, c[1].hex) - term.setPaletteColor(colors.yellow, c[2].hex) - term.setPaletteColor(colors.red, c[3].hex) - end - end - - TextBox{parent=clr_c_2,x=1,y=7,width=10,text="Color Mode"} - local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta} - - TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg} - - PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - - local function back_from_colors() - main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 4)) - tool_ctl.jumped_to_color = false - recolor(1) - end - - local function show_access() - clr_pane.set_value(2) - recolor(c_mode.get_value()) - end - - local function submit_colors() - tmp_cfg.FrontPanelTheme = fp_theme.get_value() - tmp_cfg.ColorMode = c_mode.get_value() - - if tool_ctl.jumped_to_color then - settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme) - settings.set("ColorMode", tmp_cfg.ColorMode) - - if settings.save("/reactor-plc.settings") then - load_settings(settings_cfg, true) - load_settings(ini_cfg) - clr_pane.set_value(3) - else - clr_pane.set_value(4) - end - else - 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(6) - end - end - - PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg} - - tool_ctl.color_apply.hide(true) - - local function c_go_home() - main_pane.set_value(1) - clr_pane.set_value(1) - end - - TextBox{parent=clr_c_3,x=1,y=1,text="Settings saved!"} - PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - - TextBox{parent=clr_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=clr_c_4,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)} - PushButton{parent=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,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=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,text=" Summary",fg_bg=cpair(colors.black,colors.green)} - - 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 - 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(5) - 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("/reactor-plc.settings") then - load_settings(settings_cfg, true) - load_settings(ini_cfg) - - try_set(networked, ini_cfg.Networked) - try_set(u_id, ini_cfg.UnitID) - try_set(en_em_cool, ini_cfg.EmerCoolEnable) - try_set(side, side_to_idx(ini_cfg.EmerCoolSide)) - try_set(bundled, ini_cfg.EmerCoolColor ~= nil) - if ini_cfg.EmerCoolColor ~= nil then try_set(color, color_to_idx(ini_cfg.EmerCoolColor)) end - try_set(svr_chan, ini_cfg.SVR_Channel) - try_set(plc_chan, ini_cfg.PLC_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) - try_set(fp_theme, ini_cfg.FrontPanelTheme) - try_set(c_mode, ini_cfg.ColorMode) - - tool_ctl.view_cfg.enable() - tool_ctl.self_check.enable() - tool_ctl.color_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=btn_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,text="Settings saved!"} - - local function go_home() - main_pane.set_value(1) - plc_pane.set_value(1) - net_pane.set_value(1) - clr_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("/reactor-plc/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)} + system.create(tool_ctl, main_pane, settings, divs, style, exit) --#endregion @@ -768,189 +230,9 @@ local function config_view(display) --#region Self-Check - local sc = Div{parent=check_sys,x=2,y=4,width=49} + check.create(main_pane, settings_cfg, check_sys, style) - TextBox{parent=check_sys,x=1,y=2,text=" Reactor PLC Self-Check",fg_bg=bw_fg_bg} - - local sc_log = ListBox{parent=sc,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 last_check = { nil, nil } - - function tool_ctl.self_check_msg(msg, success, fail_msg) - if type(msg) == "string" then - last_check[1] = Div{parent=sc_log,height=1} - local e = TextBox{parent=last_check[1],text=msg,fg_bg=bw_fg_bg} - last_check[2] = e.get_x()+string.len(msg) - end - - if type(fail_msg) == "string" then - TextBox{parent=last_check[1],x=last_check[2],y=1,text=tri(success,"PASS","FAIL"),fg_bg=tri(success,cpair(colors.green,colors._INHERIT),cpair(colors.red,colors._INHERIT))} - - if not success then - local fail = Div{parent=sc_log,height=#util.strwrap(fail_msg, 46)} - TextBox{parent=fail,x=3,text=fail_msg,fg_bg=cpair(colors.gray,colors.white)} - end - - tool_ctl.self_check_pass = tool_ctl.self_check_pass and success - end - end - - local function self_check() - tool_ctl.run_test_btn.disable() - - sc_log.remove_all() - ppm.mount_all() - - tool_ctl.self_check_pass = true - - local modem = ppm.get_wireless_modem() - local reactor = ppm.get_fission_reactor() - local valid_cfg = plc.validate_config(settings_cfg) - - tool_ctl.self_check_msg("> check wireless/ender modem connected...", modem ~= nil, "you must connect an ender or wireless modem to the reactor PLC") - tool_ctl.self_check_msg("> check fission reactor connected...", reactor ~= nil, "please connect the reactor PLC to the reactor's fission reactor logic adapter") - tool_ctl.self_check_msg("> check fission reactor formed...") - -- this consumes events, but that is fine here - tool_ctl.self_check_msg(nil, reactor and reactor.isFormed(), "ensure the fission reactor multiblock is formed") - - tool_ctl.self_check_msg("> check configuration...", valid_cfg, "go through Configure System again and apply settings to repair any corrupted or missing settings") - - if valid_cfg and modem then - tool_ctl.self_check_msg("> check supervisor connection...") - - tool_ctl.nic = network.nic(modem) - - tool_ctl.nic.closeAll() - tool_ctl.nic.open(settings_cfg.PLC_Channel) - - tool_ctl.sv_addr = comms.BROADCAST - tool_ctl.net_listen = true - - send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.PLC, settings_cfg.UnitID }) - - tcd.dispatch_unique(8, handle_timeout) - else - tool_ctl.run_test_btn.enable() - end - - if tool_ctl.self_check_pass then - TextBox{parent=sc_log,text="> all tests passed!",fg_bg=cpair(colors.blue,colors._INHERIT)} - TextBox{parent=sc_log,text=""} - local more = Div{parent=sc_log,height=3,fg_bg=cpair(colors.gray,colors._INHERIT)} - TextBox{parent=more,text="if you still have a problem:"} - TextBox{parent=more,text="- check the wiki on GitHub"} - TextBox{parent=more,text="- ask for help on GitHub discussions or Discord"} - end - end - - local function exit_self_check() - tcd.abort(handle_timeout) - tool_ctl.net_listen = false - tool_ctl.run_test_btn.enable() - sc_log.remove_all() - main_pane.set_value(1) - end - - PushButton{parent=sc,x=1,y=14,text="\x1b Back",callback=exit_self_check,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - tool_ctl.run_test_btn = PushButton{parent=sc,x=40,y=14,min_width=10,text="Run Test",callback=self_check,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg} - - -- set tool functions now that we have the elements - - function tool_ctl.set_networked(enable) - tmp_cfg.Networked = enable - if enable then u_id.set_max(4) else u_id.set_max(999) end - end - - function tool_ctl.bundled_emcool(en) if en then color.enable() else color.disable() end end - - -- load a legacy config file - function tool_ctl.load_legacy() - local config = require("reactor-plc.config") - - tmp_cfg.Networked = config.NETWORKED - tmp_cfg.UnitID = config.REACTOR_ID - tmp_cfg.EmerCoolEnable = type(config.EMERGENCY_COOL) == "table" - - if tmp_cfg.EmerCoolEnable then - tmp_cfg.EmerCoolSide = config.EMERGENCY_COOL.side - tmp_cfg.EmerCoolColor = config.EMERGENCY_COOL.color - else - tmp_cfg.EmerCoolSide = nil - tmp_cfg.EmerCoolColor = nil - end - - tmp_cfg.SVR_Channel = config.SVR_CHANNEL - tmp_cfg.PLC_Channel = config.PLC_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(6) - 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 plc_config - function tool_ctl.gen_summary(cfg) - setting_list.remove_all() - - local alternate = false - local inner_width = setting_list.get_width() - 1 - - if cfg.AuthKey then tool_ctl.show_key_btn.enable() else tool_ctl.show_key_btn.disable() end - 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" and raw then val = string.rep("*", string.len(val)) - elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") - elseif f[1] == "EmerCoolColor" and raw ~= nil then val = rsio.color_name(raw) - elseif f[1] == "FrontPanelTheme" then - val = util.strval(themes.fp_theme_name(raw)) - elseif f[1] == "ColorMode" then - val = util.strval(themes.color_mode_name(raw)) - 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 + --#endregion end -- reset terminal screen @@ -994,16 +276,8 @@ function configurator.configure(ask_config) if k_e then display.handle_key(k_e) end elseif event == "paste" then display.handle_paste(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 + elseif event == "modem_message" then + check.handle_msg(param1, param2, param3, param4, param5) end if event == "terminate" then return end From 8e4bb583a850e90dd226c7815b8dad58af258994 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 26 Jul 2024 23:06:42 -0400 Subject: [PATCH 11/21] #528 reactor PLC configurator fixes --- reactor-plc/config/check.lua | 7 +++++++ reactor-plc/config/system.lua | 6 +++--- reactor-plc/configure.lua | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/reactor-plc/config/check.lua b/reactor-plc/config/check.lua index de1d296..95994f3 100644 --- a/reactor-plc/config/check.lua +++ b/reactor-plc/config/check.lua @@ -124,6 +124,13 @@ local function self_check(sc_log) if valid_cfg and modem then self.self_check_msg("> check supervisor connection...") + -- init mac as needed + if self.settings.AuthKey and string.len(self.settings.AuthKey) >= 8 then + network.init_mac(self.settings.AuthKey) + else + network.deinit_mac() + end + self.nic = network.nic(modem) self.nic.closeAll() diff --git a/reactor-plc/config/system.lua b/reactor-plc/config/system.lua index 49630b0..5e49e62 100644 --- a/reactor-plc/config/system.lua +++ b/reactor-plc/config/system.lua @@ -63,13 +63,13 @@ local system = {} -- create the system configuration view ---@param tool_ctl _plc_cfg_tool_ctl ---@param main_pane graphics_element ----@param settings table +---@param cfg_sys table ---@param divs table ---@param style table ---@param exit function -function system.create(tool_ctl, main_pane, settings, divs, style, exit) +function system.create(tool_ctl, main_pane, cfg_sys, divs, style, exit) ---@type plc_config, plc_config, plc_config, table, function - local settings_cfg, ini_cfg, tmp_cfg, fields, load_settings = table.unpack(settings) + local settings_cfg, ini_cfg, tmp_cfg, fields, load_settings = table.unpack(cfg_sys) ---@type graphics_element, graphics_element, graphics_element, graphics_element, graphics_element local plc_cfg, net_cfg, log_cfg, clr_cfg, summary = table.unpack(divs) diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index fbcd888..ac28cc0 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -277,7 +277,7 @@ function configurator.configure(ask_config) elseif event == "paste" then display.handle_paste(param1) elseif event == "modem_message" then - check.handle_msg(param1, param2, param3, param4, param5) + check.receive_sv(param1, param2, param3, param4, param5) end if event == "terminate" then return end From 3ffc79b18120de607223f3724e80df8ab9db0d74 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 11:15:05 -0400 Subject: [PATCH 12/21] #530 fix RTU reconnection issue --- rtu/rtu.lua | 4 +++- rtu/startup.lua | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rtu/rtu.lua b/rtu/rtu.lua index 231c261..ae09b01 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -571,7 +571,9 @@ function rtu.comms(version, nic, conn_watchdog) end end - public.unlink(rtu_state) + -- unlink + self.sv_addr = comms.BROADCAST + rtu_state.linked = false end self.last_est_ack = est_ack diff --git a/rtu/startup.lua b/rtu/startup.lua index e0f1616..5f53bee 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.10.2" +local RTU_VERSION = "v1.10.3" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE From 01f6b1e1902f6b4e7324d04bc639af1e2bfc24c9 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 11:15:23 -0400 Subject: [PATCH 13/21] #363 added tip about self-check --- reactor-plc/config/system.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/reactor-plc/config/system.lua b/reactor-plc/config/system.lua index 5e49e62..8170eaa 100644 --- a/reactor-plc/config/system.lua +++ b/reactor-plc/config/system.lua @@ -493,6 +493,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, style, exit) 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,text="Settings saved!"} + TextBox{parent=sum_c_2,x=1,y=3,text="Tip: you can run a Self-Check from the configurator home screen to make sure everything is going to work right!"} local function go_home() main_pane.set_value(1) From 7cc088ca9571b7fbfa918f935c4ac95b47149a99 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 12:34:01 -0400 Subject: [PATCH 14/21] #523 coordinator energy scale options --- coordinator/coordinator.lua | 5 +- coordinator/iocontrol.lua | 23 +++++- coordinator/process.lua | 8 +- coordinator/ui/components/boiler.lua | 4 +- coordinator/ui/components/imatrix.lua | 30 ++++---- coordinator/ui/components/process_ctl.lua | 23 +++--- coordinator/ui/components/turbine.lua | 8 +- graphics/elements/indicators/power.lua | 11 +-- scada-common/types.lua | 19 +++++ scada-common/util.lua | 91 ++++++++++++----------- supervisor/facility.lua | 10 +-- supervisor/startup.lua | 2 +- 12 files changed, 145 insertions(+), 89 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 27543ec..094bf5b 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -38,6 +38,7 @@ function coordinator.load_config() config.SpeakerVolume = settings.get("SpeakerVolume") config.Time24Hour = settings.get("Time24Hour") config.TempScale = settings.get("TempScale") + config.EnergyScale = settings.get("EnergyScale") config.DisableFlowView = settings.get("DisableFlowView") config.MainDisplay = settings.get("MainDisplay") @@ -67,6 +68,8 @@ function coordinator.load_config() cfv.assert_type_bool(config.Time24Hour) cfv.assert_type_int(config.TempScale) cfv.assert_range(config.TempScale, 1, 4) + cfv.assert_type_int(config.EnergyScale) + cfv.assert_range(config.EnergyScale, 1, 3) cfv.assert_type_bool(config.DisableFlowView) cfv.assert_type_table(config.UnitDisplays) @@ -702,7 +705,7 @@ function coordinator.comms(version, nic, sv_watchdog) if conf.num_units == config.UnitCount then -- init io controller - iocontrol.init(conf, public, config.TempScale) + iocontrol.init(conf, public, config.TempScale, config.EnergyScale) self.sv_addr = src_addr self.sv_linked = true diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 5d8428f..e8102a4 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -14,6 +14,9 @@ local pgi = require("coordinator.ui.pgi") local ALARM_STATE = types.ALARM_STATE local PROCESS = types.PROCESS + +local ENERGY_SCALE = types.ENERGY_SCALE +local ENERGY_UNITS = types.ENERGY_SCALE_UNITS local TEMP_SCALE = types.TEMP_SCALE local TEMP_UNITS = types.TEMP_SCALE_UNITS @@ -50,8 +53,10 @@ end ---@param conf facility_conf configuration ---@param comms coord_comms comms reference ---@param temp_scale TEMP_SCALE temperature unit -function iocontrol.init(conf, comms, temp_scale) - io.temp_label = TEMP_UNITS[temp_scale] +---@param energy_scale ENERGY_SCALE energy unit +function iocontrol.init(conf, comms, temp_scale, energy_scale) + io.temp_label = TEMP_UNITS[temp_scale] + io.energy_label = ENERGY_UNITS[energy_scale] -- temperature unit label and conversion function (from Kelvin) if temp_scale == TEMP_SCALE.CELSIUS then @@ -65,6 +70,18 @@ function iocontrol.init(conf, comms, temp_scale) io.temp_convert = function (t) return t end end + -- energy unit label and conversion function (from Joules unless otherwise specified) + if energy_scale == ENERGY_SCALE.FE or energy_scale == ENERGY_SCALE.RF then + io.energy_convert = util.joules_to_fe_rf + io.energy_convert_from_fe = function (t) return t end + io.energy_convert_to_fe = function (t) return t end + else + io.energy_label = "J" + io.energy_convert = function (t) return t end + io.energy_convert_from_fe = util.fe_rf_to_joules + io.energy_convert_to_fe = util.joules_to_fe_rf + end + -- facility data structure ---@class ioctl_facility io.facility = { @@ -692,7 +709,7 @@ function iocontrol.update_facility_status(status) ps.publish("is_discharging", out_f > in_f) if data and data.build then - local cap = util.joules_to_fe(data.build.transfer_cap) + local cap = util.joules_to_fe_rf(data.build.transfer_cap) ps.publish("at_max_io", in_f >= cap or out_f >= cap) end else diff --git a/coordinator/process.lua b/coordinator/process.lua index 1016e62..0615e30 100644 --- a/coordinator/process.lua +++ b/coordinator/process.lua @@ -70,8 +70,8 @@ function process.init(iocontrol, coord_comms) 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_charge_target", self.io.energy_convert_from_fe(ctl_proc.charge_target)) + self.io.facility.ps.publish("process_gen_target", self.io.energy_convert_from_fe(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) self.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power) @@ -316,8 +316,8 @@ function process.start_ack_handle(response) 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_charge_target", self.io.energy_convert_from_fe(ctl_proc.charge_target)) + self.io.facility.ps.publish("process_gen_target", self.io.energy_convert_from_fe(ctl_proc.gen_target)) self.io.facility.start_ack(ack) end diff --git a/coordinator/ui/components/boiler.lua b/coordinator/ui/components/boiler.lua index 72eef55..86866f5 100644 --- a/coordinator/ui/components/boiler.lua +++ b/coordinator/ui/components/boiler.lua @@ -1,7 +1,7 @@ -local style = require("coordinator.ui.style") - local iocontrol = require("coordinator.iocontrol") +local style = require("coordinator.ui.style") + local core = require("graphics.core") local Rectangle = require("graphics.elements.rectangle") diff --git a/coordinator/ui/components/imatrix.lua b/coordinator/ui/components/imatrix.lua index c5a1e55..cfec34d 100644 --- a/coordinator/ui/components/imatrix.lua +++ b/coordinator/ui/components/imatrix.lua @@ -1,5 +1,7 @@ local util = require("scada-common.util") +local iocontrol = require("coordinator.iocontrol") + local style = require("coordinator.ui.style") local core = require("graphics.core") @@ -34,6 +36,8 @@ local function new_view(root, x, y, data, ps, id) local ind_yel = style.ind_yel local ind_wht = style.ind_wht + local db = iocontrol.get_db() + local title = "INDUCTION MATRIX" if type(id) == "number" then title = title .. id end @@ -48,24 +52,24 @@ local function new_view(root, x, y, data, ps, id) local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3} local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14} - local capacity = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Capacity:",format="%8.2f",value=0,width=26,fg_bg=text_fg} - local energy = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg} - local avg_chg = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",value=0,width=26,fg_bg=text_fg} - local input = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Input: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} - local avg_in = PowerIndicator{parent=rect,x=7,y=7,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} - local output = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Output: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} - local avg_out = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} - local trans_cap = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Max I/O: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} + local capacity = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Capacity:",unit=db.energy_label,format="%8.2f",value=0,width=26,fg_bg=text_fg} + local energy = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Energy: ",unit=db.energy_label,format="%8.2f",value=0,width=26,fg_bg=text_fg} + local avg_chg = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="\xb7Average:",unit=db.energy_label,format="%8.2f",value=0,width=26,fg_bg=text_fg} + local input = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Input: ",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} + local avg_in = PowerIndicator{parent=rect,x=7,y=7,lu_colors=lu_col,label="\xb7Average:",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} + local output = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Output: ",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} + local avg_out = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="\xb7Average:",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} + local trans_cap = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Max I/O: ",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg} status.register(ps, "computed_status", status.update) - capacity.register(ps, "max_energy", function (val) capacity.update(util.joules_to_fe(val)) end) - energy.register(ps, "energy", function (val) energy.update(util.joules_to_fe(val)) end) + capacity.register(ps, "max_energy", function (val) capacity.update(db.energy_convert(val)) end) + energy.register(ps, "energy", function (val) energy.update(db.energy_convert(val)) end) avg_chg.register(ps, "avg_charge", avg_chg.update) - input.register(ps, "last_input", function (val) input.update(util.joules_to_fe(val)) end) + input.register(ps, "last_input", function (val) input.update(db.energy_convert(val)) end) avg_in.register(ps, "avg_inflow", avg_in.update) - output.register(ps, "last_output", function (val) output.update(util.joules_to_fe(val)) end) + output.register(ps, "last_output", function (val) output.update(db.energy_convert(val)) end) avg_out.register(ps, "avg_outflow", avg_out.update) - trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(util.joules_to_fe(val)) end) + trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(db.energy_convert(val)) end) local fill = DataIndicator{parent=rect,x=11,y=12,lu_colors=lu_col,label="Fill: ",format="%7.2f",unit="%",value=0,width=20,fg_bg=text_fg} local cells = DataIndicator{parent=rect,x=11,y=13,lu_colors=lu_col,label="Cells: ",format="%7d",value=0,width=18,fg_bg=text_fg} diff --git a/coordinator/ui/components/process_ctl.lua b/coordinator/ui/components/process_ctl.lua index eea09e4..e1f3614 100644 --- a/coordinator/ui/components/process_ctl.lua +++ b/coordinator/ui/components/process_ctl.lua @@ -56,8 +56,10 @@ local function new_view(root, x, y) local blk_brn = cpair(colors.black, colors.brown) local blk_pur = cpair(colors.black, colors.purple) - local facility = iocontrol.get_db().facility - local units = iocontrol.get_db().units + local db = iocontrol.get_db() + + local facility = db.facility + local units = db.units local main = Div{parent=root,width=128,height=24,x=x,y=y} @@ -141,22 +143,22 @@ local function new_view(root, x, y) local chg_target = Div{parent=targets,x=9,y=6,width=23,height=3,fg_bg=s_hi_box} local c_target = SpinboxNumeric{parent=chg_target,x=2,y=1,whole_num_precision=15,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled} - TextBox{parent=chg_target,x=18,y=2,text="MFE",fg_bg=style.theme.label_fg} - local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=black,width=23,fg_bg=blk_brn} + TextBox{parent=chg_target,x=18,y=2,text="M"..db.energy_label,fg_bg=style.theme.label_fg} + local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="M"..db.energy_label,commas=true,lu_colors=black,width=23,fg_bg=blk_brn} c_target.register(facility.ps, "process_charge_target", c_target.set_value) - cur_charge.register(facility.induction_ps_tbl[1], "avg_charge", function (fe) cur_charge.update(fe / 1000000) end) + cur_charge.register(facility.induction_ps_tbl[1], "avg_charge", function (fe) cur_charge.update(db.energy_convert_from_fe(fe) / 1000000) end) local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur} TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2} local gen_target = Div{parent=targets,x=9,y=11,width=23,height=3,fg_bg=s_hi_box} local g_target = SpinboxNumeric{parent=gen_target,x=8,y=1,whole_num_precision=9,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled} - TextBox{parent=gen_target,x=18,y=2,text="kFE/t",fg_bg=style.theme.label_fg} - local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="kFE/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn} + TextBox{parent=gen_target,x=18,y=2,text="k"..db.energy_label.."/t",fg_bg=style.theme.label_fg} + local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="k"..db.energy_label.."/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn} g_target.register(facility.ps, "process_gen_target", g_target.set_value) - cur_gen.register(facility.induction_ps_tbl[1], "last_input", function (j) cur_gen.update(util.round(util.joules_to_fe(j) / 1000)) end) + cur_gen.register(facility.induction_ps_tbl[1], "last_input", function (j) cur_gen.update(util.round(db.energy_convert(j) / 1000)) end) ----------------- -- unit limits -- @@ -262,7 +264,10 @@ local function new_view(root, x, y) local limits = {} for i = 1, #rate_limits do limits[i] = rate_limits[i].get_value() end - process.save(mode.get_value(), b_target.get_value(), c_target.get_value(), g_target.get_value(), limits) + process.save(mode.get_value(), b_target.get_value(), + db.energy_convert_to_fe(c_target.get_value()), + db.energy_convert_to_fe(g_target.get_value()), + limits) end -- start automatic control after saving process control settings diff --git a/coordinator/ui/components/turbine.lua b/coordinator/ui/components/turbine.lua index 179e82d..fd6049a 100644 --- a/coordinator/ui/components/turbine.lua +++ b/coordinator/ui/components/turbine.lua @@ -1,4 +1,4 @@ -local util = require("scada-common.util") +local iocontrol = require("coordinator.iocontrol") local style = require("coordinator.ui.style") @@ -24,14 +24,16 @@ local function new_view(root, x, y, ps) local text_fg = style.theme.text_fg local lu_col = style.lu_colors + local db = iocontrol.get_db() + local turbine = Rectangle{parent=root,border=border(1,colors.gray,true),width=23,height=7,x=x,y=y} local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12} - local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg} + local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",unit=db.energy_label,format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg} local flow_rate = DataIndicator{parent=turbine,x=5,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%10.0f",value=0,commas=true,width=16,fg_bg=text_fg} status.register(ps, "computed_status", status.update) - prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end) + prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(db.energy_convert(val)) end) flow_rate.register(ps, "steam_input_rate", flow_rate.update) local steam = VerticalBar{parent=turbine,x=2,y=1,fg_bg=cpair(colors.white,colors.gray),height=4,width=1} diff --git a/graphics/elements/indicators/power.lua b/graphics/elements/indicators/power.lua index 69e4a0b..adcd100 100644 --- a/graphics/elements/indicators/power.lua +++ b/graphics/elements/indicators/power.lua @@ -6,6 +6,7 @@ local element = require("graphics.element") ---@class power_indicator_args ---@field label string indicator label +---@field unit string energy unit ---@field format string power format override (lua string format) ---@field rate boolean? whether to append /t to the end (power per tick) ---@field lu_colors? cpair label foreground color (a), unit foreground color (b) @@ -24,6 +25,7 @@ local element = require("graphics.element") ---@return graphics_element element, element_id id local function power(args) element.assert(type(args.value) == "number", "value is a required field") + element.assert(type(args.unit) == "string", "unit is a required field") element.assert(util.is_int(args.width), "width is a required field") args.height = 1 @@ -40,7 +42,7 @@ local function power(args) function e.on_update(value) e.value = value - local data_str, unit = util.power_format(value, false, args.format) + local data_str, unit = util.power_format(value, args.unit, false, args.format) -- write data e.w_set_cur(data_start, 1) @@ -53,14 +55,13 @@ local function power(args) end -- append per tick if rate is set - -- add space to FE so we don't end up with FEE (after having kFE for example) if args.rate == true then unit = unit .. "/t" - if unit == "FE/t" then unit = "FE/t " end - else - if unit == "FE" then unit = "FE " end end + -- add space to unit so we don't end up with something like FEE after having kFE + unit = util.strminw(unit, 5) + e.w_write(" " .. unit) end diff --git a/scada-common/types.lua b/scada-common/types.lua index aeeca16..e392f53 100644 --- a/scada-common/types.lua +++ b/scada-common/types.lua @@ -96,6 +96,25 @@ types.TEMP_SCALE_UNITS = { "\xb0R" } +---@enum ENERGY_SCALE +types.ENERGY_SCALE = { + JOULES = 1, + FE = 2, + RF = 3 +} + +types.ENERGY_SCALE_NAMES = { + "Joules (J)", + "Forge Energy (FE)", + "Redstone Flux (RF)" +} + +types.ENERGY_SCALE_UNITS = { + "J", + "FE", + "RF" +} + ---@enum PANEL_LINK_STATE types.PANEL_LINK_STATE = { LINKED = 1, diff --git a/scada-common/util.lua b/scada-common/util.lua index 9d36e3a..b86f8bb 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -24,7 +24,7 @@ local t_pack = table.pack local util = {} -- scada-common version -util.version = "1.4.1" +util.version = "1.4.2" util.TICK_TIME_S = 0.05 util.TICK_TIME_MS = 50 @@ -120,6 +120,13 @@ function util.strwrap(str, limit) return cc_strings.wrap(str, limit) end +-- make sure a string is at least 'width' long +---@nodiscard +---@param str string +---@param width integer minimum width +---@return string string +function util.strminw(str, width) return cc_strings.ensure_width(str, width) end + -- concatenation with built-in to string ---@nodiscard ---@vararg any @@ -375,65 +382,63 @@ end --#region MEKANISM MATH --- convert Joules to FE +-- convert Joules to FE (or RF) ---@nodiscard ---@param J number Joules ----@return number FE Forge Energy -function util.joules_to_fe(J) return (J * 0.4) end +---@return number FE Forge Energy or Redstone Flux +function util.joules_to_fe_rf(J) return (J * 0.4) end --- convert FE to Joules +-- convert FE (or RF) to Joules ---@nodiscard ----@param FE number Forge Energy +---@param FE number Forge Energy or Redstone Flux ---@return number J Joules -function util.fe_to_joules(FE) return (FE * 2.5) end +function util.fe_rf_to_joules(FE) return (FE * 2.5) end -local function kFE(fe) return fe / 1000.0 end -local function MFE(fe) return fe / 1000000.0 end -local function GFE(fe) return fe / 1000000000.0 end -local function TFE(fe) return fe / 1000000000000.0 end -local function PFE(fe) return fe / 1000000000000000.0 end -local function EFE(fe) return fe / 1000000000000000000.0 end -- if you accomplish this please touch grass -local function ZFE(fe) return fe / 1000000000000000000000.0 end -- how & why did you do this? - --- format a power value into XXX.XX UNIT format (FE, kFE, MFE, GFE, TFE, PFE, EFE, ZFE) +-- format a power value into XXX.XX UNIT format
+-- example for FE: FE, kFE, MFE, GFE, TFE, PFE, EFE, ZFE ---@nodiscard ----@param fe number forge energy value +---@param e number energy value +---@param label string energy scale label ---@param combine_label? boolean if a label should be included in the string itself ---@param format? string format override ----@return string str, string? unit -function util.power_format(fe, combine_label, format) +---@return string str, string unit +function util.power_format(e, label, combine_label, format) local unit, value if type(format) ~= "string" then format = "%.2f" end - if fe < 1000.0 then - unit = "FE" - value = fe - elseif fe < 1000000.0 then - unit = "kFE" - value = kFE(fe) - elseif fe < 1000000000.0 then - unit = "MFE" - value = MFE(fe) - elseif fe < 1000000000000.0 then - unit = "GFE" - value = GFE(fe) - elseif fe < 1000000000000000.0 then - unit = "TFE" - value = TFE(fe) - elseif fe < 1000000000000000000.0 then - unit = "PFE" - value = PFE(fe) - elseif fe < 1000000000000000000000.0 then - unit = "EFE" - value = EFE(fe) + if e < 1000.0 then + unit = "" + value = e + elseif e < 1000000.0 then + unit = "k" + value = e / 1000.0 + elseif e < 1000000000.0 then + unit = "M" + value = e / 1000000.0 + elseif e < 1000000000000.0 then + unit = "G" + value = e / 1000000000.0 + elseif e < 1000000000000000.0 then + unit = "T" + value = e / 1000000000000.0 + elseif e < 1000000000000000000.0 then + unit = "P" + value = e / 1000000000000000.0 + elseif e < 1000000000000000000000.0 then + -- if you accomplish this please touch grass + unit = "E" + value = e / 1000000000000000000.0 else - unit = "ZFE" - value = ZFE(fe) + -- how & why did you do this? + unit = "Z" + value = e / 1000000000000000000000.0 end + unit = unit .. label + if combine_label then - return util.sprintf(util.c(format, " %s"), value, unit) + return util.sprintf(util.c(format, " %s"), value, unit), unit else return util.sprintf(format, value), unit end diff --git a/supervisor/facility.lua b/supervisor/facility.lua index 8fb3746..825186f 100644 --- a/supervisor/facility.lua +++ b/supervisor/facility.lua @@ -29,7 +29,7 @@ local DTV_RTU_S_DATA = qtypes.DTV_RTU_S_DATA -- 7.14 kJ per blade for 1 mB of fissile fuel
-- 2856 FE per blade per 1 mB, 285.6 FE per blade per 0.1 mB (minimum) -local POWER_PER_BLADE = util.joules_to_fe(7140) +local POWER_PER_BLADE = util.joules_to_fe_rf(7140) local FLOW_STABILITY_DELAY_S = const.FLOW_STABILITY_DELAY_MS / 1000 @@ -332,9 +332,9 @@ function facility.new(config, cooling_conf) end if has_data then - local energy = util.joules_to_fe(db.tanks.energy) - local input = util.joules_to_fe(db.state.last_input) - local output = util.joules_to_fe(db.state.last_output) + local energy = util.joules_to_fe_rf(db.tanks.energy) + local input = util.joules_to_fe_rf(db.state.last_input) + local output = util.joules_to_fe_rf(db.state.last_output) if self.im_stat_init then self.avg_charge.record(energy, charge_update) @@ -1283,7 +1283,7 @@ function facility.new(config, cooling_conf) status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks } local fe_per_ms = self.avg_net.compute() - local remaining = util.joules_to_fe(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy)) + local remaining = util.joules_to_fe_rf(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy)) status.power[4] = remaining / fe_per_ms end diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 885dd09..ad7a5e2 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.4.1" +local SUPERVISOR_VERSION = "v1.4.2" local println = util.println local println_ts = util.println_ts From 340c6689a93ade8b57f0113d826b0468e49e3f26 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 12:35:26 -0400 Subject: [PATCH 15/21] #523 coordinator configurator updates --- coordinator/configure.lua | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index 8c764d9..fb29193 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -9,9 +9,9 @@ local ppm = require("scada-common.ppm") local tcd = require("scada-common.tcd") local types = require("scada-common.types") local util = require("scada-common.util") -local themes = require("graphics.themes") local core = require("graphics.core") +local themes = require("graphics.themes") local DisplayBox = require("graphics.elements.displaybox") local Div = require("graphics.elements.div") @@ -46,7 +46,8 @@ local RIGHT = core.ALIGN.RIGHT local changes = { { "v1.2.4", { "Added temperature scale options" } }, { "v1.2.12", { "Added main UI theme", "Added front panel UI theme", "Added color accessibility modes" } }, - { "v1.3.3", { "Added standard with black off state color mode", "Added blue indicator color modes" } } + { "v1.3.3", { "Added standard with black off state color mode", "Added blue indicator color modes" } }, + { "v1.5.1", { "Added energy scale options" } } } ---@class crd_configurator @@ -119,6 +120,7 @@ local tmp_cfg = { SpeakerVolume = 1.0, Time24Hour = true, TempScale = 1, + EnergyScale = 1, DisableFlowView = false, MainDisplay = nil, ---@type string FlowDisplay = nil, ---@type string @@ -151,7 +153,8 @@ local fields = { { "UnitDisplays", "Unit Monitors", {} }, { "SpeakerVolume", "Speaker Volume", 1.0 }, { "Time24Hour", "Use 24-hour Time Format", true }, - { "TempScale", "Temperature Scale", 1 }, + { "TempScale", "Temperature Scale", types.TEMP_SCALE.KELVIN }, + { "EnergyScale", "Energy Scale", types.ENERGY_SCALE.FE }, { "DisableFlowView", "Disable Flow Monitor (legacy, discouraged)", false }, { "SVR_Channel", "SVR Channel", 16240 }, { "CRD_Channel", "CRD Channel", 16243 }, @@ -333,7 +336,7 @@ local function config_view(display) 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)} + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had 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 @@ -759,9 +762,13 @@ local function config_view(display) TextBox{parent=crd_c_1,x=1,y=8,text="Temperature Scale"} local temp_scale = RadioButton{parent=crd_c_1,x=1,y=9,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + TextBox{parent=crd_c_1,x=24,y=8,text="Energy Scale"} + local energy_scale = RadioButton{parent=crd_c_1,x=24,y=9,default=ini_cfg.EnergyScale,options=types.ENERGY_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + local function submit_ui_opts() tmp_cfg.Time24Hour = clock_fmt.get_value() == 1 tmp_cfg.TempScale = temp_scale.get_value() + tmp_cfg.EnergyScale = energy_scale.get_value() main_pane.set_value(7) end @@ -969,6 +976,8 @@ local function config_view(display) 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(temp_scale, ini_cfg.TempScale) + try_set(energy_scale, ini_cfg.EnergyScale) try_set(mode, ini_cfg.LogMode) try_set(path, ini_cfg.LogPath) try_set(en_dbg, ini_cfg.LogDebug) @@ -1356,7 +1365,9 @@ 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] == "TempScale" then - val = types.TEMP_SCALE_NAMES[raw] + val = util.strval(types.TEMP_SCALE_NAMES[raw]) + elseif f[1] == "EnergyScale" then + val = util.strval(types.ENERGY_SCALE_NAMES[raw]) elseif f[1] == "MainTheme" then val = util.strval(themes.ui_theme_name(raw)) elseif f[1] == "FrontPanelTheme" then From d58a6a3369751206c66cf28b74f10496b564bdc0 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 12:51:46 -0400 Subject: [PATCH 16/21] #531 pocket energy scale options --- pocket/configure.lua | 23 +++++++++++++++++------ pocket/iocontrol.lua | 19 ++++++++++++++++++- pocket/pocket.lua | 5 ++++- pocket/startup.lua | 2 +- pocket/ui/pages/unit_turbine.lua | 8 ++++---- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/pocket/configure.lua b/pocket/configure.lua index 2d2f446..0002383 100644 --- a/pocket/configure.lua +++ b/pocket/configure.lua @@ -33,7 +33,8 @@ local RIGHT = core.ALIGN.RIGHT -- changes to the config data/format to let the user know local changes = { - { "v0.9.2", { "Added temperature scale options" } } + { "v0.9.2", { "Added temperature scale options" } }, + { "v0.11.3", { "Added energy scale options" } } } ---@class pkt_configurator @@ -76,6 +77,7 @@ local tool_ctl = { ---@class pkt_config local tmp_cfg = { TempScale = 1, + EnergyScale = 1, SVR_Channel = nil, ---@type integer CRD_Channel = nil, ---@type integer PKT_Channel = nil, ---@type integer @@ -94,7 +96,8 @@ local settings_cfg = {} -- all settings fields, their nice names, and their default values local fields = { - { "TempScale", "Temperature Scale", 1 }, + { "TempScale", "Temperature Scale", types.TEMP_SCALE.KELVIN }, + { "EnergyScale", "Energy Scale", types.ENERGY_SCALE.FE }, { "SVR_Channel", "SVR Channel", 16240 }, { "CRD_Channel", "CRD Channel", 16243 }, { "PKT_Channel", "PKT Channel", 16244 }, @@ -175,13 +178,17 @@ local function config_view(display) TextBox{parent=ui_cfg,x=1,y=2,text=" Pocket UI",fg_bg=cpair(colors.black,colors.lime)} - TextBox{parent=ui_c_1,x=1,y=1,height=3,text="You may use the options below to customize formats."} + TextBox{parent=ui_c_1,x=1,y=1,height=3,text="You may customize units below."} - TextBox{parent=ui_c_1,x=1,y=5,text="Temperature Scale"} - local temp_scale = RadioButton{parent=ui_c_1,x=1,y=6,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + TextBox{parent=ui_c_1,x=1,y=4,text="Temperature Scale"} + local temp_scale = RadioButton{parent=ui_c_1,x=1,y=5,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + + TextBox{parent=ui_c_1,x=1,y=10,text="Energy Scale"} + local energy_scale = RadioButton{parent=ui_c_1,x=1,y=11,default=ini_cfg.EnergyScale,options=types.ENERGY_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} local function submit_ui_opts() tmp_cfg.TempScale = temp_scale.get_value() + tmp_cfg.EnergyScale = energy_scale.get_value() main_pane.set_value(3) end @@ -378,6 +385,8 @@ local function config_view(display) load_settings(settings_cfg, true) load_settings(ini_cfg) + try_set(temp_scale, ini_cfg.TempScale) + try_set(energy_scale, ini_cfg.EnergyScale) try_set(svr_chan, ini_cfg.SVR_Channel) try_set(crd_chan, ini_cfg.CRD_Channel) try_set(pkt_chan, ini_cfg.PKT_Channel) @@ -504,7 +513,9 @@ local function config_view(display) elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") elseif f[1] == "TempScale" then - val = types.TEMP_SCALE_NAMES[raw] + val = util.strval(types.TEMP_SCALE_NAMES[raw]) + elseif f[1] == "EnergyScale" then + val = util.strval(types.ENERGY_SCALE_NAMES[raw]) end if val == "nil" then val = "" end diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index de69dd3..fa2c4aa 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -10,6 +10,9 @@ local util = require("scada-common.util") local ALARM = types.ALARM local ALARM_STATE = types.ALARM_STATE + +local ENERGY_SCALE = types.ENERGY_SCALE +local ENERGY_UNITS = types.ENERGY_SCALE_UNITS local TEMP_SCALE = types.TEMP_SCALE local TEMP_UNITS = types.TEMP_SCALE_UNITS @@ -88,8 +91,10 @@ end -- initialize facility-dependent components of pocket iocontrol ---@param conf facility_conf configuration ---@param temp_scale TEMP_SCALE temperature unit -function iocontrol.init_fac(conf, temp_scale) +---@param energy_scale ENERGY_SCALE energy unit +function iocontrol.init_fac(conf, temp_scale, energy_scale) io.temp_label = TEMP_UNITS[temp_scale] + io.energy_label = ENERGY_UNITS[energy_scale] -- temperature unit label and conversion function (from Kelvin) if temp_scale == TEMP_SCALE.CELSIUS then @@ -103,6 +108,18 @@ function iocontrol.init_fac(conf, temp_scale) io.temp_convert = function (t) return t end end + -- energy unit label and conversion function (from Joules unless otherwise specified) + if energy_scale == ENERGY_SCALE.FE or energy_scale == ENERGY_SCALE.RF then + io.energy_convert = util.joules_to_fe_rf + io.energy_convert_from_fe = function (t) return t end + io.energy_convert_to_fe = function (t) return t end + else + io.energy_label = "J" + io.energy_convert = function (t) return t end + io.energy_convert_from_fe = util.fe_rf_to_joules + io.energy_convert_to_fe = util.joules_to_fe_rf + end + -- facility data structure ---@class pioctl_facility io.facility = { diff --git a/pocket/pocket.lua b/pocket/pocket.lua index fab0827..8cd3faa 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -36,6 +36,7 @@ function pocket.load_config() if not settings.load("/pocket.settings") then return false end config.TempScale = settings.get("TempScale") + config.EnergyScale = settings.get("EnergyScale") config.SVR_Channel = settings.get("SVR_Channel") config.CRD_Channel = settings.get("CRD_Channel") @@ -52,6 +53,8 @@ function pocket.load_config() cfv.assert_type_int(config.TempScale) cfv.assert_range(config.TempScale, 1, 4) + cfv.assert_type_int(config.EnergyScale) + cfv.assert_range(config.EnergyScale, 1, 3) cfv.assert_channel(config.SVR_Channel) cfv.assert_channel(config.CRD_Channel) @@ -675,7 +678,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) -- get configuration local conf = { num_units = fac_config[1], cooling = fac_config[2] } - iocontrol.init_fac(conf, config.TempScale) + iocontrol.init_fac(conf, config.TempScale, config.EnergyScale) log.info("coordinator connection established") self.establish_delay_counter = 0 diff --git a/pocket/startup.lua b/pocket/startup.lua index b4a6f52..bf51bf1 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -20,7 +20,7 @@ local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") local threads = require("pocket.threads") -local POCKET_VERSION = "v0.11.2-alpha" +local POCKET_VERSION = "v0.11.3-alpha" local println = util.println local println_ts = util.println_ts diff --git a/pocket/ui/pages/unit_turbine.lua b/pocket/ui/pages/unit_turbine.lua index 0a14c33..df90061 100644 --- a/pocket/ui/pages/unit_turbine.lua +++ b/pocket/ui/pages/unit_turbine.lua @@ -59,13 +59,13 @@ return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, update) ccool.register(ps, "energy_fill", ccool.update) TextBox{parent=tbn_div,text="Production",x=3,y=3,width=17,fg_bg=label} - local prod_rate = PowerIndicator{parent=tbn_div,x=3,y=4,lu_colors=lu_col,label="",format="%11.2f",value=0,rate=true,width=17,fg_bg=text_fg} + local prod_rate = PowerIndicator{parent=tbn_div,x=3,y=4,lu_colors=lu_col,label="",unit=db.energy_label,format="%11.2f",value=0,rate=true,width=17,fg_bg=text_fg} TextBox{parent=tbn_div,text="Flow Rate",x=3,y=5,width=17,fg_bg=label} local flow_rate = DataIndicator{parent=tbn_div,x=3,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=17,fg_bg=text_fg} TextBox{parent=tbn_div,text="Steam Input Rate",x=3,y=7,width=17,fg_bg=label} local input_rate = DataIndicator{parent=tbn_div,x=3,y=8,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=17,fg_bg=text_fg} - prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end) + prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(db.energy_convert(val)) end) flow_rate.register(ps, "flow_rate", flow_rate.update) input_rate.register(ps, "steam_input_rate", input_rate.update) @@ -99,10 +99,10 @@ return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, update) TextBox{parent=tbn_ext_div,text="Energy Fill",x=1,y=6,width=12,fg_bg=label} local charge_p = DataIndicator{parent=tbn_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local charge_amnt = PowerIndicator{parent=tbn_ext_div,x=1,y=7,lu_colors=lu_col,label="",format="%17.4f",value=0,width=21,fg_bg=text_fg} + local charge_amnt = PowerIndicator{parent=tbn_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit=db.energy_label,format="%17.4f",value=0,width=21,fg_bg=text_fg} charge_p.register(ps, "energy_fill", function (x) charge_p.update(x * 100) end) - charge_amnt.register(ps, "energy", charge_amnt.update) + charge_amnt.register(ps, "energy", function (val) charge_amnt.update(db.energy_convert(val)) end) TextBox{parent=tbn_ext_div,text="Rotation Rate",x=1,y=9,width=13,fg_bg=label} local rotation = DataIndicator{parent=tbn_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="",format="%21.12f",value=0,width=21,fg_bg=text_fg} From f00751edebb419d9f2fb2a10693a089c68b875df Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 13:17:56 -0400 Subject: [PATCH 17/21] still display supervisor/coordinator address info if not linked to both --- pocket/iocontrol.lua | 31 ++++++++++++++++++++++--------- pocket/pocket.lua | 20 ++++++++++++-------- pocket/startup.lua | 4 ++-- pocket/ui/apps/sys_apps.lua | 4 ++-- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index fa2c4aa..aff9c17 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -38,10 +38,15 @@ local io = { ps = psil.create() } +local config = nil ---@type pkt_config + -- initialize facility-independent components of pocket iocontrol ---@param comms pocket_comms ---@param nav pocket_nav -function iocontrol.init_core(comms, nav) +---@param cfg pkt_config +function iocontrol.init_core(comms, nav, cfg) + config = cfg + io.nav = nav ---@class pocket_ioctl_diag @@ -89,10 +94,9 @@ function iocontrol.init_core(comms, nav) end -- initialize facility-dependent components of pocket iocontrol ----@param conf facility_conf configuration ----@param temp_scale TEMP_SCALE temperature unit ----@param energy_scale ENERGY_SCALE energy unit -function iocontrol.init_fac(conf, temp_scale, energy_scale) +---@param conf facility_conf facility configuration +function iocontrol.init_fac(conf) + local temp_scale, energy_scale = config.TempScale, config.EnergyScale io.temp_label = TEMP_UNITS[temp_scale] io.energy_label = ENERGY_UNITS[energy_scale] @@ -346,8 +350,8 @@ end -- set network link state ---@param state POCKET_LINK_STATE ----@param sv_addr integer? supervisor address if linked ----@param api_addr integer? coordinator address if linked +---@param sv_addr integer|false|nil supervisor address if linked, nil if unchanged, false if unlinked +---@param api_addr integer|false|nil coordinator address if linked, nil if unchanged, false if unlinked function iocontrol.report_link_state(state, sv_addr, api_addr) io.ps.publish("link_state", state) @@ -359,8 +363,17 @@ function iocontrol.report_link_state(state, sv_addr, api_addr) io.ps.publish("crd_conn_quality", 0) end - if sv_addr then io.ps.publish("sv_addr", sv_addr) end - if api_addr then io.ps.publish("api_addr", api_addr) end + if sv_addr then + io.ps.publish("sv_addr", util.c(sv_addr, ":", config.SVR_Channel)) + elseif sv_addr == false then + io.ps.publish("sv_addr", "unknown (not linked)") + end + + if api_addr then + io.ps.publish("api_addr", util.c(api_addr, ":", config.CRD_Channel)) + elseif api_addr == false then + io.ps.publish("api_addr", "unknown (not linked)") + end end -- determine supervisor connection quality (trip time) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 8cd3faa..f815c3c 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -493,7 +493,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) -- attempt to re-link if any of the dependent links aren't active function public.link_update() if not self.sv.linked then - iocontrol.report_link_state(util.trinary(self.api.linked, LINK_STATE.API_LINK_ONLY, LINK_STATE.UNLINKED)) + if self.api.linked then + iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY, false, nil) + else + iocontrol.report_link_state(LINK_STATE.UNLINKED, false, false) + end if self.establish_delay_counter <= 0 then _send_sv_establish() @@ -502,7 +506,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) self.establish_delay_counter = self.establish_delay_counter - 1 end elseif not self.api.linked then - iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY) + iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY, nil, false) if self.establish_delay_counter <= 0 then _send_api_establish() @@ -512,7 +516,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) end else -- linked, all good! - iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr) + iocontrol.report_link_state(LINK_STATE.LINKED) end end @@ -678,7 +682,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) -- get configuration local conf = { num_units = fac_config[1], cooling = fac_config[2] } - iocontrol.init_fac(conf, config.TempScale, config.EnergyScale) + iocontrol.init_fac(conf) log.info("coordinator connection established") self.establish_delay_counter = 0 @@ -686,9 +690,9 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) self.api.addr = src_addr if self.sv.linked then - iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr) + iocontrol.report_link_state(LINK_STATE.LINKED, nil, self.api.addr) else - iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY) + iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY, nil, self.api.addr) end else log.debug("invalid facility configuration table received from coordinator, establish failed") @@ -826,9 +830,9 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) self.sv.addr = src_addr if self.api.linked then - iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr) + iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, nil) else - iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY) + iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY, self.sv.addr, nil) end elseif est_ack == ESTABLISH_ACK.DENY then if self.sv.last_est_ack ~= est_ack then diff --git a/pocket/startup.lua b/pocket/startup.lua index bf51bf1..d0394c0 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -20,7 +20,7 @@ local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") local threads = require("pocket.threads") -local POCKET_VERSION = "v0.11.3-alpha" +local POCKET_VERSION = "v0.11.4-alpha" local println = util.println local println_ts = util.println_ts @@ -152,7 +152,7 @@ local function main() log.debug("startup> comms init") -- init I/O control - iocontrol.init_core(smem_sys.pocket_comms, smem_sys.nav) + iocontrol.init_core(smem_sys.pocket_comms, smem_sys.nav, config) ---------------------------------------- -- start the UI diff --git a/pocket/ui/apps/sys_apps.lua b/pocket/ui/apps/sys_apps.lua index e5fecd6..b5c8f88 100644 --- a/pocket/ui/apps/sys_apps.lua +++ b/pocket/ui/apps/sys_apps.lua @@ -75,8 +75,8 @@ local function create_pages(root) TextBox{parent=nt_div,x=2,text="Coordinator Address",alignment=ALIGN.LEFT,fg_bg=label} local coord = TextBox{parent=nt_div,x=2,text="",alignment=ALIGN.LEFT} - sv.register(db.ps, "sv_addr", function (addr) sv.set_value(util.c(addr, ":", config.SVR_Channel)) end) - coord.register(db.ps, "api_addr", function (addr) coord.set_value(util.c(addr, ":", config.CRD_Channel)) end) + sv.register(db.ps, "sv_addr", sv.set_value) + coord.register(db.ps, "api_addr", coord.set_value) nt_div.line_break() TextBox{parent=nt_div,x=2,text="Message Authentication",alignment=ALIGN.LEFT,fg_bg=label} From de6d8a89ca51a181d7009a06a7fe57b03d1f9d40 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 16:23:19 -0400 Subject: [PATCH 18/21] avoid redundant calls to report_link_state --- pocket/pocket.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index f815c3c..5ca6349 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -514,9 +514,6 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) else self.establish_delay_counter = self.establish_delay_counter - 1 end - else - -- linked, all good! - iocontrol.report_link_state(LINK_STATE.LINKED) end end From e750ffe69ddce66aa64ba75ea750a18b24dda052 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 16:23:37 -0400 Subject: [PATCH 19/21] updated element asserts for power indicator and incremented graphics version --- graphics/core.lua | 2 +- graphics/elements/indicators/power.lua | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/graphics/core.lua b/graphics/core.lua index f6a647a..4bb0298 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -7,7 +7,7 @@ local flasher = require("graphics.flasher") local core = {} -core.version = "2.3.0" +core.version = "2.3.1" core.flasher = flasher core.events = events diff --git a/graphics/elements/indicators/power.lua b/graphics/elements/indicators/power.lua index adcd100..7a09f95 100644 --- a/graphics/elements/indicators/power.lua +++ b/graphics/elements/indicators/power.lua @@ -24,8 +24,9 @@ local element = require("graphics.element") ---@param args power_indicator_args ---@return graphics_element element, element_id id local function power(args) - element.assert(type(args.value) == "number", "value is a required field") + element.assert(type(args.label) == "string", "label is a required field") element.assert(type(args.unit) == "string", "unit is a required field") + element.assert(type(args.value) == "number", "value is a required field") element.assert(util.is_int(args.width), "width is a required field") args.height = 1 From 08eee198c8bf57e89c7e920018fe936c8cb27d2a Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 20:35:09 -0400 Subject: [PATCH 20/21] cleanup and rewording notices --- reactor-plc/config/system.lua | 2 +- rtu/configure.lua | 2 +- supervisor/configure.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reactor-plc/config/system.lua b/reactor-plc/config/system.lua index 8170eaa..b6934a1 100644 --- a/reactor-plc/config/system.lua +++ b/reactor-plc/config/system.lua @@ -616,4 +616,4 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, style, exit) --#endregion end -return system \ No newline at end of file +return system diff --git a/rtu/configure.lua b/rtu/configure.lua index 4cb3ef0..b5cceeb 100644 --- a/rtu/configure.lua +++ b/rtu/configure.lua @@ -285,7 +285,7 @@ local function config_view(display) local y_start = 2 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)} + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had 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 else TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the RTU gateway configurator! Please select one of the following options."} diff --git a/supervisor/configure.lua b/supervisor/configure.lua index eedc476..982550a 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -181,7 +181,7 @@ local function config_view(display) TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Supervisor 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)} + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had 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 From 2904621e81d4bf43dd21cfcf79090110986d9e15 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 27 Jul 2024 20:55:00 -0400 Subject: [PATCH 21/21] fixed wrong disable format on self-check button --- reactor-plc/configure.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reactor-plc/configure.lua b/reactor-plc/configure.lua index ac28cc0..3cb4241 100644 --- a/reactor-plc/configure.lua +++ b/reactor-plc/configure.lua @@ -186,7 +186,7 @@ local function config_view(display) 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} - tool_ctl.self_check = PushButton{parent=main_page,x=10,y=17,min_width=12,text="Self-Check",callback=function()main_pane.set_value(8)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.orange,colors.white)} + tool_ctl.self_check = PushButton{parent=main_page,x=10,y=17,min_width=12,text="Self-Check",callback=function()main_pane.set_value(8)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg} tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg} PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}