mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
1031 lines
46 KiB
Lua
1031 lines
46 KiB
Lua
--
|
|
-- Configuration GUI
|
|
--
|
|
|
|
local comms = require("scada-common.comms")
|
|
local log = require("scada-common.log")
|
|
local network = require("scada-common.network")
|
|
local ppm = require("scada-common.ppm")
|
|
local tcd = require("scada-common.tcd")
|
|
local util = require("scada-common.util")
|
|
|
|
local core = require("graphics.core")
|
|
|
|
local DisplayBox = require("graphics.elements.displaybox")
|
|
local Div = require("graphics.elements.div")
|
|
local ListBox = require("graphics.elements.listbox")
|
|
local MultiPane = require("graphics.elements.multipane")
|
|
local TextBox = require("graphics.elements.textbox")
|
|
|
|
local CheckBox = require("graphics.elements.controls.checkbox")
|
|
local PushButton = require("graphics.elements.controls.push_button")
|
|
local Radio2D = require("graphics.elements.controls.radio_2d")
|
|
local RadioButton = require("graphics.elements.controls.radio_button")
|
|
|
|
local NumberField = require("graphics.elements.form.number_field")
|
|
local TextField = require("graphics.elements.form.text_field")
|
|
|
|
local println = util.println
|
|
local tri = util.trinary
|
|
|
|
local PROTOCOL = comms.PROTOCOL
|
|
local DEVICE_TYPE = comms.DEVICE_TYPE
|
|
local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
|
local MGMT_TYPE = comms.MGMT_TYPE
|
|
|
|
local cpair = core.cpair
|
|
|
|
local LEFT = core.ALIGN.LEFT
|
|
local CENTER = core.ALIGN.CENTER
|
|
local RIGHT = core.ALIGN.RIGHT
|
|
|
|
-- changes to the config data/format to let the user know
|
|
local changes = {}
|
|
|
|
---@class crd_configurator
|
|
local configurator = {}
|
|
|
|
local style = {}
|
|
|
|
style.root = cpair(colors.black, colors.lightGray)
|
|
style.header = cpair(colors.white, colors.gray)
|
|
|
|
style.colors = {
|
|
{ c = colors.red, hex = 0xdf4949 },
|
|
{ c = colors.orange, hex = 0xffb659 },
|
|
{ c = colors.yellow, hex = 0xfffc79 },
|
|
{ c = colors.lime, hex = 0x80ff80 },
|
|
{ c = colors.green, hex = 0x4aee8a },
|
|
{ c = colors.cyan, hex = 0x34bac8 },
|
|
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
{ c = colors.blue, hex = 0x0096ff },
|
|
{ c = colors.purple, hex = 0xb156ee },
|
|
{ c = colors.pink, hex = 0xf26ba2 },
|
|
{ c = colors.magenta, hex = 0xf9488a },
|
|
{ c = colors.lightGray, hex = 0xcacaca },
|
|
{ c = colors.gray, hex = 0x575757 }
|
|
}
|
|
|
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
|
local nav_fg_bg = bw_fg_bg
|
|
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
|
|
|
local tool_ctl = {
|
|
nic = nil, ---@type nic
|
|
net_listen = false,
|
|
sv_addr = comms.BROADCAST,
|
|
sv_seq_num = 0,
|
|
sv_cool_conf = nil, ---@type table list of boiler & turbine counts
|
|
show_sv_cfg = nil, ---@type function
|
|
|
|
ask_config = false,
|
|
has_config = false,
|
|
viewing_config = false,
|
|
importing_legacy = false,
|
|
|
|
view_cfg = nil, ---@type graphics_element
|
|
settings_apply = nil, ---@type graphics_element
|
|
|
|
gen_summary = nil, ---@type function
|
|
show_current_cfg = nil, ---@type function
|
|
load_legacy = nil, ---@type function
|
|
|
|
show_auth_key = nil, ---@type function
|
|
show_key_btn = nil, ---@type graphics_element
|
|
auth_key_textbox = nil, ---@type graphics_element
|
|
auth_key_value = "",
|
|
|
|
sv_connect = nil, ---@type function
|
|
sv_conn_button = nil, ---@type graphics_element
|
|
sv_conn_status = nil, ---@type graphics_element
|
|
sv_conn_detail = nil, ---@type graphics_element
|
|
sv_skip = nil, ---@type graphics_element
|
|
sv_next = nil, ---@type graphics_element
|
|
|
|
cooling_elems = {},
|
|
tank_elems = {},
|
|
|
|
vis_ftanks = {},
|
|
vis_utanks = {}
|
|
}
|
|
|
|
---@class crd_config
|
|
local tmp_cfg = {
|
|
UnitCount = 1,
|
|
SpeakerVolume = 1.0,
|
|
Time24Hour = true,
|
|
DisableFlowView = false,
|
|
Displays = {},
|
|
SVR_Channel = nil, ---@type integer
|
|
CRD_Channel = nil, ---@type integer
|
|
PKT_Channel = nil, ---@type integer
|
|
SVR_Timeout = nil, ---@type number
|
|
API_Timeout = nil, ---@type number
|
|
TrustedRange = nil, ---@type number
|
|
AuthKey = nil, ---@type string|nil
|
|
LogMode = 0,
|
|
LogPath = "",
|
|
LogDebug = false,
|
|
}
|
|
|
|
---@class crd_config
|
|
local ini_cfg = {}
|
|
---@class crd_config
|
|
local settings_cfg = {}
|
|
|
|
-- all settings fields, their nice names, and their default values
|
|
local fields = {
|
|
{ "UnitCount", "Number of Reactors", 1 },
|
|
{ "SpeakerVolume", "Speaker Volume", 1.0 },
|
|
{ "Use24HourTime", "Use 24-hour Time Format", true },
|
|
{ "DisableFlowView", "Don't Use Flow", {} },
|
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
|
{ "CRD_Channel", "CRD Channel", 16243 },
|
|
{ "PKT_Channel", "PKT Channel", 16244 },
|
|
{ "CRD_Timeout", "CRD Connection Timeout", 5 },
|
|
{ "API_Timeout", "API Connection Timeout", 5 },
|
|
{ "TrustedRange", "Trusted Range", 0 },
|
|
{ "AuthKey", "Facility Auth Key" , ""},
|
|
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
|
{ "LogPath", "Log Path", "/log.txt" },
|
|
{ "LogDebug","Log Debug Messages", false }
|
|
}
|
|
|
|
local function is_int_min_max(x, min, max) return util.is_int(x) and x >= min and x <= max end
|
|
|
|
-- send an management packet to the supervisor
|
|
---@param msg_type MGMT_TYPE
|
|
---@param msg table
|
|
local function send_sv(msg_type, msg)
|
|
local s_pkt = comms.scada_packet()
|
|
local pkt = comms.mgmt_packet()
|
|
|
|
pkt.make(msg_type, msg)
|
|
s_pkt.make(tool_ctl.sv_addr, tool_ctl.sv_seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable())
|
|
|
|
tool_ctl.nic.transmit(tmp_cfg.SVR_Channel, tmp_cfg.CRD_Channel, s_pkt)
|
|
tool_ctl.sv_seq_num = tool_ctl.sv_seq_num + 1
|
|
end
|
|
|
|
-- handle an establish message from the supervisor
|
|
---@param packet mgmt_frame
|
|
local function handle_packet(packet)
|
|
local error_msg = nil
|
|
|
|
if packet.scada_frame.local_channel() ~= tmp_cfg.CRD_Channel then
|
|
error_msg = "Error: unknown receive channel."
|
|
elseif packet.scada_frame.remote_channel() == tmp_cfg.SVR_Channel and packet.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
|
|
if packet.type == MGMT_TYPE.ESTABLISH then
|
|
-- connection with supervisor established
|
|
if packet.length == 2 then
|
|
local est_ack = packet.data[1]
|
|
local config = packet.data[2]
|
|
|
|
if est_ack == ESTABLISH_ACK.ALLOW then
|
|
if type(config) == "table" and #config == 2 then
|
|
local count_ok = is_int_min_max(config[1], 1, 4)
|
|
local cool_ok = type(config[2]) == "table" and type(config[2].r_cool) == "table" and #config[2].r_cool == config[1]
|
|
|
|
if count_ok and cool_ok then
|
|
tmp_cfg.UnitCount = config[1]
|
|
tool_ctl.sv_cool_conf = {}
|
|
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
local num_b = config[2].r_cool[i].BoilerCount
|
|
local num_t = config[2].r_cool[i].TurbineCount
|
|
tool_ctl.sv_cool_conf[i] = { num_b, num_t }
|
|
cool_ok = cool_ok and is_int_min_max(num_b, 0, 2) and is_int_min_max(num_t, 1, 3)
|
|
end
|
|
end
|
|
|
|
if not count_ok then
|
|
error_msg = "Error: supervisor unit count out of range."
|
|
elseif not cool_ok then
|
|
error_msg = "Error: supervisor cooling configuration malformed."
|
|
tool_ctl.sv_cool_conf = nil
|
|
end
|
|
|
|
tool_ctl.sv_addr = packet.scada_frame.src_addr()
|
|
send_sv(MGMT_TYPE.CLOSE, {})
|
|
else
|
|
error_msg = "Error: invalid cooling configuration supervisor."
|
|
end
|
|
else
|
|
error_msg = "Error: invalid allow reply length from supervisor."
|
|
end
|
|
elseif packet.length == 1 then
|
|
local est_ack = packet.data[1]
|
|
|
|
if est_ack == ESTABLISH_ACK.DENY then
|
|
error_msg = "Error: supervisor connection denied."
|
|
elseif est_ack == ESTABLISH_ACK.COLLISION then
|
|
error_msg = "Error: a coordinator is already/still connected. Please try again."
|
|
elseif est_ack == ESTABLISH_ACK.BAD_VERSION then
|
|
error_msg = "Error: coordinator comms version does not match supervisor comms version."
|
|
else
|
|
error_msg = "Error: invalid reply from supervisor."
|
|
end
|
|
else
|
|
error_msg = "Error: invalid reply length from supervisor."
|
|
end
|
|
else
|
|
error_msg = "Error: didn't get an establish reply from supervisor."
|
|
end
|
|
end
|
|
|
|
tool_ctl.net_listen = false
|
|
|
|
if error_msg then
|
|
tool_ctl.sv_conn_status.set_value("")
|
|
tool_ctl.sv_conn_detail.set_value(error_msg)
|
|
tool_ctl.sv_conn_button.enable()
|
|
else
|
|
tool_ctl.sv_conn_status.set_value("Connected!")
|
|
tool_ctl.sv_conn_detail.set_value("Data received successfully, press 'Next' to continue.")
|
|
tool_ctl.sv_skip.hide()
|
|
tool_ctl.sv_next.show()
|
|
end
|
|
end
|
|
|
|
-- handle supervisor connection failure
|
|
local function handle_timeout()
|
|
tool_ctl.net_listen = false
|
|
tool_ctl.sv_conn_button.enable()
|
|
tool_ctl.sv_conn_status.set_value("Timed out.")
|
|
tool_ctl.sv_conn_detail.set_value("Supervisor did not reply. Ensure startup app is running on the supervisor.")
|
|
end
|
|
|
|
-- load data from the settings file
|
|
---@param target crd_config
|
|
---@param raw boolean? true to not use default values
|
|
local function load_settings(target, raw)
|
|
for _, v in pairs(fields) do settings.unset(v[1]) end
|
|
|
|
local loaded = settings.load("/coordinator.settings")
|
|
|
|
for _, v in pairs(fields) do target[v[1]] = settings.get(v[1], tri(raw, nil, v[3])) end
|
|
|
|
return loaded
|
|
end
|
|
|
|
-- create the config view
|
|
---@param display graphics_element
|
|
local function config_view(display)
|
|
---@diagnostic disable-next-line: undefined-field
|
|
local function exit() os.queueEvent("terminate") end
|
|
|
|
TextBox{parent=display,y=1,text="Coordinator Configurator",alignment=CENTER,height=1,fg_bg=style.header}
|
|
|
|
local root_pane_div = Div{parent=display,x=1,y=2}
|
|
|
|
local main_page = Div{parent=root_pane_div,x=1,y=1}
|
|
local net_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local fac_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local mon_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local crd_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local log_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local summary = Div{parent=root_pane_div,x=1,y=1}
|
|
local changelog = Div{parent=root_pane_div,x=1,y=1}
|
|
local import_err = Div{parent=root_pane_div,x=1,y=1}
|
|
|
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,crd_cfg,log_cfg,summary,changelog,import_err}}
|
|
|
|
-- MAIN PAGE
|
|
|
|
local y_start = 5
|
|
|
|
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Coordinator configurator! Please select one of the following options."}
|
|
|
|
if tool_ctl.ask_config then
|
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
|
y_start = y_start + 5
|
|
end
|
|
|
|
local function view_config()
|
|
tool_ctl.viewing_config = true
|
|
tool_ctl.gen_summary(settings_cfg)
|
|
tool_ctl.settings_apply.hide(true)
|
|
main_pane.set_value(5)
|
|
end
|
|
|
|
if fs.exists("/supervisor/config.lua") then
|
|
PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg}
|
|
y_start = y_start + 2
|
|
end
|
|
|
|
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
|
|
|
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
|
|
|
|
PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#region NET CONFIG
|
|
|
|
local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_4 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
|
|
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
|
|
|
TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=1,height=1,text="Please set the network channels below."}
|
|
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 3 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=8,height=1,width=18,text="Supervisor Channel"}
|
|
local svr_chan = NumberField{parent=net_c_1,x=21,y=8,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=8,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=10,height=1,width=19,text="Coordinator Channel"}
|
|
local crd_chan = NumberField{parent=net_c_1,x=21,y=10,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=10,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_1,x=1,y=12,height=1,width=14,text="Pocket Channel"}
|
|
local pkt_chan = NumberField{parent=net_c_1,x=21,y=12,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
TextBox{parent=net_c_1,x=29,y=12,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
|
|
local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_channels()
|
|
local svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
|
if svr_c ~= nil and crd_c ~= nil and pkt_c ~= nil then
|
|
tmp_cfg.SVR_Channel, tmp_cfg.CRD_Channel, tmp_cfg.PKT_Channel = svr_c, crd_c, pkt_c
|
|
net_pane.set_value(2)
|
|
chan_err.hide(true)
|
|
else chan_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Please set the connection timeouts below."}
|
|
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally should not need to modify these. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection. The default for all is 5 seconds.",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=1,y=8,height=1,width=19,text="Supervisor Timeout"}
|
|
local svr_timeout = NumberField{parent=net_c_2,x=20,y=8,width=7,default=ini_cfg.SVR_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=1,y=10,height=1,width=14,text="Pocket Timeout"}
|
|
local pkt_timeout = NumberField{parent=net_c_2,x=20,y=10,width=7,default=ini_cfg.PKT_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=28,y=8,height=4,width=7,text="seconds\n\nseconds",fg_bg=g_lg_fg_bg}
|
|
|
|
local ct_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_timeouts()
|
|
local svr_cto, pkt_cto = tonumber(svr_timeout.get_value()), tonumber(pkt_timeout.get_value())
|
|
if svr_cto ~= nil and pkt_cto ~= nil then
|
|
tmp_cfg.SVR_Timeout, tmp_cfg.PKT_Timeout = svr_cto, pkt_cto
|
|
net_pane.set_value(3)
|
|
ct_err.hide(true)
|
|
else ct_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=net_c_3,x=1,y=1,height=1,text="Please set the trusted range below."}
|
|
TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
|
TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
|
|
|
local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
local tr_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_tr()
|
|
local range_val = tonumber(range.get_value())
|
|
if range_val ~= nil then
|
|
tmp_cfg.TrustedRange = range_val
|
|
comms.set_trusted_range(range_val)
|
|
net_pane.set_value(4)
|
|
tr_err.hide(true)
|
|
else tr_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
|
TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=net_c_4,x=1,y=11,height=1,text="Facility Auth Key"}
|
|
local key, _, censor = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
|
|
|
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
|
|
|
local hide_key = CheckBox{parent=net_c_4,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
|
|
|
hide_key.set_value(true)
|
|
censor_key(true)
|
|
|
|
local key_err = TextBox{parent=net_c_4,x=8,y=14,height=1,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_auth()
|
|
local v = key.get_value()
|
|
if string.len(v) == 0 or string.len(v) >= 8 then
|
|
tmp_cfg.AuthKey = key.get_value()
|
|
main_pane.set_value(3)
|
|
key_err.hide(true)
|
|
|
|
-- init mac for supervisor connection
|
|
if string.len(v) >= 8 then network.init_mac(tmp_cfg.AuthKey) end
|
|
|
|
-- prep supervisor connection screen
|
|
tool_ctl.sv_conn_button.enable()
|
|
tool_ctl.sv_conn_status.set_value("")
|
|
tool_ctl.sv_conn_detail.set_value("")
|
|
tool_ctl.sv_next.hide()
|
|
tool_ctl.sv_skip.show()
|
|
else key_err.show() end
|
|
end
|
|
|
|
PushButton{parent=net_c_4,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=net_c_4,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#endregion
|
|
|
|
-- FACILITY CONFIG
|
|
|
|
local fac_c_1 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
|
local fac_c_2 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
|
local fac_c_3 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
|
|
|
local fac_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3}}
|
|
|
|
TextBox{parent=fac_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
|
|
|
TextBox{parent=fac_c_1,x=1,y=1,height=4,text="This tool can attempt to connect to your supervisor computer. This would load facility information in order to get the unit count and aid monitor setup."}
|
|
TextBox{parent=fac_c_1,x=1,y=6,height=2,text="The supervisor startup app must be running and fully configured on your supervisor computer."}
|
|
|
|
tool_ctl.sv_conn_status = TextBox{parent=fac_c_1,x=11,y=9,height=1,text=""}
|
|
tool_ctl.sv_conn_detail = TextBox{parent=fac_c_1,x=1,y=11,height=2,text=""}
|
|
|
|
tool_ctl.sv_conn_button = PushButton{parent=fac_c_1,x=1,y=9,text="Connect",min_width=9,callback=function()tool_ctl.sv_connect()end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
|
|
|
function tool_ctl.sv_connect()
|
|
tool_ctl.sv_conn_button.disable()
|
|
tool_ctl.sv_conn_detail.set_value("")
|
|
|
|
local modem = ppm.get_wireless_modem()
|
|
if modem == nil then
|
|
tool_ctl.sv_conn_status.set_value("Please connect an ender/wireless modem.")
|
|
else
|
|
tool_ctl.sv_conn_status.set_value("Modem found, connecting...")
|
|
if tool_ctl.nic == nil then tool_ctl.nic = network.nic(modem) end
|
|
|
|
tool_ctl.nic.closeAll()
|
|
tool_ctl.nic.open(tmp_cfg.CRD_Channel)
|
|
|
|
tool_ctl.sv_addr = comms.BROADCAST
|
|
tool_ctl.sv_seq_num = 0
|
|
tool_ctl.net_listen = true
|
|
|
|
send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.CRD })
|
|
|
|
tcd.dispatch_unique(8, handle_timeout)
|
|
end
|
|
end
|
|
|
|
local function sv_skip()
|
|
tcd.abort(handle_timeout)
|
|
tool_ctl.sv_fac_conf = nil
|
|
tool_ctl.net_listen = false
|
|
fac_pane.set_value(2)
|
|
end
|
|
|
|
local function sv_next()
|
|
tool_ctl.show_sv_cfg()
|
|
fac_pane.set_value(3)
|
|
end
|
|
|
|
PushButton{parent=fac_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.sv_skip = PushButton{parent=fac_c_1,x=44,y=14,text="Skip \x1a",callback=sv_skip,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.sv_next = PushButton{parent=fac_c_1,x=44,y=14,text="Next \x1a",callback=sv_next,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,hidden=true}
|
|
|
|
TextBox{parent=fac_c_2,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
|
local num_units = NumberField{parent=fac_c_2,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
|
TextBox{parent=fac_c_2,x=7,y=5,height=1,text="reactors"}
|
|
TextBox{parent=fac_c_2,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg}
|
|
|
|
local nu_error = TextBox{parent=fac_c_2,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_num_units()
|
|
local count = tonumber(num_units.get_value())
|
|
if count ~= nil and count > 0 and count < 5 then
|
|
nu_error.hide(true)
|
|
tmp_cfg.UnitCount = count
|
|
main_pane.set_value(3)
|
|
else nu_error.show() end
|
|
end
|
|
|
|
PushButton{parent=fac_c_2,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=fac_c_2,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=fac_c_3,x=1,y=1,height=2,text="The following facility configuration was fetched from your supervisor computer."}
|
|
|
|
local fac_config_list = ListBox{parent=fac_c_3,x=1,y=4,height=9,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
PushButton{parent=fac_c_3,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=fac_c_3,x=44,y=14,text="Next \x1a",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- MONITOR CONFIG
|
|
|
|
local mon_c_1 = Div{parent=mon_cfg,x=2,y=4,width=49}
|
|
|
|
local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1}}
|
|
|
|
TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.lime)}
|
|
|
|
TextBox{parent=mon_c_1,x=1,y=1,height=3,text="Your configuration requires the following monitors:"}
|
|
|
|
local nu_error = TextBox{parent=mon_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_num_units()
|
|
-- local count = tonumber(num_units.get_value())
|
|
-- if count ~= nil and count > 0 and count < 5 then
|
|
-- nu_error.hide(true)
|
|
-- tmp_cfg.UnitCount = count
|
|
|
|
-- local confs = tool_ctl.cooling_elems
|
|
-- if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end
|
|
-- if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end
|
|
-- if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end
|
|
|
|
-- crd_pane.set_value(2)
|
|
-- else nu_error.show() end
|
|
end
|
|
|
|
PushButton{parent=mon_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- COORDINATOR CONFIG
|
|
|
|
local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
|
local crd_c_2 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
|
local crd_c_3 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
|
local crd_c_4 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
|
local crd_c_5 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
|
local crd_c_6 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
|
|
|
local crd_pane = MultiPane{parent=crd_cfg,x=1,y=4,panes={crd_c_1}}
|
|
|
|
TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator Configuration",fg_bg=cpair(colors.black,colors.lime)}
|
|
|
|
TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
|
local num_units = NumberField{parent=crd_c_1,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
|
TextBox{parent=crd_c_1,x=7,y=5,height=1,text="reactors"}
|
|
TextBox{parent=crd_c_1,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg}
|
|
|
|
local nu_error = TextBox{parent=crd_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_num_units()
|
|
local count = tonumber(num_units.get_value())
|
|
if count ~= nil and count > 0 and count < 5 then
|
|
nu_error.hide(true)
|
|
tmp_cfg.UnitCount = count
|
|
|
|
local confs = tool_ctl.cooling_elems
|
|
if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end
|
|
if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end
|
|
if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end
|
|
|
|
crd_pane.set_value(2)
|
|
else nu_error.show() end
|
|
end
|
|
|
|
PushButton{parent=crd_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=crd_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- LOG CONFIG
|
|
|
|
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
|
|
|
TextBox{parent=log_c_1,x=1,y=1,height=1,text="Please configure logging below."}
|
|
|
|
TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"}
|
|
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
|
|
|
TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"}
|
|
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
|
|
|
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
|
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
|
|
|
local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_log()
|
|
if path.get_value() ~= "" then
|
|
path_err.hide(true)
|
|
tmp_cfg.LogMode = mode.get_value() - 1
|
|
tmp_cfg.LogPath = path.get_value()
|
|
tmp_cfg.LogDebug = en_dbg.get_value()
|
|
tool_ctl.gen_summary(tmp_cfg)
|
|
tool_ctl.viewing_config = false
|
|
tool_ctl.importing_legacy = false
|
|
tool_ctl.settings_apply.show()
|
|
main_pane.set_value(5)
|
|
else path_err.show() end
|
|
end
|
|
|
|
PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- SUMMARY OF CHANGES
|
|
|
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_2 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_3 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_4 = Div{parent=summary,x=2,y=4,width=49}
|
|
|
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
|
|
|
TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
|
|
|
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
local function back_from_settings()
|
|
if tool_ctl.viewing_config or tool_ctl.importing_legacy then
|
|
main_pane.set_value(1)
|
|
tool_ctl.viewing_config = false
|
|
tool_ctl.importing_legacy = false
|
|
tool_ctl.settings_apply.show()
|
|
else
|
|
main_pane.set_value(4)
|
|
end
|
|
end
|
|
|
|
---@param element graphics_element
|
|
---@param data any
|
|
local function try_set(element, data)
|
|
if data ~= nil then element.set_value(data) end
|
|
end
|
|
|
|
local function save_and_continue()
|
|
for k, v in pairs(tmp_cfg) do settings.set(k, v) end
|
|
|
|
if settings.save("supervisor.settings") then
|
|
load_settings(settings_cfg, true)
|
|
load_settings(ini_cfg)
|
|
|
|
try_set(num_units, ini_cfg.UnitCount)
|
|
try_set(tank_mode, ini_cfg.FacilityTankMode)
|
|
try_set(svr_chan, ini_cfg.SVR_Channel)
|
|
try_set(plc_chan, ini_cfg.PLC_Channel)
|
|
try_set(rtu_chan, ini_cfg.RTU_Channel)
|
|
try_set(crd_chan, ini_cfg.CRD_Channel)
|
|
try_set(pkt_chan, ini_cfg.PKT_Channel)
|
|
try_set(plc_timeout, ini_cfg.PLC_Timeout)
|
|
try_set(rtu_timeout, ini_cfg.RTU_Timeout)
|
|
try_set(crd_timeout, ini_cfg.CRD_Timeout)
|
|
try_set(pkt_timeout, ini_cfg.PKT_Timeout)
|
|
try_set(range, ini_cfg.TrustedRange)
|
|
try_set(key, ini_cfg.AuthKey)
|
|
try_set(mode, ini_cfg.LogMode)
|
|
try_set(path, ini_cfg.LogPath)
|
|
try_set(en_dbg, ini_cfg.LogDebug)
|
|
|
|
for i = 1, #ini_cfg.CoolingConfig do
|
|
local cfg, elems = ini_cfg.CoolingConfig[i], tool_ctl.cooling_elems[i]
|
|
try_set(elems.boilers, cfg.BoilerCount)
|
|
try_set(elems.turbines, cfg.TurbineCount)
|
|
try_set(elems.tank, cfg.TankConnection)
|
|
end
|
|
|
|
for i = 1, #ini_cfg.FacilityTankDefs do
|
|
try_set(tool_ctl.tank_elems[i].tank_opt, ini_cfg.FacilityTankDefs[i])
|
|
end
|
|
|
|
en_fac_tanks.set_value(ini_cfg.FacilityTankMode > 0)
|
|
|
|
tool_ctl.view_cfg.enable()
|
|
|
|
if tool_ctl.importing_legacy then
|
|
tool_ctl.importing_legacy = false
|
|
sum_pane.set_value(3)
|
|
else
|
|
sum_pane.set_value(2)
|
|
end
|
|
else
|
|
sum_pane.set_value(4)
|
|
end
|
|
end
|
|
|
|
PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_settings,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
|
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"}
|
|
|
|
local function go_home()
|
|
main_pane.set_value(1)
|
|
svr_pane.set_value(1)
|
|
net_pane.set_value(1)
|
|
sum_pane.set_value(1)
|
|
end
|
|
|
|
PushButton{parent=sum_c_2,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_2,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."}
|
|
|
|
local function delete_legacy()
|
|
fs.delete("/supervisor/config.lua")
|
|
exit()
|
|
end
|
|
|
|
PushButton{parent=sum_c_3,x=1,y=14,min_width=8,text="Cancel",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_3,x=44,y=14,min_width=6,text="OK",callback=delete_legacy,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
TextBox{parent=sum_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
|
PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
-- CONFIG CHANGE LOG
|
|
|
|
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg}
|
|
|
|
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
for _, change in ipairs(changes) do
|
|
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
|
for _, v in ipairs(change[2]) do
|
|
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
|
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
|
|
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
|
end
|
|
end
|
|
|
|
PushButton{parent=cl,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
-- IMPORT ERROR
|
|
|
|
local i_err = Div{parent=import_err,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=import_err,x=1,y=2,height=1,text=" Import Error",fg_bg=cpair(colors.black,colors.red)}
|
|
TextBox{parent=i_err,x=1,y=1,height=1,text="There is a problem with your config.lua file:"}
|
|
|
|
local import_err_msg = TextBox{parent=i_err,x=1,y=3,height=6,text=""}
|
|
|
|
PushButton{parent=i_err,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=i_err,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
-- set tool functions now that we have the elements
|
|
|
|
-- load a legacy config file
|
|
function tool_ctl.load_legacy()
|
|
local config = require("supervisor.config")
|
|
|
|
tmp_cfg.UnitCount = config.NUM_REACTORS
|
|
|
|
if config.REACTOR_COOLING == nil or tmp_cfg.UnitCount ~= #config.REACTOR_COOLING then
|
|
import_err_msg.set_value("Cooling configuration table length must match the number of units.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
local cfg = config.REACTOR_COOLING[i]
|
|
|
|
if type(cfg) ~= "table" then
|
|
import_err_msg.set_value("Cooling configuration for unit " .. i .. " must be a table.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
tmp_cfg.CoolingConfig[i] = { BoilerCount = cfg.BOILERS or 0, TurbineCount = cfg.TURBINES or 1, TankConnection = cfg.TANK or false }
|
|
end
|
|
|
|
tmp_cfg.FacilityTankMode = config.FAC_TANK_MODE
|
|
|
|
if not (util.is_int(tmp_cfg.FacilityTankMode) and tmp_cfg.FacilityTankMode >= 0 and tmp_cfg.FacilityTankMode <= 8) then
|
|
import_err_msg.set_value("Invalid tank mode present in config. FAC_TANK_MODE must be a number 0 through 8.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
if config.FAC_TANK_MODE > 0 then
|
|
if config.FAC_TANK_DEFS == nil or tmp_cfg.UnitCount ~= #config.FAC_TANK_DEFS then
|
|
import_err_msg.set_value("Facility tank definitions table length must match the number of units when using facility tanks.")
|
|
main_pane.set_value(7)
|
|
return
|
|
end
|
|
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
tmp_cfg.FacilityTankDefs[i] = config.FAC_TANK_DEFS[i]
|
|
end
|
|
else
|
|
tmp_cfg.FacilityTankMode = 0
|
|
tmp_cfg.FacilityTankDefs = {}
|
|
end
|
|
|
|
tmp_cfg.SVR_Channel = config.SVR_CHANNEL
|
|
tmp_cfg.PLC_Channel = config.PLC_CHANNEL
|
|
tmp_cfg.RTU_Channel = config.RTU_CHANNEL
|
|
tmp_cfg.CRD_Channel = config.CRD_CHANNEL
|
|
tmp_cfg.PKT_Channel = config.PKT_CHANNEL
|
|
|
|
tmp_cfg.PLC_Timeout = config.PLC_TIMEOUT
|
|
tmp_cfg.RTU_Timeout = config.RTU_TIMEOUT
|
|
tmp_cfg.CRD_Timeout = config.CRD_TIMEOUT
|
|
tmp_cfg.PKT_Timeout = config.PKT_TIMEOUT
|
|
|
|
tmp_cfg.TrustedRange = config.TRUSTED_RANGE
|
|
tmp_cfg.AuthKey = config.AUTH_KEY or ""
|
|
tmp_cfg.LogMode = config.LOG_MODE
|
|
tmp_cfg.LogPath = config.LOG_PATH
|
|
tmp_cfg.LogDebug = config.LOG_DEBUG or false
|
|
|
|
tool_ctl.gen_summary(tmp_cfg)
|
|
sum_pane.set_value(1)
|
|
main_pane.set_value(5)
|
|
tool_ctl.importing_legacy = true
|
|
end
|
|
|
|
-- show the facility's unit count and cooling configuration data
|
|
function tool_ctl.show_sv_cfg()
|
|
local conf = tool_ctl.sv_cool_conf
|
|
fac_config_list.remove_all()
|
|
|
|
local str = util.sprintf("Facility has %d reactor units:", #conf)
|
|
|
|
local line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)}
|
|
TextBox{parent=line,text=str,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)}
|
|
|
|
for i = 1, #conf do
|
|
local num_b, num_t = conf[i][1], conf[i][2]
|
|
str = util.sprintf("\x07 Unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s"))
|
|
|
|
local c_line = Div{parent=fac_config_list,height=1,fg_bg=cpair(colors.gray,colors.white)}
|
|
TextBox{parent=c_line,text=str,fg_bg=cpair(colors.black,line.get_fg_bg().bkg)}
|
|
end
|
|
end
|
|
|
|
-- expose the auth key on the summary page
|
|
function tool_ctl.show_auth_key()
|
|
tool_ctl.show_key_btn.disable()
|
|
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()
|
|
|
|
ppm.mount_all()
|
|
|
|
-- set overridden colors
|
|
for i = 1, #style.colors do
|
|
term.setPaletteColor(style.colors[i].c, style.colors[i].hex)
|
|
end
|
|
|
|
local status, error = pcall(function ()
|
|
local display = DisplayBox{window=term.current(),fg_bg=style.root}
|
|
config_view(display)
|
|
|
|
while true do
|
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
|
|
|
-- handle event
|
|
if event == "timer" then
|
|
tcd.handle(param1)
|
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then
|
|
local m_e = core.events.new_mouse_event(event, param1, param2, param3)
|
|
if m_e then display.handle_mouse(m_e) end
|
|
elseif event == "char" or event == "key" or event == "key_up" then
|
|
local k_e = core.events.new_key_event(event, param1, param2)
|
|
if k_e then display.handle_key(k_e) end
|
|
elseif event == "paste" then
|
|
display.handle_paste(param1)
|
|
elseif event == "peripheral_detach" then
|
|
ppm.handle_unmount(param1)
|
|
elseif event == "peripheral" then
|
|
ppm.mount(param1)
|
|
elseif event == "modem_message" and tool_ctl.nic ~= nil and tool_ctl.net_listen then
|
|
local s_pkt = tool_ctl.nic.receive(param1, param2, param3, param4, param5)
|
|
|
|
if s_pkt and s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
|
local mgmt_pkt = comms.mgmt_packet()
|
|
if mgmt_pkt.decode(s_pkt) then
|
|
tcd.abort(handle_timeout)
|
|
handle_packet(mgmt_pkt.get())
|
|
end
|
|
end
|
|
end
|
|
|
|
if event == "terminate" then return end
|
|
end
|
|
end)
|
|
|
|
-- restore colors
|
|
for i = 1, #style.colors do
|
|
local r, g, b = term.nativePaletteColor(style.colors[i].c)
|
|
term.setPaletteColor(style.colors[i].c, r, g, b)
|
|
end
|
|
|
|
reset_term()
|
|
if not status then
|
|
println("configurator error: " .. error)
|
|
end
|
|
|
|
return status, error
|
|
end
|
|
|
|
return configurator
|