diff --git a/coordinator/config/facility.lua b/coordinator/config/facility.lua index 00f8c99..423eb19 100644 --- a/coordinator/config/facility.lua +++ b/coordinator/config/facility.lua @@ -231,7 +231,7 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) 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."} tool_ctl.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,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=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=cpair(colors.yellow,colors._INHERIT)} 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,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index f7dde3d..07479e0 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -88,6 +88,8 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) tank_mode = conf.cooling.fac_tank_mode, tank_defs = conf.cooling.fac_tank_defs, tank_list = conf.cooling.fac_tank_list, + tank_conns = conf.cooling.fac_tank_conns, + tank_fluid_types = conf.cooling.tank_fluid_types, all_sys_ok = false, rtu_count = 0, @@ -116,7 +118,7 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale) radiation = types.new_zero_radiation_reading(), - save_cfg_ack = nil, ---@type fun(success: boolean) + save_cfg_ack = nil, ---@type fun(success: boolean) ---@type { [TONE]: boolean } alarm_tones = { false, false, false, false, false, false, false, false }, diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 8f7f1a6..25b48d4 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer") local sounder = require("coordinator.sounder") local threads = require("coordinator.threads") -local COORDINATOR_VERSION = "v1.5.17" +local COORDINATOR_VERSION = "v1.6.0" local CHUNK_LOAD_DELAY_S = 30.0 diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index 27e4d0d..2c1ed35 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -2,8 +2,11 @@ -- Basic Unit Flow Overview -- +local types = require("scada-common.types") local util = require("scada-common.util") +local iocontrol = require("coordinator.iocontrol") + local style = require("coordinator.ui.style") local core = require("graphics.core") @@ -19,6 +22,8 @@ local DataIndicator = require("graphics.elements.indicators.DataIndicator") local IndicatorLight = require("graphics.elements.indicators.IndicatorLight") local TriIndicatorLight = require("graphics.elements.indicators.TriIndicatorLight") +local COOLANT_TYPE = types.COOLANT_TYPE + local ALIGN = core.ALIGN local sprintf = util.sprintf @@ -35,8 +40,8 @@ local lg_gray = style.lg_gray ---@param x integer top left x ---@param y integer top left y ---@param wide boolean whether to render wide version ----@param unit ioctl_unit unit database entry -local function make(parent, x, y, wide, unit) +---@param unit_id integer unit index +local function make(parent, x, y, wide, unit_id) local s_field = style.theme.field_box local text_c = style.text_colors @@ -48,6 +53,12 @@ local function make(parent, x, y, wide, unit) local height = 16 + local facility = iocontrol.get_db().facility + local unit = iocontrol.get_db().units[unit_id] + + local tank_conns = facility.tank_conns + local tank_types = facility.tank_fluid_types + local v_start = 1 + ((unit.unit_id - 1) * 5) local prv_start = 1 + ((unit.unit_id - 1) * 3) local v_fields = { "pu", "po", "pl", "am" } @@ -80,21 +91,22 @@ local function make(parent, x, y, wide, unit) local rc_pipes = {} - local emc_x = 42 -- emergency coolant connection x point - if unit.num_boilers > 0 then table.insert(rc_pipes, pipe(0, 1, _wide(28, 19), 1, colors.lightBlue, true)) table.insert(rc_pipes, pipe(0, 3, _wide(28, 19), 3, colors.orange, true)) table.insert(rc_pipes, pipe(_wide(46 ,39), 1, _wide(72,58), 1, colors.blue, true)) table.insert(rc_pipes, pipe(_wide(46,39), 3, _wide(72,58), 3, colors.white, true)) else - emc_x = 3 table.insert(rc_pipes, pipe(0, 1, _wide(72,58), 1, colors.blue, true)) table.insert(rc_pipes, pipe(0, 3, _wide(72,58), 3, colors.white, true)) end if unit.has_tank then - table.insert(rc_pipes, pipe(emc_x, 1, emc_x, 0, colors.blue, true, true)) + local is_water = tank_types[tank_conns[unit_id]] == COOLANT_TYPE.WATER + -- emergency coolant connection x point + local emc_x = util.trinary(is_water and (unit.num_boilers > 0), 42, 3) + + table.insert(rc_pipes, pipe(emc_x, 1, emc_x, 0, util.trinary(is_water, colors.blue, colors.lightBlue), true, true)) end local prv_yo = math.max(3 - unit.num_turbines, 0) diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index b002f24..7b89212 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -24,6 +24,7 @@ local IndicatorLight = require("graphics.elements.indicators.IndicatorLight") local StateIndicator = require("graphics.elements.indicators.StateIndicator") local CONTAINER_MODE = types.CONTAINER_MODE +local COOLANT_TYPE = types.COOLANT_TYPE local ALIGN = core.ALIGN @@ -45,8 +46,10 @@ local function init(main) local facility = iocontrol.get_db().facility local units = iocontrol.get_db().units - local tank_defs = facility.tank_defs - local tank_list = facility.tank_list + local tank_defs = facility.tank_defs + local tank_conns = facility.tank_conns + local tank_list = facility.tank_list + local tank_types = facility.tank_fluid_types -- window header message local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=ALIGN.CENTER,fg_bg=style.theme.header} @@ -56,12 +59,16 @@ local function init(main) datetime.register(facility.ps, "date_time", datetime.set_value) local po_pipes = {} - local water_pipes = {} + local emcool_pipes = {} -- get the y offset for this unit index ---@param idx integer unit index local function y_ofs(idx) return ((idx - 1) * 20) end + -- get the coolant color + ---@param idx integer tank index + local function c_clr(idx) return util.trinary(tank_types[tank_conns[idx]] == COOLANT_TYPE.WATER, colors.blue, colors.lightBlue) end + -- determinte facility tank start/end from the definitions list ---@param start_idx integer start index of table iteration ---@param end_idx integer end index of table iteration @@ -81,11 +88,13 @@ local function init(main) for i = 1, facility.num_units do if units[i].has_tank then local y = y_ofs(i) - table.insert(water_pipes, pipe(2, y, 2, y + 3, colors.blue, true)) - table.insert(water_pipes, pipe(2, y, 21, y, colors.blue, true)) + local color = c_clr(i) - local x = util.trinary(units[i].num_boilers == 0, 45, 84) - table.insert(water_pipes, pipe(21, y, x, y + 2, colors.blue, true, true)) + table.insert(emcool_pipes, pipe(2, y, 2, y + 3, color, true)) + table.insert(emcool_pipes, pipe(2, y, 21, y, color, true)) + + local x = util.trinary((tank_types[tank_conns[i]] == COOLANT_TYPE.SODIUM) or (units[i].num_boilers == 0), 45, 84) + table.insert(emcool_pipes, pipe(21, y, x, y + 2, color, true, true)) end end else @@ -93,16 +102,17 @@ local function init(main) for i = 1, #tank_defs do if tank_defs[i] > 0 then local y = y_ofs(i) + local color = c_clr(i) if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(1, y, 21, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(1, y, 21, y, color, true)) else - table.insert(water_pipes, pipe(2, y, 2, y + 3, colors.blue, true)) - table.insert(water_pipes, pipe(2, y, 21, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(2, y, 2, y + 3, color, true)) + table.insert(emcool_pipes, pipe(2, y, 21, y, color, true)) end - local x = util.trinary(units[i].num_boilers == 0, 45, 84) - table.insert(water_pipes, pipe(21, y, x, y + 2, colors.blue, true, true)) + local x = util.trinary((tank_types[tank_conns[i]] == COOLANT_TYPE.SODIUM) or (units[i].num_boilers == 0), 45, 84) + table.insert(emcool_pipes, pipe(21, y, x, y + 2, color, true, true)) end end @@ -112,13 +122,14 @@ local function init(main) for i = 1, #tank_defs do local y = y_ofs(i) + if i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, c_clr(i), true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y, c_clr(first_fdef), true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y + 5, c_clr(first_fdef), true)) end end end @@ -128,17 +139,19 @@ local function init(main) for i = 1, #tank_defs do local y = y_ofs(i) + local color = c_clr(first_fdef) + if i == 4 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, c_clr(i), true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, color, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y, color, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y + 5, color, true)) end end end @@ -147,12 +160,12 @@ local function init(main) for _, a in pairs({ 1, 3 }) do local b = a + 1 if tank_defs[a] == 2 then - table.insert(water_pipes, pipe(0, y_ofs(a), 1, y_ofs(a) + 6, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y_ofs(a), 1, y_ofs(a) + 6, c_clr(a), true)) if tank_defs[b] == 2 then - table.insert(water_pipes, pipe(0, y_ofs(b) - 13, 1, y_ofs(b), colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y_ofs(b) - 13, 1, y_ofs(b), c_clr(a), true)) end elseif tank_defs[b] == 2 then - table.insert(water_pipes, pipe(0, y_ofs(b), 1, y_ofs(b) + 6, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y_ofs(b), 1, y_ofs(b) + 6, c_clr(b), true)) end end elseif facility.tank_mode == 4 then @@ -161,17 +174,19 @@ local function init(main) for i = 1, #tank_defs do local y = y_ofs(i) + local color = c_clr(first_fdef) + if i == 1 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, c_clr(i), true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, color, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y, color, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y + 5, color, true)) end end end @@ -181,17 +196,19 @@ local function init(main) for i = 1, #tank_defs do local y = y_ofs(i) + local color = c_clr(first_fdef) + if i == 3 or i == 4 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, c_clr(i), true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, color, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y, color, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y + 5, color, true)) end end end @@ -201,17 +218,19 @@ local function init(main) for i = 1, #tank_defs do local y = y_ofs(i) + local color = c_clr(first_fdef) + if i == 1 or i == 4 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, c_clr(i), true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, color, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y, color, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y + 5, color, true)) end end end @@ -221,17 +240,19 @@ local function init(main) for i = 1, #tank_defs do local y = y_ofs(i) + local color = c_clr(first_fdef) + if i == 1 or i == 2 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, c_clr(i), true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y, 1, y + 5, color, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y, color, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) + table.insert(emcool_pipes, pipe(0, y - 14, 0, y + 5, color, true)) end end end @@ -239,14 +260,14 @@ local function init(main) end local flow_x = 3 - if #water_pipes > 0 then + if #emcool_pipes > 0 then flow_x = 25 - PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=style.theme.bg} + PipeNetwork{parent=main,x=2,y=3,pipes=emcool_pipes,bg=style.theme.bg} end for i = 1, facility.num_units do local y_offset = y_ofs(i) - unit_flow(main, flow_x, 5 + y_offset, #water_pipes == 0, units[i]) + unit_flow(main, flow_x, 5 + y_offset, #emcool_pipes == 0, i) table.insert(po_pipes, pipe(0, 3 + y_offset, 4, 0, colors.cyan, true, true)) util.nop() end @@ -301,8 +322,10 @@ local function init(main) local tank_pcnt = DataIndicator{parent=tank_box,x=10,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_col} local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,commas=true,unit="mB",lu_colors=lu_col,width=16,fg_bg=s_field} - TextBox{parent=tank_box,x=2,y=6,text="Water Level",width=11,fg_bg=style.label} - local level = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16} + local is_water = tank_types[i] == COOLANT_TYPE.WATER + + TextBox{parent=tank_box,x=2,y=6,text=util.trinary(is_water,"Water","Sodium").." Level",width=12,fg_bg=style.label} + local level = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(util.trinary(is_water,colors.blue,colors.lightBlue),colors.gray),height=1,width=16} TextBox{parent=tank_box,x=2,y=9,text="In/Out Mode",width=11,fg_bg=style.label} local can_fill = IndicatorLight{parent=tank_box,x=2,y=10,label="FILL",colors=style.ind_wht} diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index b860aa4..e606b07 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -138,6 +138,9 @@ function iocontrol.init_fac(conf) num_units = conf.num_units, tank_mode = conf.cooling.fac_tank_mode, tank_defs = conf.cooling.fac_tank_defs, + tank_list = conf.cooling.fac_tank_list, + tank_conns = conf.cooling.fac_tank_conns, + tank_fluid_types = conf.cooling.tank_fluid_types, all_sys_ok = false, rtu_count = 0, diff --git a/pocket/startup.lua b/pocket/startup.lua index 0058904..4782325 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -20,7 +20,7 @@ local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") local threads = require("pocket.threads") -local POCKET_VERSION = "v0.12.11-alpha" +local POCKET_VERSION = "v0.12.12-alpha" local println = util.println local println_ts = util.println_ts diff --git a/pocket/ui/apps/unit.lua b/pocket/ui/apps/unit.lua index 8335ff1..2f916d3 100644 --- a/pocket/ui/apps/unit.lua +++ b/pocket/ui/apps/unit.lua @@ -372,7 +372,7 @@ local function new_view(root) if #unit.tank_data_tbl > 0 then local tank_pane = Div{parent=page_div} - nav_links[i].d_tank = dyn_tank(app, u_page, panes, tank_pane, unit.tank_ps_tbl[1], update) + nav_links[i].d_tank = dyn_tank(app, u_page, panes, tank_pane, i, unit.tank_ps_tbl[1], update) end --#endregion diff --git a/pocket/ui/pages/dynamic_tank.lua b/pocket/ui/pages/dynamic_tank.lua index bfc89de..356f7e6 100644 --- a/pocket/ui/pages/dynamic_tank.lua +++ b/pocket/ui/pages/dynamic_tank.lua @@ -1,4 +1,7 @@ local types = require("scada-common.types") +local util = require("scada-common.util") + +local iocontrol = require("pocket.iocontrol") local style = require("pocket.ui.style") @@ -13,6 +16,7 @@ local IconIndicator = require("graphics.elements.indicators.IconIndicator") local StateIndicator = require("graphics.elements.indicators.StateIndicator") local CONTAINER_MODE = types.CONTAINER_MODE +local COOLANT_TYPE = types.COOLANT_TYPE local cpair = core.cpair @@ -30,9 +34,12 @@ local mode_ind_s = { ---@param page nav_tree_page ---@param panes Div[] ---@param tank_pane Div +---@param tank_id integer global facility tank ID (as used for tank list, etc) ---@param ps psil ---@param update function -return function (app, page, panes, tank_pane, ps, update) +return function (app, page, panes, tank_pane, tank_id, ps, update) + local fac = iocontrol.get_db().facility + local tank_div = Div{parent=tank_pane,x=2,width=tank_pane.get_width()-2} table.insert(panes, tank_div) @@ -47,8 +54,10 @@ return function (app, page, panes, tank_pane, ps, update) local tank_pcnt = DataIndicator{parent=tank_div,x=14,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_fg} local tank_amnt = DataIndicator{parent=tank_div,label="",format="%18d",value=0,commas=true,unit="mB",lu_colors=lu_col,width=21,fg_bg=text_fg} - TextBox{parent=tank_div,y=6,text="Fluid Level",width=11,fg_bg=label} - local level = HorizontalBar{parent=tank_div,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=21} + local is_water = fac.tank_fluid_types[tank_id] == COOLANT_TYPE.WATER + + TextBox{parent=tank_div,y=6,text=util.trinary(is_water,"Water","Sodium").." Level",width=12,fg_bg=label} + local level = HorizontalBar{parent=tank_div,y=7,bar_fg_bg=cpair(util.trinary(is_water,colors.blue,colors.lightBlue),colors.gray),height=1,width=21} TextBox{parent=tank_div,y=9,text="Tank Fill Mode",width=14,fg_bg=label} local can_fill = IconIndicator{parent=tank_div,y=10,label="Fill",states=mode_ind_s} diff --git a/scada-common/types.lua b/scada-common/types.lua index 19392fe..0d562a6 100644 --- a/scada-common/types.lua +++ b/scada-common/types.lua @@ -418,6 +418,12 @@ types.AUTO_GROUP_NAMES = { "Backup" } +---@enum COOLANT_TYPE +types.COOLANT_TYPE = { + WATER = 1, + SODIUM = 2 +} + ---@enum WASTE_MODE types.WASTE_MODE = { AUTO = 1, diff --git a/scada-common/util.lua b/scada-common/util.lua index a6fcc34..40a9fe3 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -24,7 +24,7 @@ local t_pack = table.pack local util = {} -- scada-common version -util.version = "1.4.9" +util.version = "1.4.10" util.TICK_TIME_S = 0.05 util.TICK_TIME_MS = 50 diff --git a/supervisor/config/facility.lua b/supervisor/config/facility.lua index 84c9c51..13e238c 100644 --- a/supervisor/config/facility.lua +++ b/supervisor/config/facility.lua @@ -18,12 +18,148 @@ local tri = util.trinary local cpair = core.cpair local self = { - vis_ftanks = {}, ---@type { line: Div, pipe_conn?: TextBox, pipe_chain?: TextBox, pipe_direct?: TextBox, label?: TextBox }[] - vis_utanks = {} ---@type { line: Div, label: TextBox }[] + tank_fluid_opts = {}, ---@type Radio2D[] + + vis_draw = nil, ---@type function + draw_fluid_ops = nil, ---@type function + + vis_ftanks = {}, ---@type { line: Div, pipe_conn?: TextBox, pipe_chain?: TextBox, pipe_direct?: TextBox, label?: TextBox }[] + vis_utanks = {} ---@type { line: Div, label: TextBox }[] } local facility = {} +-- generate the tank list and tank connections tables +---@param mode integer facility tank mode +---@param defs table facility tank definitions +---@return table tank_list, table tank_conns +function facility.generate_tank_list_and_conns(mode, defs) + local tank_mode = mode + local tank_defs = defs + local tank_list = { table.unpack(tank_defs) } + local tank_conns = { table.unpack(tank_defs) } + + local function calc_fdef(start_idx, end_idx) + local first = 4 + for i = start_idx, end_idx do + if tank_defs[i] == 2 then + if i < first then first = i end + end + end + return first + end + + -- set units using their own tanks as connected to their respective unit tank + for i = 1, #tank_defs do + if tank_defs[i] == 1 then tank_conns[i] = i end + end + + if tank_mode == 1 then + -- (1) 1 total facility tank (A A A A) + local first_fdef = calc_fdef(1, #tank_defs) + for i = 1, #tank_defs do + if (i >= first_fdef) and (tank_defs[i] == 2) then + tank_conns[i] = first_fdef + + if i > first_fdef then tank_list[i] = 0 end + end + end + elseif tank_mode == 2 then + -- (2) 2 total facility tanks (A A A B) + local first_fdef = calc_fdef(1, math.min(3, #tank_defs)) + for i = 1, #tank_defs do + if (i >= first_fdef) and (tank_defs[i] == 2) then + if i == 4 then + tank_conns[i] = 4 + else + tank_conns[i] = first_fdef + + if i > first_fdef then tank_list[i] = 0 end + end + end + end + elseif tank_mode == 3 then + -- (3) 2 total facility tanks (A A B B) + for _, a in pairs({ 1, 3 }) do + local b = a + 1 + + if tank_defs[a] == 2 then + tank_conns[a] = a + elseif tank_defs[b] == 2 then + tank_conns[b] = b + end + + if (tank_defs[a] == 2) and (tank_defs[b] == 2) then + tank_list[b] = 0 + tank_conns[b] = a + end + end + elseif tank_mode == 4 then + -- (4) 2 total facility tanks (A B B B) + local first_fdef = calc_fdef(2, #tank_defs) + for i = 1, #tank_defs do + if tank_defs[i] == 2 then + if i == 1 then + tank_conns[i] = 1 + elseif i >= first_fdef then + tank_conns[i] = first_fdef + + if i > first_fdef then tank_list[i] = 0 end + end + end + end + elseif tank_mode == 5 then + -- (5) 3 total facility tanks (A A B C) + local first_fdef = calc_fdef(1, math.min(2, #tank_defs)) + for i = 1, #tank_defs do + if (i >= first_fdef) and (tank_defs[i] == 2) then + if i == 3 or i == 4 then + tank_conns[i] = i + elseif i >= first_fdef then + tank_conns[i] = first_fdef + + if i > first_fdef then tank_list[i] = 0 end + end + end + end + elseif tank_mode == 6 then + -- (6) 3 total facility tanks (A B B C) + local first_fdef = calc_fdef(2, math.min(3, #tank_defs)) + for i = 1, #tank_defs do + if tank_defs[i] == 2 then + if i == 1 or i == 4 then + tank_conns[i] = i + elseif i >= first_fdef then + tank_conns[i] = first_fdef + + if i > first_fdef then tank_list[i] = 0 end + end + end + end + elseif tank_mode == 7 then + -- (7) 3 total facility tanks (A B C C) + local first_fdef = calc_fdef(3, #tank_defs) + for i = 1, #tank_defs do + if tank_defs[i] == 2 then + if i == 1 or i == 2 then + tank_conns[i] = i + elseif i >= first_fdef then + tank_conns[i] = first_fdef + + if i > first_fdef then tank_list[i] = 0 end + end + end + end + elseif tank_mode == 8 then + -- (8) 4 total facility tanks (A B C D) + for i = 1, #tank_defs do + if tank_defs[i] == 2 then tank_conns[i] = i end + end + end + + return tank_list, tank_conns +end + -- create the facility configuration view ---@param tool_ctl _svr_cfg_tool_ctl ---@param main_pane MultiPane @@ -48,14 +184,18 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) local fac_c_5 = Div{parent=fac_cfg,x=2,y=4,width=49} local fac_c_6 = Div{parent=fac_cfg,x=2,y=4,width=49} local fac_c_7 = Div{parent=fac_cfg,x=2,y=4,width=49} + local fac_c_8 = Div{parent=fac_cfg,x=2,y=4,width=49} - local fac_pane = MultiPane{parent=fac_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3,fac_c_4,fac_c_5,fac_c_6,fac_c_7}} + local fac_pane = MultiPane{parent=fac_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3,fac_c_4,fac_c_5,fac_c_6,fac_c_7, fac_c_8}} TextBox{parent=fac_cfg,x=1,y=2,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)} + --#region Unit Count + TextBox{parent=fac_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."} tool_ctl.num_units = NumberField{parent=fac_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=fac_c_1,x=7,y=5,text="reactors"} + TextBox{parent=fac_c_1,x=1,y=7,height=3,text="If you already configured your coordinator, make sure you update the coordinator's configured unit count.",fg_bg=cpair(colors.yellow,colors._INHERIT)} local nu_error = TextBox{parent=fac_c_1,x=8,y=14,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true} @@ -77,6 +217,9 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) PushButton{parent=fac_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=fac_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} + --#endregion + --#region Cooling Configuration + TextBox{parent=fac_c_2,x=1,y=1,height=4,text="Please provide the reactor cooling configuration below. This includes the number of turbines, boilers, and if that reactor has a connection to a dynamic tank for emergency coolant."} TextBox{parent=fac_c_2,x=1,y=6,text="UNIT TURBINES BOILERS HAS TANK CONNECTION?",fg_bg=g_lg_fg_bg} @@ -149,6 +292,9 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) 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_cooling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + --#endregion + --#region Facility Tanks Option + TextBox{parent=fac_c_3,x=1,y=1,height=6,text="You have set one or more of your units to use dynamic tanks for emergency coolant. You have two paths for configuration. The first is to assign dynamic tanks to reactor units; one tank per reactor, only connected to that reactor. RTU configurations must also assign it as such."} TextBox{parent=fac_c_3,x=1,y=8,height=3,text="Alternatively, you can configure them as facility tanks to connect to multiple reactor units. These can intermingle with unit-specific tanks."} @@ -161,6 +307,16 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) else tmp_cfg.FacilityTankMode = 0 tmp_cfg.FacilityTankDefs = {} + + -- on facility tank mode 0, setup tank defs to match unit tank option + for i = 1, tmp_cfg.UnitCount do + tmp_cfg.FacilityTankDefs[i] = tri(tmp_cfg.CoolingConfig[i].TankConnection, 1, 0) + end + + tmp_cfg.FacilityTankList, tmp_cfg.FacilityTankConns = facility.generate_tank_list_and_conns(tmp_cfg.FacilityTankMode, tmp_cfg.FacilityTankDefs) + + self.draw_fluid_ops() + fac_pane.set_value(7) end end @@ -168,6 +324,9 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) PushButton{parent=fac_c_3,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(2)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=submit_en_fac_tank,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + --#endregion + --#region Facility Tank Connections + TextBox{parent=fac_c_4,x=1,y=1,height=4,text="Please set unit connections to dynamic tanks, selecting at least one facility tank. The layout for facility tanks will be configured next."} for i = 1, 4 do @@ -220,7 +379,7 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) self.vis_utanks[i].line.hide(true) end - tool_ctl.vis_draw(tmp_cfg.FacilityTankMode) + self.vis_draw(tmp_cfg.FacilityTankMode) if any_fac then tank_err.hide(true) @@ -231,6 +390,9 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) PushButton{parent=fac_c_4,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=fac_c_4,x=44,y=14,text="Next \x1a",callback=submit_tank_defs,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + --#endregion + --#region Facility Tank Mode + TextBox{parent=fac_c_5,x=1,y=1,text="Please select your dynamic tank layout."} TextBox{parent=fac_c_5,x=12,y=3,text="Facility Tanks Unit Tanks",fg_bg=g_lg_fg_bg} @@ -269,7 +431,7 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) -- draw the pipe visualization ---@param mode integer pipe mode - function tool_ctl.vis_draw(mode) + function self.vis_draw(mode) -- is a facility tank connected to this unit ---@param i integer unit 1 - 4 ---@return boolean connected @@ -391,7 +553,7 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) local function change_mode(mode) tmp_cfg.FacilityTankMode = mode - tool_ctl.vis_draw(mode) + self.vis_draw(mode) end local tank_modes = { "Mode 1", "Mode 2", "Mode 3", "Mode 4", "Mode 5", "Mode 6", "Mode 7", "Mode 8" } @@ -399,24 +561,127 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) --#endregion + local function next_from_tank_mode() + -- determine tank list and connections + tmp_cfg.FacilityTankList, tmp_cfg.FacilityTankConns = facility.generate_tank_list_and_conns(tmp_cfg.FacilityTankMode, tmp_cfg.FacilityTankDefs) + + self.draw_fluid_ops() + + fac_pane.set_value(7) + end + PushButton{parent=fac_c_5,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=fac_c_5,x=44,y=14,text="Next \x1a",callback=function()fac_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=fac_c_5,x=44,y=14,text="Next \x1a",callback=next_from_tank_mode,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=fac_c_5,x=8,y=14,min_width=7,text="About",callback=function()fac_pane.set_value(6)end,fg_bg=cpair(colors.black,colors.lightBlue),active_fg_bg=btn_act_fg_bg} + --#endregion + --#region Facility Tank Mode About + TextBox{parent=fac_c_6,height=3,text="This visualization tool shows the pipe connections required for a particular dynamic tank configuration you have selected."} TextBox{parent=fac_c_6,y=5,height=4,text="Examples: A U2 tank should be configured on an RTU as the dynamic tank for unit #2. An F3 tank should be configured on an RTU as the #3 dynamic tank for the facility."} TextBox{parent=fac_c_6,y=10,height=3,text="Some modes may look the same if you are not using 4 total reactor units. The wiki has details. Modes that look the same will function the same.",fg_bg=g_lg_fg_bg} PushButton{parent=fac_c_6,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - TextBox{parent=fac_c_7,height=6,text="Charge control provides automatic control to maintain an induction matrix charge level. In order to have smoother control, reactors that were activated will be held on at 0.01 mB/t for a short period before allowing them to turn off. This minimizes overshooting the charge target."} - TextBox{parent=fac_c_7,y=8,height=3,text="You can extend this to a full minute to minimize reactors flickering on/off, but there may be more overshoot of the target."} + --#endregion + --#region Dynamic Tank Fluid Types - local ext_idling = Checkbox{parent=fac_c_7,x=1,y=12,label="Enable Extended Idling",default=ini_cfg.ExtChargeIdling,box_fg_bg=cpair(colors.yellow,colors.black)} + TextBox{parent=fac_c_7,height=3,text="Specify each tank's coolant type, for display use only. Water is the only option if one or more of the connected units is water cooled."} + + local tank_fluid_list = Div{parent=fac_c_7,x=1,y=5,height=8} + + function self.draw_fluid_ops() + tank_fluid_list.remove_all() + + local tank_list = tmp_cfg.FacilityTankList + local tank_conns = tmp_cfg.FacilityTankConns + + local next_f = 1 + + for i = 1, #tank_list do + local type = tmp_cfg.TankFluidTypes[i] + + if type == 0 then type = 1 end + + self.tank_fluid_opts[i] = nil + + if tank_list[i] == 1 then + local row = Div{parent=tank_fluid_list,height=2} + + TextBox{parent=row,width=11,text="Unit Tank "..i} + TextBox{parent=row,text="Connected to: Unit "..i,fg_bg=cpair(colors.gray,colors.lightGray)} + + local tank_fluid = Radio2D{parent=row,x=34,y=1,rows=1,columns=2,default=type,options={"Water","Sodium"},radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg} + + if tmp_cfg.CoolingConfig[i].BoilerCount == 0 then + tank_fluid.set_value(1) + tank_fluid.disable() + end + + self.tank_fluid_opts[i] = tank_fluid + elseif tank_list[i] == 2 then + local row = Div{parent=tank_fluid_list,height=2} + + TextBox{parent=row,width=15,text="Facility Tank "..next_f} + + local conns = "" + local any_bwr = false + + for u = 1, #tank_conns do + if tank_conns[u] == i then + conns = conns .. tri(conns == "", "", ", ") .. "Unit " .. u + any_bwr = any_bwr or (tmp_cfg.CoolingConfig[u].BoilerCount == 0) + end + end + + TextBox{parent=row,text="Connected to: "..conns,fg_bg=cpair(colors.gray,colors.lightGray)} + + local tank_fluid = Radio2D{parent=row,x=34,y=1,rows=1,columns=2,default=type,options={"Water","Sodium"},radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg} + + if any_bwr then + tank_fluid.set_value(1) + tank_fluid.disable() + end + + self.tank_fluid_opts[i] = tank_fluid + + next_f = next_f + 1 + end + end + end + + local function back_from_fluids() + fac_pane.set_value(tri(tmp_cfg.FacilityTankMode == 0, 3, 5)) + end + + local function submit_tank_fluids() + tmp_cfg.TankFluidTypes = {} + + for i = 1, #tmp_cfg.FacilityTankList do + if self.tank_fluid_opts[i] ~= nil then + tmp_cfg.TankFluidTypes[i] = self.tank_fluid_opts[i].get_value() + else + tmp_cfg.TankFluidTypes[i] = 0 + end + end + + fac_pane.set_value(8) + end + + PushButton{parent=fac_c_7,x=1,y=14,text="\x1b Back",callback=back_from_fluids,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=fac_c_7,x=44,y=14,text="Next \x1a",callback=submit_tank_fluids,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#endregion + --#region Extended Idling + + TextBox{parent=fac_c_8,height=6,text="Charge control provides automatic control to maintain an induction matrix charge level. In order to have smoother control, reactors that were activated will be held on at 0.01 mB/t for a short period before allowing them to turn off. This minimizes overshooting the charge target."} + TextBox{parent=fac_c_8,y=8,height=3,text="You can extend this to a full minute to minimize reactors flickering on/off, but there may be more overshoot of the target."} + + local ext_idling = Checkbox{parent=fac_c_8,x=1,y=12,label="Enable Extended Idling",default=ini_cfg.ExtChargeIdling,box_fg_bg=cpair(colors.yellow,colors.black)} local function back_from_idling() - fac_pane.set_value(tri(tmp_cfg.FacilityTankMode == 0, 3, 5)) + fac_pane.set_value(tri(tmp_cfg.FacilityTankMode == 0, 3, 7)) end local function submit_idling() @@ -424,8 +689,10 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style) main_pane.set_value(3) end - PushButton{parent=fac_c_7,x=1,y=14,text="\x1b Back",callback=back_from_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} - PushButton{parent=fac_c_7,x=44,y=14,text="Next \x1a",callback=submit_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=fac_c_8,x=1,y=14,text="\x1b Back",callback=back_from_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=fac_c_8,x=44,y=14,text="Next \x1a",callback=submit_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#endregion --#endregion diff --git a/supervisor/config/system.lua b/supervisor/config/system.lua index 9e0b0c7..71483f9 100644 --- a/supervisor/config/system.lua +++ b/supervisor/config/system.lua @@ -1,6 +1,9 @@ local log = require("scada-common.log") +local types = require("scada-common.types") local util = require("scada-common.util") +local facility = require("supervisor.config.facility") + local core = require("graphics.core") local themes = require("graphics.themes") @@ -508,8 +511,15 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit else tmp_cfg.FacilityTankMode = 0 tmp_cfg.FacilityTankDefs = {} + + -- on facility tank mode 0, setup tank defs to match unit tank option + for i = 1, tmp_cfg.UnitCount do + tmp_cfg.FacilityTankDefs[i] = tri(tmp_cfg.CoolingConfig[i].TankConnection, 1, 0) + end end + tmp_cfg.FacilityTankList, tmp_cfg.FacilityTankConns = facility.generate_tank_list_and_conns(tmp_cfg.FacilityTankMode, tmp_cfg.FacilityTankDefs) + tmp_cfg.SVR_Channel = config.SVR_CHANNEL tmp_cfg.PLC_Channel = config.PLC_CHANNEL tmp_cfg.RTU_Channel = config.RTU_CHANNEL @@ -557,6 +567,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit local val_max_w = (inner_width - label_w) + 1 local raw = cfg[f[1]] local val = util.strval(raw) + local skip = false if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) elseif f[1] == "LogMode" then val = tri(raw == log.MODE.APPEND, "append", "replace") @@ -579,43 +590,88 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit 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 type(cfg.FacilityTankDefs) == "table" then + local tank_name_list = { table.unpack(cfg.FacilityTankList) } ---@type (string|integer)[] + local next_f = 1 + val = "" + for idx = 1, #tank_name_list do + if tank_name_list[idx] == 1 then + tank_name_list[idx] = "U" .. idx + elseif tank_name_list[idx] == 2 then + tank_name_list[idx] = "F" .. next_f + next_f = next_f + 1 + end + end + 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" + t_mode = "connected to its unit tank (" .. tank_name_list[cfg.FacilityTankConns[idx]] .. ")" elseif cfg.FacilityTankDefs[idx] == 2 then - t_mode = "connected to a facility tank" + t_mode = "connected to facility tank " .. tank_name_list[cfg.FacilityTankConns[idx]] end val = val .. tri(idx == 1, "", "\n") .. util.sprintf(" \x07 unit %d - %s", idx, t_mode) end if val == "" then val = "no facility tanks" end + elseif f[1] == "FacilityTankList" or f[1] == "FacilityTankConns" then + -- hide these since this info is available in the FacilityTankDefs list (connections) and TankFluidTypes list (list of tanks) + skip = true + elseif f[1] == "TankFluidTypes" and type(cfg.TankFluidTypes) == "table" and type(cfg.FacilityTankList) == "table" then + local tank_list = cfg.FacilityTankList + local next_f = 1 + + val = "" + + for idx = 1, #tank_list do + local prefix = "?" + local fluid = "water" + local type = cfg.TankFluidTypes[idx] + + if tank_list[idx] > 0 then + if tank_list[idx] == 1 then + prefix = "U" .. idx + elseif tank_list[idx] == 2 then + prefix = "F" .. next_f + next_f = next_f + 1 + end + + if type == types.COOLANT_TYPE.SODIUM then + fluid = "sodium" + end + + val = val .. tri(val == "", "", "\n") .. util.sprintf(" \x07 tank %s - %s", prefix, fluid) + end + end + + if val == "" then val = "no emergency coolant tanks" end end - if val == "nil" then val = "" end + if not skip then + if val == "nil" then val = "" end - local c = tri(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white)) - alternate = not alternate + local c = tri(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 + 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} + else + textbox = TextBox{parent=line,x=label_w+1,y=1,text=val,alignment=RIGHT} + end + + if f[1] == "AuthKey" then self.auth_key_textbox = textbox end end - - 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} - else - textbox = TextBox{parent=line,x=label_w+1,y=1,text=val,alignment=RIGHT} - end - - if f[1] == "AuthKey" then self.auth_key_textbox = textbox end end end diff --git a/supervisor/configure.lua b/supervisor/configure.lua index 0b1b558..6e760bd 100644 --- a/supervisor/configure.lua +++ b/supervisor/configure.lua @@ -30,7 +30,8 @@ local CENTER = core.ALIGN.CENTER -- changes to the config data/format to let the user know local changes = { { "v1.2.12", { "Added front panel UI theme", "Added color accessibility modes" } }, - { "v1.3.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } } + { "v1.3.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } }, + { "v1.6.0", { "Added sodium emergency coolant option" } } } ---@class svr_configurator @@ -78,8 +79,11 @@ local tool_ctl = { local tmp_cfg = { UnitCount = 1, CoolingConfig = {}, ---@type { TurbineCount: integer, BoilerCount: integer, TankConnection: boolean }[] - FacilityTankMode = 0, - FacilityTankDefs = {}, ---@type integer[] + FacilityTankMode = 0, -- dynamic tank emergency coolant layout + FacilityTankDefs = {}, ---@type integer[] each unit's tank connection target (0 = disconnected, 1 = unit, 2 = facility) + FacilityTankList = {}, ---@type integer[] list of tanks by slot (0 = none or covered by an above tank, 1 = unit tank, 2 = facility tank) + FacilityTankConns = {}, ---@type integer[] map of unit tank connections (indicies are units, values are tank indicies in the tank list) + TankFluidTypes = {}, ---@type integer[] which type of fluid each tank in the tank list should be containing ExtChargeIdling = false, SVR_Channel = nil, ---@type integer PLC_Channel = nil, ---@type integer @@ -110,6 +114,9 @@ local fields = { { "CoolingConfig", "Cooling Configuration", {} }, { "FacilityTankMode", "Facility Tank Mode", 0 }, { "FacilityTankDefs", "Facility Tank Definitions", {} }, + { "FacilityTankList", "Facility Tank List", {} }, -- hidden + { "FacilityTankConns", "Facility Tank Connections", {} }, -- hidden + { "TankFluidTypes", "Tank Fluid Types", {} }, { "ExtChargeIdling", "Extended Charge Idling", false }, { "SVR_Channel", "SVR Channel", 16240 }, { "PLC_Channel", "PLC Channel", 16241 }, @@ -176,7 +183,7 @@ local function config_view(display) TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Supervisor configurator! Please select one of the following options."} if tool_ctl.ask_config then - TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)} + TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device is not configured for this version of the supervisor. If you previously had a valid config, it's not lost. 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 @@ -275,6 +282,10 @@ function configurator.configure(ask_config) load_settings(settings_cfg, true) tool_ctl.has_config = load_settings(ini_cfg) + -- these need to be initialized as they are used before being set + tmp_cfg.FacilityTankMode = ini_cfg.FacilityTankMode + tmp_cfg.TankFluidTypes = { table.unpack(ini_cfg.TankFluidTypes) } + reset_term() -- set overridden colors diff --git a/supervisor/facility.lua b/supervisor/facility.lua index 604771a..e47842f 100644 --- a/supervisor/facility.lua +++ b/supervisor/facility.lua @@ -51,7 +51,9 @@ function facility.new(config) r_cool = config.CoolingConfig, fac_tank_mode = config.FacilityTankMode, fac_tank_defs = config.FacilityTankDefs, - fac_tank_list = {} ---@type integer[] + fac_tank_list = config.FacilityTankList, + fac_tank_conns = config.FacilityTankConns, + tank_fluid_types = config.TankFluidTypes }, -- rtus rtu_gw_conn_count = 0, @@ -147,99 +149,6 @@ function facility.new(config) table.insert(self.test_tone_states, false) end - --#region decode tank configuration - - local cool_conf = self.cooling_conf - - -- determine tank information - if cool_conf.fac_tank_mode == 0 then - cool_conf.fac_tank_defs = {} - - -- on facility tank mode 0, setup tank defs to match unit tank option - for i = 1, config.UnitCount do - cool_conf.fac_tank_defs[i] = util.trinary(cool_conf.r_cool[i].TankConnection, 1, 0) - end - - cool_conf.fac_tank_list = { table.unpack(cool_conf.fac_tank_defs) } - else - -- decode the layout of tanks from the connections definitions - local tank_mode = cool_conf.fac_tank_mode - local tank_defs = cool_conf.fac_tank_defs - local tank_list = { table.unpack(tank_defs) } - - local function calc_fdef(start_idx, end_idx) - local first = 4 - for i = start_idx, end_idx do - if tank_defs[i] == 2 then - if i < first then first = i end - end - end - return first - end - - if tank_mode == 1 then - -- (1) 1 total facility tank (A A A A) - local first_fdef = calc_fdef(1, #tank_defs) - for i = 1, #tank_defs do - if i > first_fdef and tank_defs[i] == 2 then - tank_list[i] = 0 - end - end - elseif tank_mode == 2 then - -- (2) 2 total facility tanks (A A A B) - local first_fdef = calc_fdef(1, math.min(3, #tank_defs)) - for i = 1, #tank_defs do - if (i ~= 4) and (i > first_fdef) and (tank_defs[i] == 2) then - tank_list[i] = 0 - end - end - elseif tank_mode == 3 then - -- (3) 2 total facility tanks (A A B B) - for _, a in pairs({ 1, 3 }) do - local b = a + 1 - if (tank_defs[a] == 2) and (tank_defs[b] == 2) then - tank_list[b] = 0 - end - end - elseif tank_mode == 4 then - -- (4) 2 total facility tanks (A B B B) - local first_fdef = calc_fdef(2, #tank_defs) - for i = 1, #tank_defs do - if (i ~= 1) and (i > first_fdef) and (tank_defs[i] == 2) then - tank_list[i] = 0 - end - end - elseif tank_mode == 5 then - -- (5) 3 total facility tanks (A A B C) - local first_fdef = calc_fdef(1, math.min(2, #tank_defs)) - for i = 1, #tank_defs do - if (not (i == 3 or i == 4)) and (i > first_fdef) and (tank_defs[i] == 2) then - tank_list[i] = 0 - end - end - elseif tank_mode == 6 then - -- (6) 3 total facility tanks (A B B C) - local first_fdef = calc_fdef(2, math.min(3, #tank_defs)) - for i = 1, #tank_defs do - if (not (i == 1 or i == 4)) and (i > first_fdef) and (tank_defs[i] == 2) then - tank_list[i] = 0 - end - end - elseif tank_mode == 7 then - -- (7) 3 total facility tanks (A B C C) - local first_fdef = calc_fdef(3, #tank_defs) - for i = 1, #tank_defs do - if (not (i == 1 or i == 2)) and (i > first_fdef) and (tank_defs[i] == 2) then - tank_list[i] = 0 - end - end - end - - cool_conf.fac_tank_list = tank_list - end - - --#endregion - -- PUBLIC FUNCTIONS -- ---@class facility diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 85a229e..c401dea 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -22,7 +22,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v1.5.18" +local SUPERVISOR_VERSION = "v1.6.0" local println = util.println local println_ts = util.println_ts diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 98cdc78..fe7b011 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -27,6 +27,9 @@ function supervisor.load_config() config.CoolingConfig = settings.get("CoolingConfig") config.FacilityTankMode = settings.get("FacilityTankMode") config.FacilityTankDefs = settings.get("FacilityTankDefs") + config.FacilityTankList = settings.get("FacilityTankList") + config.FacilityTankConns = settings.get("FacilityTankConns") + config.TankFluidTypes = settings.get("TankFluidTypes") config.ExtChargeIdling = settings.get("ExtChargeIdling") config.SVR_Channel = settings.get("SVR_Channel") @@ -56,8 +59,11 @@ function supervisor.load_config() cfv.assert_range(config.UnitCount, 1, 4) cfv.assert_type_table(config.CoolingConfig) - cfv.assert_type_table(config.FacilityTankDefs) cfv.assert_type_int(config.FacilityTankMode) + cfv.assert_type_table(config.FacilityTankDefs) + cfv.assert_type_table(config.FacilityTankList) + cfv.assert_type_table(config.FacilityTankConns) + cfv.assert_type_table(config.TankFluidTypes) cfv.assert_range(config.FacilityTankMode, 0, 8) cfv.assert_type_bool(config.ExtChargeIdling)