mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
1089 lines
50 KiB
Lua
1089 lines
50 KiB
Lua
--
|
|
-- Configuration GUI
|
|
--
|
|
|
|
local log = require("scada-common.log")
|
|
local tcd = require("scada-common.tcd")
|
|
local util = require("scada-common.util")
|
|
|
|
local core = require("graphics.core")
|
|
|
|
local DisplayBox = require("graphics.elements.displaybox")
|
|
local Div = require("graphics.elements.div")
|
|
local ListBox = require("graphics.elements.listbox")
|
|
local MultiPane = require("graphics.elements.multipane")
|
|
local TextBox = require("graphics.elements.textbox")
|
|
|
|
local CheckBox = require("graphics.elements.controls.checkbox")
|
|
local PushButton = require("graphics.elements.controls.push_button")
|
|
local Radio2D = require("graphics.elements.controls.radio_2d")
|
|
local RadioButton = require("graphics.elements.controls.radio_button")
|
|
|
|
local NumberField = require("graphics.elements.form.number_field")
|
|
local TextField = require("graphics.elements.form.text_field")
|
|
|
|
local println = util.println
|
|
local tri = util.trinary
|
|
|
|
local cpair = core.cpair
|
|
|
|
local LEFT = core.ALIGN.LEFT
|
|
local CENTER = core.ALIGN.CENTER
|
|
local RIGHT = core.ALIGN.RIGHT
|
|
|
|
-- changes to the config data/format to let the user know
|
|
local changes = {}
|
|
|
|
---@class svr_configurator
|
|
local configurator = {}
|
|
|
|
local style = {}
|
|
|
|
style.root = cpair(colors.black, colors.lightGray)
|
|
style.header = cpair(colors.white, colors.gray)
|
|
|
|
style.colors = {
|
|
{ c = colors.red, hex = 0xdf4949 },
|
|
{ c = colors.orange, hex = 0xffb659 },
|
|
{ c = colors.yellow, hex = 0xfffc79 },
|
|
{ c = colors.lime, hex = 0x80ff80 },
|
|
{ c = colors.green, hex = 0x4aee8a },
|
|
{ c = colors.cyan, hex = 0x34bac8 },
|
|
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
{ c = colors.blue, hex = 0x0096ff },
|
|
{ c = colors.purple, hex = 0xb156ee },
|
|
{ c = colors.pink, hex = 0xf26ba2 },
|
|
{ c = colors.magenta, hex = 0xf9488a },
|
|
{ c = colors.lightGray, hex = 0xcacaca },
|
|
{ c = colors.gray, hex = 0x575757 }
|
|
}
|
|
|
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
|
local nav_fg_bg = bw_fg_bg
|
|
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
|
|
|
local tool_ctl = {
|
|
ask_config = false,
|
|
has_config = false,
|
|
viewing_config = false,
|
|
importing_legacy = false,
|
|
|
|
view_cfg = nil, ---@type graphics_element
|
|
settings_apply = nil, ---@type graphics_element
|
|
|
|
gen_summary = nil, ---@type function
|
|
show_current_cfg = nil, ---@type function
|
|
load_legacy = nil, ---@type function
|
|
|
|
show_auth_key = nil, ---@type function
|
|
show_key_btn = nil, ---@type graphics_element
|
|
auth_key_textbox = nil, ---@type graphics_element
|
|
auth_key_value = "",
|
|
|
|
cooling_elems = {},
|
|
tank_elems = {},
|
|
|
|
vis_ftanks = {},
|
|
vis_utanks = {}
|
|
}
|
|
|
|
---@class svr_config
|
|
local tmp_cfg = {
|
|
UnitCount = 1,
|
|
CoolingConfig = {},
|
|
FacilityTankMode = 0,
|
|
FacilityTankDefs = {},
|
|
SVR_Channel = nil, ---@type integer
|
|
PLC_Channel = nil, ---@type integer
|
|
RTU_Channel = nil, ---@type integer
|
|
CRD_Channel = nil, ---@type integer
|
|
PKT_Channel = nil, ---@type integer
|
|
PLC_Timeout = nil, ---@type number
|
|
RTU_Timeout = nil, ---@type number
|
|
CRD_Timeout = nil, ---@type number
|
|
PKT_Timeout = nil, ---@type number
|
|
TrustedRange = nil, ---@type number
|
|
AuthKey = nil, ---@type string|nil
|
|
LogMode = 0,
|
|
LogPath = "",
|
|
LogDebug = false,
|
|
}
|
|
|
|
---@class svr_config
|
|
local ini_cfg = {}
|
|
---@class svr_config
|
|
local settings_cfg = {}
|
|
|
|
-- all settings fields, their nice names, and their default values
|
|
local fields = {
|
|
{ "UnitCount", "Number of Reactors", 1 },
|
|
{ "CoolingConfig", "Cooling Configuration", {} },
|
|
{ "FacilityTankMode", "Facility Tank Mode", 0 },
|
|
{ "FacilityTankDefs", "Facility Tank Definitions", {} },
|
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
|
{ "PLC_Channel", "PLC Channel", 16241 },
|
|
{ "RTU_Channel", "RTU Channel", 16242 },
|
|
{ "CRD_Channel", "CRD Channel", 16243 },
|
|
{ "PKT_Channel", "PKT Channel", 16244 },
|
|
{ "PLC_Timeout", "PLC Connection Timeout", 5 },
|
|
{ "RTU_Timeout", "RTU Connection Timeout", 5 },
|
|
{ "CRD_Timeout", "CRD Connection Timeout", 5 },
|
|
{ "PKT_Timeout", "PKT Connection Timeout", 5 },
|
|
{ "TrustedRange", "Trusted Range", 0 },
|
|
{ "AuthKey", "Facility Auth Key" , ""},
|
|
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
|
{ "LogPath", "Log Path", "/log.txt" },
|
|
{ "LogDebug","Log Debug Messages", false }
|
|
}
|
|
|
|
-- load data from the settings file
|
|
---@param target svr_config
|
|
---@param raw boolean? true to not use default values
|
|
local function load_settings(target, raw)
|
|
for _, v in pairs(fields) do settings.unset(v[1]) end
|
|
|
|
local loaded = settings.load("/supervisor.settings")
|
|
|
|
for _, v in pairs(fields) do target[v[1]] = settings.get(v[1], tri(raw, nil, v[3])) end
|
|
|
|
return loaded
|
|
end
|
|
|
|
-- create the config view
|
|
---@param display graphics_element
|
|
local function config_view(display)
|
|
---@diagnostic disable-next-line: undefined-field
|
|
local function exit() os.queueEvent("terminate") end
|
|
|
|
TextBox{parent=display,y=1,text="Supervisor Configurator",alignment=CENTER,height=1,fg_bg=style.header}
|
|
|
|
local root_pane_div = Div{parent=display,x=1,y=2}
|
|
|
|
local main_page = Div{parent=root_pane_div,x=1,y=1}
|
|
local svr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local net_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local log_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local summary = Div{parent=root_pane_div,x=1,y=1}
|
|
local changelog = Div{parent=root_pane_div,x=1,y=1}
|
|
local import_err = Div{parent=root_pane_div,x=1,y=1}
|
|
|
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,svr_cfg,net_cfg,log_cfg,summary,changelog,import_err}}
|
|
|
|
-- MAIN PAGE
|
|
|
|
local y_start = 5
|
|
|
|
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the 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)}
|
|
y_start = y_start + 5
|
|
end
|
|
|
|
local function view_config()
|
|
tool_ctl.viewing_config = true
|
|
tool_ctl.gen_summary(settings_cfg)
|
|
tool_ctl.settings_apply.hide(true)
|
|
main_pane.set_value(5)
|
|
end
|
|
|
|
if fs.exists("/supervisor/config.lua") then
|
|
PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg}
|
|
y_start = y_start + 2
|
|
end
|
|
|
|
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
|
|
|
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
|
|
|
|
PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- SUPERVISOR CONFIG
|
|
|
|
local svr_c_1 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
|
local svr_c_2 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
|
local svr_c_3 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
|
local svr_c_4 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
|
local svr_c_5 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
|
local svr_c_6 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
|
|
|
local svr_pane = MultiPane{parent=svr_cfg,x=1,y=4,panes={svr_c_1,svr_c_2,svr_c_3,svr_c_4,svr_c_5,svr_c_6}}
|
|
|
|
TextBox{parent=svr_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.green)}
|
|
|
|
TextBox{parent=svr_c_1,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
|
local num_units = NumberField{parent=svr_c_1,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
|
TextBox{parent=svr_c_1,x=7,y=5,height=1,text="reactors"}
|
|
|
|
local nu_error = TextBox{parent=svr_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_num_units()
|
|
local count = tonumber(num_units.get_value())
|
|
if count ~= nil and count > 0 and count < 5 then
|
|
nu_error.hide(true)
|
|
tmp_cfg.UnitCount = count
|
|
|
|
local confs = tool_ctl.cooling_elems
|
|
if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end
|
|
if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end
|
|
if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end
|
|
|
|
svr_pane.set_value(2)
|
|
else nu_error.show() end
|
|
end
|
|
|
|
PushButton{parent=svr_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=svr_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=svr_c_2,x=1,y=1,height=4,text="Please provide the reactor cooling configuration below. This includes the number of turbines, boilers, and if that reactor has a connection to a dynamic tank for emergency coolant."}
|
|
TextBox{parent=svr_c_2,x=1,y=6,height=1,text="UNIT TURBINES BOILERS HAS TANK CONNECTION?",fg_bg=g_lg_fg_bg}
|
|
|
|
for i = 1, 4 do
|
|
local num_t, num_b, has_t = 1, 0, false
|
|
|
|
if ini_cfg.CoolingConfig[i] then
|
|
local conf = ini_cfg.CoolingConfig[i]
|
|
if util.is_int(conf.TurbineCount) then num_t = math.min(3, math.max(1, conf.TurbineCount or 1)) end
|
|
if util.is_int(conf.BoilerCount) then num_b = math.min(2, math.max(0, conf.BoilerCount or 0)) end
|
|
has_t = conf.TankConnection == true
|
|
end
|
|
|
|
local line = Div{parent=svr_c_2,x=1,y=7+i,height=1}
|
|
|
|
TextBox{parent=line,text="Unit "..i,width=6}
|
|
local turbines = NumberField{parent=line,x=9,y=1,width=5,max_chars=2,default=num_t,min=1,max=3,fg_bg=bw_fg_bg}
|
|
local boilers = NumberField{parent=line,x=20,y=1,width=5,max_chars=2,default=num_b,min=0,max=2,fg_bg=bw_fg_bg}
|
|
local tank = CheckBox{parent=line,x=30,y=1,label="Is Connected",default=has_t,box_fg_bg=cpair(colors.green,colors.black)}
|
|
|
|
tool_ctl.cooling_elems[i] = { line = line, turbines = turbines, boilers = boilers, tank = tank }
|
|
end
|
|
|
|
local cool_err = TextBox{parent=svr_c_2,x=8,y=14,height=1,width=33,text="Please fill out all fields.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_cooling()
|
|
local any_missing = false
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
local conf = tool_ctl.cooling_elems[i]
|
|
any_missing = any_missing or (tonumber(conf.turbines.get_value()) == nil)
|
|
any_missing = any_missing or (tonumber(conf.boilers.get_value()) == nil)
|
|
end
|
|
|
|
if any_missing then
|
|
cool_err.show()
|
|
else
|
|
local any_has_tank = false
|
|
|
|
tmp_cfg.CoolingConfig = {}
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
local conf = tool_ctl.cooling_elems[i]
|
|
tmp_cfg.CoolingConfig[i] = { TurbineCount = tonumber(conf.turbines.get_value()), BoilerCount = tonumber(conf.boilers.get_value()), TankConnection = conf.tank.get_value() }
|
|
if conf.tank.get_value() then any_has_tank = true end
|
|
end
|
|
|
|
for i = 1, 4 do
|
|
local elem = tool_ctl.tank_elems[i]
|
|
if i <= tmp_cfg.UnitCount then
|
|
elem.div.show()
|
|
if tmp_cfg.CoolingConfig[i].TankConnection then
|
|
elem.no_tank.hide()
|
|
elem.tank_opt.show()
|
|
else
|
|
elem.tank_opt.hide(true)
|
|
elem.no_tank.show()
|
|
end
|
|
else elem.div.hide(true) end
|
|
end
|
|
|
|
if any_has_tank then svr_pane.set_value(3) else main_pane.set_value(3) end
|
|
end
|
|
end
|
|
|
|
PushButton{parent=svr_c_2,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=svr_c_2,x=44,y=14,text="Next \x1a",callback=submit_cooling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=svr_c_3,x=1,y=1,height=6,text="You have set one or more of your units to use dynamic tanks for emergency coolant. You have two paths for configuration. The first is to assign dynamic tanks to reactor units; one tank per reactor, only connected to that reactor. RTU configurations must also assign it as such."}
|
|
TextBox{parent=svr_c_3,x=1,y=8,height=3,text="Alternatively, you can configure them as facility tanks to connect to multiple reactor units. These can intermingle with unit-specific tanks."}
|
|
|
|
local en_fac_tanks = CheckBox{parent=svr_c_3,x=1,y=12,label="Use Facility Dynamic Tanks",default=ini_cfg.FacilityTankMode>0,box_fg_bg=cpair(colors.green,colors.black)}
|
|
|
|
local function submit_en_fac_tank()
|
|
if en_fac_tanks.get_value() then
|
|
svr_pane.set_value(4)
|
|
tmp_cfg.FacilityTankMode = util.trinary(tmp_cfg.FacilityTankMode == 0, 1, math.min(8, math.max(1, ini_cfg.FacilityTankMode)))
|
|
else
|
|
tmp_cfg.FacilityTankMode = 0
|
|
tmp_cfg.FacilityTankDefs = {}
|
|
main_pane.set_value(3)
|
|
end
|
|
end
|
|
|
|
PushButton{parent=svr_c_3,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=svr_c_3,x=44,y=14,text="Next \x1a",callback=submit_en_fac_tank,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=svr_c_4,x=1,y=1,height=4,text="Please set unit connections to dynamic tanks, selecting at least one facility tank. The layout for facility tanks will be configured next."}
|
|
|
|
for i = 1, 4 do
|
|
local val = math.max(1, ini_cfg.FacilityTankDefs[i] or 2)
|
|
local div = Div{parent=svr_c_4,x=1,y=3+(2*i),height=2}
|
|
|
|
TextBox{parent=div,x=1,y=1,width=33,height=1,text="Unit "..i.." will be connected to..."}
|
|
TextBox{parent=div,x=6,y=2,width=3,height=1,text="..."}
|
|
local tank_opt = Radio2D{parent=div,x=10,y=2,rows=1,columns=2,default=val,options={"its own Unit Tank","a Facility Tank"},radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.green,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
|
|
local no_tank = TextBox{parent=div,x=9,y=2,width=34,height=1,text="no tank (as you set two steps ago)",fg_bg=cpair(colors.gray,colors.lightGray),hidden=true}
|
|
|
|
tool_ctl.tank_elems[i] = { div = div, tank_opt = tank_opt, no_tank = no_tank }
|
|
end
|
|
|
|
local tank_err = TextBox{parent=svr_c_4,x=8,y=14,height=1,width=33,text="You selected no facility tanks.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function hide_fconn(i)
|
|
if i > 1 then tool_ctl.vis_ftanks[i].pipe_conn.hide(true)
|
|
else tool_ctl.vis_ftanks[i].line.hide(true) end
|
|
end
|
|
|
|
local function submit_tank_defs()
|
|
local any_fac = false
|
|
|
|
tmp_cfg.FacilityTankDefs = {}
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
local def
|
|
|
|
if tmp_cfg.CoolingConfig[i].TankConnection then
|
|
def = tool_ctl.tank_elems[i].tank_opt.get_value()
|
|
any_fac = any_fac or (def == 2)
|
|
else def = 0 end
|
|
|
|
if def == 1 then
|
|
tool_ctl.vis_utanks[i].line.show()
|
|
tool_ctl.vis_utanks[i].label.set_value("Tank U" .. i)
|
|
hide_fconn(i)
|
|
else
|
|
if def == 2 then
|
|
if i > 1 then tool_ctl.vis_ftanks[i].pipe_conn.show()
|
|
else tool_ctl.vis_ftanks[i].line.show() end
|
|
else hide_fconn(i) end
|
|
tool_ctl.vis_utanks[i].line.hide(true)
|
|
end
|
|
|
|
tmp_cfg.FacilityTankDefs[i] = def
|
|
end
|
|
|
|
for i = tmp_cfg.UnitCount + 1, 4 do
|
|
tool_ctl.vis_utanks[i].line.hide(true)
|
|
end
|
|
|
|
tool_ctl.vis_draw(tmp_cfg.FacilityTankMode)
|
|
|
|
if any_fac then
|
|
tank_err.hide(true)
|
|
svr_pane.set_value(5)
|
|
else tank_err.show() end
|
|
end
|
|
|
|
PushButton{parent=svr_c_4,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=svr_c_4,x=44,y=14,text="Next \x1a",callback=submit_tank_defs,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=svr_c_5,x=1,y=1,height=1,text="Please select your dynamic tank layout."}
|
|
TextBox{parent=svr_c_5,x=12,y=3,height=1,text="Facility Tanks Unit Tanks",fg_bg=g_lg_fg_bg}
|
|
|
|
--#region Tank Layout Visualizer
|
|
|
|
local pipe_cpair = cpair(colors.blue,colors.lightGray)
|
|
|
|
local vis = Div{parent=svr_c_5,x=14,y=5,height=7}
|
|
|
|
local vis_unit_list = TextBox{parent=vis,x=15,y=1,width=6,height=7,text="Unit 1\n\nUnit 2\n\nUnit 3\n\nUnit 4"}
|
|
|
|
-- draw unit tanks and their pipes
|
|
for i = 1, 4 do
|
|
local line = Div{parent=vis,x=22,y=(i*2)-1,width=13,height=1}
|
|
TextBox{parent=line,width=5,height=1,text=string.rep("\x8c",5),fg_bg=pipe_cpair}
|
|
local label = TextBox{parent=line,x=7,y=1,width=7,height=1,text="Tank ?"}
|
|
tool_ctl.vis_utanks[i] = { line = line, label = label }
|
|
end
|
|
|
|
-- draw facility tank connections
|
|
|
|
local ftank_1 = Div{parent=vis,x=1,y=1,width=13,height=1}
|
|
TextBox{parent=ftank_1,width=7,height=1,text="Tank F1"}
|
|
tool_ctl.vis_ftanks[1] = {
|
|
line = ftank_1, pipe_direct = TextBox{parent=ftank_1,x=9,y=1,width=5,text=string.rep("\x8c",5),fg_bg=pipe_cpair}
|
|
}
|
|
|
|
for i = 2, 4 do
|
|
local line = Div{parent=vis,x=1,y=(i-1)*2,width=13,height=2}
|
|
local pipe_conn = TextBox{parent=line,x=13,y=2,width=1,height=1,text="\x8c",fg_bg=pipe_cpair}
|
|
local pipe_chain = TextBox{parent=line,x=12,y=1,width=1,height=2,text="\x95\n\x8d",fg_bg=pipe_cpair}
|
|
local pipe_direct = TextBox{parent=line,x=9,y=2,width=4,height=1,text="\x8c\x8c\x8c\x8c",fg_bg=pipe_cpair}
|
|
local label = TextBox{parent=line,x=1,y=2,width=7,height=1,text=""}
|
|
tool_ctl.vis_ftanks[i] = { line = line, pipe_conn = pipe_conn, pipe_chain = pipe_chain, pipe_direct = pipe_direct, label = label }
|
|
end
|
|
|
|
-- draw the pipe visualization
|
|
---@param mode integer pipe mode
|
|
function tool_ctl.vis_draw(mode)
|
|
-- is a facility tank connected to this unit
|
|
---@param i integer unit 1 - 4
|
|
---@return boolean connected
|
|
local function is_ft(i) return tmp_cfg.FacilityTankDefs[i] == 2 end
|
|
|
|
local u_text = ""
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
u_text = u_text .. "Unit " .. i .. "\n\n"
|
|
end
|
|
|
|
vis_unit_list.set_value(u_text)
|
|
|
|
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")
|
|
else
|
|
tool_ctl.vis_ftanks[1].pipe_direct.set_value(string.rep("\x8c",5))
|
|
end
|
|
end
|
|
|
|
local _2_12_need_passt = (mode == 1 and (is_ft(3) or is_ft(4))) or (mode == 2 and is_ft(3))
|
|
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)
|
|
|
|
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)
|
|
if _2_12_need_passt then
|
|
tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x9d")
|
|
else
|
|
tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x8d")
|
|
end
|
|
tool_ctl.vis_ftanks[2].pipe_chain.show()
|
|
else
|
|
tool_ctl.vis_ftanks[2].label.show()
|
|
next_idx = next_idx + 1
|
|
|
|
tool_ctl.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")
|
|
else
|
|
tool_ctl.vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x8c")
|
|
end
|
|
tool_ctl.vis_ftanks[2].pipe_direct.show()
|
|
end
|
|
|
|
tool_ctl.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()
|
|
else
|
|
tool_ctl.vis_ftanks[2].line.hide(true)
|
|
end
|
|
|
|
if is_ft(3) then
|
|
tool_ctl.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)
|
|
if (mode == 1 or mode == 4) and is_ft(4) then
|
|
tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x9d")
|
|
else
|
|
tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x8d")
|
|
end
|
|
tool_ctl.vis_ftanks[3].pipe_chain.show()
|
|
else
|
|
tool_ctl.vis_ftanks[3].label.show()
|
|
next_idx = next_idx + 1
|
|
|
|
tool_ctl.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")
|
|
else
|
|
tool_ctl.vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x8c")
|
|
end
|
|
tool_ctl.vis_ftanks[3].pipe_direct.show()
|
|
end
|
|
|
|
tool_ctl.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()
|
|
else
|
|
tool_ctl.vis_ftanks[3].line.hide(true)
|
|
end
|
|
|
|
if is_ft(4) then
|
|
tool_ctl.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()
|
|
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()
|
|
end
|
|
|
|
tool_ctl.vis_ftanks[4].line.show()
|
|
else
|
|
tool_ctl.vis_ftanks[4].line.hide(true)
|
|
end
|
|
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.green}
|
|
|
|
--#endregion
|
|
|
|
local function submit_mode()
|
|
tmp_cfg.FacilityTankMode = tank_mode.get_value()
|
|
main_pane.set_value(3)
|
|
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=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}
|
|
|
|
TextBox{parent=svr_c_6,height=3,text="This visualization tool shows the pipe connections required for a particular dynamic tank configuration you have selected."}
|
|
TextBox{parent=svr_c_6,y=5,height=4,text="Examples: A U2 tank should be configured on an RTU as the dynamic tank for unit #2. An F3 tank should be configured on an RTU as the #3 dynamic tank for the facility."}
|
|
TextBox{parent=svr_c_6,y=10,height=3,text="Some modes may look the same if you are not using 4 total reactor units. The wiki has details. Modes that look the same will function the same.",fg_bg=g_lg_fg_bg}
|
|
|
|
PushButton{parent=svr_c_6,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- NET CONFIG
|
|
|
|
local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_4 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
|
|
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
|
|
|
TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=1,height=1,text="Please set the network channels below."}
|
|
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=8,height=1,width=18,text="Supervisor Channel"}
|
|
local svr_chan = NumberField{parent=net_c_1,x=21,y=8,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=8,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=9,height=1,width=11,text="PLC Channel"}
|
|
local plc_chan = NumberField{parent=net_c_1,x=21,y=9,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=9,height=4,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=10,height=1,width=19,text="RTU Gateway Channel"}
|
|
local rtu_chan = NumberField{parent=net_c_1,x=21,y=10,width=7,default=ini_cfg.RTU_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=10,height=4,text="[RTU_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=11,height=1,width=19,text="Coordinator Channel"}
|
|
local crd_chan = NumberField{parent=net_c_1,x=21,y=11,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=11,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=12,height=1,width=14,text="Pocket Channel"}
|
|
local pkt_chan = NumberField{parent=net_c_1,x=21,y=12,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=12,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_channels()
|
|
local svr_c, plc_c, rtu_c = tonumber(svr_chan.get_value()), tonumber(plc_chan.get_value()), tonumber(rtu_chan.get_value())
|
|
local crd_c, pkt_c = tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
|
if svr_c ~= nil and plc_c ~= nil and rtu_c ~= nil and crd_c ~= nil and pkt_c ~= nil then
|
|
tmp_cfg.SVR_Channel, tmp_cfg.PLC_Channel, tmp_cfg.RTU_Channel = svr_c, plc_c, rtu_c
|
|
tmp_cfg.CRD_Channel, tmp_cfg.PKT_Channel = crd_c, pkt_c
|
|
net_pane.set_value(2)
|
|
chan_err.hide(true)
|
|
else chan_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(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,height=1,text="Please set the connection timeouts below."}
|
|
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally should not need to modify these. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection. The default for all is 5 seconds.",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=1,y=8,height=1,width=11,text="PLC Timeout"}
|
|
local plc_timeout = NumberField{parent=net_c_2,x=21,y=8,width=7,default=ini_cfg.PLC_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=1,y=9,height=1,width=19,text="RTU Gateway Timeout"}
|
|
local rtu_timeout = NumberField{parent=net_c_2,x=21,y=9,width=7,default=ini_cfg.RTU_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=1,y=10,height=1,width=19,text="Coordinator Timeout"}
|
|
local crd_timeout = NumberField{parent=net_c_2,x=21,y=10,width=7,default=ini_cfg.CRD_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=1,y=11,height=1,width=14,text="Pocket Timeout"}
|
|
local pkt_timeout = NumberField{parent=net_c_2,x=21,y=11,width=7,default=ini_cfg.PKT_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=29,y=8,height=4,width=7,text="seconds\nseconds\nseconds\nseconds",fg_bg=g_lg_fg_bg}
|
|
|
|
local ct_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_timeouts()
|
|
local plc_cto, rtu_cto, crd_cto, pkt_cto = tonumber(plc_timeout.get_value()), tonumber(rtu_timeout.get_value()), tonumber(crd_timeout.get_value()), tonumber(pkt_timeout.get_value())
|
|
if plc_cto ~= nil and rtu_cto ~= nil and crd_cto ~= nil and pkt_cto ~= nil then
|
|
tmp_cfg.PLC_Timeout, tmp_cfg.RTU_Timeout, tmp_cfg.CRD_Timeout, tmp_cfg.PKT_Timeout = plc_cto, rtu_cto, crd_cto, pkt_cto
|
|
net_pane.set_value(3)
|
|
ct_err.hide(true)
|
|
else ct_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=net_c_3,x=1,y=1,height=1,text="Please set the trusted range below."}
|
|
TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
|
TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
|
|
|
local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
local tr_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_tr()
|
|
local range_val = tonumber(range.get_value())
|
|
if range_val ~= nil then
|
|
tmp_cfg.TrustedRange = range_val
|
|
net_pane.set_value(4)
|
|
tr_err.hide(true)
|
|
else tr_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
|
TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_4,x=1,y=11,height=1,text="Facility Auth Key"}
|
|
local key, _, censor = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
|
|
|
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
|
|
|
local hide_key = CheckBox{parent=net_c_4,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
|
|
|
hide_key.set_value(true)
|
|
censor_key(true)
|
|
|
|
local key_err = TextBox{parent=net_c_4,x=8,y=14,height=1,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_auth()
|
|
local v = key.get_value()
|
|
if string.len(v) == 0 or string.len(v) >= 8 then
|
|
tmp_cfg.AuthKey = key.get_value()
|
|
main_pane.set_value(4)
|
|
key_err.hide(true)
|
|
else key_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_4,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=net_c_4,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- LOG CONFIG
|
|
|
|
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
|
|
|
TextBox{parent=log_c_1,x=1,y=1,height=1,text="Please configure logging below."}
|
|
|
|
TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"}
|
|
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
|
|
|
TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"}
|
|
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
|
|
|
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
|
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
|
|
|
local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_log()
|
|
if path.get_value() ~= "" then
|
|
path_err.hide(true)
|
|
tmp_cfg.LogMode = mode.get_value() - 1
|
|
tmp_cfg.LogPath = path.get_value()
|
|
tmp_cfg.LogDebug = en_dbg.get_value()
|
|
tool_ctl.gen_summary(tmp_cfg)
|
|
tool_ctl.viewing_config = false
|
|
tool_ctl.importing_legacy = false
|
|
tool_ctl.settings_apply.show()
|
|
main_pane.set_value(5)
|
|
else path_err.show() end
|
|
end
|
|
|
|
PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- SUMMARY OF CHANGES
|
|
|
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_2 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_3 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_4 = Div{parent=summary,x=2,y=4,width=49}
|
|
|
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
|
|
|
TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
|
|
|
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
local function back_from_settings()
|
|
if tool_ctl.viewing_config or tool_ctl.importing_legacy then
|
|
main_pane.set_value(1)
|
|
tool_ctl.viewing_config = false
|
|
tool_ctl.importing_legacy = false
|
|
tool_ctl.settings_apply.show()
|
|
else
|
|
main_pane.set_value(4)
|
|
end
|
|
end
|
|
|
|
---@param element graphics_element
|
|
---@param data any
|
|
local function try_set(element, data)
|
|
if data ~= nil then element.set_value(data) end
|
|
end
|
|
|
|
local function save_and_continue()
|
|
for k, v in pairs(tmp_cfg) do settings.set(k, v) end
|
|
|
|
if settings.save("supervisor.settings") then
|
|
load_settings(settings_cfg, true)
|
|
load_settings(ini_cfg)
|
|
|
|
try_set(num_units, ini_cfg.UnitCount)
|
|
try_set(tank_mode, ini_cfg.FacilityTankMode)
|
|
try_set(svr_chan, ini_cfg.SVR_Channel)
|
|
try_set(plc_chan, ini_cfg.PLC_Channel)
|
|
try_set(rtu_chan, ini_cfg.RTU_Channel)
|
|
try_set(crd_chan, ini_cfg.CRD_Channel)
|
|
try_set(pkt_chan, ini_cfg.PKT_Channel)
|
|
try_set(plc_timeout, ini_cfg.PLC_Timeout)
|
|
try_set(rtu_timeout, ini_cfg.RTU_Timeout)
|
|
try_set(crd_timeout, ini_cfg.CRD_Timeout)
|
|
try_set(pkt_timeout, ini_cfg.PKT_Timeout)
|
|
try_set(range, ini_cfg.TrustedRange)
|
|
try_set(key, ini_cfg.AuthKey)
|
|
try_set(mode, ini_cfg.LogMode)
|
|
try_set(path, ini_cfg.LogPath)
|
|
try_set(en_dbg, ini_cfg.LogDebug)
|
|
|
|
for i = 1, #ini_cfg.CoolingConfig do
|
|
local cfg, elems = ini_cfg.CoolingConfig[i], tool_ctl.cooling_elems[i]
|
|
try_set(elems.boilers, cfg.BoilerCount)
|
|
try_set(elems.turbines, cfg.TurbineCount)
|
|
try_set(elems.tank, cfg.TankConnection)
|
|
end
|
|
|
|
for i = 1, #ini_cfg.FacilityTankDefs do
|
|
try_set(tool_ctl.tank_elems[i].tank_opt, ini_cfg.FacilityTankDefs[i])
|
|
end
|
|
|
|
en_fac_tanks.set_value(ini_cfg.FacilityTankMode > 0)
|
|
|
|
tool_ctl.view_cfg.enable()
|
|
|
|
if tool_ctl.importing_legacy then
|
|
tool_ctl.importing_legacy = false
|
|
sum_pane.set_value(3)
|
|
else
|
|
sum_pane.set_value(2)
|
|
end
|
|
else
|
|
sum_pane.set_value(4)
|
|
end
|
|
end
|
|
|
|
PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_settings,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
|
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"}
|
|
|
|
local function go_home()
|
|
main_pane.set_value(1)
|
|
svr_pane.set_value(1)
|
|
net_pane.set_value(1)
|
|
sum_pane.set_value(1)
|
|
end
|
|
|
|
PushButton{parent=sum_c_2,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_2,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."}
|
|
|
|
local function delete_legacy()
|
|
fs.delete("/supervisor/config.lua")
|
|
exit()
|
|
end
|
|
|
|
PushButton{parent=sum_c_3,x=1,y=14,min_width=8,text="Cancel",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_3,x=44,y=14,min_width=6,text="OK",callback=delete_legacy,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
TextBox{parent=sum_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
|
PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
-- CONFIG CHANGE LOG
|
|
|
|
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg}
|
|
|
|
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
for _, change in ipairs(changes) do
|
|
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
|
for _, v in ipairs(change[2]) do
|
|
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
|
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
|
|
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
|
end
|
|
end
|
|
|
|
PushButton{parent=cl,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- IMPORT ERROR
|
|
|
|
local i_err = Div{parent=import_err,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=import_err,x=1,y=2,height=1,text=" Import Error",fg_bg=cpair(colors.black,colors.red)}
|
|
TextBox{parent=i_err,x=1,y=1,height=1,text="There is a problem with your config.lua file:"}
|
|
|
|
local import_err_msg = TextBox{parent=i_err,x=1,y=3,height=6,text=""}
|
|
|
|
PushButton{parent=i_err,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=i_err,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
-- set tool functions now that we have the elements
|
|
|
|
-- load a legacy config file
|
|
function tool_ctl.load_legacy()
|
|
local config = require("supervisor.config")
|
|
|
|
tmp_cfg.UnitCount = config.NUM_REACTORS
|
|
|
|
if config.REACTOR_COOLING == nil or tmp_cfg.UnitCount ~= #config.REACTOR_COOLING then
|
|
import_err_msg.set_value("Cooling configuration table length must match the number of units.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
local cfg = config.REACTOR_COOLING[i]
|
|
|
|
if type(cfg) ~= "table" then
|
|
import_err_msg.set_value("Cooling configuration for unit " .. i .. " must be a table.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
tmp_cfg.CoolingConfig[i] = { BoilerCount = cfg.BOILERS or 0, TurbineCount = cfg.TURBINES or 1, TankConnection = cfg.TANK or false }
|
|
end
|
|
|
|
tmp_cfg.FacilityTankMode = config.FAC_TANK_MODE
|
|
|
|
if not (util.is_int(tmp_cfg.FacilityTankMode) and tmp_cfg.FacilityTankMode >= 0 and tmp_cfg.FacilityTankMode <= 8) then
|
|
import_err_msg.set_value("Invalid tank mode present in config. FAC_TANK_MODE must be a number 0 through 8.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
if config.FAC_TANK_MODE > 0 then
|
|
if config.FAC_TANK_DEFS == nil or tmp_cfg.UnitCount ~= #config.FAC_TANK_DEFS then
|
|
import_err_msg.set_value("Facility tank definitions table length must match the number of units when using facility tanks.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
tmp_cfg.FacilityTankDefs[i] = config.FAC_TANK_DEFS[i]
|
|
end
|
|
else
|
|
tmp_cfg.FacilityTankMode = 0
|
|
tmp_cfg.FacilityTankDefs = {}
|
|
end
|
|
|
|
tmp_cfg.SVR_Channel = config.SVR_CHANNEL
|
|
tmp_cfg.PLC_Channel = config.PLC_CHANNEL
|
|
tmp_cfg.RTU_Channel = config.RTU_CHANNEL
|
|
tmp_cfg.CRD_Channel = config.CRD_CHANNEL
|
|
tmp_cfg.PKT_Channel = config.PKT_CHANNEL
|
|
|
|
tmp_cfg.PLC_Timeout = config.PLC_TIMEOUT
|
|
tmp_cfg.RTU_Timeout = config.RTU_TIMEOUT
|
|
tmp_cfg.CRD_Timeout = config.CRD_TIMEOUT
|
|
tmp_cfg.PKT_Timeout = config.PKT_TIMEOUT
|
|
|
|
tmp_cfg.TrustedRange = config.TRUSTED_RANGE
|
|
tmp_cfg.AuthKey = config.AUTH_KEY or ""
|
|
tmp_cfg.LogMode = config.LOG_MODE
|
|
tmp_cfg.LogPath = config.LOG_PATH
|
|
tmp_cfg.LogDebug = config.LOG_DEBUG or false
|
|
|
|
tool_ctl.gen_summary(tmp_cfg)
|
|
sum_pane.set_value(1)
|
|
main_pane.set_value(5)
|
|
tool_ctl.importing_legacy = true
|
|
end
|
|
|
|
-- expose the auth key on the summary page
|
|
function tool_ctl.show_auth_key()
|
|
tool_ctl.show_key_btn.disable()
|
|
tool_ctl.auth_key_textbox.set_value(tool_ctl.auth_key_value)
|
|
end
|
|
|
|
-- generate the summary list
|
|
---@param cfg svr_config
|
|
function tool_ctl.gen_summary(cfg)
|
|
setting_list.remove_all()
|
|
|
|
local alternate = false
|
|
local inner_width = setting_list.get_width() - 1
|
|
|
|
tool_ctl.show_key_btn.enable()
|
|
tool_ctl.auth_key_value = cfg.AuthKey or "" -- to show auth key
|
|
|
|
for i = 1, #fields do
|
|
local f = fields[i]
|
|
local height = 1
|
|
local label_w = string.len(f[2])
|
|
local val_max_w = (inner_width - label_w) + 1
|
|
local raw = cfg[f[1]]
|
|
local val = util.strval(raw)
|
|
|
|
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
|
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
|
elseif f[1] == "CoolingConfig" and cfg.CoolingConfig then
|
|
val = ""
|
|
|
|
for idx = 1, #cfg.CoolingConfig do
|
|
local ccfg = cfg.CoolingConfig[idx]
|
|
local b_plural = util.trinary(ccfg.BoilerCount == 1, "", "s")
|
|
local t_plural = util.trinary(ccfg.TurbineCount == 1, "", "s")
|
|
local tank = util.trinary(ccfg.TankConnection, "has tank conn", "no tank conn")
|
|
val = val .. util.trinary(idx == 1, "", "\n") ..
|
|
util.sprintf(" \x07 unit %d - %d boiler%s, %d turbine%s, %s", idx, ccfg.BoilerCount, b_plural, ccfg.TurbineCount, t_plural, tank)
|
|
end
|
|
|
|
if val == "" then val = "no facility tanks" end
|
|
elseif f[1] == "FacilityTankMode" and raw == 0 then val = "0 (n/a, unit mode)"
|
|
elseif f[1] == "FacilityTankDefs" and cfg.FacilityTankDefs then
|
|
val = ""
|
|
|
|
for idx = 1, #cfg.FacilityTankDefs do
|
|
local t_mode = "not connected to a tank"
|
|
if cfg.FacilityTankDefs[idx] == 1 then
|
|
t_mode = "connected to its unit tank"
|
|
elseif cfg.FacilityTankDefs[idx] == 2 then
|
|
t_mode = "connected to a facility tank"
|
|
end
|
|
|
|
val = val .. util.trinary(idx == 1, "", "\n") .. util.sprintf(" \x07 unit %d - %s", idx, t_mode)
|
|
end
|
|
|
|
if val == "" then val = "no facility tanks" end
|
|
end
|
|
|
|
if val == "nil" then val = "<not set>" end
|
|
|
|
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
|
alternate = not alternate
|
|
|
|
if string.len(val) > val_max_w then
|
|
local lines = util.strwrap(val, inner_width)
|
|
height = #lines + 1
|
|
end
|
|
|
|
local line = Div{parent=setting_list,height=height,fg_bg=c}
|
|
TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)}
|
|
|
|
local textbox
|
|
if height > 1 then
|
|
textbox = TextBox{parent=line,x=1,y=2,text=val,height=height-1,alignment=LEFT}
|
|
else
|
|
textbox = TextBox{parent=line,x=label_w+1,y=1,text=val,alignment=RIGHT}
|
|
end
|
|
|
|
if f[1] == "AuthKey" then tool_ctl.auth_key_textbox = textbox end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- reset terminal screen
|
|
local function reset_term()
|
|
term.setTextColor(colors.white)
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
end
|
|
|
|
-- run the supervisor configurator
|
|
---@param ask_config? boolean indicate if this is being called by the supervisor startup app due to an invalid configuration
|
|
function configurator.configure(ask_config)
|
|
tool_ctl.ask_config = ask_config == true
|
|
|
|
load_settings(settings_cfg, true)
|
|
tool_ctl.has_config = load_settings(ini_cfg)
|
|
|
|
reset_term()
|
|
|
|
-- set overridden colors
|
|
for i = 1, #style.colors do
|
|
term.setPaletteColor(style.colors[i].c, style.colors[i].hex)
|
|
end
|
|
|
|
local status, error = pcall(function ()
|
|
local display = DisplayBox{window=term.current(),fg_bg=style.root}
|
|
config_view(display)
|
|
|
|
while true do
|
|
local event, param1, param2, param3 = util.pull_event()
|
|
|
|
-- handle event
|
|
if event == "timer" then
|
|
tcd.handle(param1)
|
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then
|
|
local m_e = core.events.new_mouse_event(event, param1, param2, param3)
|
|
if m_e then display.handle_mouse(m_e) end
|
|
elseif event == "char" or event == "key" or event == "key_up" then
|
|
local k_e = core.events.new_key_event(event, param1, param2)
|
|
if k_e then display.handle_key(k_e) end
|
|
elseif event == "paste" then
|
|
display.handle_paste(param1)
|
|
end
|
|
|
|
if event == "terminate" then return end
|
|
end
|
|
end)
|
|
|
|
-- restore colors
|
|
for i = 1, #style.colors do
|
|
local r, g, b = term.nativePaletteColor(style.colors[i].c)
|
|
term.setPaletteColor(style.colors[i].c, r, g, b)
|
|
end
|
|
|
|
reset_term()
|
|
if not status then
|
|
println("configurator error: " .. error)
|
|
end
|
|
|
|
return status, error
|
|
end
|
|
|
|
return configurator
|