mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
1490 lines
68 KiB
Lua
1490 lines
68 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 themes = require("graphics.themes")
|
|
|
|
local core = require("graphics.core")
|
|
|
|
local DisplayBox = require("graphics.elements.displaybox")
|
|
local Div = require("graphics.elements.div")
|
|
local ListBox = require("graphics.elements.listbox")
|
|
local MultiPane = require("graphics.elements.multipane")
|
|
local TextBox = require("graphics.elements.textbox")
|
|
|
|
local CheckBox = require("graphics.elements.controls.checkbox")
|
|
local PushButton = require("graphics.elements.controls.push_button")
|
|
local RadioButton = require("graphics.elements.controls.radio_button")
|
|
|
|
local NumberField = require("graphics.elements.form.number_field")
|
|
local TextField = require("graphics.elements.form.text_field")
|
|
|
|
local IndLight = require("graphics.elements.indicators.light")
|
|
|
|
local println = util.println
|
|
local tri = util.trinary
|
|
|
|
local PROTOCOL = comms.PROTOCOL
|
|
local DEVICE_TYPE = comms.DEVICE_TYPE
|
|
local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
|
local MGMT_TYPE = comms.MGMT_TYPE
|
|
|
|
local cpair = core.cpair
|
|
|
|
local LEFT = core.ALIGN.LEFT
|
|
local CENTER = core.ALIGN.CENTER
|
|
local RIGHT = core.ALIGN.RIGHT
|
|
|
|
-- changes to the config data/format to let the user know
|
|
local changes = {
|
|
{ "v1.2.4", { "Added temperature scale options" } },
|
|
{ "v1.2.12", { "Added main UI theme", "Added front panel UI theme", "Added color accessibility modes" } },
|
|
{ "v1.3.3", { "Added standard with black off state color mode", "Added blue indicator color modes" } }
|
|
}
|
|
|
|
---@class crd_configurator
|
|
local configurator = {}
|
|
|
|
local style = {}
|
|
|
|
style.root = cpair(colors.black, colors.lightGray)
|
|
style.header = cpair(colors.white, colors.gray)
|
|
|
|
style.colors = themes.smooth_stone.colors
|
|
|
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
|
local nav_fg_bg = bw_fg_bg
|
|
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
|
local dis_fg_bg = cpair(colors.lightGray,colors.white)
|
|
|
|
---@class _crd_cfg_tool_ctl
|
|
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
|
|
|
|
start_fail = 0,
|
|
fail_message = "",
|
|
has_config = false,
|
|
viewing_config = false,
|
|
importing_legacy = false,
|
|
jumped_to_color = false,
|
|
|
|
view_cfg = nil, ---@type graphics_element
|
|
color_cfg = nil, ---@type graphics_element
|
|
color_next = nil, ---@type graphics_element
|
|
color_apply = 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
|
|
|
|
apply_mon = nil, ---@type graphics_element
|
|
|
|
update_mon_reqs = nil, ---@type function
|
|
gen_mon_list = function () end,
|
|
edit_monitor = nil, ---@type function
|
|
|
|
mon_iface = "",
|
|
mon_expect = {}
|
|
}
|
|
|
|
---@class crd_config
|
|
local tmp_cfg = {
|
|
UnitCount = 1,
|
|
SpeakerVolume = 1.0,
|
|
Time24Hour = true,
|
|
TempScale = 1,
|
|
DisableFlowView = false,
|
|
MainDisplay = nil, ---@type string
|
|
FlowDisplay = nil, ---@type string
|
|
UnitDisplays = {},
|
|
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,
|
|
MainTheme = 1,
|
|
FrontPanelTheme = 1,
|
|
ColorMode = 1
|
|
}
|
|
|
|
---@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 },
|
|
{ "MainDisplay", "Main Monitor", nil },
|
|
{ "FlowDisplay", "Flow Monitor", nil },
|
|
{ "UnitDisplays", "Unit Monitors", {} },
|
|
{ "SpeakerVolume", "Speaker Volume", 1.0 },
|
|
{ "Time24Hour", "Use 24-hour Time Format", true },
|
|
{ "TempScale", "Temperature Scale", 1 },
|
|
{ "DisableFlowView", "Disable Flow Monitor (legacy, discouraged)", false },
|
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
|
{ "CRD_Channel", "CRD Channel", 16243 },
|
|
{ "PKT_Channel", "PKT Channel", 16244 },
|
|
{ "SVR_Timeout", "Supervisor 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 },
|
|
{ "MainTheme", "Main UI Theme", themes.UI_THEME.SMOOTH_STONE },
|
|
{ "FrontPanelTheme", "Front Panel Theme", themes.FP_THEME.SANDSTONE },
|
|
{ "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD }
|
|
}
|
|
|
|
-- check if a value is an integer within a range (inclusive)
|
|
---@param x integer
|
|
---@param min integer
|
|
---@param max integer
|
|
local function is_int_min_max(x, min, max) return util.is_int(x) and x >= min and x <= max end
|
|
|
|
-- send a management packet to the supervisor
|
|
---@param msg_type MGMT_TYPE
|
|
---@param msg table
|
|
local function send_sv(msg_type, msg)
|
|
local s_pkt = comms.scada_packet()
|
|
local pkt = comms.mgmt_packet()
|
|
|
|
pkt.make(msg_type, msg)
|
|
s_pkt.make(tool_ctl.sv_addr, tool_ctl.sv_seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable())
|
|
|
|
tool_ctl.nic.transmit(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
|
|
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 tmp_cfg fields from ini_cfg fields for displays
|
|
local function preset_monitor_fields()
|
|
tmp_cfg.DisableFlowView = ini_cfg.DisableFlowView
|
|
|
|
tmp_cfg.MainDisplay = ini_cfg.MainDisplay
|
|
tmp_cfg.FlowDisplay = ini_cfg.FlowDisplay
|
|
for i = 1, ini_cfg.UnitCount do
|
|
tmp_cfg.UnitDisplays[i] = ini_cfg.UnitDisplays[i]
|
|
end
|
|
end
|
|
|
|
-- load data from the settings file
|
|
---@param target crd_config
|
|
---@param raw boolean? true to not use default values
|
|
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 spkr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local crd_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local log_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local clr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
|
local summary = Div{parent=root_pane_div,x=1,y=1}
|
|
local changelog = Div{parent=root_pane_div,x=1,y=1}
|
|
|
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,spkr_cfg,crd_cfg,log_cfg,clr_cfg,summary,changelog}}
|
|
|
|
-- 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.start_fail == 2 then
|
|
local msg = util.c("Notice: There is a problem with your monitor configuration. ", tool_ctl.fail_message, " Please reconfigure monitors or correct their sizes.")
|
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text=msg,fg_bg=cpair(colors.red,colors.lightGray)}
|
|
y_start = y_start + 5
|
|
elseif tool_ctl.start_fail > 0 then
|
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
|
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(9)
|
|
end
|
|
|
|
if fs.exists("/coordinator/config.lua") then
|
|
PushButton{parent=main_page,x=2,y=y_start,min_width=28,text="Import Legacy 'config.lua'",callback=function()tool_ctl.load_legacy()end,fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=btn_act_fg_bg}
|
|
y_start = y_start + 2
|
|
end
|
|
|
|
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
|
|
|
local function jump_color()
|
|
tool_ctl.jumped_to_color = true
|
|
tool_ctl.color_next.hide(true)
|
|
tool_ctl.color_apply.show()
|
|
main_pane.set_value(8)
|
|
end
|
|
|
|
PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(10)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
if not tool_ctl.has_config then
|
|
tool_ctl.view_cfg.disable()
|
|
tool_ctl.color_cfg.disable()
|
|
end
|
|
|
|
--#region Network
|
|
|
|
local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
local net_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 api_timeout = NumberField{parent=net_c_2,x=20,y=10,width=7,default=ini_cfg.API_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=net_c_2,x=28,y=8,height=4,width=7,text="seconds\n\nseconds",fg_bg=g_lg_fg_bg}
|
|
|
|
local ct_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_timeouts()
|
|
local svr_cto, api_cto = tonumber(svr_timeout.get_value()), tonumber(api_timeout.get_value())
|
|
if svr_cto ~= nil and api_cto ~= nil then
|
|
tmp_cfg.SVR_Timeout, tmp_cfg.API_Timeout = svr_cto, api_cto
|
|
net_pane.set_value(3)
|
|
ct_err.hide(true)
|
|
else ct_err.show() end
|
|
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
|
|
-- prep supervisor connection screen
|
|
tool_ctl.sv_next.hide()
|
|
tool_ctl.sv_skip.disable()
|
|
tool_ctl.sv_skip.show()
|
|
tool_ctl.sv_conn_button.enable()
|
|
tool_ctl.sv_conn_status.set_value("")
|
|
tool_ctl.sv_conn_detail.set_value("")
|
|
|
|
tmp_cfg.AuthKey = key.get_value()
|
|
key_err.hide(true)
|
|
|
|
-- init mac for supervisor connection
|
|
if string.len(v) >= 8 then network.init_mac(tmp_cfg.AuthKey) else network.deinit_mac() end
|
|
|
|
main_pane.set_value(3)
|
|
|
|
tcd.dispatch_unique(2, function () tool_ctl.sv_skip.enable() end)
|
|
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
|
|
|
|
--#region Facility
|
|
|
|
local fac_c_1 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
|
local fac_c_2 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
|
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=dis_fg_bg}
|
|
|
|
local function sv_skip()
|
|
tcd.abort(handle_timeout)
|
|
tool_ctl.sv_cool_conf = nil
|
|
tool_ctl.net_listen = false
|
|
fac_pane.set_value(2)
|
|
end
|
|
|
|
local function sv_next()
|
|
tool_ctl.show_sv_cfg()
|
|
tool_ctl.update_mon_reqs()
|
|
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,dis_fg_bg=dis_fg_bg}
|
|
tool_ctl.sv_next = PushButton{parent=fac_c_1,x=44,y=14,text="Next \x1a",callback=sv_next,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,hidden=true}
|
|
|
|
TextBox{parent=fac_c_2,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
|
local num_units = NumberField{parent=fac_c_2,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
|
TextBox{parent=fac_c_2,x=7,y=5,height=1,text="reactors"}
|
|
TextBox{parent=fac_c_2,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg}
|
|
TextBox{parent=fac_c_2,x=1,y=10,height=3,text="Since you skipped supervisor sync, the main monitor minimum height can't be determined precisely. It is marked with * on the next page.",fg_bg=g_lg_fg_bg}
|
|
|
|
local nu_error = TextBox{parent=fac_c_2,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
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
|
|
tool_ctl.update_mon_reqs()
|
|
main_pane.set_value(4)
|
|
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=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
PushButton{parent=fac_c_3,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=fac_c_3,x=44,y=14,text="Next \x1a",callback=function()main_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#endregion
|
|
|
|
--#region Monitors
|
|
|
|
local mon_c_1 = Div{parent=mon_cfg,x=2,y=4,width=49}
|
|
local mon_c_2 = Div{parent=mon_cfg,x=2,y=4,width=49}
|
|
local mon_c_3 = Div{parent=mon_cfg,x=2,y=4,width=49}
|
|
local mon_c_4 = Div{parent=mon_cfg,x=2,y=4,width=49}
|
|
|
|
local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3,mon_c_4}}
|
|
|
|
TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.blue)}
|
|
|
|
TextBox{parent=mon_c_1,x=1,y=1,height=5,text="Your configuration requires the following monitors. The main and flow monitors' heights are dependent on your unit count and cooling setup. If you manually entered the unit count, a * will be shown on potentially inaccurate calculations."}
|
|
local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
local function next_from_reqs()
|
|
-- unassign unit monitors above the unit count
|
|
for i = tmp_cfg.UnitCount + 1, 4 do tmp_cfg.UnitDisplays[i] = nil end
|
|
|
|
tool_ctl.gen_mon_list()
|
|
mon_pane.set_value(2)
|
|
end
|
|
|
|
PushButton{parent=mon_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=mon_c_1,x=8,y=14,text="Legacy Options",min_width=16,callback=function()mon_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=mon_c_1,x=44,y=14,text="Next \x1a",callback=next_from_reqs,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=mon_c_2,x=1,y=1,height=5,text="Please configure your monitors below. You can go back to the prior page without losing progress to double check what you need. All of those monitors must be assigned before you can proceed."}
|
|
|
|
local mon_list = ListBox{parent=mon_c_2,x=1,y=6,height=7,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
local assign_err = TextBox{parent=mon_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_monitors()
|
|
if tmp_cfg.MainDisplay == nil then
|
|
assign_err.set_value("Please assign the main monitor.")
|
|
elseif tmp_cfg.FlowDisplay == nil and not tmp_cfg.DisableFlowView then
|
|
assign_err.set_value("Please assign the flow monitor.")
|
|
elseif util.table_len(tmp_cfg.UnitDisplays) ~= tmp_cfg.UnitCount then
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
if tmp_cfg.UnitDisplays[i] == nil then
|
|
assign_err.set_value("Please assign the unit " .. i .. " monitor.")
|
|
break
|
|
end
|
|
end
|
|
else
|
|
assign_err.hide(true)
|
|
main_pane.set_value(5)
|
|
return
|
|
end
|
|
|
|
assign_err.show()
|
|
end
|
|
|
|
PushButton{parent=mon_c_2,x=1,y=14,text="\x1b Back",callback=function()mon_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=mon_c_2,x=44,y=14,text="Next \x1a",callback=submit_monitors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
local mon_desc = TextBox{parent=mon_c_3,x=1,y=1,height=4,text=""}
|
|
|
|
local mon_unit_l, mon_unit = nil, nil ---@type graphics_element, graphics_element
|
|
|
|
local mon_warn = TextBox{parent=mon_c_3,x=1,y=11,height=2,text="",fg_bg=cpair(colors.red,colors.lightGray)}
|
|
|
|
---@param val integer assignment type
|
|
local function on_assign_mon(val)
|
|
if val == 2 and tmp_cfg.DisableFlowView then
|
|
tool_ctl.apply_mon.disable()
|
|
mon_warn.set_value("You disabled having a flow view monitor. It can't be set unless you go back and enable it.")
|
|
mon_warn.show()
|
|
elseif not util.table_contains(tool_ctl.mon_expect, val) then
|
|
tool_ctl.apply_mon.disable()
|
|
mon_warn.set_value("That assignment doesn't fit monitor dimensions. You'll need to resize the monitor for it to work.")
|
|
mon_warn.show()
|
|
else
|
|
tool_ctl.apply_mon.enable()
|
|
mon_warn.hide(true)
|
|
end
|
|
|
|
if val == 3 then
|
|
mon_unit_l.show()
|
|
mon_unit.show()
|
|
else
|
|
mon_unit_l.hide(true)
|
|
mon_unit.hide(true)
|
|
end
|
|
|
|
local value = mon_unit.get_value()
|
|
mon_unit.set_max(tmp_cfg.UnitCount)
|
|
if value == "0" or value == nil then mon_unit.set_value(0) end
|
|
end
|
|
|
|
TextBox{parent=mon_c_3,x=1,y=6,width=10,height=1,text="Assignment"}
|
|
local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign_mon,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.blue}
|
|
|
|
mon_unit_l = TextBox{parent=mon_c_3,x=18,y=6,width=7,height=1,text="Unit ID"}
|
|
mon_unit = NumberField{parent=mon_c_3,x=18,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
|
|
|
local mon_u_err = TextBox{parent=mon_c_3,x=8,y=14,height=1,width=35,text="Please provide a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
-- purge all assignments for a given monitor
|
|
---@param iface string
|
|
local function purge_assignments(iface)
|
|
if tmp_cfg.MainDisplay == iface then
|
|
tmp_cfg.MainDisplay = nil
|
|
elseif tmp_cfg.FlowDisplay == iface then
|
|
tmp_cfg.FlowDisplay = nil
|
|
else
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
if tmp_cfg.UnitDisplays[i] == iface then tmp_cfg.UnitDisplays[i] = nil end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function apply_monitor()
|
|
local iface = tool_ctl.mon_iface
|
|
local type = mon_assign.get_value()
|
|
local u_id = tonumber(mon_unit.get_value())
|
|
|
|
if type == 1 then
|
|
purge_assignments(iface)
|
|
tmp_cfg.MainDisplay = iface
|
|
elseif type == 2 then
|
|
purge_assignments(iface)
|
|
tmp_cfg.FlowDisplay = iface
|
|
elseif u_id and u_id > 0 then
|
|
purge_assignments(iface)
|
|
tmp_cfg.UnitDisplays[u_id] = iface
|
|
else
|
|
mon_u_err.show()
|
|
return
|
|
end
|
|
|
|
tool_ctl.gen_mon_list()
|
|
mon_u_err.hide(true)
|
|
mon_pane.set_value(2)
|
|
end
|
|
|
|
PushButton{parent=mon_c_3,x=1,y=14,text="\x1b Back",callback=function()mon_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.apply_mon = PushButton{parent=mon_c_3,x=43,y=14,min_width=7,text="Apply",callback=apply_monitor,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
|
|
|
TextBox{parent=mon_c_4,x=1,y=1,height=3,text="For legacy compatibility with facilities built without space for a flow monitor, you can disable the flow monitor requirement here."}
|
|
TextBox{parent=mon_c_4,x=1,y=5,height=3,text="Please be aware that THIS OPTION WILL BE REMOVED ON RELEASE. Disabling it will only be available for the remainder of the beta."}
|
|
|
|
local dis_flow_view = CheckBox{parent=mon_c_4,x=1,y=9,default=ini_cfg.DisableFlowView,label="Disable Flow View Monitor",box_fg_bg=cpair(colors.blue,colors.black)}
|
|
|
|
local function back_from_legacy()
|
|
tmp_cfg.DisableFlowView = dis_flow_view.get_value()
|
|
tool_ctl.update_mon_reqs()
|
|
mon_pane.set_value(1)
|
|
end
|
|
|
|
PushButton{parent=mon_c_4,x=44,y=14,min_width=6,text="Done",callback=back_from_legacy,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#endregion
|
|
|
|
--#region Speaker
|
|
|
|
local spkr_c = Div{parent=spkr_cfg,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=spkr_cfg,x=1,y=2,height=1,text=" Speaker Configuration",fg_bg=cpair(colors.black,colors.cyan)}
|
|
|
|
TextBox{parent=spkr_c,x=1,y=1,height=2,text="The coordinator uses a speaker to play alarm sounds."}
|
|
TextBox{parent=spkr_c,x=1,y=4,height=3,text="You can change the speaker audio volume from the default. The range is 0.0 to 3.0, where 1.0 is standard volume."}
|
|
|
|
local s_vol = NumberField{parent=spkr_c,x=1,y=8,width=9,max_chars=7,allow_decimal=true,default=ini_cfg.SpeakerVolume,min=0,max=3,fg_bg=bw_fg_bg}
|
|
|
|
TextBox{parent=spkr_c,x=1,y=10,height=3,text="Note: alarm sine waves are at half scale so that multiple will be required to reach full scale.",fg_bg=g_lg_fg_bg}
|
|
|
|
local s_vol_err = TextBox{parent=spkr_c,x=8,y=14,height=1,width=35,text="Please set a volume.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
|
|
local function submit_vol()
|
|
local vol = tonumber(s_vol.get_value())
|
|
if vol ~= nil then
|
|
s_vol_err.hide(true)
|
|
tmp_cfg.SpeakerVolume = vol
|
|
main_pane.set_value(6)
|
|
else s_vol_err.show() end
|
|
end
|
|
|
|
PushButton{parent=spkr_c,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=spkr_c,x=44,y=14,text="Next \x1a",callback=submit_vol,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#endregion
|
|
|
|
--#region Coordinator UI
|
|
|
|
local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
|
|
|
TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator UI Configuration",fg_bg=cpair(colors.black,colors.lime)}
|
|
|
|
TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Configure the UI interface options below if you wish to customize formats."}
|
|
|
|
TextBox{parent=crd_c_1,x=1,y=4,height=1,text="Clock Time Format"}
|
|
local clock_fmt = RadioButton{parent=crd_c_1,x=1,y=5,default=util.trinary(ini_cfg.Time24Hour,1,2),options={"24-Hour","12-Hour"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
|
|
|
TextBox{parent=crd_c_1,x=1,y=8,height=1,text="Temperature Scale"}
|
|
local temp_scale = RadioButton{parent=crd_c_1,x=1,y=9,default=ini_cfg.TempScale,options={"Kelvin","Celsius","Fahrenheit","Rankine"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
|
|
|
local function submit_ui_opts()
|
|
tmp_cfg.Time24Hour = clock_fmt.get_value() == 1
|
|
tmp_cfg.TempScale = temp_scale.get_value()
|
|
main_pane.set_value(7)
|
|
end
|
|
|
|
PushButton{parent=crd_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=crd_c_1,x=44,y=14,text="Next \x1a",callback=submit_ui_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#endregion
|
|
|
|
--#region Logging
|
|
|
|
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.color_apply.hide(true)
|
|
tool_ctl.color_next.show()
|
|
main_pane.set_value(8)
|
|
else path_err.show() end
|
|
end
|
|
|
|
PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#endregion
|
|
|
|
--#region Color Options
|
|
|
|
local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
|
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
|
|
|
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
|
|
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color themes for the different UI displays."}
|
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
|
|
|
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Main UI Theme"}
|
|
local main_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.MainTheme,options=themes.UI_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
|
|
|
TextBox{parent=clr_c_1,x=18,y=7,height=1,text="Front Panel Theme"}
|
|
local fp_theme = RadioButton{parent=clr_c_1,x=18,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
|
|
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
|
|
|
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
|
local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true}
|
|
local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true}
|
|
|
|
local function recolor(value)
|
|
local c = themes.smooth_stone.color_modes[value]
|
|
|
|
if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then
|
|
b_off.hide()
|
|
g_off.show()
|
|
else
|
|
g_off.hide()
|
|
b_off.show()
|
|
end
|
|
|
|
if #c == 0 then
|
|
for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end
|
|
else
|
|
term.setPaletteColor(colors.green, c[1].hex)
|
|
term.setPaletteColor(colors.yellow, c[2].hex)
|
|
term.setPaletteColor(colors.red, c[3].hex)
|
|
end
|
|
end
|
|
|
|
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
|
|
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
|
|
|
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
local function back_from_colors()
|
|
main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 7))
|
|
tool_ctl.jumped_to_color = false
|
|
recolor(1)
|
|
end
|
|
|
|
local function show_access()
|
|
clr_pane.set_value(2)
|
|
recolor(c_mode.get_value())
|
|
end
|
|
|
|
local function submit_colors()
|
|
tmp_cfg.MainTheme = main_theme.get_value()
|
|
tmp_cfg.FrontPanelTheme = fp_theme.get_value()
|
|
tmp_cfg.ColorMode = c_mode.get_value()
|
|
|
|
if tool_ctl.jumped_to_color then
|
|
settings.set("MainTheme", tmp_cfg.MainTheme)
|
|
settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme)
|
|
settings.set("ColorMode", tmp_cfg.ColorMode)
|
|
|
|
if settings.save("/coordinator.settings") then
|
|
load_settings(settings_cfg, true)
|
|
load_settings(ini_cfg)
|
|
clr_pane.set_value(3)
|
|
else
|
|
clr_pane.set_value(4)
|
|
end
|
|
else
|
|
tool_ctl.gen_summary(tmp_cfg)
|
|
tool_ctl.viewing_config = false
|
|
tool_ctl.importing_legacy = false
|
|
tool_ctl.settings_apply.show()
|
|
main_pane.set_value(9)
|
|
end
|
|
end
|
|
|
|
PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
|
|
|
tool_ctl.color_apply.hide(true)
|
|
|
|
local function c_go_home()
|
|
main_pane.set_value(1)
|
|
clr_pane.set_value(1)
|
|
end
|
|
|
|
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
|
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=clr_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
|
PushButton{parent=clr_c_4,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
PushButton{parent=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
|
|
--#endregion
|
|
|
|
--#region Summary and Saving
|
|
|
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_2 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_3 = Div{parent=summary,x=2,y=4,width=49}
|
|
local sum_c_4 = Div{parent=summary,x=2,y=4,width=49}
|
|
|
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
|
|
|
TextBox{parent=summary,x=1,y=2,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=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
local function back_from_summary()
|
|
if tool_ctl.viewing_config or tool_ctl.importing_legacy then
|
|
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(8)
|
|
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("/coordinator.settings") then
|
|
load_settings(settings_cfg, true)
|
|
load_settings(ini_cfg)
|
|
|
|
try_set(svr_chan, ini_cfg.SVR_Channel)
|
|
try_set(crd_chan, ini_cfg.CRD_Channel)
|
|
try_set(pkt_chan, ini_cfg.PKT_Channel)
|
|
try_set(svr_timeout, ini_cfg.SVR_Timeout)
|
|
try_set(api_timeout, ini_cfg.API_Timeout)
|
|
try_set(range, ini_cfg.TrustedRange)
|
|
try_set(key, ini_cfg.AuthKey)
|
|
try_set(num_units, ini_cfg.UnitCount)
|
|
try_set(dis_flow_view, ini_cfg.DisableFlowView)
|
|
try_set(s_vol, ini_cfg.SpeakerVolume)
|
|
try_set(clock_fmt, util.trinary(ini_cfg.Time24Hour, 1, 2))
|
|
try_set(mode, ini_cfg.LogMode)
|
|
try_set(path, ini_cfg.LogPath)
|
|
try_set(en_dbg, ini_cfg.LogDebug)
|
|
try_set(main_theme, ini_cfg.MainTheme)
|
|
try_set(fp_theme, ini_cfg.FrontPanelTheme)
|
|
try_set(c_mode, ini_cfg.ColorMode)
|
|
|
|
preset_monitor_fields()
|
|
|
|
tool_ctl.gen_mon_list()
|
|
|
|
tool_ctl.view_cfg.enable()
|
|
tool_ctl.color_cfg.enable()
|
|
|
|
if tool_ctl.importing_legacy then
|
|
tool_ctl.importing_legacy = false
|
|
sum_pane.set_value(3)
|
|
else
|
|
sum_pane.set_value(2)
|
|
end
|
|
else
|
|
sum_pane.set_value(4)
|
|
end
|
|
end
|
|
|
|
PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_summary,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
|
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
|
|
|
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"}
|
|
|
|
local function go_home()
|
|
main_pane.set_value(1)
|
|
net_pane.set_value(1)
|
|
fac_pane.set_value(1)
|
|
mon_pane.set_value(1)
|
|
clr_pane.set_value(1)
|
|
sum_pane.set_value(1)
|
|
end
|
|
|
|
PushButton{parent=sum_c_2,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_2,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua and coord.settings files will now be deleted, then the configurator will exit."}
|
|
|
|
local function delete_legacy()
|
|
fs.delete("/coordinator/config.lua")
|
|
fs.delete("/coord.settings")
|
|
exit()
|
|
end
|
|
|
|
PushButton{parent=sum_c_3,x=1,y=14,min_width=8,text="Cancel",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_3,x=44,y=14,min_width=6,text="OK",callback=delete_legacy,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
TextBox{parent=sum_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
|
PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
|
|
--#endregion
|
|
|
|
-- 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=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
|
|
for _, change in ipairs(changes) do
|
|
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
|
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}
|
|
|
|
-- set tool functions now that we have the elements
|
|
|
|
-- load a legacy config file
|
|
function tool_ctl.load_legacy()
|
|
local config = require("coordinator.config")
|
|
|
|
tmp_cfg.SVR_Channel = config.SVR_CHANNEL
|
|
tmp_cfg.CRD_Channel = config.CRD_CHANNEL
|
|
tmp_cfg.PKT_Channel = config.PKT_CHANNEL
|
|
tmp_cfg.SVR_Timeout = config.SV_TIMEOUT
|
|
tmp_cfg.API_Timeout = config.API_TIMEOUT
|
|
tmp_cfg.TrustedRange = config.TRUSTED_RANGE
|
|
tmp_cfg.AuthKey = config.AUTH_KEY or ""
|
|
|
|
tmp_cfg.UnitCount = config.NUM_UNITS
|
|
tmp_cfg.DisableFlowView = config.DISABLE_FLOW_VIEW
|
|
tmp_cfg.SpeakerVolume = config.SOUNDER_VOLUME
|
|
tmp_cfg.Time24Hour = config.TIME_24_HOUR
|
|
|
|
tmp_cfg.LogMode = config.LOG_MODE
|
|
tmp_cfg.LogPath = config.LOG_PATH
|
|
tmp_cfg.LogDebug = config.LOG_DEBUG or false
|
|
|
|
settings.load("/coord.settings")
|
|
|
|
tmp_cfg.MainDisplay = settings.get("PRIMARY_DISPLAY")
|
|
tmp_cfg.FlowDisplay = settings.get("FLOW_DISPLAY")
|
|
tmp_cfg.UnitDisplays = settings.get("UNIT_DISPLAYS", {})
|
|
|
|
-- if there are extra monitor entries, delete them now
|
|
-- not doing so will cause the app to fail to start
|
|
if is_int_min_max(tmp_cfg.UnitCount, 1, 4) then
|
|
for i = tmp_cfg.UnitCount + 1, 4 do tmp_cfg.UnitDisplays[i] = nil end
|
|
end
|
|
|
|
if settings.get("ControlStates") == nil then
|
|
local ctrl_states = {
|
|
process = settings.get("PROCESS"),
|
|
waste_modes = settings.get("WASTE_MODES"),
|
|
priority_groups = settings.get("PRIORITY_GROUPS"),
|
|
}
|
|
|
|
settings.set("ControlStates", ctrl_states)
|
|
end
|
|
|
|
settings.unset("PRIMARY_DISPLAY")
|
|
settings.unset("FLOW_DISPLAY")
|
|
settings.unset("UNIT_DISPLAYS")
|
|
settings.unset("PROCESS")
|
|
settings.unset("WASTE_MODES")
|
|
settings.unset("PRIORITY_GROUPS")
|
|
|
|
tool_ctl.gen_summary(tmp_cfg)
|
|
sum_pane.set_value(1)
|
|
main_pane.set_value(9)
|
|
tool_ctl.importing_legacy = true
|
|
end
|
|
|
|
-- attempt a connection to the supervisor to get cooling info
|
|
function tool_ctl.sv_connect()
|
|
tool_ctl.sv_conn_button.disable()
|
|
tool_ctl.sv_conn_detail.set_value("")
|
|
|
|
local modem = ppm.get_wireless_modem()
|
|
if modem == nil then
|
|
tool_ctl.sv_conn_status.set_value("Please connect an ender/wireless modem.")
|
|
else
|
|
tool_ctl.sv_conn_status.set_value("Modem found, connecting...")
|
|
if tool_ctl.nic == nil then tool_ctl.nic = network.nic(modem) end
|
|
|
|
tool_ctl.nic.closeAll()
|
|
tool_ctl.nic.open(tmp_cfg.CRD_Channel)
|
|
|
|
tool_ctl.sv_addr = comms.BROADCAST
|
|
tool_ctl.sv_seq_num = 0
|
|
tool_ctl.net_listen = true
|
|
|
|
send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.CRD })
|
|
|
|
tcd.dispatch_unique(8, handle_timeout)
|
|
end
|
|
end
|
|
|
|
-- show the facility's unit count and cooling configuration data
|
|
function tool_ctl.show_sv_cfg()
|
|
local conf = tool_ctl.sv_cool_conf
|
|
fac_config_list.remove_all()
|
|
|
|
local str = util.sprintf("Facility has %d reactor unit%s:", #conf, util.trinary(#conf==1,"","s"))
|
|
TextBox{parent=fac_config_list,height=1,text=str,fg_bg=cpair(colors.gray,colors.white)}
|
|
|
|
for i = 1, #conf do
|
|
local num_b, num_t = conf[i][1], conf[i][2]
|
|
str = util.sprintf("\x07 Unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s"))
|
|
TextBox{parent=fac_config_list,height=1,text=str,fg_bg=cpair(colors.gray,colors.white)}
|
|
end
|
|
end
|
|
|
|
-- update list of monitor requirements
|
|
function tool_ctl.update_mon_reqs()
|
|
local plural = tmp_cfg.UnitCount > 1
|
|
|
|
if tool_ctl.sv_cool_conf ~= nil then
|
|
local cnf = tool_ctl.sv_cool_conf
|
|
|
|
local row1_tall = cnf[1][1] > 1 or cnf[1][2] > 2 or (cnf[2] and (cnf[2][1] > 1 or cnf[2][2] > 2))
|
|
local row1_short = (cnf[1][1] == 0 and cnf[1][2] == 1) and (cnf[2] == nil or (cnf[2][1] == 0 and cnf[2][2] == 1))
|
|
local row2_tall = (cnf[3] and (cnf[3][1] > 1 or cnf[3][2] > 2)) or (cnf[4] and (cnf[4][1] > 1 or cnf[4][2] > 2))
|
|
local row2_short = (cnf[3] == nil or (cnf[3][1] == 0 and cnf[3][2] == 1)) and (cnf[4] == nil or (cnf[4][1] == 0 and cnf[4][2] == 1))
|
|
|
|
if tmp_cfg.UnitCount <= 2 then
|
|
tool_ctl.main_mon_h = util.trinary(row1_tall, 5, 4)
|
|
else
|
|
-- is only one tall and the other short, or are both tall? -> 5 or 6; are neither tall? -> 5
|
|
if row1_tall or row2_tall then
|
|
tool_ctl.main_mon_h = util.trinary((row1_short and row2_tall) or (row1_tall and row2_short), 5, 6)
|
|
else tool_ctl.main_mon_h = 5 end
|
|
end
|
|
else
|
|
tool_ctl.main_mon_h = util.trinary(tmp_cfg.UnitCount <= 2, 4, 5)
|
|
end
|
|
|
|
tool_ctl.flow_mon_h = 2 + tmp_cfg.UnitCount
|
|
|
|
local asterisk = util.trinary(tool_ctl.sv_cool_conf == nil, "*", "")
|
|
local m_at_least = util.trinary(tool_ctl.main_mon_h < 6, "at least ", "")
|
|
local f_at_least = util.trinary(tool_ctl.flow_mon_h < 6, "at least ", "")
|
|
|
|
mon_reqs.remove_all()
|
|
|
|
TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a "..tmp_cfg.UnitCount.." Unit View Monitor"..util.trinary(plural,"s","")}
|
|
TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" "..util.trinary(plural,"each ","").."must be 4 blocks wide by 4 tall",fg_bg=cpair(colors.gray,colors.white)}
|
|
TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Main View Monitor"}
|
|
TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..m_at_least..tool_ctl.main_mon_h..asterisk.." tall",fg_bg=cpair(colors.gray,colors.white)}
|
|
if not tmp_cfg.DisableFlowView then
|
|
TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Flow View Monitor"}
|
|
TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)}
|
|
end
|
|
end
|
|
|
|
-- set/edit a monitor's assignment
|
|
---@param iface string
|
|
---@param device ppm_entry
|
|
function tool_ctl.edit_monitor(iface, device)
|
|
tool_ctl.mon_iface = iface
|
|
|
|
local dev = device.dev
|
|
local w, h = ppm.monitor_block_size(dev.getSize())
|
|
|
|
local msg = "This size doesn't match a required screen. Please go back and resize it, or configure below at the risk of it not working."
|
|
|
|
tool_ctl.mon_expect = {}
|
|
mon_assign.set_value(1)
|
|
mon_unit.set_value(0)
|
|
|
|
if w == 4 and h == 4 then
|
|
msg = "This could work as a unit display. Please configure below."
|
|
tool_ctl.mon_expect = { 3 }
|
|
mon_assign.set_value(3)
|
|
elseif w == 8 then
|
|
if h >= tool_ctl.main_mon_h and h >= tool_ctl.flow_mon_h then
|
|
msg = "This could work as either your main monitor or flow monitor. Please configure below."
|
|
tool_ctl.mon_expect = { 1, 2 }
|
|
if tmp_cfg.MainDisplay then mon_assign.set_value(2) end
|
|
elseif h >= tool_ctl.main_mon_h then
|
|
msg = "This could work as your main monitor. Please configure below."
|
|
tool_ctl.mon_expect = { 1 }
|
|
elseif h >= tool_ctl.flow_mon_h then
|
|
msg = "This could work as your flow monitor. Please configure below."
|
|
tool_ctl.mon_expect = { 2 }
|
|
mon_assign.set_value(2)
|
|
end
|
|
end
|
|
|
|
-- override if a config exists
|
|
if tmp_cfg.MainDisplay == iface then
|
|
mon_assign.set_value(1)
|
|
elseif tmp_cfg.FlowDisplay == iface then
|
|
mon_assign.set_value(2)
|
|
else
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
if tmp_cfg.UnitDisplays[i] == iface then
|
|
mon_assign.set_value(3)
|
|
mon_unit.set_value(i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
on_assign_mon(mon_assign.get_value())
|
|
|
|
mon_desc.set_value(util.c("You have selected '", iface, "', which has a block size of ", w, " wide by ", h, " tall. ", msg))
|
|
mon_pane.set_value(3)
|
|
end
|
|
|
|
-- generate the list of available monitors
|
|
function tool_ctl.gen_mon_list()
|
|
mon_list.remove_all()
|
|
|
|
local missing = { main = tmp_cfg.MainDisplay ~= nil, flow = tmp_cfg.FlowDisplay ~= nil, unit = {} }
|
|
for i = 1, tmp_cfg.UnitCount do missing.unit[i] = tmp_cfg.UnitDisplays[i] ~= nil end
|
|
|
|
-- list connected monitors
|
|
local monitors = ppm.get_monitor_list()
|
|
for iface, device in pairs(monitors) do
|
|
local dev = device.dev
|
|
|
|
dev.setTextScale(0.5)
|
|
dev.setTextColor(colors.white)
|
|
dev.setBackgroundColor(colors.black)
|
|
dev.clear()
|
|
dev.setCursorPos(1, 1)
|
|
dev.setTextColor(colors.magenta)
|
|
dev.write("This is monitor")
|
|
dev.setCursorPos(1, 2)
|
|
dev.setTextColor(colors.white)
|
|
dev.write(iface)
|
|
|
|
local assignment = "Unused"
|
|
|
|
if tmp_cfg.MainDisplay == iface then
|
|
assignment = "Main"
|
|
missing.main = false
|
|
elseif tmp_cfg.FlowDisplay == iface then
|
|
assignment = "Flow"
|
|
missing.flow = false
|
|
else
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
if tmp_cfg.UnitDisplays[i] == iface then
|
|
missing.unit[i] = false
|
|
assignment = "Unit " .. i
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local line = Div{parent=mon_list,x=1,y=1,height=1}
|
|
|
|
TextBox{parent=line,x=1,y=1,width=6,height=1,text=assignment,fg_bg=cpair(util.trinary(assignment=="Unused",colors.red,colors.blue),colors.white)}
|
|
TextBox{parent=line,x=8,y=1,height=1,text=iface}
|
|
|
|
local w, h = ppm.monitor_block_size(dev.getSize())
|
|
|
|
local function unset_mon()
|
|
purge_assignments(iface)
|
|
tool_ctl.gen_mon_list()
|
|
end
|
|
|
|
TextBox{parent=line,x=33,y=1,width=4,height=1,text=w.."x"..h,fg_bg=cpair(colors.black,colors.white)}
|
|
PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.edit_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
|
local unset = PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)}
|
|
|
|
if assignment == "Unused" then unset.disable() end
|
|
end
|
|
|
|
local dc_list = {} -- disconnected monitor list
|
|
|
|
if missing.main then table.insert(dc_list, { "Main", tmp_cfg.MainDisplay }) end
|
|
if missing.flow then table.insert(dc_list, { "Flow", tmp_cfg.FlowDisplay }) end
|
|
for i = 1, tmp_cfg.UnitCount do
|
|
if missing.unit[i] then table.insert(dc_list, { "Unit " .. i, tmp_cfg.UnitDisplays[i] }) end
|
|
end
|
|
|
|
-- add monitors that are assigned but not connected
|
|
for i = 1, #dc_list do
|
|
local line = Div{parent=mon_list,x=1,y=1,height=1}
|
|
|
|
TextBox{parent=line,x=1,y=1,width=6,height=1,text=dc_list[i][1],fg_bg=cpair(colors.blue,colors.white)}
|
|
TextBox{parent=line,x=8,y=1,height=1,text="disconnected",fg_bg=cpair(colors.red,colors.white)}
|
|
|
|
local function unset_mon()
|
|
purge_assignments(dc_list[i][2])
|
|
tool_ctl.gen_mon_list()
|
|
end
|
|
|
|
TextBox{parent=line,x=33,y=1,width=4,height=1,text="?x?",fg_bg=cpair(colors.black,colors.white)}
|
|
PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()end,dis_fg_bg=cpair(colors.black,colors.gray)}.disable()
|
|
PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)}
|
|
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 crd_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] == "TempScale" then
|
|
if raw == 1 then val = "Kelvin" elseif raw == 2 then val = "Celsius" elseif raw == 3 then val = "Fahrenheit" elseif raw == 4 then val = "Rankine" end
|
|
elseif f[1] == "MainTheme" then
|
|
val = util.strval(themes.ui_theme_name(raw))
|
|
elseif f[1] == "FrontPanelTheme" then
|
|
val = util.strval(themes.fp_theme_name(raw))
|
|
elseif f[1] == "ColorMode" then
|
|
val = util.strval(themes.color_mode_name(raw))
|
|
elseif f[1] == "UnitDisplays" and type(cfg.UnitDisplays) == "table" then
|
|
val = ""
|
|
for idx = 1, #cfg.UnitDisplays do
|
|
val = val .. util.trinary(idx == 1, "", "\n") .. util.sprintf(" \x07 Unit %d - %s", idx, cfg.UnitDisplays[idx])
|
|
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
|
|
|
|
if (f[1] == "UnitDisplays") and (height == 1) and (val ~= "<not set>") then height = 2 end
|
|
|
|
local line = Div{parent=setting_list,height=height,fg_bg=c}
|
|
TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)}
|
|
|
|
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 coordinator configurator<br>
|
|
-- start_fail of 0 is OK (default if not provided), 1 is bad config, 2 is bad monitor config
|
|
---@param start_code? 0|1|2 indicate error state when called from the startup app
|
|
---@param message? any string message to display on a start_fail of 2
|
|
function configurator.configure(start_code, message)
|
|
tool_ctl.start_fail = start_code or 0
|
|
tool_ctl.fail_message = util.trinary(type(message) == "string", message, "")
|
|
|
|
load_settings(settings_cfg, true)
|
|
tool_ctl.has_config = load_settings(ini_cfg)
|
|
|
|
-- copy in some important values to start with
|
|
preset_monitor_fields()
|
|
|
|
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)
|
|
|
|
tool_ctl.gen_mon_list()
|
|
|
|
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)
|
|
tool_ctl.gen_mon_list()
|
|
elseif event == "peripheral" then
|
|
ppm.mount(param1)
|
|
tool_ctl.gen_mon_list()
|
|
elseif event == "monitor_resize" then
|
|
tool_ctl.gen_mon_list()
|
|
elseif event == "modem_message" and tool_ctl.nic ~= nil and tool_ctl.net_listen then
|
|
local s_pkt = tool_ctl.nic.receive(param1, param2, param3, param4, param5)
|
|
|
|
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
|