From e0809f52a6f9e16741c6b479617d51af6dd886d4 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 9 Aug 2023 23:26:06 -0400 Subject: [PATCH 01/22] #232 WIP coordinator flow view --- coordinator/coordinator.lua | 36 ++- coordinator/renderer.lua | 15 +- coordinator/startup.lua | 2 +- coordinator/ui/components/flow_overview.lua | 159 ++++++++++ coordinator/ui/layout/flow_view.lua | 41 +++ graphics/core.lua | 2 +- graphics/elements/pipenet.lua | 319 +++++++++++++++----- 7 files changed, 492 insertions(+), 82 deletions(-) create mode 100644 coordinator/ui/components/flow_overview.lua create mode 100644 coordinator/ui/layout/flow_view.lua diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index c12f9fb..b5c09e6 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -55,6 +55,8 @@ function coordinator.configure_monitors(num_units) local monitors = { primary = nil, primary_name = "", + flow = nil, + flow_name = "", unit_displays = {}, unit_name_map = {} } @@ -69,8 +71,8 @@ function coordinator.configure_monitors(num_units) table.insert(available, iface) end - -- we need a certain number of monitors (1 per unit + 1 primary display) - local num_displays_needed = num_units + 1 + -- we need a certain number of monitors (1 per unit + 1 primary display + 1 flow display) + local num_displays_needed = num_units + 2 if #names < num_displays_needed then local message = "not enough monitors connected (need " .. num_displays_needed .. ")" println(message) @@ -83,10 +85,12 @@ function coordinator.configure_monitors(num_units) log.warning("configure_monitors(): failed to load coordinator settings file (may not exist yet)") else local _primary = settings.get("PRIMARY_DISPLAY") + local _flow = settings.get("FLOW_DISPLAY") local _unitd = settings.get("UNIT_DISPLAYS") -- filter out already assigned monitors util.filter_table(available, function (x) return x ~= _primary end) + util.filter_table(available, function (x) return x ~= _flow end) if type(_unitd) == "table" then util.filter_table(available, function (x) return not util.table_contains(_unitd, x) end) end @@ -106,7 +110,6 @@ function coordinator.configure_monitors(num_units) end while iface_primary_display == nil and #available > 0 do - -- lets get a monitor iface_primary_display = ask_monitor(available) end @@ -118,6 +121,31 @@ function coordinator.configure_monitors(num_units) monitors.primary = ppm.get_periph(iface_primary_display) monitors.primary_name = iface_primary_display + -------------------------- + -- FLOW MONITOR DISPLAY -- + -------------------------- + + local iface_flow_display = settings.get("FLOW_DISPLAY") ---@type boolean|string|nil + + if not util.table_contains(names, iface_flow_display) then + println("flow monitor display is not connected") + local response = dialog.ask_y_n("would you like to change it", true) + if response == false then return false end + iface_flow_display = nil + end + + while iface_flow_display == nil and #available > 0 do + iface_flow_display = ask_monitor(available) + end + + if type(iface_flow_display) ~= "string" then return false end + + settings.set("FLOW_DISPLAY", iface_flow_display) + util.filter_table(available, function (x) return x ~= iface_flow_display end) + + monitors.flow = ppm.get_periph(iface_flow_display) + monitors.flow_name = iface_flow_display + ------------------- -- UNIT DISPLAYS -- ------------------- @@ -130,7 +158,6 @@ function coordinator.configure_monitors(num_units) local display = nil while display == nil and #available > 0 do - -- lets get a monitor println("please select monitor for unit #" .. i) display = ask_monitor(available) end @@ -152,7 +179,6 @@ function coordinator.configure_monitors(num_units) end while display == nil and #available > 0 do - -- lets get a monitor display = ask_monitor(available) end diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index 5a6a605..b4709b6 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -10,6 +10,7 @@ local iocontrol = require("coordinator.iocontrol") local style = require("coordinator.ui.style") local pgi = require("coordinator.ui.pgi") +local flow_view = require("coordinator.ui.layout.flow_view") local panel_view = require("coordinator.ui.layout.front_panel") local main_view = require("coordinator.ui.layout.main_view") local unit_view = require("coordinator.ui.layout.unit_view") @@ -29,6 +30,7 @@ local engine = { ui = { front_panel = nil, ---@type graphics_element|nil main_display = nil, ---@type graphics_element|nil + flow_display = nil, ---@type graphics_element|nil unit_displays = {} } } @@ -60,8 +62,9 @@ end -- init all displays in use by the renderer function renderer.init_displays() - -- init primary monitor + -- init primary and flow monitors _init_display(engine.monitors.primary) + _init_display(engine.monitors.flow) -- init unit displays for _, monitor in ipairs(engine.monitors.unit_displays) do @@ -169,6 +172,12 @@ function renderer.start_ui() main_view(engine.ui.main_display) end + -- show flow view on flow monitor + if engine.monitors.flow ~= nil then + engine.ui.flow_display = DisplayBox{window=engine.monitors.flow,fg_bg=style.root} + flow_view(engine.ui.flow_display) + end + -- show unit views on unit displays for idx, display in pairs(engine.monitors.unit_displays) do engine.ui.unit_displays[idx] = DisplayBox{window=display,fg_bg=style.root} @@ -192,6 +201,7 @@ function renderer.close_ui() -- delete element trees if engine.ui.main_display ~= nil then engine.ui.main_display.delete() end + if engine.ui.flow_display ~= nil then engine.ui.flow_display.delete() end for _, display in pairs(engine.ui.unit_displays) do display.delete() end -- report ui as not ready @@ -199,6 +209,7 @@ function renderer.close_ui() -- clear root UI elements engine.ui.main_display = nil + engine.ui.flow_display = nil engine.ui.unit_displays = {} -- clear unit monitors @@ -317,6 +328,8 @@ function renderer.handle_mouse(event) elseif engine.ui_ready then if event.monitor == engine.monitors.primary_name then engine.ui.main_display.handle_mouse(event) + elseif event.monitor == engine.monitors.flow_name then + engine.ui.flow_display.handle_mouse(event) else for id, monitor in ipairs(engine.monitors.unit_name_map) do if event.monitor == monitor then diff --git a/coordinator/startup.lua b/coordinator/startup.lua index a3cf6fa..9951d20 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder") local apisessions = require("coordinator.session.apisessions") -local COORDINATOR_VERSION = "v0.21.2" +local COORDINATOR_VERSION = "v1.0.0" local println = util.println local println_ts = util.println_ts diff --git a/coordinator/ui/components/flow_overview.lua b/coordinator/ui/components/flow_overview.lua new file mode 100644 index 0000000..6116667 --- /dev/null +++ b/coordinator/ui/components/flow_overview.lua @@ -0,0 +1,159 @@ +-- +-- Basic Unit Flow Overview +-- + +local util = require("scada-common.util") + +local core = require("graphics.core") + +local style = require("coordinator.ui.style") + +local reactor_view = require("coordinator.ui.components.reactor") +local boiler_view = require("coordinator.ui.components.boiler") +local turbine_view = require("coordinator.ui.components.turbine") + +local Div = require("graphics.elements.div") +local PipeNetwork = require("graphics.elements.pipenet") +local TextBox = require("graphics.elements.textbox") + +local Rectangle = require("graphics.elements.rectangle") + +local DataIndicator = require("graphics.elements.indicators.data") +local HorizontalBar = require("graphics.elements.indicators.hbar") +local StateIndicator = require("graphics.elements.indicators.state") + +local IndicatorLight = require("graphics.elements.indicators.light") +local TriIndicatorLight = require("graphics.elements.indicators.trilight") +local VerticalBar = require("graphics.elements.indicators.vbar") + +local cpair = core.cpair +local border = core.border + +local TEXT_ALIGN = core.TEXT_ALIGN + +local pipe = core.pipe + +-- make a new unit overview window +---@param parent graphics_element parent +---@param x integer top left x +---@param y integer top left y +---@param unit ioctl_unit unit database entry +local function make(parent, x, y, unit) + local height = 16 + + local v_start = 1 + ((unit.unit_id - 1) * 4) + local v_names = { + util.sprintf("PV%02d-PU", v_start), + util.sprintf("PV%02d-PO", v_start + 1), + util.sprintf("PV%02d-PL", v_start + 2), + util.sprintf("PV%02d-AM", v_start + 3) + } + + assert(parent.get_height() >= (y + height), "flow display not of sufficient vertical resolution (add an additional row of monitors) " .. y .. "," .. parent.get_height()) + + -- bounding box div + local root = Div{parent=parent,x=x,y=y,width=114,height=height} + + local text_fg_bg = cpair(colors.black, colors.white) + local lu_col = cpair(colors.gray, colors.gray) + + ------------- + -- REACTOR -- + ------------- + + local reactor = Rectangle{parent=root,x=1,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=cpair(colors.white,colors.gray)} + TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=root,x=4,y=5,text="\x19",width=1,height=1,fg_bg=cpair(colors.lightGray,colors.gray)} + + local rc_pipes = {} + + table.insert(rc_pipes, pipe(0, 1, 19, 1, colors.lightBlue, true)) + table.insert(rc_pipes, pipe(0, 3, 19, 3, colors.orange, true)) + table.insert(rc_pipes, pipe(39, 1, 58, 1, colors.blue, true)) + table.insert(rc_pipes, pipe(39, 3, 58, 3, colors.white, true)) + + table.insert(rc_pipes, pipe(78, 0, 83, 0, colors.white, true)) + table.insert(rc_pipes, pipe(78, 2, 83, 2, colors.white, true)) + table.insert(rc_pipes, pipe(78, 4, 83, 4, colors.white, true)) + + PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=colors.lightGray} + + local hc_rate = DataIndicator{parent=root,x=22,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} + local cc_rate = DataIndicator{parent=root,x=22,y=5,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} + + local boiler = Rectangle{parent=root,x=40,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=cpair(colors.white,colors.gray)} + TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=boiler,y=3,text="BOILERS",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=root,x=40,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=root,x=58,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} + + local wt_rate = DataIndicator{parent=root,x=61,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} + local st_rate = DataIndicator{parent=root,x=61,y=5,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} + + local turbine = Rectangle{parent=root,x=79,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=cpair(colors.white,colors.gray)} + TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=root,x=79,y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} + + TextBox{parent=root,x=101,y=3,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} + TextBox{parent=root,x=103,y=3,text="\x7f",fg_bg=cpair(colors.white,colors.lightGray),width=1,height=1} + local conn = TriIndicatorLight{parent=root,x=106,y=1,label="PRV01",c1=colors.gray,c2=colors.yellow,c3=colors.red} + local conn = TriIndicatorLight{parent=root,x=106,y=3,label="PRV02",c1=colors.gray,c2=colors.yellow,c3=colors.red} + local conn = TriIndicatorLight{parent=root,x=106,y=5,label="PRV03",c1=colors.gray,c2=colors.yellow,c3=colors.red} + + local waste = Div{parent=root,x=3,y=6} + + local waste_pipes = { + pipe(0, 0, 16, 1, colors.brown, true), + pipe(12, 1, 16, 5, colors.brown, true), + pipe(18, 1, 44, 1, colors.brown, true), + pipe(18, 5, 23, 5, colors.brown, true), + pipe(52, 1, 80, 1, colors.green, true), + pipe(42, 4, 60, 4, colors.cyan, true), + pipe(56, 4, 60, 8, colors.cyan, true), + pipe(62, 4, 80, 4, colors.cyan, true), + pipe(62, 8, 110, 8, colors.cyan, true), + pipe(93, 1, 94, 3, colors.black, true, true), + pipe(93, 4, 109, 6, colors.black, true, true), + pipe(109, 6, 107, 6, colors.black, true, true) + } + + PipeNetwork{parent=waste,x=2,y=1,pipes=waste_pipes,bg=colors.lightGray} + + local function _valve(vx, vy, n) + TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} + local conn = IndicatorLight{parent=waste,x=vx-3,y=vy+1,label=v_names[n],colors=cpair(colors.green,colors.gray)} + local state = IndicatorLight{parent=waste,x=vx-3,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)} + end + + local function _machine(mx, my, name) + local l = string.len(name) + 2 + TextBox{parent=waste,x=mx,y=my,text=util.strrep("\x8f",l),alignment=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.lightGray,colors.gray),width=l,height=1} + TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.white,colors.gray),width=l,height=1} + end + + local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%7.2f",value=1234.56,width=12,fg_bg=text_fg_bg} + local pu_rate = DataIndicator{parent=waste,x=70,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} + local po_rate = DataIndicator{parent=waste,x=45,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} + local popl_rate = DataIndicator{parent=waste,x=70,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} + local poam_rate = DataIndicator{parent=waste,x=70,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} + local spent_rate = DataIndicator{parent=waste,x=99,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} + + _valve(18, 2, 1); _valve(18, 6, 2); _valve(62, 5, 3); _valve(62, 9, 4) + _machine(45, 1, "CENTRIFUGE \x1a"); _machine(83, 1, "PRC [Pu] \x1a"); _machine(83, 4, "PRC [Po] \x1a"); _machine(94, 6, "SPENT WASTE \x1b") + + + TextBox{parent=waste,x=25,y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=cpair(colors.white,colors.gray)} + local sna_po = Rectangle{parent=waste,x=25,y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=cpair(colors.black,colors.white)} + local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=cpair(colors.green,colors.red)} + local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_col,label="CNT",unit="",format="%2d",value=99,width=7,fg_bg=text_fg_bg} + local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_col,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} + local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_col,label="MAX ",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} + local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_col,label="IN ",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} + + return root +end + +return make diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua new file mode 100644 index 0000000..2e865c7 --- /dev/null +++ b/coordinator/ui/layout/flow_view.lua @@ -0,0 +1,41 @@ +-- +-- Flow Monitor GUI +-- + +local util = require("scada-common.util") + +local iocontrol = require("coordinator.iocontrol") + +local style = require("coordinator.ui.style") + +local flow_overview = require("coordinator.ui.components.flow_overview") + +local core = require("graphics.core") + +local TextBox = require("graphics.elements.textbox") + +local DataIndicator = require("graphics.elements.indicators.data") + +local TEXT_ALIGN = core.TEXT_ALIGN + +local cpair = core.cpair + +-- create new flow view +---@param main graphics_element main displaybox +local function init(main) + local facility = iocontrol.get_db().facility + local units = iocontrol.get_db().units + + -- window header message + local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} + -- max length example: "01:23:45 AM - Wednesday, September 28 2022" + local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=TEXT_ALIGN.RIGHT,width=42,height=1,fg_bg=style.header} + + datetime.register(facility.ps, "date_time", datetime.set_value) + + for i = 1, 4 do + flow_overview(main, 25, 5 + ((i - 1) * 20), units[i]) + end +end + +return init diff --git a/graphics/core.lua b/graphics/core.lua index 534d4e2..5abc28d 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -7,7 +7,7 @@ local flasher = require("graphics.flasher") local core = {} -core.version = "1.0.2" +core.version = "1.0.3" core.flasher = flasher core.events = events diff --git a/graphics/elements/pipenet.lua b/graphics/elements/pipenet.lua index efe6179..5319e68 100644 --- a/graphics/elements/pipenet.lua +++ b/graphics/elements/pipenet.lua @@ -1,6 +1,7 @@ -- Pipe Graphics Element local util = require("scada-common.util") +local log = require("scada-common.log") local core = require("graphics.core") local element = require("graphics.element") @@ -14,6 +15,12 @@ local element = require("graphics.element") ---@field y? integer auto incremented if omitted ---@field hidden? boolean true to hide on initial draw +---@class _pipe_map_entry +---@field atr boolean align top right (or bottom left for false) +---@field thin boolean thin pipe or not +---@field fg string foreground blit +---@field bg string background blit + -- new pipe network ---@param args pipenet_args ---@return graphics_element element, element_id id @@ -44,102 +51,266 @@ local function pipenet(args) -- create new graphics element base object local e = element.new(args) - -- draw all pipes + -- determine if there are any thin pipes involved + local any_thin = false for p = 1, #args.pipes do - local pipe = args.pipes[p] ---@type pipe + any_thin = args.pipes[p].thin + if any_thin then break end + end - local x = 1 + pipe.x1 - local y = 1 + pipe.y1 + if not any_thin then + -- draw all pipes + for p = 1, #args.pipes do + local pipe = args.pipes[p] ---@type pipe - local x_step = util.trinary(pipe.x1 >= pipe.x2, -1, 1) - local y_step = util.trinary(pipe.y1 >= pipe.y2, -1, 1) + local x = 1 + pipe.x1 + local y = 1 + pipe.y1 - e.window.setCursorPos(x, y) + local x_step = util.trinary(pipe.x1 >= pipe.x2, -1, 1) + local y_step = util.trinary(pipe.y1 >= pipe.y2, -1, 1) - local c = core.cpair(pipe.color, e.fg_bg.bkg) + if pipe.thin then + x_step = util.trinary(pipe.x1 == pipe.x2, 0, x_step) + y_step = util.trinary(pipe.y1 == pipe.y2, 0, y_step) + end - if pipe.align_tr then - -- cross width then height - for i = 1, pipe.w do - if pipe.thin then - if i == pipe.w then - -- corner - if y_step > 0 then - e.window.blit("\x93", c.blit_bkg, c.blit_fgd) + e.window.setCursorPos(x, y) + + local c = core.cpair(pipe.color, e.fg_bg.bkg) + + if pipe.align_tr then + -- cross width then height + for i = 1, pipe.w do + if pipe.thin then + if i == pipe.w then + -- corner + if y_step > 0 then + e.window.blit("\x93", c.blit_bkg, c.blit_fgd) + else + e.window.blit("\x8e", c.blit_fgd, c.blit_bkg) + end else - e.window.blit("\x8e", c.blit_fgd, c.blit_bkg) + e.window.blit("\x8c", c.blit_fgd, c.blit_bkg) end else + if i == pipe.w and y_step > 0 then + -- corner + e.window.blit(" ", c.blit_bkg, c.blit_fgd) + else + e.window.blit("\x8f", c.blit_fgd, c.blit_bkg) + end + end + + x = x + x_step + e.window.setCursorPos(x, y) + end + + -- back up one + x = x - x_step + + for _ = 1, pipe.h - 1 do + y = y + y_step + e.window.setCursorPos(x, y) + + if pipe.thin then + e.window.blit("\x95", c.blit_bkg, c.blit_fgd) + else + e.window.blit(" ", c.blit_bkg, c.blit_fgd) + end + end + else + -- cross height then width + for i = 1, pipe.h do + if pipe.thin then + if i == pipe.h then + -- corner + if y_step < 0 then + e.window.blit("\x97", c.blit_bkg, c.blit_fgd) + elseif y_step > 0 then + e.window.blit("\x8d", c.blit_fgd, c.blit_bkg) + else + e.window.blit("\x8c", c.blit_fgd, c.blit_bkg) + end + else + e.window.blit("\x95", c.blit_fgd, c.blit_bkg) + end + else + if i == pipe.h and y_step < 0 then + -- corner + e.window.blit("\x83", c.blit_bkg, c.blit_fgd) + else + e.window.blit(" ", c.blit_bkg, c.blit_fgd) + end + end + + y = y + y_step + e.window.setCursorPos(x, y) + end + + -- back up one + y = y - y_step + + for _ = 1, pipe.w - 1 do + x = x + x_step + e.window.setCursorPos(x, y) + + if pipe.thin then e.window.blit("\x8c", c.blit_fgd, c.blit_bkg) - end - else - if i == pipe.w and y_step > 0 then - -- corner - e.window.blit(" ", c.blit_bkg, c.blit_fgd) else - e.window.blit("\x8f", c.blit_fgd, c.blit_bkg) - end - end - - x = x + x_step - e.window.setCursorPos(x, y) - end - - -- back up one - x = x - x_step - - for _ = 1, pipe.h - 1 do - y = y + y_step - e.window.setCursorPos(x, y) - - if pipe.thin then - e.window.blit("\x95", c.blit_bkg, c.blit_fgd) - else - e.window.blit(" ", c.blit_bkg, c.blit_fgd) - end - end - else - -- cross height then width - for i = 1, pipe.h do - if pipe.thin then - if i == pipe.h then - -- corner - if y_step < 0 then - e.window.blit("\x97", c.blit_bkg, c.blit_fgd) - else - e.window.blit("\x8d", c.blit_fgd, c.blit_bkg) - end - else - e.window.blit("\x95", c.blit_fgd, c.blit_bkg) - end - else - if i == pipe.h and y_step < 0 then - -- corner e.window.blit("\x83", c.blit_bkg, c.blit_fgd) - else - e.window.blit(" ", c.blit_bkg, c.blit_fgd) end end - - y = y + y_step - e.window.setCursorPos(x, y) end + end + else + -- build map if using thin pipes, easist way to check adjacent blocks (cannot 'cheat' like with standard width) + local map = {} - -- back up one - y = y - y_step + -- allocate map + for x = 1, args.width do + table.insert(map, {}) + for _ = 1, args.height do table.insert(map[x], false) end + end - for _ = 1, pipe.w - 1 do - x = x + x_step - e.window.setCursorPos(x, y) + -- build map + for p = 1, #args.pipes do + local pipe = args.pipes[p] ---@type pipe - if pipe.thin then - e.window.blit("\x8c", c.blit_fgd, c.blit_bkg) - else - e.window.blit("\x83", c.blit_bkg, c.blit_fgd) + local x = 1 + pipe.x1 + local y = 1 + pipe.y1 + + local x_step = util.trinary(pipe.x1 >= pipe.x2, -1, 1) + local y_step = util.trinary(pipe.y1 >= pipe.y2, -1, 1) + + local entry = { atr = pipe.align_tr, thin = pipe.thin, fg = colors.toBlit(pipe.color), bg = e.fg_bg.blit_bkg } + + if pipe.align_tr then + -- cross width then height + for _ = 1, pipe.w do + map[x][y] = entry + x = x + x_step + end + + x = x - x_step -- back up one + + for _ = 1, pipe.h do + map[x][y] = entry + y = y + y_step + end + else + -- cross height then width + for _ = 1, pipe.h do + map[x][y] = entry + y = y + y_step + end + + y = y - y_step -- back up one + + for _ = 1, pipe.w do + map[x][y] = entry + x = x + x_step end end end + -- for x = 1, args.width do + -- for y = 1, args.height do + -- local entry = map[x][y] ---@type _pipe_map_entry|false + -- if entry == false then + -- e.window.setCursorPos(x, y) + -- e.window.blit("x", "f", "e") + -- end + -- end + -- end + + -- render + for x = 1, args.width do + for y = 1, args.height do + local entry = map[x][y] ---@type _pipe_map_entry|false + local char = "" + local invert = false + + if entry ~= false then + local function check(cx, cy) + return (map[cx] ~= nil) and (map[cx][cy] ~= nil) and (map[cx][cy] ~= false) and (map[cx][cy].fg == entry.fg) + end + + if entry.thin then + if check(x - 1, y) then -- if left + if check(x, y - 1) then -- if above + if check(x + 1, y) then -- if right + if check(x, y + 1) then -- if below + char = util.trinary(entry.atr, "\x91", "\x9d") + invert = entry.atr + else -- not below + char = util.trinary(entry.atr, "\x8e", "\x8d") + end + else -- not right + if check(x, y + 1) then -- if below + char = util.trinary(entry.atr, "\x91", "\x95") + invert = entry.atr + else -- not below + char = util.trinary(entry.atr, "\x8e", "\x85") + end + end + elseif check(x, y + 1) then-- not above, if below + if check(x + 1, y) then -- if right + char = util.trinary(entry.atr, "\x93", "\x9c") + invert = entry.atr + else -- not right + char = util.trinary(entry.atr, "\x93", "\x94") + invert = entry.atr + end + else -- not above, not below + char = "\x8c" + end + elseif check(x + 1, y) then -- not left, if right + if check(x, y - 1) then -- if above + if check(x, y + 1) then -- if below + char = util.trinary(entry.atr, "\x95", "\x9d") + invert = entry.atr + else -- not below + char = util.trinary(entry.atr, "\x8a", "\x8d") + end + else -- not above + if check(x, y + 1) then -- if below + char = util.trinary(entry.atr, "\x97", "\x9c") + invert = entry.atr + else -- not below + char = "\x8c" + end + end + else -- not left, not right + char = "\x95" + invert = entry.atr + end + else + if check(x, y - 1) then -- above + -- not below and (if left or right) + if (not check(x, y + 1)) and (check(x - 1, y) or check(x + 1, y)) then + char = util.trinary(entry.atr, "\x8f", "\x83") + invert = not entry.atr + else -- not above w/ sides only + char = " " + invert = true + end + elseif check(x, y + 1) then -- not above, if below + char = util.trinary(entry.atr, "\x8f", "\x83") + invert = not entry.atr + else -- not above, not below + end + end + + e.window.setCursorPos(x, y) + + if invert then + e.window.blit(char, entry.bg, entry.fg) + else + e.window.blit(char, entry.fg, entry.bg) + end + end + end + end end return e.complete() From d85385c1fee1e60baa814bab9c6d03169ccad35a Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 10 Aug 2023 23:31:38 -0400 Subject: [PATCH 02/22] #232 continued work on flow monitor, added SPS display --- coordinator/ui/components/flow_overview.lua | 69 ++++++++++++--------- coordinator/ui/components/imatrix.lua | 4 +- coordinator/ui/layout/flow_view.lua | 38 +++++++++++- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/coordinator/ui/components/flow_overview.lua b/coordinator/ui/components/flow_overview.lua index 6116667..f66d949 100644 --- a/coordinator/ui/components/flow_overview.lua +++ b/coordinator/ui/components/flow_overview.lua @@ -2,35 +2,25 @@ -- Basic Unit Flow Overview -- -local util = require("scada-common.util") +local util = require("scada-common.util") -local core = require("graphics.core") +local core = require("graphics.core") -local style = require("coordinator.ui.style") +local Div = require("graphics.elements.div") +local PipeNetwork = require("graphics.elements.pipenet") +local TextBox = require("graphics.elements.textbox") -local reactor_view = require("coordinator.ui.components.reactor") -local boiler_view = require("coordinator.ui.components.boiler") -local turbine_view = require("coordinator.ui.components.turbine") +local Rectangle = require("graphics.elements.rectangle") -local Div = require("graphics.elements.div") -local PipeNetwork = require("graphics.elements.pipenet") -local TextBox = require("graphics.elements.textbox") - -local Rectangle = require("graphics.elements.rectangle") - -local DataIndicator = require("graphics.elements.indicators.data") -local HorizontalBar = require("graphics.elements.indicators.hbar") -local StateIndicator = require("graphics.elements.indicators.state") +local DataIndicator = require("graphics.elements.indicators.data") local IndicatorLight = require("graphics.elements.indicators.light") local TriIndicatorLight = require("graphics.elements.indicators.trilight") -local VerticalBar = require("graphics.elements.indicators.vbar") - -local cpair = core.cpair -local border = core.border local TEXT_ALIGN = core.TEXT_ALIGN +local cpair = core.cpair +local border = core.border local pipe = core.pipe -- make a new unit overview window @@ -42,11 +32,15 @@ local function make(parent, x, y, unit) local height = 16 local v_start = 1 + ((unit.unit_id - 1) * 4) + local prv_start = 1 + ((unit.unit_id - 1) * 3) local v_names = { util.sprintf("PV%02d-PU", v_start), util.sprintf("PV%02d-PO", v_start + 1), util.sprintf("PV%02d-PL", v_start + 2), - util.sprintf("PV%02d-AM", v_start + 3) + util.sprintf("PV%02d-AM", v_start + 3), + util.sprintf("PRV%02d", prv_start), + util.sprintf("PRV%02d", prv_start + 1), + util.sprintf("PRV%02d", prv_start + 2) } assert(parent.get_height() >= (y + height), "flow display not of sufficient vertical resolution (add an additional row of monitors) " .. y .. "," .. parent.get_height()) @@ -57,9 +51,9 @@ local function make(parent, x, y, unit) local text_fg_bg = cpair(colors.black, colors.white) local lu_col = cpair(colors.gray, colors.gray) - ------------- - -- REACTOR -- - ------------- + ------------------ + -- COOLING LOOP -- + ------------------ local reactor = Rectangle{parent=root,x=1,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=cpair(colors.white,colors.gray)} TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=TEXT_ALIGN.CENTER,height=1} @@ -97,11 +91,18 @@ local function make(parent, x, y, unit) TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=root,x=79,y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} - TextBox{parent=root,x=101,y=3,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} - TextBox{parent=root,x=103,y=3,text="\x7f",fg_bg=cpair(colors.white,colors.lightGray),width=1,height=1} - local conn = TriIndicatorLight{parent=root,x=106,y=1,label="PRV01",c1=colors.gray,c2=colors.yellow,c3=colors.red} - local conn = TriIndicatorLight{parent=root,x=106,y=3,label="PRV02",c1=colors.gray,c2=colors.yellow,c3=colors.red} - local conn = TriIndicatorLight{parent=root,x=106,y=5,label="PRV03",c1=colors.gray,c2=colors.yellow,c3=colors.red} + local function _relief(rx, ry, name) + TextBox{parent=root,x=rx,y=ry,text="\x10\x11\x7f",fg_bg=cpair(colors.black,colors.lightGray),width=3,height=1} + local conn = TriIndicatorLight{parent=root,x=rx+4,y=ry,label=name,c1=colors.gray,c2=colors.yellow,c3=colors.red} + end + + _relief(103, 1, v_names[5]) + _relief(103, 3, v_names[6]) + _relief(103, 5, v_names[7]) + + ---------------------- + -- WASTE PROCESSING -- + ---------------------- local waste = Div{parent=root,x=3,y=6} @@ -141,13 +142,19 @@ local function make(parent, x, y, unit) local poam_rate = DataIndicator{parent=waste,x=70,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} local spent_rate = DataIndicator{parent=waste,x=99,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} - _valve(18, 2, 1); _valve(18, 6, 2); _valve(62, 5, 3); _valve(62, 9, 4) - _machine(45, 1, "CENTRIFUGE \x1a"); _machine(83, 1, "PRC [Pu] \x1a"); _machine(83, 4, "PRC [Po] \x1a"); _machine(94, 6, "SPENT WASTE \x1b") + _valve(18, 2, 1) + _valve(18, 6, 2) + _valve(62, 5, 3) + _valve(62, 9, 4) + _machine(45, 1, "CENTRIFUGE \x1a"); + _machine(83, 1, "PRC [Pu] \x1a"); + _machine(83, 4, "PRC [Po] \x1a"); + _machine(94, 6, "SPENT WASTE \x1b") TextBox{parent=waste,x=25,y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=cpair(colors.white,colors.gray)} local sna_po = Rectangle{parent=waste,x=25,y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=cpair(colors.black,colors.white)} - local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=cpair(colors.green,colors.red)} + local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=cpair(colors.green,colors.gray)} local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_col,label="CNT",unit="",format="%2d",value=99,width=7,fg_bg=text_fg_bg} local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_col,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_col,label="MAX ",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} diff --git a/coordinator/ui/components/imatrix.lua b/coordinator/ui/components/imatrix.lua index a234cbc..2d553a5 100644 --- a/coordinator/ui/components/imatrix.lua +++ b/coordinator/ui/components/imatrix.lua @@ -83,9 +83,7 @@ local function new_view(root, x, y, data, ps, id) local function calc_saturation(val) if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then return val / data.build.transfer_cap - else - return 0 - end + else return 0 end end charge.register(ps, "energy_fill", charge.update) diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 2e865c7..dae12e6 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -16,9 +16,25 @@ local TextBox = require("graphics.elements.textbox") local DataIndicator = require("graphics.elements.indicators.data") +local Div = require("graphics.elements.div") +local PipeNetwork = require("graphics.elements.pipenet") +local TextBox = require("graphics.elements.textbox") + +local Rectangle = require("graphics.elements.rectangle") + +local DataIndicator = require("graphics.elements.indicators.data") +local HorizontalBar = require("graphics.elements.indicators.hbar") +local StateIndicator = require("graphics.elements.indicators.state") + +local IndicatorLight = require("graphics.elements.indicators.light") +local TriIndicatorLight = require("graphics.elements.indicators.trilight") +local VerticalBar = require("graphics.elements.indicators.vbar") + local TEXT_ALIGN = core.TEXT_ALIGN local cpair = core.cpair +local border = core.border +local pipe = core.pipe -- create new flow view ---@param main graphics_element main displaybox @@ -33,9 +49,27 @@ local function init(main) datetime.register(facility.ps, "date_time", datetime.set_value) - for i = 1, 4 do - flow_overview(main, 25, 5 + ((i - 1) * 20), units[i]) + local po_pipes = {} + + for i = 1, facility.num_units do + local y_offset = ((i - 1) * 20) + flow_overview(main, 25, 5 + y_offset, units[i]) + table.insert(po_pipes, pipe(0, 6 + y_offset, 8, 0, colors.cyan, true, true)) end + + local text_fg_bg = cpair(colors.black, colors.white) + local lu_col = cpair(colors.gray, colors.gray) + + PipeNetwork{parent=main,x=139,y=12,pipes=po_pipes,bg=colors.lightGray} + + local sps = Div{parent=main,x=142,y=5,height=8} + + TextBox{parent=sps,x=1,y=1,text="SPS",alignment=TEXT_ALIGN.CENTER,width=21,height=1,fg_bg=cpair(colors.white,colors.gray)} + local sps_box = Rectangle{parent=sps,x=1,y=2,border=border(1, colors.gray, true),width=21,height=7,thin=true,fg_bg=cpair(colors.black,colors.white)} + local sps_conn = IndicatorLight{parent=sps_box,label="CONNECTED",colors=cpair(colors.green,colors.gray)} + local sps_act = IndicatorLight{parent=sps_box,label="ACTIVE",colors=cpair(colors.green,colors.gray)} + local sps_in = DataIndicator{parent=sps_box,y=4,lu_colors=lu_col,label="IN ",unit="mB/t",format="%9.2f",value=123.456,width=19,fg_bg=text_fg_bg} + local sps_rate = DataIndicator{parent=sps_box,lu_colors=lu_col,label="RATE",unit="\xb5B/t",format="%9.2f",value=123456.78,width=19,fg_bg=text_fg_bg} end return init From ac1733c46ebfdadf4be1707b46a983d346182502 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 12 Aug 2023 15:16:37 -0400 Subject: [PATCH 03/22] #314 20s grace period for coordinator render to finish to prevent timeouts --- supervisor/session/coordinator.lua | 14 +++++++++++++- supervisor/startup.lua | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/supervisor/session/coordinator.lua b/supervisor/session/coordinator.lua index 98b7431..c2d20cd 100644 --- a/supervisor/session/coordinator.lua +++ b/supervisor/session/coordinator.lua @@ -17,6 +17,9 @@ local FAC_COMMAND = comms.FAC_COMMAND local SV_Q_DATA = svqtypes.SV_Q_DATA +-- grace period in seconds for coordinator to finish UI draw to prevent timeout +local WATCHDOG_GRACE = 20.0 + -- retry time constants in ms -- local INITIAL_WAIT = 1500 local RETRY_PERIOD = 1000 @@ -61,6 +64,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil r_seq_num = nil, connected = true, conn_watchdog = util.new_watchdog(timeout), + establish_time = util.time_s(), last_rtt = 0, -- periodic messages periodics = { @@ -354,7 +358,15 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil -- check if a timer matches this session's watchdog ---@nodiscard function public.check_wd(timer) - return self.conn_watchdog.is_timer(timer) and self.connected + local is_wd = self.conn_watchdog.is_timer(timer) and self.connected + + -- if we are waiting for initial coordinator UI draw, don't close yet + if is_wd and (util.time_s() - self.establish_time) <= WATCHDOG_GRACE then + self.conn_watchdog.feed() + is_wd = false + end + + return is_wd end -- close the connection diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 59f5c4f..1ba2a4f 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.20.4" +local SUPERVISOR_VERSION = "v0.20.5" local println = util.println local println_ts = util.println_ts From 76ab4e17bfe0553d5f416e870f8cd81297006487 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 13 Aug 2023 00:11:58 -0400 Subject: [PATCH 04/22] #232 WIP full flow view drawn out --- .../{flow_overview.lua => unit_flow.lua} | 18 ++-- coordinator/ui/layout/flow_view.lua | 89 ++++++++++++------- coordinator/ui/style.lua | 28 +++++- 3 files changed, 94 insertions(+), 41 deletions(-) rename coordinator/ui/components/{flow_overview.lua => unit_flow.lua} (95%) diff --git a/coordinator/ui/components/flow_overview.lua b/coordinator/ui/components/unit_flow.lua similarity index 95% rename from coordinator/ui/components/flow_overview.lua rename to coordinator/ui/components/unit_flow.lua index f66d949..382a277 100644 --- a/coordinator/ui/components/flow_overview.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -19,11 +19,13 @@ local TriIndicatorLight = require("graphics.elements.indicators.trilight") local TEXT_ALIGN = core.TEXT_ALIGN +local sprintf = util.sprintf + local cpair = core.cpair local border = core.border local pipe = core.pipe --- make a new unit overview window +-- make a new unit flow window ---@param parent graphics_element parent ---@param x integer top left x ---@param y integer top left y @@ -34,13 +36,13 @@ local function make(parent, x, y, unit) local v_start = 1 + ((unit.unit_id - 1) * 4) local prv_start = 1 + ((unit.unit_id - 1) * 3) local v_names = { - util.sprintf("PV%02d-PU", v_start), - util.sprintf("PV%02d-PO", v_start + 1), - util.sprintf("PV%02d-PL", v_start + 2), - util.sprintf("PV%02d-AM", v_start + 3), - util.sprintf("PRV%02d", prv_start), - util.sprintf("PRV%02d", prv_start + 1), - util.sprintf("PRV%02d", prv_start + 2) + sprintf("PV%02d-PU", v_start), + sprintf("PV%02d-PO", v_start + 1), + sprintf("PV%02d-PL", v_start + 2), + sprintf("PV%02d-AM", v_start + 3), + sprintf("PRV%02d", prv_start), + sprintf("PRV%02d", prv_start + 1), + sprintf("PRV%02d", prv_start + 2) } assert(parent.get_height() >= (y + height), "flow display not of sufficient vertical resolution (add an additional row of monitors) " .. y .. "," .. parent.get_height()) diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index dae12e6..5d6328a 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -2,34 +2,26 @@ -- Flow Monitor GUI -- -local util = require("scada-common.util") +local util = require("scada-common.util") -local iocontrol = require("coordinator.iocontrol") +local iocontrol = require("coordinator.iocontrol") -local style = require("coordinator.ui.style") +local style = require("coordinator.ui.style") -local flow_overview = require("coordinator.ui.components.flow_overview") +local unit_flow = require("coordinator.ui.components.unit_flow") -local core = require("graphics.core") - -local TextBox = require("graphics.elements.textbox") - -local DataIndicator = require("graphics.elements.indicators.data") - -local Div = require("graphics.elements.div") -local PipeNetwork = require("graphics.elements.pipenet") -local TextBox = require("graphics.elements.textbox") +local core = require("graphics.core") +local Div = require("graphics.elements.div") +local PipeNetwork = require("graphics.elements.pipenet") local Rectangle = require("graphics.elements.rectangle") +local TextBox = require("graphics.elements.textbox") local DataIndicator = require("graphics.elements.indicators.data") local HorizontalBar = require("graphics.elements.indicators.hbar") +local IndicatorLight = require("graphics.elements.indicators.light") local StateIndicator = require("graphics.elements.indicators.state") -local IndicatorLight = require("graphics.elements.indicators.light") -local TriIndicatorLight = require("graphics.elements.indicators.trilight") -local VerticalBar = require("graphics.elements.indicators.vbar") - local TEXT_ALIGN = core.TEXT_ALIGN local cpair = core.cpair @@ -51,25 +43,58 @@ local function init(main) local po_pipes = {} - for i = 1, facility.num_units do - local y_offset = ((i - 1) * 20) - flow_overview(main, 25, 5 + y_offset, units[i]) - table.insert(po_pipes, pipe(0, 6 + y_offset, 8, 0, colors.cyan, true, true)) - end - - local text_fg_bg = cpair(colors.black, colors.white) + local bw_fg_bg = cpair(colors.black, colors.white) + local text_col = cpair(colors.black, colors.lightGray) local lu_col = cpair(colors.gray, colors.gray) - PipeNetwork{parent=main,x=139,y=12,pipes=po_pipes,bg=colors.lightGray} + local water_pipes = {} - local sps = Div{parent=main,x=142,y=5,height=8} + local fac_tanks = true - TextBox{parent=sps,x=1,y=1,text="SPS",alignment=TEXT_ALIGN.CENTER,width=21,height=1,fg_bg=cpair(colors.white,colors.gray)} - local sps_box = Rectangle{parent=sps,x=1,y=2,border=border(1, colors.gray, true),width=21,height=7,thin=true,fg_bg=cpair(colors.black,colors.white)} - local sps_conn = IndicatorLight{parent=sps_box,label="CONNECTED",colors=cpair(colors.green,colors.gray)} - local sps_act = IndicatorLight{parent=sps_box,label="ACTIVE",colors=cpair(colors.green,colors.gray)} - local sps_in = DataIndicator{parent=sps_box,y=4,lu_colors=lu_col,label="IN ",unit="mB/t",format="%9.2f",value=123.456,width=19,fg_bg=text_fg_bg} - local sps_rate = DataIndicator{parent=sps_box,lu_colors=lu_col,label="RATE",unit="\xb5B/t",format="%9.2f",value=123456.78,width=19,fg_bg=text_fg_bg} + for i = 1, 4 do + local y = ((i - 1) * 20) + table.insert(water_pipes, pipe(2, y, 2, y + 5, colors.blue, true)) + table.insert(water_pipes, pipe(2, y, 82, y, colors.blue, true)) + table.insert(water_pipes, pipe(82, y, 82, y + 2, colors.blue, true)) + if fac_tanks and i > 1 then table.insert(water_pipes, pipe(21, y - 19, 21, y, colors.blue, true)) end + end + + PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=colors.lightGray} + + for i = 1, facility.num_units do + local y_offset = ((i - 1) * 20) + unit_flow(main, 25, 5 + y_offset, units[i]) + table.insert(po_pipes, pipe(0, 3 + y_offset, 8, 0, colors.cyan, true, true)) + + local vx, vy = 11, 3 + y_offset + TextBox{parent=main,x=vx,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} + local conn = IndicatorLight{parent=main,x=vx-3,y=vy+1,label=util.sprintf("PV%02d", i + 13),colors=cpair(colors.green,colors.gray)} + local state = IndicatorLight{parent=main,x=vx-3,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)} + + local tank = Div{parent=main,x=2,y=8+y_offset,width=20,height=12} + TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=tank,text="DYNAMIC TANK "..i,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.white,colors.gray)} + local tank_box = Rectangle{parent=tank,border=border(1, colors.gray, true),width=20,height=10} + local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14} + TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label} + 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,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg} + TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label} + local ccool = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16} + ccool.update(1) + end + + PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray} + + local sps = Div{parent=main,x=140,y=3,height=12} + TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=cpair(colors.white,colors.gray)} + local sps_box = Rectangle{parent=sps,border=border(1, colors.gray, true),width=24,height=10} + local status = StateIndicator{parent=sps_box,x=5,y=1,states=style.sps.states,value=1,min_width=14} + TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label} + local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} + TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label} + local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} end return init diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua index ea52556..947fdb5 100644 --- a/coordinator/ui/style.lua +++ b/coordinator/ui/style.lua @@ -210,12 +210,38 @@ style.sps = { text = "IDLE" }, { - color = cpair(colors.black, colors.green), + color = cpair(colors.black, colors.blue), text = "ACTIVE" } } } +style.dtank = { + -- dynamic tank states + states = { + { + color = cpair(colors.black, colors.yellow), + text = "OFF-LINE" + }, + { + color = cpair(colors.black, colors.orange), + text = "NOT FORMED" + }, + { + color = cpair(colors.black, colors.orange), + text = "RTU FAULT" + }, + { + color = cpair(colors.black, colors.green), + text = "ONLINE" + }, + { + color = cpair(colors.black, colors.yellow), + text = "LOW FILL" + } + } +} + style.waste = { -- auto waste processing states states = { From ce780c3d722866d078753fb53eb4e0d53b7da2f9 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 13 Aug 2023 00:51:37 -0400 Subject: [PATCH 05/22] added common color pairs to coordinator style --- coordinator/ui/components/process_ctl.lua | 47 +++++----- coordinator/ui/components/unit_detail.lua | 101 ++++++++++++---------- coordinator/ui/components/unit_flow.lua | 75 +++++++++------- coordinator/ui/layout/flow_view.lua | 9 +- coordinator/ui/style.lua | 15 +++- 5 files changed, 139 insertions(+), 108 deletions(-) diff --git a/coordinator/ui/components/process_ctl.lua b/coordinator/ui/components/process_ctl.lua index 0716619..22d1e22 100644 --- a/coordinator/ui/components/process_ctl.lua +++ b/coordinator/ui/components/process_ctl.lua @@ -28,6 +28,16 @@ local TEXT_ALIGN = core.TEXT_ALIGN local cpair = core.cpair local border = core.border +local bw_fg_bg = style.bw_fg_bg +local lu_cpair = style.lu_colors +local hzd_fg_bg = style.hzd_fg_bg +local dis_colors = style.dis_colors + +local ind_grn = style.ind_grn +local ind_yel = style.ind_yel +local ind_red = style.ind_red +local ind_wht = style.ind_wht + local period = core.flasher.PERIOD -- new process control view @@ -40,11 +50,6 @@ local function new_view(root, x, y) local facility = iocontrol.get_db().facility local units = iocontrol.get_db().units - local bw_fg_bg = cpair(colors.black, colors.white) - local hzd_fg_bg = cpair(colors.white, colors.gray) - local lu_cpair = cpair(colors.gray, colors.gray) - local dis_colors = cpair(colors.white, colors.lightGray) - local main = Div{parent=root,width=128,height=24,x=x,y=y} local scram = HazardButton{parent=main,x=1,y=1,text="FAC SCRAM",accent=colors.yellow,dis_colors=dis_colors,callback=process.fac_scram,fg_bg=hzd_fg_bg} @@ -55,8 +60,8 @@ local function new_view(root, x, y) local all_ok = IndicatorLight{parent=main,y=5,label="Unit Systems Online",colors=cpair(colors.green,colors.red)} local rad_mon = TriIndicatorLight{parent=main,label="Radiation Monitor",c1=colors.gray,c2=colors.yellow,c3=colors.green} - local ind_mat = IndicatorLight{parent=main,label="Induction Matrix",colors=cpair(colors.green,colors.gray)} - local sps = IndicatorLight{parent=main,label="SPS Connected",colors=cpair(colors.green,colors.gray)} + local ind_mat = IndicatorLight{parent=main,label="Induction Matrix",colors=ind_grn} + local sps = IndicatorLight{parent=main,label="SPS Connected",colors=ind_grn} all_ok.register(facility.ps, "all_sys_ok", all_ok.update) rad_mon.register(facility.ps, "rad_computed_status", rad_mon.update) @@ -66,9 +71,9 @@ local function new_view(root, x, y) main.line_break() local auto_ready = IndicatorLight{parent=main,label="Configured Units Ready",colors=cpair(colors.green,colors.red)} - local auto_act = IndicatorLight{parent=main,label="Process Active",colors=cpair(colors.green,colors.gray)} - local auto_ramp = IndicatorLight{parent=main,label="Process Ramping",colors=cpair(colors.white,colors.gray),flash=true,period=period.BLINK_250_MS} - local auto_sat = IndicatorLight{parent=main,label="Min/Max Burn Rate",colors=cpair(colors.yellow,colors.gray)} + local auto_act = IndicatorLight{parent=main,label="Process Active",colors=ind_grn} + local auto_ramp = IndicatorLight{parent=main,label="Process Ramping",colors=ind_wht,flash=true,period=period.BLINK_250_MS} + local auto_sat = IndicatorLight{parent=main,label="Min/Max Burn Rate",colors=ind_yel} auto_ready.register(facility.ps, "auto_ready", auto_ready.update) auto_act.register(facility.ps, "auto_active", auto_act.update) @@ -77,12 +82,12 @@ local function new_view(root, x, y) main.line_break() - local auto_scram = IndicatorLight{parent=main,label="Automatic SCRAM",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} - local matrix_dc = IndicatorLight{parent=main,label="Matrix Disconnected",colors=cpair(colors.yellow,colors.gray),flash=true,period=period.BLINK_500_MS} - local matrix_fill = IndicatorLight{parent=main,label="Matrix Charge High",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_500_MS} - local unit_crit = IndicatorLight{parent=main,label="Unit Critical Alarm",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} - local fac_rad_h = IndicatorLight{parent=main,label="Facility Radiation High",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} - local gen_fault = IndicatorLight{parent=main,label="Gen. Control Fault",colors=cpair(colors.yellow,colors.gray),flash=true,period=period.BLINK_500_MS} + local auto_scram = IndicatorLight{parent=main,label="Automatic SCRAM",colors=ind_red,flash=true,period=period.BLINK_250_MS} + local matrix_dc = IndicatorLight{parent=main,label="Matrix Disconnected",colors=ind_yel,flash=true,period=period.BLINK_500_MS} + local matrix_fill = IndicatorLight{parent=main,label="Matrix Charge High",colors=ind_red,flash=true,period=period.BLINK_500_MS} + local unit_crit = IndicatorLight{parent=main,label="Unit Critical Alarm",colors=ind_red,flash=true,period=period.BLINK_250_MS} + local fac_rad_h = IndicatorLight{parent=main,label="Facility Radiation High",colors=ind_red,flash=true,period=period.BLINK_250_MS} + local gen_fault = IndicatorLight{parent=main,label="Gen. Control Fault",colors=ind_yel,flash=true,period=period.BLINK_500_MS} auto_scram.register(facility.ps, "auto_scram", auto_scram.update) matrix_dc.register(facility.ps, "as_matrix_dc", matrix_dc.update) @@ -198,12 +203,12 @@ local function new_view(root, x, y) local stat_div = Div{parent=proc,width=22,height=24,x=57,y=6} for i = 1, 4 do - local tag_fg_bg = cpair(colors.gray,colors.white) - local ind_fg_bg = cpair(colors.lightGray,colors.white) + local tag_fg_bg = cpair(colors.gray, colors.white) + local ind_fg_bg = cpair(colors.lightGray, colors.white) local ind_off = colors.lightGray if i <= facility.num_units then - tag_fg_bg = cpair(colors.black,colors.cyan) + tag_fg_bg = cpair(colors.black, colors.cyan) ind_fg_bg = bw_fg_bg ind_off = colors.gray end @@ -307,7 +312,7 @@ local function new_view(root, x, y) local unit = units[i] ---@type ioctl_unit TextBox{parent=waste_status,y=i,text="U"..i.." Waste",width=8,height=1} - local a_waste = IndicatorLight{parent=waste_status,x=10,y=i,label="Auto",colors=cpair(colors.white,colors.gray)} + local a_waste = IndicatorLight{parent=waste_status,x=10,y=i,label="Auto",colors=ind_wht} local waste_m = StateIndicator{parent=waste_status,x=17,y=i,states=style.waste.states_abbrv,value=1,min_width=6} a_waste.register(unit.unit_ps, "U_AutoWaste", a_waste.update) @@ -330,7 +335,7 @@ local function new_view(root, x, y) waste_prod.register(facility.ps, "process_waste_product", waste_prod.set_value) pu_fallback.register(facility.ps, "process_pu_fallback", pu_fallback.set_value) - local fb_active = IndicatorLight{parent=rect,x=2,y=9,label="Fallback Active",colors=cpair(colors.white,colors.gray)} + local fb_active = IndicatorLight{parent=rect,x=2,y=9,label="Fallback Active",colors=ind_wht} fb_active.register(facility.ps, "pu_fallback_active", fb_active.update) diff --git a/coordinator/ui/components/unit_detail.lua b/coordinator/ui/components/unit_detail.lua index 7d7bc92..877feda 100644 --- a/coordinator/ui/components/unit_detail.lua +++ b/coordinator/ui/components/unit_detail.lua @@ -31,6 +31,15 @@ local TEXT_ALIGN = core.TEXT_ALIGN local cpair = core.cpair local border = core.border +local bw_fg_bg = style.bw_fg_bg +local lu_cpair = style.lu_colors +local hzd_fg_bg = style.hzd_fg_bg + +local ind_grn = style.ind_grn +local ind_yel = style.ind_yel +local ind_red = style.ind_red +local ind_wht = style.ind_wht + local period = core.flasher.PERIOD -- create a unit view @@ -50,10 +59,6 @@ local function init(parent, id) TextBox{parent=main,text="Reactor Unit #" .. id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} - local bw_fg_bg = cpair(colors.black, colors.white) - local hzd_fg_bg = cpair(colors.white, colors.gray) - local lu_cpair = cpair(colors.gray, colors.gray) - ----------------------------- -- main stats and core map -- ----------------------------- @@ -140,7 +145,7 @@ local function init(parent, id) -- connectivity local plc_online = IndicatorLight{parent=annunciator,label="PLC Online",colors=cpair(colors.green,colors.red)} - local plc_hbeat = IndicatorLight{parent=annunciator,label="PLC Heartbeat",colors=cpair(colors.white,colors.gray)} + local plc_hbeat = IndicatorLight{parent=annunciator,label="PLC Heartbeat",colors=ind_wht} local rad_mon = TriIndicatorLight{parent=annunciator,label="Radiation Monitor",c1=colors.gray,c2=colors.yellow,c3=colors.green} plc_online.register(u_ps, "PLCOnline", plc_online.update) @@ -150,25 +155,25 @@ local function init(parent, id) annunciator.line_break() -- operating state - local r_active = IndicatorLight{parent=annunciator,label="Active",colors=cpair(colors.green,colors.gray)} - local r_auto = IndicatorLight{parent=annunciator,label="Automatic Control",colors=cpair(colors.white,colors.gray)} + local r_active = IndicatorLight{parent=annunciator,label="Active",colors=ind_grn} + local r_auto = IndicatorLight{parent=annunciator,label="Automatic Control",colors=ind_wht} r_active.register(u_ps, "status", r_active.update) r_auto.register(u_ps, "AutoControl", r_auto.update) -- main unit transient/warning annunciator panel - local r_scram = IndicatorLight{parent=annunciator,label="Reactor SCRAM",colors=cpair(colors.red,colors.gray)} - local r_mscrm = IndicatorLight{parent=annunciator,label="Manual Reactor SCRAM",colors=cpair(colors.red,colors.gray)} - local r_ascrm = IndicatorLight{parent=annunciator,label="Auto Reactor SCRAM",colors=cpair(colors.red,colors.gray)} - local rad_wrn = IndicatorLight{parent=annunciator,label="Radiation Warning",colors=cpair(colors.yellow,colors.gray)} - local r_rtrip = IndicatorLight{parent=annunciator,label="RCP Trip",colors=cpair(colors.red,colors.gray)} - local r_cflow = IndicatorLight{parent=annunciator,label="RCS Flow Low",colors=cpair(colors.yellow,colors.gray)} - local r_clow = IndicatorLight{parent=annunciator,label="Coolant Level Low",colors=cpair(colors.yellow,colors.gray)} - local r_temp = IndicatorLight{parent=annunciator,label="Reactor Temp. High",colors=cpair(colors.red,colors.gray)} - local r_rhdt = IndicatorLight{parent=annunciator,label="Reactor High Delta T",colors=cpair(colors.yellow,colors.gray)} - local r_firl = IndicatorLight{parent=annunciator,label="Fuel Input Rate Low",colors=cpair(colors.yellow,colors.gray)} - local r_wloc = IndicatorLight{parent=annunciator,label="Waste Line Occlusion",colors=cpair(colors.yellow,colors.gray)} - local r_hsrt = IndicatorLight{parent=annunciator,label="Startup Rate High",colors=cpair(colors.yellow,colors.gray)} + local r_scram = IndicatorLight{parent=annunciator,label="Reactor SCRAM",colors=ind_red} + local r_mscrm = IndicatorLight{parent=annunciator,label="Manual Reactor SCRAM",colors=ind_red} + local r_ascrm = IndicatorLight{parent=annunciator,label="Auto Reactor SCRAM",colors=ind_red} + local rad_wrn = IndicatorLight{parent=annunciator,label="Radiation Warning",colors=ind_yel} + local r_rtrip = IndicatorLight{parent=annunciator,label="RCP Trip",colors=ind_red} + local r_cflow = IndicatorLight{parent=annunciator,label="RCS Flow Low",colors=ind_yel} + local r_clow = IndicatorLight{parent=annunciator,label="Coolant Level Low",colors=ind_yel} + local r_temp = IndicatorLight{parent=annunciator,label="Reactor Temp. High",colors=ind_red} + local r_rhdt = IndicatorLight{parent=annunciator,label="Reactor High Delta T",colors=ind_yel} + local r_firl = IndicatorLight{parent=annunciator,label="Fuel Input Rate Low",colors=ind_yel} + local r_wloc = IndicatorLight{parent=annunciator,label="Waste Line Occlusion",colors=ind_yel} + local r_hsrt = IndicatorLight{parent=annunciator,label="Startup Rate High",colors=ind_yel} r_scram.register(u_ps, "ReactorSCRAM", r_scram.update) r_mscrm.register(u_ps, "ManualReactorSCRAM", r_mscrm.update) @@ -189,15 +194,15 @@ local function init(parent, id) local rps = Rectangle{parent=main,border=border(1,colors.cyan,true),thin=true,width=33,height=12,x=46,y=9} local rps_annunc = Div{parent=rps,width=31,height=10,x=2,y=1} - local rps_trp = IndicatorLight{parent=rps_annunc,label="RPS Trip",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} - local rps_dmg = IndicatorLight{parent=rps_annunc,label="Damage Level High",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} - local rps_exh = IndicatorLight{parent=rps_annunc,label="Excess Heated Coolant",colors=cpair(colors.yellow,colors.gray)} - local rps_exw = IndicatorLight{parent=rps_annunc,label="Excess Waste",colors=cpair(colors.yellow,colors.gray)} - local rps_tmp = IndicatorLight{parent=rps_annunc,label="Core Temperature High",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} - local rps_nof = IndicatorLight{parent=rps_annunc,label="No Fuel",colors=cpair(colors.yellow,colors.gray)} - local rps_loc = IndicatorLight{parent=rps_annunc,label="Coolant Level Low Low",colors=cpair(colors.yellow,colors.gray)} - local rps_flt = IndicatorLight{parent=rps_annunc,label="PPM Fault",colors=cpair(colors.yellow,colors.gray),flash=true,period=period.BLINK_500_MS} - local rps_tmo = IndicatorLight{parent=rps_annunc,label="Connection Timeout",colors=cpair(colors.yellow,colors.gray),flash=true,period=period.BLINK_500_MS} + local rps_trp = IndicatorLight{parent=rps_annunc,label="RPS Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS} + local rps_dmg = IndicatorLight{parent=rps_annunc,label="Damage Level High",colors=ind_red,flash=true,period=period.BLINK_250_MS} + local rps_exh = IndicatorLight{parent=rps_annunc,label="Excess Heated Coolant",colors=ind_yel} + local rps_exw = IndicatorLight{parent=rps_annunc,label="Excess Waste",colors=ind_yel} + local rps_tmp = IndicatorLight{parent=rps_annunc,label="Core Temperature High",colors=ind_red,flash=true,period=period.BLINK_250_MS} + local rps_nof = IndicatorLight{parent=rps_annunc,label="No Fuel",colors=ind_yel} + local rps_loc = IndicatorLight{parent=rps_annunc,label="Coolant Level Low Low",colors=ind_yel} + local rps_flt = IndicatorLight{parent=rps_annunc,label="PPM Fault",colors=ind_yel,flash=true,period=period.BLINK_500_MS} + local rps_tmo = IndicatorLight{parent=rps_annunc,label="Connection Timeout",colors=ind_yel,flash=true,period=period.BLINK_500_MS} local rps_sfl = IndicatorLight{parent=rps_annunc,label="System Failure",colors=cpair(colors.orange,colors.gray),flash=true,period=period.BLINK_500_MS} rps_trp.register(u_ps, "rps_tripped", rps_trp.update) @@ -218,12 +223,12 @@ local function init(parent, id) local rcs_annunc = Div{parent=rcs,width=27,height=22,x=3,y=1} local rcs_tags = Div{parent=rcs,width=2,height=16,x=1,y=7} - local c_flt = IndicatorLight{parent=rcs_annunc,label="RCS Hardware Fault",colors=cpair(colors.yellow,colors.gray)} + local c_flt = IndicatorLight{parent=rcs_annunc,label="RCS Hardware Fault",colors=ind_yel} local c_emg = TriIndicatorLight{parent=rcs_annunc,label="Emergency Coolant",c1=colors.gray,c2=colors.white,c3=colors.green} - local c_cfm = IndicatorLight{parent=rcs_annunc,label="Coolant Feed Mismatch",colors=cpair(colors.yellow,colors.gray)} - local c_brm = IndicatorLight{parent=rcs_annunc,label="Boil Rate Mismatch",colors=cpair(colors.yellow,colors.gray)} - local c_sfm = IndicatorLight{parent=rcs_annunc,label="Steam Feed Mismatch",colors=cpair(colors.yellow,colors.gray)} - local c_mwrf = IndicatorLight{parent=rcs_annunc,label="Max Water Return Feed",colors=cpair(colors.yellow,colors.gray)} + local c_cfm = IndicatorLight{parent=rcs_annunc,label="Coolant Feed Mismatch",colors=ind_yel} + local c_brm = IndicatorLight{parent=rcs_annunc,label="Boil Rate Mismatch",colors=ind_yel} + local c_sfm = IndicatorLight{parent=rcs_annunc,label="Steam Feed Mismatch",colors=ind_yel} + local c_mwrf = IndicatorLight{parent=rcs_annunc,label="Max Water Return Feed",colors=ind_yel} c_flt.register(u_ps, "RCSFault", c_flt.update) c_emg.register(u_ps, "EmergencyCoolant", c_emg.update) @@ -246,11 +251,11 @@ local function init(parent, id) if unit.num_boilers > 0 then TextBox{parent=rcs_tags,x=1,text="B1",width=2,height=1,fg_bg=bw_fg_bg} - local b1_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=cpair(colors.red,colors.gray)} + local b1_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red} b1_wll.register(b_ps[1], "WasterLevelLow", b1_wll.update) TextBox{parent=rcs_tags,text="B1",width=2,height=1,fg_bg=bw_fg_bg} - local b1_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=cpair(colors.yellow,colors.gray)} + local b1_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel} b1_hr.register(b_ps[1], "HeatingRateLow", b1_hr.update) end if unit.num_boilers > 1 then @@ -262,11 +267,11 @@ local function init(parent, id) end TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=bw_fg_bg} - local b2_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=cpair(colors.red,colors.gray)} + local b2_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red} b2_wll.register(b_ps[2], "WasterLevelLow", b2_wll.update) TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=bw_fg_bg} - local b2_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=cpair(colors.yellow,colors.gray)} + local b2_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel} b2_hr.register(b_ps[2], "HeatingRateLow", b2_hr.update) end @@ -279,15 +284,15 @@ local function init(parent, id) t1_sdo.register(t_ps[1], "SteamDumpOpen", t1_sdo.update) TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=bw_fg_bg} - local t1_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} + local t1_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red} t1_tos.register(t_ps[1], "TurbineOverSpeed", t1_tos.update) TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=bw_fg_bg} - local t1_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=cpair(colors.yellow,colors.gray),flash=true,period=period.BLINK_250_MS} + local t1_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS} t1_gtrp.register(t_ps[1], "GeneratorTrip", t1_gtrp.update) TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=bw_fg_bg} - local t1_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} + local t1_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS} t1_trp.register(t_ps[1], "TurbineTrip", t1_trp.update) if unit.num_turbines > 1 then @@ -300,15 +305,15 @@ local function init(parent, id) t2_sdo.register(t_ps[2], "SteamDumpOpen", t2_sdo.update) TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=bw_fg_bg} - local t2_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} + local t2_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red} t2_tos.register(t_ps[2], "TurbineOverSpeed", t2_tos.update) TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=bw_fg_bg} - local t2_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=cpair(colors.yellow,colors.gray),flash=true,period=period.BLINK_250_MS} + local t2_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS} t2_gtrp.register(t_ps[2], "GeneratorTrip", t2_gtrp.update) TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=bw_fg_bg} - local t2_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} + local t2_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS} t2_trp.register(t_ps[2], "TurbineTrip", t2_trp.update) end @@ -320,15 +325,15 @@ local function init(parent, id) t3_sdo.register(t_ps[3], "SteamDumpOpen", t3_sdo.update) TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=bw_fg_bg} - local t3_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} + local t3_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red} t3_tos.register(t_ps[3], "TurbineOverSpeed", t3_tos.update) TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=bw_fg_bg} - local t3_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=cpair(colors.yellow,colors.gray),flash=true,period=period.BLINK_250_MS} + local t3_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS} t3_gtrp.register(t_ps[3], "GeneratorTrip", t3_gtrp.update) TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=bw_fg_bg} - local t3_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} + local t3_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS} t3_trp.register(t_ps[3], "TurbineTrip", t3_trp.update) end @@ -486,8 +491,8 @@ local function init(parent, id) auto_div.line_break() - local a_rdy = IndicatorLight{parent=auto_div,label="Ready",x=2,colors=cpair(colors.green,colors.gray)} - local a_stb = IndicatorLight{parent=auto_div,label="Standby",x=2,colors=cpair(colors.white,colors.gray),flash=true,period=period.BLINK_1000_MS} + local a_rdy = IndicatorLight{parent=auto_div,label="Ready",x=2,colors=ind_grn} + local a_stb = IndicatorLight{parent=auto_div,label="Standby",x=2,colors=ind_wht,flash=true,period=period.BLINK_1000_MS} a_rdy.register(u_ps, "U_AutoReady", a_rdy.update) diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index 382a277..efef2dc 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -4,6 +4,8 @@ local util = require("scada-common.util") +local style = require("coordinator.ui.style") + local core = require("graphics.core") local Div = require("graphics.elements.div") @@ -25,6 +27,13 @@ local cpair = core.cpair local border = core.border local pipe = core.pipe +local bw_fg_bg = style.bw_fg_bg +local text_c = style.text_colors +local lu_c = style.lu_colors + +local ind_grn = style.ind_grn +local ind_wht = style.ind_wht + -- make a new unit flow window ---@param parent graphics_element parent ---@param x integer top left x @@ -50,18 +59,18 @@ local function make(parent, x, y, unit) -- bounding box div local root = Div{parent=parent,x=x,y=y,width=114,height=height} - local text_fg_bg = cpair(colors.black, colors.white) - local lu_col = cpair(colors.gray, colors.gray) + local lg_gray = cpair(colors.lightGray, colors.gray) + local wh_gray = cpair(colors.white, colors.gray) ------------------ -- COOLING LOOP -- ------------------ - local reactor = Rectangle{parent=root,x=1,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=cpair(colors.white,colors.gray)} + local reactor = Rectangle{parent=root,x=1,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} - TextBox{parent=root,x=4,y=5,text="\x19",width=1,height=1,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} + TextBox{parent=root,x=4,y=5,text="\x19",width=1,height=1,fg_bg=lg_gray} local rc_pipes = {} @@ -76,25 +85,25 @@ local function make(parent, x, y, unit) PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=colors.lightGray} - local hc_rate = DataIndicator{parent=root,x=22,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} - local cc_rate = DataIndicator{parent=root,x=22,y=5,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} + local hc_rate = DataIndicator{parent=root,x=22,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local cc_rate = DataIndicator{parent=root,x=22,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local boiler = Rectangle{parent=root,x=40,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=cpair(colors.white,colors.gray)} + local boiler = Rectangle{parent=root,x=40,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=boiler,y=3,text="BOILERS",alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=root,x=40,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} - TextBox{parent=root,x=58,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=root,x=40,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} + TextBox{parent=root,x=58,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} - local wt_rate = DataIndicator{parent=root,x=61,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} - local st_rate = DataIndicator{parent=root,x=61,y=5,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=text_fg_bg} + local wt_rate = DataIndicator{parent=root,x=61,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local st_rate = DataIndicator{parent=root,x=61,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local turbine = Rectangle{parent=root,x=79,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=cpair(colors.white,colors.gray)} + local turbine = Rectangle{parent=root,x=79,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=root,x=79,y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=root,x=79,y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=lg_gray} local function _relief(rx, ry, name) - TextBox{parent=root,x=rx,y=ry,text="\x10\x11\x7f",fg_bg=cpair(colors.black,colors.lightGray),width=3,height=1} + TextBox{parent=root,x=rx,y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1} local conn = TriIndicatorLight{parent=root,x=rx+4,y=ry,label=name,c1=colors.gray,c2=colors.yellow,c3=colors.red} end @@ -126,23 +135,23 @@ local function make(parent, x, y, unit) PipeNetwork{parent=waste,x=2,y=1,pipes=waste_pipes,bg=colors.lightGray} local function _valve(vx, vy, n) - TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} - local conn = IndicatorLight{parent=waste,x=vx-3,y=vy+1,label=v_names[n],colors=cpair(colors.green,colors.gray)} - local state = IndicatorLight{parent=waste,x=vx-3,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)} + TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1} + local conn = IndicatorLight{parent=waste,x=vx-3,y=vy+1,label=v_names[n],colors=ind_grn} + local state = IndicatorLight{parent=waste,x=vx-3,y=vy+2,label="STATE",colors=ind_wht} end local function _machine(mx, my, name) local l = string.len(name) + 2 - TextBox{parent=waste,x=mx,y=my,text=util.strrep("\x8f",l),alignment=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.lightGray,colors.gray),width=l,height=1} - TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.white,colors.gray),width=l,height=1} + TextBox{parent=waste,x=mx,y=my,text=util.strrep("\x8f",l),alignment=TEXT_ALIGN.CENTER,fg_bg=lg_gray,width=l,height=1} + TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=TEXT_ALIGN.CENTER,fg_bg=wh_gray,width=l,height=1} end - local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%7.2f",value=1234.56,width=12,fg_bg=text_fg_bg} - local pu_rate = DataIndicator{parent=waste,x=70,y=3,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} - local po_rate = DataIndicator{parent=waste,x=45,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} - local popl_rate = DataIndicator{parent=waste,x=70,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} - local poam_rate = DataIndicator{parent=waste,x=70,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} - local spent_rate = DataIndicator{parent=waste,x=99,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=text_fg_bg} + local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=1234.56,width=12,fg_bg=bw_fg_bg} + local pu_rate = DataIndicator{parent=waste,x=70,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local po_rate = DataIndicator{parent=waste,x=45,y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local popl_rate = DataIndicator{parent=waste,x=70,y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local poam_rate = DataIndicator{parent=waste,x=70,y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local spent_rate = DataIndicator{parent=waste,x=99,y=4,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} _valve(18, 2, 1) _valve(18, 6, 2) @@ -154,13 +163,13 @@ local function make(parent, x, y, unit) _machine(83, 4, "PRC [Po] \x1a"); _machine(94, 6, "SPENT WASTE \x1b") - TextBox{parent=waste,x=25,y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=cpair(colors.white,colors.gray)} - local sna_po = Rectangle{parent=waste,x=25,y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=cpair(colors.black,colors.white)} - local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=cpair(colors.green,colors.gray)} - local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_col,label="CNT",unit="",format="%2d",value=99,width=7,fg_bg=text_fg_bg} - local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_col,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} - local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_col,label="MAX ",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} - local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_col,label="IN ",unit="mB/t",format="%7.2f",value=1000,width=17,fg_bg=text_fg_bg} + TextBox{parent=waste,x=25,y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} + local sna_po = Rectangle{parent=waste,x=25,y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=bw_fg_bg} + local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn} + local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c,label="CNT",unit="",format="%2d",value=99,width=7} + local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17} + local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c,label="MAX ",unit="mB/t",format="%7.2f",value=1000,width=17} + local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c,label="IN ",unit="mB/t",format="%7.2f",value=1000,width=17} return root end diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 5d6328a..26a1617 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -28,6 +28,10 @@ local cpair = core.cpair local border = core.border local pipe = core.pipe +local bw_fg_bg = style.bw_fg_bg +local text_col = style.text_colors +local lu_col = style.lu_colors + -- create new flow view ---@param main graphics_element main displaybox local function init(main) @@ -43,10 +47,6 @@ local function init(main) local po_pipes = {} - local bw_fg_bg = cpair(colors.black, colors.white) - local text_col = cpair(colors.black, colors.lightGray) - local lu_col = cpair(colors.gray, colors.gray) - local water_pipes = {} local fac_tanks = true @@ -81,7 +81,6 @@ local function init(main) local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg} TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label} local ccool = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16} - ccool.update(1) end PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray} diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua index 947fdb5..837b879 100644 --- a/coordinator/ui/style.lua +++ b/coordinator/ui/style.lua @@ -68,7 +68,20 @@ style.colors = { -- { c = colors.brown, hex = 0x7f664c } } --- MAIN LAYOUT -- +-- COMMON COLOR PAIRS -- + +style.bw_fg_bg = cpair(colors.black, colors.white) +style.text_colors = cpair(colors.black, colors.lightGray) +style.lu_colors = cpair(colors.gray, colors.gray) +style.hzd_fg_bg = cpair(colors.white, colors.gray) +style.dis_colors = cpair(colors.white, colors.lightGray) + +style.ind_grn = cpair(colors.green, colors.gray) +style.ind_yel = cpair(colors.yellow, colors.gray) +style.ind_red = cpair(colors.red, colors.gray) +style.ind_wht = cpair(colors.white, colors.gray) + +-- UI COMPONENTS -- style.reactor = { -- reactor states From d17e2b8321d5feb5220030dc9ac7916a84253e32 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 19 Aug 2023 13:38:05 -0400 Subject: [PATCH 06/22] #232 completed display of flow/dynamic tank/sps, dynamically sized --- coordinator/coordinator.lua | 16 +- coordinator/iocontrol.lua | 25 ++- coordinator/startup.lua | 4 +- coordinator/ui/components/unit_flow.lua | 142 +++++++------ coordinator/ui/layout/flow_view.lua | 271 +++++++++++++++++++++--- supervisor/config.lua | 30 ++- supervisor/facility.lua | 4 +- supervisor/session/svsessions.lua | 2 +- supervisor/startup.lua | 8 +- supervisor/supervisor.lua | 11 +- 10 files changed, 387 insertions(+), 126 deletions(-) diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index b5c09e6..36636bc 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -243,12 +243,13 @@ end ---@nodiscard ---@param version string coordinator version ---@param nic nic network interface device +---@param num_units integer number of configured units for number of monitors, checked against SV ---@param crd_channel integer port of configured supervisor ---@param svr_channel integer listening port for supervisor replys ---@param pkt_channel integer listening port for pocket API ---@param range integer trusted device connection range ---@param sv_watchdog watchdog -function coordinator.comms(version, nic, crd_channel, svr_channel, pkt_channel, range, sv_watchdog) +function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pkt_channel, range, sv_watchdog) local self = { sv_linked = false, sv_addr = comms.BROADCAST, @@ -707,21 +708,16 @@ function coordinator.comms(version, nic, crd_channel, svr_channel, pkt_channel, -- reset to disconnected before validating iocontrol.fp_link_state(types.PANEL_LINK_STATE.DISCONNECTED) - if type(config) == "table" and #config > 1 then + if type(config) == "table" and #config == 2 then -- get configuration ---@class facility_conf local conf = { num_units = config[1], ---@type integer - defs = {} -- boilers and turbines + cooling = config[2] ---@type sv_cooling_conf } - if (#config - 1) == (conf.num_units * 2) then - -- record sequence of pairs of [#boilers, #turbines] per unit - for i = 2, #config do - table.insert(conf.defs, config[i]) - end - + if conf.num_units == num_units then -- init io controller iocontrol.init(conf, public) @@ -733,7 +729,7 @@ function coordinator.comms(version, nic, crd_channel, svr_channel, pkt_channel, iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED) else self.sv_config_err = true - log.warning("invalid supervisor configuration definitions received, establish failed") + log.warning("supervisor config's number of units don't match coordinator's config, establish failed") end else log.debug("invalid supervisor configuration table received, establish failed") diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index ce6d667..d2ee8ef 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -38,9 +38,7 @@ local function __generic_ack(success) end ---@param comms_v string comms version function iocontrol.init_fp(firmware_v, comms_v) ---@class ioctl_front_panel - io.fp = { - ps = psil.create() - } + io.fp = { ps = psil.create() } io.fp.ps.publish("version", firmware_v) io.fp.ps.publish("comms_version", comms_v) @@ -52,7 +50,9 @@ end function iocontrol.init(conf, comms) ---@class ioctl_facility io.facility = { - num_units = conf.num_units, ---@type integer + num_units = conf.num_units, + tank_mode = conf.cooling.fac_tank_mode, + tank_defs = conf.cooling.fac_tank_list, all_sys_ok = false, rtu_count = 0, @@ -116,6 +116,7 @@ function iocontrol.init(conf, comms) num_boilers = 0, num_turbines = 0, num_snas = 0, + has_tank = conf.cooling.r_cool[i].TANK, control_state = false, burn_rate_cmd = 0.0, @@ -191,14 +192,14 @@ function iocontrol.init(conf, comms) } -- create boiler tables - for _ = 1, conf.defs[(i * 2) - 1] do + for _ = 1, conf.cooling.r_cool[i].BOILERS do local data = {} ---@type boilerv_session_db table.insert(entry.boiler_ps_tbl, psil.create()) table.insert(entry.boiler_data_tbl, data) end -- create turbine tables - for _ = 1, conf.defs[i * 2] do + for _ = 1, conf.cooling.r_cool[i].TURBINES do local data = {} ---@type turbinev_session_db table.insert(entry.turbine_ps_tbl, psil.create()) table.insert(entry.turbine_data_tbl, data) @@ -210,6 +211,18 @@ function iocontrol.init(conf, comms) table.insert(io.units, entry) end + -- on facility tank mode 0, setup tank list to match unit TANK option + if io.facility.tank_mode == 0 then + for i = 1, #io.units do + io.facility.tank_defs[i] = util.trinary(conf.cooling.r_cool[i].TANK, 1, 0) + end + -- on other facility modes, overwrite unit TANK option with facility tank list + else + for i = 1, #io.units do + io.units[i].has_tank = conf.cooling.fac_tank_list[i] > 0 + end + end + -- pass IO control here since it can't be require'd due to a require loop process.init(io, comms) end diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 9951d20..f22326b 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -162,8 +162,8 @@ local function main() -- create network interface then setup comms local nic = network.nic(modem) - local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, config.CRD_CHANNEL, config.SVR_CHANNEL, - config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog) + local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, config.NUM_UNITS, config.CRD_CHANNEL, + config.SVR_CHANNEL, config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog) log.debug("startup> comms init") log_comms("comms initialized") diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index efef2dc..2c8db41 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -38,8 +38,9 @@ local ind_wht = style.ind_wht ---@param parent graphics_element parent ---@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, unit) +local function make(parent, x, y, wide, unit) local height = 16 local v_start = 1 + ((unit.unit_id - 1) * 4) @@ -56,8 +57,10 @@ local function make(parent, x, y, unit) assert(parent.get_height() >= (y + height), "flow display not of sufficient vertical resolution (add an additional row of monitors) " .. y .. "," .. parent.get_height()) + local function _wide(a, b) return util.trinary(wide, a, b) end + -- bounding box div - local root = Div{parent=parent,x=x,y=y,width=114,height=height} + local root = Div{parent=parent,x=x,y=y,width=_wide(136, 114),height=height} local lg_gray = cpair(colors.lightGray, colors.gray) local wh_gray = cpair(colors.white, colors.gray) @@ -70,46 +73,62 @@ local function make(parent, x, y, unit) TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} - TextBox{parent=root,x=4,y=5,text="\x19",width=1,height=1,fg_bg=lg_gray} + TextBox{parent=root,x=3,y=5,text="\x19",width=1,height=1,fg_bg=lg_gray} local rc_pipes = {} - table.insert(rc_pipes, pipe(0, 1, 19, 1, colors.lightBlue, true)) - table.insert(rc_pipes, pipe(0, 3, 19, 3, colors.orange, true)) - table.insert(rc_pipes, pipe(39, 1, 58, 1, colors.blue, true)) - table.insert(rc_pipes, pipe(39, 3, 58, 3, colors.white, true)) + local emc_x = 42 -- emergency coolant connection x point - table.insert(rc_pipes, pipe(78, 0, 83, 0, colors.white, true)) - table.insert(rc_pipes, pipe(78, 2, 83, 2, colors.white, true)) - table.insert(rc_pipes, pipe(78, 4, 83, 4, colors.white, true)) + 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)) + end + + local prv_yo = math.max(3 - unit.num_turbines, 0) + for i = 1, unit.num_turbines do + local py = 2 * (i - 1) + prv_yo + table.insert(rc_pipes, pipe(_wide(92, 78), py, _wide(104, 83), py, colors.white, true)) + end PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=colors.lightGray} - local hc_rate = DataIndicator{parent=root,x=22,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local cc_rate = DataIndicator{parent=root,x=22,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + if unit.num_boilers > 0 then + local hc_rate = DataIndicator{parent=root,x=_wide(25,22),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local cc_rate = DataIndicator{parent=root,x=_wide(25,22),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local boiler = Rectangle{parent=root,x=40,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} - TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=boiler,y=3,text="BOILERS",alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=root,x=40,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} - TextBox{parent=root,x=58,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} + local boiler = Rectangle{parent=root,x=_wide(47,40),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} + TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=boiler,y=3,text="BOILERS",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=root,x=_wide(47,40),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} + TextBox{parent=root,x=_wide(65,58),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} - local wt_rate = DataIndicator{parent=root,x=61,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local st_rate = DataIndicator{parent=root,x=61,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - - local turbine = Rectangle{parent=root,x=79,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} - TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=root,x=79,y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=lg_gray} - - local function _relief(rx, ry, name) - TextBox{parent=root,x=rx,y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1} - local conn = TriIndicatorLight{parent=root,x=rx+4,y=ry,label=name,c1=colors.gray,c2=colors.yellow,c3=colors.red} + local wt_rate = DataIndicator{parent=root,x=_wide(71,61),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local st_rate = DataIndicator{parent=root,x=_wide(71,61),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + else + local wt_rate = DataIndicator{parent=root,x=28,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local st_rate = DataIndicator{parent=root,x=28,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} end - _relief(103, 1, v_names[5]) - _relief(103, 3, v_names[6]) - _relief(103, 5, v_names[7]) + local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} + TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=root,x=_wide(93,79),y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=lg_gray} + + for i = 1, unit.num_turbines do + local ry = 1 + (2 * (i - 1)) + prv_yo + TextBox{parent=root,x=_wide(125,103),y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1} + local state = TriIndicatorLight{parent=root,x=_wide(129,107),y=ry,label=v_names[i+4],c1=colors.gray,c2=colors.yellow,c3=colors.red} + end ---------------------- -- WASTE PROCESSING -- @@ -118,21 +137,24 @@ local function make(parent, x, y, unit) local waste = Div{parent=root,x=3,y=6} local waste_pipes = { - pipe(0, 0, 16, 1, colors.brown, true), - pipe(12, 1, 16, 5, colors.brown, true), - pipe(18, 1, 44, 1, colors.brown, true), - pipe(18, 5, 23, 5, colors.brown, true), - pipe(52, 1, 80, 1, colors.green, true), - pipe(42, 4, 60, 4, colors.cyan, true), - pipe(56, 4, 60, 8, colors.cyan, true), - pipe(62, 4, 80, 4, colors.cyan, true), - pipe(62, 8, 110, 8, colors.cyan, true), - pipe(93, 1, 94, 3, colors.black, true, true), - pipe(93, 4, 109, 6, colors.black, true, true), - pipe(109, 6, 107, 6, colors.black, true, true) + pipe(0, 0, _wide(19, 16), 1, colors.brown, true), + pipe(_wide(14, 13), 1, _wide(19, 17), 5, colors.brown, true), + pipe(_wide(22, 19), 1, _wide(49, 45), 1, colors.brown, true), + pipe(_wide(22, 19), 5, _wide(28, 24), 5, colors.brown, true), + + pipe(_wide(64, 53), 1, _wide(95, 81), 1, colors.green, true), + + pipe(_wide(48, 43), 4, _wide(71, 61), 4, colors.cyan, true), + pipe(_wide(66, 57), 4, _wide(71, 61), 8, colors.cyan, true), + pipe(_wide(74, 63), 4, _wide(95, 81), 4, colors.cyan, true), + pipe(_wide(74, 63), 8, _wide(133, 111), 8, colors.cyan, true), + + pipe(_wide(108, 94), 1, _wide(132, 110), 6, colors.black, true, true), + pipe(_wide(108, 94), 4, _wide(111, 95), 1, colors.black, true, true), + pipe(_wide(132, 110), 6, _wide(130, 108), 6, colors.black, true, true) } - PipeNetwork{parent=waste,x=2,y=1,pipes=waste_pipes,bg=colors.lightGray} + PipeNetwork{parent=waste,x=1,y=1,pipes=waste_pipes,bg=colors.lightGray} local function _valve(vx, vy, n) TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1} @@ -142,29 +164,29 @@ local function make(parent, x, y, unit) local function _machine(mx, my, name) local l = string.len(name) + 2 - TextBox{parent=waste,x=mx,y=my,text=util.strrep("\x8f",l),alignment=TEXT_ALIGN.CENTER,fg_bg=lg_gray,width=l,height=1} + TextBox{parent=waste,x=mx,y=my,text=string.rep("\x8f",l),alignment=TEXT_ALIGN.CENTER,fg_bg=lg_gray,width=l,height=1} TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=TEXT_ALIGN.CENTER,fg_bg=wh_gray,width=l,height=1} end local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=1234.56,width=12,fg_bg=bw_fg_bg} - local pu_rate = DataIndicator{parent=waste,x=70,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local po_rate = DataIndicator{parent=waste,x=45,y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local popl_rate = DataIndicator{parent=waste,x=70,y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local poam_rate = DataIndicator{parent=waste,x=70,y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local spent_rate = DataIndicator{parent=waste,x=99,y=4,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local pu_rate = DataIndicator{parent=waste,x=_wide(82,70),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local spent_rate = DataIndicator{parent=waste,x=_wide(117,99),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - _valve(18, 2, 1) - _valve(18, 6, 2) - _valve(62, 5, 3) - _valve(62, 9, 4) + _valve(_wide(21, 18), 2, 1) + _valve(_wide(21, 18), 6, 2) + _valve(_wide(73, 62), 5, 3) + _valve(_wide(73, 62), 9, 4) - _machine(45, 1, "CENTRIFUGE \x1a"); - _machine(83, 1, "PRC [Pu] \x1a"); - _machine(83, 4, "PRC [Po] \x1a"); - _machine(94, 6, "SPENT WASTE \x1b") + _machine(_wide(51, 45), 1, "CENTRIFUGE \x1a"); + _machine(_wide(97, 83), 1, "PRC [Pu] \x1a"); + _machine(_wide(97, 83), 4, "PRC [Po] \x1a"); + _machine(_wide(116, 94), 6, "SPENT WASTE \x1b") - TextBox{parent=waste,x=25,y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} - local sna_po = Rectangle{parent=waste,x=25,y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=bw_fg_bg} + TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} + local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=bw_fg_bg} local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn} local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c,label="CNT",unit="",format="%2d",value=99,width=7} local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17} diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 26a1617..f309b63 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -38,6 +38,9 @@ local function init(main) local facility = iocontrol.get_db().facility local units = iocontrol.get_db().units + local tank_defs = facility.tank_defs + local tank_draw = { table.unpack(tank_defs) } + -- window header message local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} -- max length example: "01:23:45 AM - Wednesday, September 28 2022" @@ -46,52 +49,266 @@ local function init(main) datetime.register(facility.ps, "date_time", datetime.set_value) local po_pipes = {} - local water_pipes = {} - local fac_tanks = true + -- get the y offset for this unit index + local function y_ofs(idx) return ((idx - 1) * 20) end - for i = 1, 4 do - local y = ((i - 1) * 20) - table.insert(water_pipes, pipe(2, y, 2, y + 5, colors.blue, true)) - table.insert(water_pipes, pipe(2, y, 82, y, colors.blue, true)) - table.insert(water_pipes, pipe(82, y, 82, y + 2, colors.blue, true)) - if fac_tanks and i > 1 then table.insert(water_pipes, pipe(21, y - 19, 21, y, colors.blue, true)) end + local function calc_fdef(start_idx, end_idx) + local first, last = 4, 0 + for i = start_idx, end_idx do + if tank_defs[i] == 2 then + last = i + if i < first then first = i end + end + end + return first, last end - PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=colors.lightGray} + if facility.tank_mode == 0 or facility.tank_mode == 8 then + -- (0) tanks belong to reactor units OR (8) 4 total facility tanks (A B C D) + 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 + 5, colors.blue, true)) + table.insert(water_pipes, pipe(2, y, 21, y, colors.blue, true)) + + local u = units[i] ---@type ioctl_unit + local x = util.trinary(u.num_boilers == 0, 45, 84) + table.insert(water_pipes, pipe(21, y, x, y + 2, colors.blue, true, true)) + end + end + else + -- setup connections for units with emergency coolant, always the same + for i = 1, #tank_defs do + if tank_defs[i] > 0 then + local y = y_ofs(i) + + if tank_defs[i] == 2 then + table.insert(water_pipes, pipe(1, y, 21, y, colors.blue, true)) + else + table.insert(water_pipes, pipe(2, y, 2, y + 5, colors.blue, true)) + table.insert(water_pipes, pipe(2, y, 21, y, colors.blue, true)) + end + + local u = units[i] ---@type ioctl_unit + local x = util.trinary(u.num_boilers == 0, 45, 84) + table.insert(water_pipes, pipe(21, y, x, y + 2, colors.blue, true, true)) + end + end + + if facility.tank_mode == 1 then + -- (1) 1 total facility tank (A A A A) + local first_fdef, last_fdef = calc_fdef(1, #tank_defs) + + 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 + 6, colors.blue, true)) + elseif i > first_fdef then + if tank_defs[i] == 2 then tank_draw[i] = 0 end + if i == last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + elseif i < last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + end + end + end + elseif facility.tank_mode == 2 then + -- (2) 2 total facility tanks (A A A B) + local first_fdef, last_fdef = calc_fdef(1, math.min(3, #tank_defs)) + + for i = 1, #tank_defs do + local y = y_ofs(i) + if i == 4 then + if tank_defs[i] == 2 then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + end + elseif i == first_fdef then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + elseif i > first_fdef then + if tank_defs[i] == 2 then tank_draw[i] = 0 end + if i == last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + elseif i < last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + end + end + end + elseif facility.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 + table.insert(water_pipes, pipe(0, y_ofs(a), 1, y_ofs(a) + 6, colors.blue, true)) + if tank_defs[b] == 2 then + table.insert(water_pipes, pipe(0, y_ofs(b) - 13, 1, y_ofs(b), colors.blue, true)) + tank_draw[b] = 0 + end + elseif tank_defs[b] == 2 then + table.insert(water_pipes, pipe(0, y_ofs(b), 1, y_ofs(b) + 6, colors.blue, true)) + end + end + elseif facility.tank_mode == 4 then + -- (4) 2 total facility tanks (A B B B) + local first_fdef, last_fdef = calc_fdef(2, #tank_defs) + + for i = 1, #tank_defs do + local y = y_ofs(i) + if i == 1 then + if tank_defs[i] == 2 then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + end + elseif i == first_fdef then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + elseif i > first_fdef then + if tank_defs[i] == 2 then tank_draw[i] = 0 end + if i == last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + elseif i < last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + end + end + end + elseif facility.tank_mode == 5 then + -- (5) 3 total facility tanks (A A B C) + local first_fdef, last_fdef = calc_fdef(1, math.min(2, #tank_defs)) + + for i = 1, #tank_defs do + local y = y_ofs(i) + if i == 3 or i == 4 then + if tank_defs[i] == 2 then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + end + elseif i == first_fdef then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + elseif i > first_fdef then + if tank_defs[i] == 2 then tank_draw[i] = 0 end + if i == last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + elseif i < last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + end + end + end + elseif facility.tank_mode == 6 then + -- (6) 3 total facility tanks (A B B C) + local first_fdef, last_fdef = calc_fdef(2, math.min(3, #tank_defs)) + + for i = 1, #tank_defs do + local y = y_ofs(i) + if i == 1 or i == 4 then + if tank_defs[i] == 2 then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + end + elseif i == first_fdef then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + elseif i > first_fdef then + if tank_defs[i] == 2 then tank_draw[i] = 0 end + if i == last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + elseif i < last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + end + end + end + elseif facility.tank_mode == 7 then + -- (7) 3 total facility tanks (A B C C) + local first_fdef, last_fdef = calc_fdef(3, #tank_defs) + + for i = 1, #tank_defs do + local y = y_ofs(i) + if i == 1 or i == 2 then + if tank_defs[i] == 2 then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + end + elseif i == first_fdef then + table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + elseif i > first_fdef then + if tank_defs[i] == 2 then tank_draw[i] = 0 end + if i == last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + elseif i < last_fdef then + table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + end + end + end + end + end + + local flow_x = 3 + if #water_pipes > 0 then + flow_x = 25 + PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=colors.lightGray} + end for i = 1, facility.num_units do - local y_offset = ((i - 1) * 20) - unit_flow(main, 25, 5 + y_offset, units[i]) + local y_offset = y_ofs(i) + unit_flow(main, flow_x, 5 + y_offset, #water_pipes == 0, units[i]) table.insert(po_pipes, pipe(0, 3 + y_offset, 8, 0, colors.cyan, true, true)) - - local vx, vy = 11, 3 + y_offset - TextBox{parent=main,x=vx,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} - local conn = IndicatorLight{parent=main,x=vx-3,y=vy+1,label=util.sprintf("PV%02d", i + 13),colors=cpair(colors.green,colors.gray)} - local state = IndicatorLight{parent=main,x=vx-3,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)} - - local tank = Div{parent=main,x=2,y=8+y_offset,width=20,height=12} - TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} - TextBox{parent=tank,text="DYNAMIC TANK "..i,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.white,colors.gray)} - local tank_box = Rectangle{parent=tank,border=border(1, colors.gray, true),width=20,height=10} - local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14} - TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label} - 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,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg} - TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label} - local ccool = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16} end PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray} + -- TANK VALVES -- + + local next_f_id = 1 + + for i = 1, #tank_defs do + if tank_defs[i] > 0 then + local vy = 3 + y_ofs(i) + + TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} + + local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i + 13),colors=cpair(colors.green,colors.gray)} + local state = IndicatorLight{parent=main,x=9,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)} + end + end + + -- DYNAMIC TANKS -- + + for i = 1, #tank_draw do + if tank_draw[i] > 0 then + local id = "U-" .. i + if tank_draw[i] == 2 then + id = "F-" .. next_f_id + next_f_id = next_f_id + 1 + end + + local y_offset = y_ofs(i) + + local tank = Div{parent=main,x=3,y=8+y_offset,width=20,height=12} + + TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} + TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.white,colors.gray)} + + local tank_box = Rectangle{parent=tank,border=border(1, colors.gray, true),width=20,height=10} + + local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14} + + TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label} + 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,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg} + + TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label} + local ccool = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16} + end + end + + -- SPS -- + local sps = Div{parent=main,x=140,y=3,height=12} + TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=cpair(colors.white,colors.gray)} + local sps_box = Rectangle{parent=sps,border=border(1, colors.gray, true),width=24,height=10} + local status = StateIndicator{parent=sps_box,x=5,y=1,states=style.sps.states,value=1,min_width=14} + TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label} local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} + TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label} local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} end diff --git a/supervisor/config.lua b/supervisor/config.lua index 2373f0d..3ea0da8 100644 --- a/supervisor/config.lua +++ b/supervisor/config.lua @@ -10,27 +10,39 @@ config.RTU_CHANNEL = 16242 config.CRD_CHANNEL = 16243 -- pocket comms channel config.PKT_CHANNEL = 16244 --- max trusted modem message distance (0 to disable check) +-- max trusted modem message distance +-- (0 to disable check) config.TRUSTED_RANGE = 0 --- time in seconds (>= 2) before assuming a remote device is no longer active +-- time in seconds (>= 2) before assuming a remote +-- device is no longer active config.PLC_TIMEOUT = 5 config.RTU_TIMEOUT = 5 config.CRD_TIMEOUT = 5 config.PKT_TIMEOUT = 5 --- facility authentication key (do NOT use one of your passwords) +-- facility authentication key +-- (do NOT use one of your passwords) -- this enables verifying that messages are authentic --- all devices on the same network must use the same key +-- all devices on this network must use this key -- config.AUTH_KEY = "SCADAfacility123" -- expected number of reactors config.NUM_REACTORS = 4 --- expected number of boilers/turbines for each reactor +-- expected number of devices for each unit config.REACTOR_COOLING = { - { BOILERS = 1, TURBINES = 1 }, -- reactor unit 1 - { BOILERS = 1, TURBINES = 1 }, -- reactor unit 2 - { BOILERS = 1, TURBINES = 1 }, -- reactor unit 3 - { BOILERS = 1, TURBINES = 1 } -- reactor unit 4 +-- reactor unit 1 +{ BOILERS = 1, TURBINES = 1, TANK = false }, +-- reactor unit 2 +{ BOILERS = 1, TURBINES = 1, TANK = false }, +-- reactor unit 3 +{ BOILERS = 1, TURBINES = 1, TANK = false }, +-- reactor unit 4 +{ BOILERS = 1, TURBINES = 1, TANK = false } } +-- advanced facility dynamic tank configuration +-- (see wiki for details) +-- by default, dynamic tanks are for each unit +config.FAC_TANK_MODE = 0 +config.FAC_TANK_LIST = { 0, 0, 0, 0 } -- log path config.LOG_PATH = "/log.txt" diff --git a/supervisor/facility.lua b/supervisor/facility.lua index 77aa676..d03ad94 100644 --- a/supervisor/facility.lua +++ b/supervisor/facility.lua @@ -54,7 +54,7 @@ local facility = {} -- create a new facility management object ---@nodiscard ---@param num_reactors integer number of reactor units ----@param cooling_conf table cooling configurations of reactor units +---@param cooling_conf sv_cooling_conf cooling configurations of reactor units function facility.new(num_reactors, cooling_conf) local self = { units = {}, @@ -118,7 +118,7 @@ function facility.new(num_reactors, cooling_conf) -- create units for i = 1, num_reactors do - table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES)) + table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BOILERS, cooling_conf.r_cool[i].TURBINES)) table.insert(self.group_map, 0) end diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index 075f090..e890401 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -198,7 +198,7 @@ end ---@param nic nic network interface device ---@param fp_ok boolean front panel active ---@param num_reactors integer number of reactors ----@param cooling_conf table cooling configuration definition +---@param cooling_conf sv_cooling_conf cooling configuration definition function svsessions.init(nic, fp_ok, num_reactors, cooling_conf) self.nic = nic self.fp_ok = fp_ok diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 1ba2a4f..4da9937 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.20.5" +local SUPERVISOR_VERSION = "v1.0.0" local println = util.println local println_ts = util.println_ts @@ -48,11 +48,16 @@ cfv.assert_type_num(config.PKT_TIMEOUT) cfv.assert_min(config.PKT_TIMEOUT, 2) cfv.assert_type_int(config.NUM_REACTORS) cfv.assert_type_table(config.REACTOR_COOLING) +cfv.assert_type_int(config.FAC_TANK_MODE) +cfv.assert_type_table(config.FAC_TANK_LIST) cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_int(config.LOG_MODE) assert(cfv.valid(), "bad config file: missing/invalid fields") +assert((config.FAC_TANK_MODE ~= 0) and (config.NUM_REACTORS == #config.FAC_TANK_LIST), + "bad config file: FAC_TANK_LIST length not equal to NUM_REACTORS") + cfv.assert_eq(#config.REACTOR_COOLING, config.NUM_REACTORS) assert(cfv.valid(), "config: number of cooling configs different than number of units") @@ -61,6 +66,7 @@ for i = 1, config.NUM_REACTORS do assert(cfv.valid(), "config: missing cooling entry for reactor " .. i) cfv.assert_type_int(config.REACTOR_COOLING[i].BOILERS) cfv.assert_type_int(config.REACTOR_COOLING[i].TURBINES) + cfv.assert_type_bool(config.REACTOR_COOLING[i].TANK) assert(cfv.valid(), "config: missing boilers/turbines for reactor " .. i) cfv.assert_min(config.REACTOR_COOLING[i].BOILERS, 0) cfv.assert_min(config.REACTOR_COOLING[i].TURBINES, 1) diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index fb33b06..e0e87e8 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -32,7 +32,8 @@ function supervisor.comms(_version, nic, fp_ok) -- configuration data local num_reactors = config.NUM_REACTORS - local cooling_conf = config.REACTOR_COOLING + ---@class sv_cooling_conf + local cooling_conf = { r_cool = config.REACTOR_COOLING, fac_tank_mode = config.FAC_TANK_MODE, fac_tank_list = config.FAC_TANK_LIST } local self = { last_est_acks = {} @@ -295,16 +296,10 @@ function supervisor.comms(_version, nic, fp_ok) local s_id = svsessions.establish_crd_session(src_addr, firmware_v) if s_id ~= false then - local cfg = { num_reactors } - for i = 1, #cooling_conf do - table.insert(cfg, cooling_conf[i].BOILERS) - table.insert(cfg, cooling_conf[i].TURBINES) - end - println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected")) log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id)) - _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, cfg) + _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, { num_reactors, cooling_conf }) else if last_ack ~= ESTABLISH_ACK.COLLISION then log.info("CRD_ESTABLISH: denied new coordinator [@" .. src_addr .. "] due to already being connected to another coordinator") From 1275f611136490009f26702572cfc75066bba3fc Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 19 Aug 2023 13:42:07 -0400 Subject: [PATCH 07/22] #232 refactor and fixed sv config verify --- coordinator/iocontrol.lua | 4 ++-- supervisor/config.lua | 2 +- supervisor/startup.lua | 6 +++--- supervisor/supervisor.lua | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index d2ee8ef..9345075 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -52,7 +52,7 @@ function iocontrol.init(conf, comms) io.facility = { num_units = conf.num_units, tank_mode = conf.cooling.fac_tank_mode, - tank_defs = conf.cooling.fac_tank_list, + tank_defs = conf.cooling.fac_tank_defs, all_sys_ok = false, rtu_count = 0, @@ -219,7 +219,7 @@ function iocontrol.init(conf, comms) -- on other facility modes, overwrite unit TANK option with facility tank list else for i = 1, #io.units do - io.units[i].has_tank = conf.cooling.fac_tank_list[i] > 0 + io.units[i].has_tank = conf.cooling.fac_tank_defs[i] > 0 end end diff --git a/supervisor/config.lua b/supervisor/config.lua index 3ea0da8..5ea565b 100644 --- a/supervisor/config.lua +++ b/supervisor/config.lua @@ -42,7 +42,7 @@ config.REACTOR_COOLING = { -- (see wiki for details) -- by default, dynamic tanks are for each unit config.FAC_TANK_MODE = 0 -config.FAC_TANK_LIST = { 0, 0, 0, 0 } +config.FAC_TANK_DEFS = { 0, 0, 0, 0 } -- log path config.LOG_PATH = "/log.txt" diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 4da9937..c1a7036 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -49,14 +49,14 @@ cfv.assert_min(config.PKT_TIMEOUT, 2) cfv.assert_type_int(config.NUM_REACTORS) cfv.assert_type_table(config.REACTOR_COOLING) cfv.assert_type_int(config.FAC_TANK_MODE) -cfv.assert_type_table(config.FAC_TANK_LIST) +cfv.assert_type_table(config.FAC_TANK_DEFS) cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_int(config.LOG_MODE) assert(cfv.valid(), "bad config file: missing/invalid fields") -assert((config.FAC_TANK_MODE ~= 0) and (config.NUM_REACTORS == #config.FAC_TANK_LIST), - "bad config file: FAC_TANK_LIST length not equal to NUM_REACTORS") +assert((config.FAC_TANK_MODE == 0) or (config.NUM_REACTORS == #config.FAC_TANK_DEFS), + "bad config file: FAC_TANK_DEFS length not equal to NUM_REACTORS") cfv.assert_eq(#config.REACTOR_COOLING, config.NUM_REACTORS) assert(cfv.valid(), "config: number of cooling configs different than number of units") diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index e0e87e8..37707a3 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -33,7 +33,7 @@ function supervisor.comms(_version, nic, fp_ok) -- configuration data local num_reactors = config.NUM_REACTORS ---@class sv_cooling_conf - local cooling_conf = { r_cool = config.REACTOR_COOLING, fac_tank_mode = config.FAC_TANK_MODE, fac_tank_list = config.FAC_TANK_LIST } + local cooling_conf = { r_cool = config.REACTOR_COOLING, fac_tank_mode = config.FAC_TANK_MODE, fac_tank_defs = config.FAC_TANK_DEFS } local self = { last_est_acks = {} From 11d86d92eb47daf45a63f3358d893f8c9d20bea2 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 19 Aug 2023 20:06:37 -0400 Subject: [PATCH 08/22] #232 bugfixes and linked up indicators to data --- coordinator/iocontrol.lua | 172 +++++++++++++++++++++--- coordinator/ui/components/turbine.lua | 2 +- coordinator/ui/components/unit_flow.lua | 57 +++++--- coordinator/ui/layout/flow_view.lua | 40 ++++-- coordinator/ui/style.lua | 10 +- supervisor/unit.lua | 7 +- 6 files changed, 232 insertions(+), 56 deletions(-) diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 9345075..9944fe7 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -48,6 +48,7 @@ end ---@param conf facility_conf configuration ---@param comms coord_comms comms reference function iocontrol.init(conf, comms) + -- facility data structure ---@class ioctl_facility io.facility = { num_units = conf.num_units, @@ -104,6 +105,101 @@ function iocontrol.init(conf, comms) table.insert(io.facility.sps_ps_tbl, psil.create()) table.insert(io.facility.sps_data_tbl, {}) + -- determine tank information + if io.facility.tank_mode == 0 then + io.facility.tank_defs = {} + -- on facility tank mode 0, setup tank defs to match unit TANK option + for i = 1, conf.num_units do + io.facility.tank_defs[i] = util.trinary(conf.cooling.r_cool[i].TANK, 1, 0) + end + + io.facility.tank_list = { table.unpack(io.facility.tank_defs) } + else + -- decode the layout of tanks from the connections definitions + local tank_mode = io.facility.tank_mode + local tank_defs = io.facility.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 io.facility.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 + + io.facility.tank_list = tank_list + end + + -- create facility tanks + for i = 1, #io.facility.tank_list do + if io.facility.tank_list[i] == 2 then + table.insert(io.facility.tank_ps_tbl, psil.create()) + table.insert(io.facility.tank_data_tbl, {}) + end + end + + -- create unit data structures io.units = {} for i = 1, conf.num_units do local function ack(alarm) process.ack_alarm(i, alarm) end @@ -191,18 +287,27 @@ function iocontrol.init(conf, comms) tank_data_tbl = {} } + -- on other facility modes, overwrite unit TANK option with facility tank defs + if io.facility.tank_mode ~= 0 then + entry.has_tank = conf.cooling.fac_tank_defs[i] > 0 + end + -- create boiler tables for _ = 1, conf.cooling.r_cool[i].BOILERS do - local data = {} ---@type boilerv_session_db table.insert(entry.boiler_ps_tbl, psil.create()) - table.insert(entry.boiler_data_tbl, data) + table.insert(entry.boiler_data_tbl, {}) end -- create turbine tables for _ = 1, conf.cooling.r_cool[i].TURBINES do - local data = {} ---@type turbinev_session_db table.insert(entry.turbine_ps_tbl, psil.create()) - table.insert(entry.turbine_data_tbl, data) + table.insert(entry.turbine_data_tbl, {}) + end + + -- create tank tables + if entry.has_tank then + table.insert(entry.tank_ps_tbl, psil.create()) + table.insert(entry.tank_data_tbl, {}) end entry.num_boilers = #entry.boiler_data_tbl @@ -211,18 +316,6 @@ function iocontrol.init(conf, comms) table.insert(io.units, entry) end - -- on facility tank mode 0, setup tank list to match unit TANK option - if io.facility.tank_mode == 0 then - for i = 1, #io.units do - io.facility.tank_defs[i] = util.trinary(conf.cooling.r_cool[i].TANK, 1, 0) - end - -- on other facility modes, overwrite unit TANK option with facility tank list - else - for i = 1, #io.units do - io.units[i].has_tank = conf.cooling.fac_tank_defs[i] > 0 - end - end - -- pass IO control here since it can't be require'd due to a require loop process.init(io, comms) end @@ -697,8 +790,7 @@ function iocontrol.update_unit_statuses(statuses) else local burn_rate_sum = 0.0 local sna_count_sum = 0 - local pu_rate = 0.0 - local po_rate = 0.0 + local pu_rate, po_rate, po_am_rate = 0.0, 0.0, 0.0 -- get all unit statuses for i = 1, #statuses do @@ -792,6 +884,8 @@ function iocontrol.update_unit_statuses(statuses) if type(rtu_statuses) == "table" then -- boiler statuses if type(rtu_statuses.boilers) == "table" then + local boil_sum = 0 + for id = 1, #unit.boiler_ps_tbl do if rtu_statuses.boilers[i] == nil then -- disconnected @@ -809,6 +903,8 @@ function iocontrol.update_unit_statuses(statuses) if rtu_faulted then ps.publish("computed_status", 3) -- faulted elseif data.formed then + boil_sum = boil_sum + data.state.boil_rate + if data.state.boil_rate > 0 then ps.publish("computed_status", 5) -- active else @@ -822,6 +918,8 @@ function iocontrol.update_unit_statuses(statuses) valid = false end end + + unit.unit_ps.publish("boiler_boil_sum", boil_sum) else log.debug(log_header .. "boiler list not a table") valid = false @@ -829,6 +927,8 @@ function iocontrol.update_unit_statuses(statuses) -- turbine statuses if type(rtu_statuses.turbines) == "table" then + local flow_sum = 0 + for id = 1, #unit.turbine_ps_tbl do if rtu_statuses.turbines[i] == nil then -- disconnected @@ -846,6 +946,8 @@ function iocontrol.update_unit_statuses(statuses) if rtu_faulted then ps.publish("computed_status", 3) -- faulted elseif data.formed then + flow_sum = flow_sum + data.state.flow_rate + if data.tanks.energy_fill >= 0.99 then ps.publish("computed_status", 6) -- trip elseif data.state.flow_rate < 100 then @@ -861,6 +963,8 @@ function iocontrol.update_unit_statuses(statuses) valid = false end end + + unit.unit_ps.publish("turbine_flow_sum", flow_sum) else log.debug(log_header .. "turbine list not a table") valid = false @@ -909,8 +1013,11 @@ function iocontrol.update_unit_statuses(statuses) if type(rtu_statuses.sna) == "table" then unit.num_snas = rtu_statuses.sna[1] ---@type integer unit.sna_prod_rate = rtu_statuses.sna[2] ---@type number + unit.sna_peak_rate = rtu_statuses.sna[3] ---@type number + unit.unit_ps.publish("sna_count", unit.num_snas) unit.unit_ps.publish("sna_prod_rate", unit.sna_prod_rate) + unit.unit_ps.publish("sna_peak_rate", unit.sna_peak_rate) sna_count_sum = sna_count_sum + unit.num_snas else @@ -1016,9 +1123,33 @@ function iocontrol.update_unit_statuses(statuses) end -- determine waste production for this unit, add to statistics + local is_pu = unit.waste_product == types.WASTE_PRODUCT.PLUTONIUM - pu_rate = pu_rate + util.trinary(is_pu, burn_rate / 10.0, 0.0) - po_rate = po_rate + util.trinary(not is_pu, math.min(burn_rate / 10.0, unit.sna_prod_rate), 0.0) + local waste_rate = burn_rate / 10.0 + + local u_pu_rate = util.trinary(is_pu, waste_rate, 0.0) + local u_po_rate = util.trinary(not is_pu, math.min(waste_rate, unit.sna_prod_rate), 0.0) + + unit.unit_ps.publish("ws_rate", waste_rate) + unit.unit_ps.publish("pu_rate", u_pu_rate) + unit.unit_ps.publish("po_rate", u_po_rate) + + unit.unit_ps.publish("sna_in", util.trinary(is_pu, 0, burn_rate)) + + if unit.waste_product == types.WASTE_PRODUCT.POLONIUM then + unit.unit_ps.publish("po_pl_rate", u_po_rate) + unit.unit_ps.publish("po_am_rate", 0) + elseif unit.waste_product == types.WASTE_PRODUCT.ANTI_MATTER then + unit.unit_ps.publish("po_pl_rate", 0) + unit.unit_ps.publish("po_am_rate", u_po_rate) + po_am_rate = po_am_rate + u_po_rate + else + unit.unit_ps.publish("po_pl_rate", 0) + unit.unit_ps.publish("po_am_rate", 0) + end + + pu_rate = pu_rate + u_pu_rate + po_rate = po_rate + u_po_rate end end @@ -1026,6 +1157,7 @@ function iocontrol.update_unit_statuses(statuses) io.facility.ps.publish("sna_count", sna_count_sum) io.facility.ps.publish("pu_rate", pu_rate) io.facility.ps.publish("po_rate", po_rate) + io.facility.ps.publish("po_am_rate", po_am_rate) -- update alarm sounder sounder.eval(io.units) diff --git a/coordinator/ui/components/turbine.lua b/coordinator/ui/components/turbine.lua index 0e4cb21..9fb6f50 100644 --- a/coordinator/ui/components/turbine.lua +++ b/coordinator/ui/components/turbine.lua @@ -31,7 +31,7 @@ local function new_view(root, x, y, ps) local flow_rate = DataIndicator{parent=turbine,x=5,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%10.0f",value=0,commas=true,width=16,fg_bg=text_fg_bg} status.register(ps, "computed_status", status.update) - prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end) + prod_rate.register(ps, "steam_input_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end) flow_rate.register(ps, "flow_rate", flow_rate.update) local steam = VerticalBar{parent=turbine,x=2,y=1,fg_bg=cpair(colors.white,colors.gray),height=4,width=1} diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index 2c8db41..6a81e22 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -103,8 +103,11 @@ local function make(parent, x, y, wide, unit) PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=colors.lightGray} if unit.num_boilers > 0 then - local hc_rate = DataIndicator{parent=root,x=_wide(25,22),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local cc_rate = DataIndicator{parent=root,x=_wide(25,22),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local cc_rate = DataIndicator{parent=root,x=_wide(25,22),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg} + local hc_rate = DataIndicator{parent=root,x=_wide(25,22),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg} + + cc_rate.register(unit.unit_ps, "boil_sum", cc_rate.update) + hc_rate.register(unit.unit_ps, "heating_rate", hc_rate.update) local boiler = Rectangle{parent=root,x=_wide(47,40),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=TEXT_ALIGN.CENTER,height=1} @@ -112,22 +115,29 @@ local function make(parent, x, y, wide, unit) TextBox{parent=root,x=_wide(47,40),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} TextBox{parent=root,x=_wide(65,58),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} - local wt_rate = DataIndicator{parent=root,x=_wide(71,61),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local st_rate = DataIndicator{parent=root,x=_wide(71,61),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local wt_rate = DataIndicator{parent=root,x=_wide(71,61),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg} + local st_rate = DataIndicator{parent=root,x=_wide(71,61),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg} + + wt_rate.register(unit.unit_ps, "turbine_flow_sum", wt_rate.update) + st_rate.register(unit.unit_ps, "boil_sum", st_rate.update) else - local wt_rate = DataIndicator{parent=root,x=28,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} - local st_rate = DataIndicator{parent=root,x=28,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} + local wt_rate = DataIndicator{parent=root,x=28,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg} + local st_rate = DataIndicator{parent=root,x=28,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg} + + wt_rate.register(unit.unit_ps, "turbine_flow_sum", wt_rate.update) + st_rate.register(unit.unit_ps, "heating_rate", st_rate.update) end local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1} + TextBox{parent=turbine,y=3,text=util.trinary(unit.num_turbines>1,"GENERATORS","GENERATOR"),alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=root,x=_wide(93,79),y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=lg_gray} for i = 1, unit.num_turbines do local ry = 1 + (2 * (i - 1)) + prv_yo TextBox{parent=root,x=_wide(125,103),y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1} local state = TriIndicatorLight{parent=root,x=_wide(129,107),y=ry,label=v_names[i+4],c1=colors.gray,c2=colors.yellow,c3=colors.red} + state.register(unit.turbine_ps_tbl[i], "SteamDumpOpen", state.update) end ---------------------- @@ -168,12 +178,19 @@ local function make(parent, x, y, wide, unit) TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=TEXT_ALIGN.CENTER,fg_bg=wh_gray,width=l,height=1} end - local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=1234.56,width=12,fg_bg=bw_fg_bg} - local pu_rate = DataIndicator{parent=waste,x=_wide(82,70),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} - local spent_rate = DataIndicator{parent=waste,x=_wide(117,99),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} + local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=bw_fg_bg} + local pu_rate = DataIndicator{parent=waste,x=_wide(82,70),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg} + local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg} + local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg} + local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg} + local spent_rate = DataIndicator{parent=waste,x=_wide(117,99),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg} + + waste_rate.register(unit.unit_ps, "act_burn_rate", waste_rate.update) + pu_rate.register(unit.unit_ps, "pu_rate", pu_rate.update) + po_rate.register(unit.unit_ps, "po_rate", po_rate.update) + popl_rate.register(unit.unit_ps, "po_pl_rate", popl_rate.update) + poam_rate.register(unit.unit_ps, "po_am_rate", poam_rate.update) + spent_rate.register(unit.unit_ps, "ws_rate", spent_rate.update) _valve(_wide(21, 18), 2, 1) _valve(_wide(21, 18), 6, 2) @@ -188,10 +205,16 @@ local function make(parent, x, y, wide, unit) TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=bw_fg_bg} local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn} - local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c,label="CNT",unit="",format="%2d",value=99,width=7} - local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17} - local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c,label="MAX ",unit="mB/t",format="%7.2f",value=1000,width=17} - local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c,label="IN ",unit="mB/t",format="%7.2f",value=1000,width=17} + local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c,label="CNT",unit="",format="%2d",value=0,width=7} + local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=0,width=17} + local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c,label="MAX ",unit="mB/t",format="%7.2f",value=0,width=17} + local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c,label="IN ",unit="mB/t",format="%7.2f",value=0,width=17} + + sna_act.register(unit.unit_ps, "po_rate", function (r) sna_act.update(r > 0) end) + sna_cnt.register(unit.unit_ps, "sna_count", sna_cnt.update) + sna_pk.register(unit.unit_ps, "sna_peak_rate", sna_pk.update) + sna_max.register(unit.unit_ps, "sna_prod_rate", sna_max.update) + sna_in.register(unit.unit_ps, "sna_in", sna_in.update) return root end diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index f309b63..ef527cf 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -39,7 +39,7 @@ local function init(main) local units = iocontrol.get_db().units local tank_defs = facility.tank_defs - local tank_draw = { table.unpack(tank_defs) } + local tank_list = facility.tank_list -- window header message local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} @@ -106,7 +106,6 @@ local function init(main) if i == first_fdef then table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) elseif i > first_fdef then - if tank_defs[i] == 2 then tank_draw[i] = 0 end if i == last_fdef then table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) elseif i < last_fdef then @@ -127,7 +126,6 @@ local function init(main) elseif i == first_fdef then table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) elseif i > first_fdef then - if tank_defs[i] == 2 then tank_draw[i] = 0 end if i == last_fdef then table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) elseif i < last_fdef then @@ -143,7 +141,6 @@ local function init(main) table.insert(water_pipes, pipe(0, y_ofs(a), 1, y_ofs(a) + 6, colors.blue, true)) if tank_defs[b] == 2 then table.insert(water_pipes, pipe(0, y_ofs(b) - 13, 1, y_ofs(b), colors.blue, true)) - tank_draw[b] = 0 end elseif tank_defs[b] == 2 then table.insert(water_pipes, pipe(0, y_ofs(b), 1, y_ofs(b) + 6, colors.blue, true)) @@ -162,7 +159,6 @@ local function init(main) elseif i == first_fdef then table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) elseif i > first_fdef then - if tank_defs[i] == 2 then tank_draw[i] = 0 end if i == last_fdef then table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) elseif i < last_fdef then @@ -183,7 +179,6 @@ local function init(main) elseif i == first_fdef then table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) elseif i > first_fdef then - if tank_defs[i] == 2 then tank_draw[i] = 0 end if i == last_fdef then table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) elseif i < last_fdef then @@ -204,7 +199,6 @@ local function init(main) elseif i == first_fdef then table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) elseif i > first_fdef then - if tank_defs[i] == 2 then tank_draw[i] = 0 end if i == last_fdef then table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) elseif i < last_fdef then @@ -225,7 +219,6 @@ local function init(main) elseif i == first_fdef then table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) elseif i > first_fdef then - if tank_defs[i] == 2 then tank_draw[i] = 0 end if i == last_fdef then table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) elseif i < last_fdef then @@ -267,10 +260,11 @@ local function init(main) -- DYNAMIC TANKS -- - for i = 1, #tank_draw do - if tank_draw[i] > 0 then + for i = 1, #tank_list do + if tank_list[i] > 0 then local id = "U-" .. i - if tank_draw[i] == 2 then + local f_id = next_f_id + if tank_list[i] == 2 then id = "F-" .. next_f_id next_f_id = next_f_id + 1 end @@ -291,7 +285,19 @@ local function init(main) local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg} TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label} - local ccool = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16} + local level = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16} + + if tank_list[i] == 1 then + status.register(units[i].tank_ps_tbl[1], "computed_status", status.update) + tank_pcnt.register(units[i].tank_ps_tbl[1], "fill", function (f) tank_pcnt.update(f * 100) end) + tank_amnt.register(units[i].tank_ps_tbl[1], "stored", function (sto) tank_amnt.update(sto.amount) end) + level.register(units[i].tank_ps_tbl[1], "fill", level.update) + else + status.register(facility.tank_ps_tbl[f_id], "computed_status", status.update) + tank_pcnt.register(facility.tank_ps_tbl[f_id], "fill", function (f) tank_pcnt.update(f * 100) end) + tank_amnt.register(facility.tank_ps_tbl[f_id], "stored", function (sto) tank_amnt.update(sto.amount) end) + level.register(facility.tank_ps_tbl[f_id], "fill", level.update) + end end end @@ -306,11 +312,17 @@ local function init(main) local status = StateIndicator{parent=sps_box,x=5,y=1,states=style.sps.states,value=1,min_width=14} + status.register(facility.sps_ps_tbl[1], "computed_status", status.update) + TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label} - local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} + local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.3f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} + + sps_in.register(facility.ps, "po_am_rate", sps_in.update) TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label} - local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} + local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15d",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} + + sps_rate.register(facility.sps_ps_tbl[1], "process_rate", function (r) sps_rate.update(r * 1000) end) end return init diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua index 837b879..c759643 100644 --- a/coordinator/ui/style.lua +++ b/coordinator/ui/style.lua @@ -219,11 +219,11 @@ style.sps = { text = "RTU FAULT" }, { - color = cpair(colors.black, colors.gray), + color = cpair(colors.white, colors.gray), text = "IDLE" }, { - color = cpair(colors.black, colors.blue), + color = cpair(colors.black, colors.green), text = "ACTIVE" } } @@ -251,7 +251,11 @@ style.dtank = { { color = cpair(colors.black, colors.yellow), text = "LOW FILL" - } + }, + { + color = cpair(colors.black, colors.green), + text = "FILLED" + }, } } diff --git a/supervisor/unit.lua b/supervisor/unit.lua index c233631..ae646f9 100644 --- a/supervisor/unit.lua +++ b/supervisor/unit.lua @@ -813,7 +813,12 @@ function unit.new(reactor_id, num_boilers, num_turbines) end -- basic SNA statistical information - status.sna = { #self.snas, public.get_sna_rate() } + local total_peak = 0 + for i = 1, #self.snas do + local db = self.snas[i].get_db() ---@type sna_session_db + total_peak = total_peak + db.state.peak_production + end + status.sna = { #self.snas, public.get_sna_rate(), total_peak } -- radiation monitors (environment detectors) status.rad_mon = {} From 59950e9d159ef8968472cda2e58495c87a06accc Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 19 Aug 2023 23:24:20 -0400 Subject: [PATCH 09/22] #232 connected valve indicators --- coordinator/iocontrol.lua | 26 ++++++++++++++++- coordinator/ui/components/unit_flow.lua | 5 +++- coordinator/ui/layout/flow_view.lua | 5 +++- supervisor/session/coordinator.lua | 3 +- supervisor/session/rsctl.lua | 12 ++++++++ supervisor/unit.lua | 39 +++++++++++++++++++++---- 6 files changed, 80 insertions(+), 10 deletions(-) diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 9944fe7..b41f185 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -801,7 +801,7 @@ function iocontrol.update_unit_statuses(statuses) local burn_rate = 0.0 - if type(status) ~= "table" or #status ~= 5 then + if type(status) ~= "table" or #status ~= 6 then log.debug(log_header .. "invalid status entry in unit statuses (not a table or invalid length)") valid = false else @@ -1122,6 +1122,30 @@ function iocontrol.update_unit_statuses(statuses) valid = false end + -- valve states + local valve_states = status[6] + + if type(valve_states) == "table" then + if #valve_states == 5 then + unit.unit_ps.publish("V_pu_conn", valve_states[1] > 0) + unit.unit_ps.publish("V_pu_state", valve_states[1] == 2) + unit.unit_ps.publish("V_po_conn", valve_states[2] > 0) + unit.unit_ps.publish("V_po_state", valve_states[2] == 2) + unit.unit_ps.publish("V_pl_conn", valve_states[3] > 0) + unit.unit_ps.publish("V_pl_state", valve_states[3] == 2) + unit.unit_ps.publish("V_am_conn", valve_states[4] > 0) + unit.unit_ps.publish("V_am_state", valve_states[4] == 2) + unit.unit_ps.publish("V_emc_conn", valve_states[5] > 0) + unit.unit_ps.publish("V_emc_state", valve_states[5] == 2) + else + log.debug(log_header .. "valve states length mismatch") + valid = false + end + else + log.debug(log_header .. "valve states not a table") + valid = false + end + -- determine waste production for this unit, add to statistics local is_pu = unit.waste_product == types.WASTE_PRODUCT.PLUTONIUM diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index 6a81e22..5ca17b7 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -45,6 +45,7 @@ local function make(parent, x, y, wide, unit) local v_start = 1 + ((unit.unit_id - 1) * 4) local prv_start = 1 + ((unit.unit_id - 1) * 3) + local v_fields = { "pu", "po", "pl", "am" } local v_names = { sprintf("PV%02d-PU", v_start), sprintf("PV%02d-PO", v_start + 1), @@ -169,7 +170,9 @@ local function make(parent, x, y, wide, unit) local function _valve(vx, vy, n) TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1} local conn = IndicatorLight{parent=waste,x=vx-3,y=vy+1,label=v_names[n],colors=ind_grn} - local state = IndicatorLight{parent=waste,x=vx-3,y=vy+2,label="STATE",colors=ind_wht} + local open = IndicatorLight{parent=waste,x=vx-3,y=vy+2,label="OPEN",colors=ind_wht} + conn.register(unit.unit_ps, util.c("V_", v_fields[n], "_conn"), conn.update) + open.register(unit.unit_ps, util.c("V_", v_fields[n], "_state"), open.update) end local function _machine(mx, my, name) diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index ef527cf..18b898b 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -254,7 +254,10 @@ local function init(main) TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i + 13),colors=cpair(colors.green,colors.gray)} - local state = IndicatorLight{parent=main,x=9,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)} + local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=cpair(colors.white,colors.gray)} + + conn.register(units[i].unit_ps, "V_emc_conn", conn.update) + open.register(units[i].unit_ps, "V_emc_state", open.update) end end diff --git a/supervisor/session/coordinator.lua b/supervisor/session/coordinator.lua index c2d20cd..015b599 100644 --- a/supervisor/session/coordinator.lua +++ b/supervisor/session/coordinator.lua @@ -172,7 +172,8 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil unit.get_rtu_statuses(), unit.get_annunciator(), unit.get_alarms(), - unit.get_state() + unit.get_state(), + unit.get_valves() } end diff --git a/supervisor/session/rsctl.lua b/supervisor/session/rsctl.lua index fb17efe..1bdef5a 100644 --- a/supervisor/session/rsctl.lua +++ b/supervisor/session/rsctl.lua @@ -11,6 +11,18 @@ function rsctl.new(redstone_rtus) ---@class rs_controller local public = {} + -- check if a redstone port has available connections + ---@param port IO_PORT + ---@return boolean + function public.is_connected(port) + for i = 1, #redstone_rtus do + local db = redstone_rtus[i].get_db() ---@type redstone_session_db + if db.io[port] ~= nil then return true end + end + + return false + end + -- write to a digital redstone port (applies to all RTUs) ---@param port IO_PORT ---@param value boolean diff --git a/supervisor/unit.lua b/supervisor/unit.lua index ae646f9..cae748d 100644 --- a/supervisor/unit.lua +++ b/supervisor/unit.lua @@ -333,14 +333,28 @@ function unit.new(reactor_id, num_boilers, num_turbines) --#region redstone I/O - local __rs_w = self.io_ctl.digital_write + -- create a generic valve interface + ---@nodiscard + ---@param port IO_PORT + local function _make_valve_iface(port) + ---@class unit_valve_iface + local iface = { + open = function () self.io_ctl.digital_write(port, true) end, + close = function () self.io_ctl.digital_write(port, false) end, + -- check valve state + ---@nodiscard + ---@return 0|1|2 0 for not connected, 1 for inactive, 2 for active + check = function () return util.trinary(self.io_ctl.is_connected(port), util.trinary(self.io_ctl.digital_read(port), 2, 1), 0) end + } + return iface + end -- valves - local waste_pu = { open = function () __rs_w(IO.WASTE_PU, true) end, close = function () __rs_w(IO.WASTE_PU, false) end } - local waste_sna = { open = function () __rs_w(IO.WASTE_PO, true) end, close = function () __rs_w(IO.WASTE_PO, false) end } - local waste_po = { open = function () __rs_w(IO.WASTE_POPL, true) end, close = function () __rs_w(IO.WASTE_POPL, false) end } - local waste_sps = { open = function () __rs_w(IO.WASTE_AM, true) end, close = function () __rs_w(IO.WASTE_AM, false) end } - local emer_cool = { open = function () __rs_w(IO.U_EMER_COOL, true) end, close = function () __rs_w(IO.U_EMER_COOL, false) end } + local waste_pu = _make_valve_iface(IO.WASTE_PU) + local waste_sna = _make_valve_iface(IO.WASTE_PO) + local waste_po = _make_valve_iface(IO.WASTE_POPL) + local waste_sps = _make_valve_iface(IO.WASTE_AM) + local emer_cool = _make_valve_iface(IO.U_EMER_COOL) ---@class unit_valves self.valves = { @@ -869,6 +883,19 @@ function unit.new(reactor_id, num_boilers, num_turbines) } end + -- get valve states + ---@nodiscard + function public.get_valves() + local v = self.valves + return { + v.waste_pu.check(), + v.waste_sna.check(), + v.waste_po.check(), + v.waste_sps.check(), + v.emer_cool.check() + } + end + -- get the reactor ID ---@nodiscard function public.get_id() return self.r_id end From f477ad9426ac7ce2728ebdd4035a920ea0ed0290 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 19 Aug 2023 23:28:03 -0400 Subject: [PATCH 10/22] #232 re-indexed valve IDs --- coordinator/ui/components/unit_flow.lua | 2 +- coordinator/ui/layout/flow_view.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index 5ca17b7..5ba01b1 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -43,7 +43,7 @@ local ind_wht = style.ind_wht local function make(parent, x, y, wide, unit) local height = 16 - local v_start = 1 + ((unit.unit_id - 1) * 4) + 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" } local v_names = { diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 18b898b..be71d28 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -253,7 +253,7 @@ local function init(main) TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} - local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i + 13),colors=cpair(colors.green,colors.gray)} + local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=cpair(colors.green,colors.gray)} local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=cpair(colors.white,colors.gray)} conn.register(units[i].unit_ps, "V_emc_conn", conn.update) From 7656936982046b2d7b3a69f60bc67278d69767c5 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 16:52:12 -0400 Subject: [PATCH 11/22] #232 cleanup, added general stats --- coordinator/iocontrol.lua | 11 ++++++-- coordinator/ui/components/unit_flow.lua | 2 +- coordinator/ui/layout/flow_view.lua | 35 +++++++++++++++++++------ coordinator/ui/style.lua | 6 +++-- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index b41f185..e4ff9b5 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -790,7 +790,7 @@ function iocontrol.update_unit_statuses(statuses) else local burn_rate_sum = 0.0 local sna_count_sum = 0 - local pu_rate, po_rate, po_am_rate = 0.0, 0.0, 0.0 + local pu_rate, po_rate, po_pl_rate, po_am_rate, spent_rate = 0.0, 0.0, 0.0, 0.0, 0.0 -- get all unit statuses for i = 1, #statuses do @@ -1151,10 +1151,10 @@ function iocontrol.update_unit_statuses(statuses) local is_pu = unit.waste_product == types.WASTE_PRODUCT.PLUTONIUM local waste_rate = burn_rate / 10.0 + local u_spent_rate = waste_rate local u_pu_rate = util.trinary(is_pu, waste_rate, 0.0) local u_po_rate = util.trinary(not is_pu, math.min(waste_rate, unit.sna_prod_rate), 0.0) - unit.unit_ps.publish("ws_rate", waste_rate) unit.unit_ps.publish("pu_rate", u_pu_rate) unit.unit_ps.publish("po_rate", u_po_rate) @@ -1163,17 +1163,22 @@ function iocontrol.update_unit_statuses(statuses) if unit.waste_product == types.WASTE_PRODUCT.POLONIUM then unit.unit_ps.publish("po_pl_rate", u_po_rate) unit.unit_ps.publish("po_am_rate", 0) + po_pl_rate = po_pl_rate + u_po_rate elseif unit.waste_product == types.WASTE_PRODUCT.ANTI_MATTER then unit.unit_ps.publish("po_pl_rate", 0) unit.unit_ps.publish("po_am_rate", u_po_rate) po_am_rate = po_am_rate + u_po_rate + u_spent_rate = 0 else unit.unit_ps.publish("po_pl_rate", 0) unit.unit_ps.publish("po_am_rate", 0) end + unit.unit_ps.publish("ws_rate", u_spent_rate) + pu_rate = pu_rate + u_pu_rate po_rate = po_rate + u_po_rate + spent_rate = spent_rate + u_spent_rate end end @@ -1181,7 +1186,9 @@ function iocontrol.update_unit_statuses(statuses) io.facility.ps.publish("sna_count", sna_count_sum) io.facility.ps.publish("pu_rate", pu_rate) io.facility.ps.publish("po_rate", po_rate) + io.facility.ps.publish("po_pl_rate", po_pl_rate) io.facility.ps.publish("po_am_rate", po_am_rate) + io.facility.ps.publish("spent_waste_rate", spent_rate) -- update alarm sounder sounder.eval(io.units) diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index 5ba01b1..aa13e49 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -27,6 +27,7 @@ local cpair = core.cpair local border = core.border local pipe = core.pipe +local wh_gray = style.wh_gray local bw_fg_bg = style.bw_fg_bg local text_c = style.text_colors local lu_c = style.lu_colors @@ -64,7 +65,6 @@ local function make(parent, x, y, wide, unit) local root = Div{parent=parent,x=x,y=y,width=_wide(136, 114),height=height} local lg_gray = cpair(colors.lightGray, colors.gray) - local wh_gray = cpair(colors.white, colors.gray) ------------------ -- COOLING LOOP -- diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index be71d28..5fa1cbd 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -28,8 +28,9 @@ local cpair = core.cpair local border = core.border local pipe = core.pipe -local bw_fg_bg = style.bw_fg_bg -local text_col = style.text_colors +local wh_gray = style.wh_gray +local bw_fg_bg = style.bw_fg_bg +local text_col = style.text_colors local lu_col = style.lu_colors -- create new flow view @@ -238,12 +239,12 @@ local function init(main) 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]) - table.insert(po_pipes, pipe(0, 3 + y_offset, 8, 0, colors.cyan, true, true)) + table.insert(po_pipes, pipe(0, 3 + y_offset, 4, 0, colors.cyan, true, true)) end PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray} - -- TANK VALVES -- + -- tank valves -- local next_f_id = 1 @@ -253,15 +254,15 @@ local function init(main) TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} - local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=cpair(colors.green,colors.gray)} - local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=cpair(colors.white,colors.gray)} + local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=style.ind_grn} + local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=style.ind_wht} conn.register(units[i].unit_ps, "V_emc_conn", conn.update) open.register(units[i].unit_ps, "V_emc_state", open.update) end end - -- DYNAMIC TANKS -- + -- dynamic tanks -- for i = 1, #tank_list do if tank_list[i] > 0 then @@ -309,7 +310,7 @@ local function init(main) local sps = Div{parent=main,x=140,y=3,height=12} TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} - TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=cpair(colors.white,colors.gray)} + TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=wh_gray} local sps_box = Rectangle{parent=sps,border=border(1, colors.gray, true),width=24,height=10} @@ -326,6 +327,24 @@ local function init(main) local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15d",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} sps_rate.register(facility.sps_ps_tbl[1], "process_rate", function (r) sps_rate.update(r * 1000) end) + + -- statistics -- + + TextBox{parent=main,x=145,y=16,text="PROC. WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} + local pr_waste = Rectangle{parent=main,x=145,y=17,border=border(1, colors.gray, true),width=19,height=5,thin=true,fg_bg=bw_fg_bg} + local pu = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17} + local po = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Po",unit="mB/t",format="%9.3f",value=0,width=17} + local popl = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="PoPl",unit="mB/t",format="%7.3f",value=0,width=17} + + pu.register(facility.ps, "pu_rate", pu.update) + po.register(facility.ps, "po_rate", po.update) + popl.register(facility.ps, "po_pl_rate", popl.update) + + TextBox{parent=main,x=145,y=23,text="SPENT WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} + local sp_waste = Rectangle{parent=main,x=145,y=24,border=border(1, colors.gray, true),width=19,height=3,thin=true,fg_bg=bw_fg_bg} + local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_col,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17} + + sum_sp_waste.register(facility.ps, "spent_waste_rate", sum_sp_waste.update) end return init diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua index c759643..ad23989 100644 --- a/coordinator/ui/style.lua +++ b/coordinator/ui/style.lua @@ -70,16 +70,18 @@ style.colors = { -- COMMON COLOR PAIRS -- +style.wh_gray = cpair(colors.white, colors.gray) + style.bw_fg_bg = cpair(colors.black, colors.white) style.text_colors = cpair(colors.black, colors.lightGray) style.lu_colors = cpair(colors.gray, colors.gray) -style.hzd_fg_bg = cpair(colors.white, colors.gray) +style.hzd_fg_bg = style.wh_gray style.dis_colors = cpair(colors.white, colors.lightGray) style.ind_grn = cpair(colors.green, colors.gray) style.ind_yel = cpair(colors.yellow, colors.gray) style.ind_red = cpair(colors.red, colors.gray) -style.ind_wht = cpair(colors.white, colors.gray) +style.ind_wht = style.wh_gray -- UI COMPONENTS -- From 9bf07e6c3e3f21f7cda1413a243641c21e5d6ddc Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 17:04:14 -0400 Subject: [PATCH 12/22] completed work on updated pipenet --- graphics/elements/pipenet.lua | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/graphics/elements/pipenet.lua b/graphics/elements/pipenet.lua index 5319e68..599f1f1 100644 --- a/graphics/elements/pipenet.lua +++ b/graphics/elements/pipenet.lua @@ -1,7 +1,6 @@ -- Pipe Graphics Element local util = require("scada-common.util") -local log = require("scada-common.log") local core = require("graphics.core") local element = require("graphics.element") @@ -213,16 +212,6 @@ local function pipenet(args) end end - -- for x = 1, args.width do - -- for y = 1, args.height do - -- local entry = map[x][y] ---@type _pipe_map_entry|false - -- if entry == false then - -- e.window.setCursorPos(x, y) - -- e.window.blit("x", "f", "e") - -- end - -- end - -- end - -- render for x = 1, args.width do for y = 1, args.height do @@ -288,16 +277,24 @@ local function pipenet(args) if check(x, y - 1) then -- above -- not below and (if left or right) if (not check(x, y + 1)) and (check(x - 1, y) or check(x + 1, y)) then - char = util.trinary(entry.atr, "\x8f", "\x83") + char = util.trinary(entry.atr, "\x8f", " ") invert = not entry.atr - else -- not above w/ sides only + else -- not below w/ sides only char = " " invert = true end elseif check(x, y + 1) then -- not above, if below + -- if left or right + if (check(x - 1, y) or check(x + 1, y)) then + char = "\x83" + invert = true + else -- not left or right + char = " " + invert = true + end + else -- not above, not below char = util.trinary(entry.atr, "\x8f", "\x83") invert = not entry.atr - else -- not above, not below end end From c5d38a5584d6b973eb9590ebbdddf532e7f9c736 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 17:30:34 -0400 Subject: [PATCH 13/22] #232 added container mode indicators for tanks --- coordinator/ui/components/unit_flow.lua | 8 +-- coordinator/ui/layout/flow_view.lua | 79 +++++++++++++++---------- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index aa13e49..a3b17f5 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -70,7 +70,7 @@ local function make(parent, x, y, wide, unit) -- COOLING LOOP -- ------------------ - local reactor = Rectangle{parent=root,x=1,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} + local reactor = Rectangle{parent=root,x=1,y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray} TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} @@ -206,12 +206,12 @@ local function make(parent, x, y, wide, unit) _machine(_wide(116, 94), 6, "SPENT WASTE \x1b") TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} - local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=bw_fg_bg} + local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1,colors.gray,true),width=19,height=7,thin=true,fg_bg=bw_fg_bg} local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn} local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c,label="CNT",unit="",format="%2d",value=0,width=7} local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=0,width=17} - local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c,label="MAX ",unit="mB/t",format="%7.2f",value=0,width=17} - local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c,label="IN ",unit="mB/t",format="%7.2f",value=0,width=17} + local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c,label="MAX",unit="mB/t",format="%8.2f",value=0,width=17} + local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c,label="IN",unit="mB/t",format="%9.2f",value=0,width=17} sna_act.register(unit.unit_ps, "po_rate", function (r) sna_act.update(r > 0) end) sna_cnt.register(unit.unit_ps, "sna_count", sna_cnt.update) diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 5fa1cbd..5577604 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -2,6 +2,7 @@ -- Flow Monitor GUI -- +local types = require("scada-common.types") local util = require("scada-common.util") local iocontrol = require("coordinator.iocontrol") @@ -22,6 +23,8 @@ local HorizontalBar = require("graphics.elements.indicators.hbar") local IndicatorLight = require("graphics.elements.indicators.light") local StateIndicator = require("graphics.elements.indicators.state") +local CONTAINER_MODE = types.CONTAINER_MODE + local TEXT_ALIGN = core.TEXT_ALIGN local cpair = core.cpair @@ -71,7 +74,7 @@ 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 + 5, colors.blue, true)) + 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 u = units[i] ---@type ioctl_unit @@ -88,7 +91,7 @@ local function init(main) if tank_defs[i] == 2 then table.insert(water_pipes, pipe(1, y, 21, y, colors.blue, true)) else - table.insert(water_pipes, pipe(2, y, 2, y + 5, colors.blue, true)) + 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)) end @@ -105,12 +108,12 @@ 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 + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) end end end @@ -122,15 +125,15 @@ local function init(main) local y = y_ofs(i) if i == 4 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) end end end @@ -155,15 +158,15 @@ local function init(main) local y = y_ofs(i) if i == 1 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) end end end @@ -175,15 +178,15 @@ local function init(main) local y = y_ofs(i) if i == 3 or i == 4 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) end end end @@ -195,15 +198,15 @@ local function init(main) local y = y_ofs(i) if i == 1 or i == 4 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) end end end @@ -215,15 +218,15 @@ local function init(main) local y = y_ofs(i) if i == 1 or i == 2 then if tank_defs[i] == 2 then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) end elseif i == first_fdef then - table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y, 1, y + 5, colors.blue, true)) elseif i > first_fdef then if i == last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y, colors.blue, true)) elseif i < last_fdef then - table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true)) + table.insert(water_pipes, pipe(0, y - 14, 0, y + 5, colors.blue, true)) end end end @@ -275,12 +278,12 @@ local function init(main) local y_offset = y_ofs(i) - local tank = Div{parent=main,x=3,y=8+y_offset,width=20,height=12} + local tank = Div{parent=main,x=3,y=7+y_offset,width=20,height=14} TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.white,colors.gray)} - local tank_box = Rectangle{parent=tank,border=border(1, colors.gray, true),width=20,height=10} + local tank_box = Rectangle{parent=tank,border=border(1,colors.gray,true),width=20,height=12} local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14} @@ -291,16 +294,32 @@ local function init(main) TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,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} + TextBox{parent=tank_box,x=2,y=9,text="In/Out Mode",height=1,width=11,fg_bg=style.label} + local can_fill = IndicatorLight{parent=tank_box,x=2,y=10,label="FILL",colors=style.ind_wht} + local can_empty = IndicatorLight{parent=tank_box,x=10,y=10,label="EMPTY",colors=style.ind_wht} + + local function _can_fill(mode) + can_fill.update((mode == CONTAINER_MODE.BOTH) or (mode == CONTAINER_MODE.FILL)) + end + + local function _can_empty(mode) + can_empty.update((mode == CONTAINER_MODE.BOTH) or (mode == CONTAINER_MODE.EMPTY)) + end + if tank_list[i] == 1 then status.register(units[i].tank_ps_tbl[1], "computed_status", status.update) tank_pcnt.register(units[i].tank_ps_tbl[1], "fill", function (f) tank_pcnt.update(f * 100) end) tank_amnt.register(units[i].tank_ps_tbl[1], "stored", function (sto) tank_amnt.update(sto.amount) end) level.register(units[i].tank_ps_tbl[1], "fill", level.update) + can_fill.register(units[i].tank_ps_tbl[1], "container_mode", _can_fill) + can_empty.register(units[i].tank_ps_tbl[1], "container_mode", _can_empty) else status.register(facility.tank_ps_tbl[f_id], "computed_status", status.update) tank_pcnt.register(facility.tank_ps_tbl[f_id], "fill", function (f) tank_pcnt.update(f * 100) end) tank_amnt.register(facility.tank_ps_tbl[f_id], "stored", function (sto) tank_amnt.update(sto.amount) end) level.register(facility.tank_ps_tbl[f_id], "fill", level.update) + can_fill.register(facility.tank_ps_tbl[f_id], "container_mode", _can_fill) + can_empty.register(facility.tank_ps_tbl[f_id], "container_mode", _can_empty) end end end @@ -312,7 +331,7 @@ local function init(main) TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=wh_gray} - local sps_box = Rectangle{parent=sps,border=border(1, colors.gray, true),width=24,height=10} + local sps_box = Rectangle{parent=sps,border=border(1,colors.gray,true),width=24,height=10} local status = StateIndicator{parent=sps_box,x=5,y=1,states=style.sps.states,value=1,min_width=14} @@ -331,7 +350,7 @@ local function init(main) -- statistics -- TextBox{parent=main,x=145,y=16,text="PROC. WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} - local pr_waste = Rectangle{parent=main,x=145,y=17,border=border(1, colors.gray, true),width=19,height=5,thin=true,fg_bg=bw_fg_bg} + local pr_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=bw_fg_bg} local pu = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17} local po = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Po",unit="mB/t",format="%9.3f",value=0,width=17} local popl = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="PoPl",unit="mB/t",format="%7.3f",value=0,width=17} @@ -341,7 +360,7 @@ local function init(main) popl.register(facility.ps, "po_pl_rate", popl.update) TextBox{parent=main,x=145,y=23,text="SPENT WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} - local sp_waste = Rectangle{parent=main,x=145,y=24,border=border(1, colors.gray, true),width=19,height=3,thin=true,fg_bg=bw_fg_bg} + local sp_waste = Rectangle{parent=main,x=145,y=24,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=bw_fg_bg} local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_col,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17} sum_sp_waste.register(facility.ps, "spent_waste_rate", sum_sp_waste.update) From a92f18215650492413a2c77afaabbf6f7c655960 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 22:53:14 -0400 Subject: [PATCH 14/22] #232 fixed incorrect arrows on turbine flow view --- coordinator/ui/components/unit_flow.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coordinator/ui/components/unit_flow.lua b/coordinator/ui/components/unit_flow.lua index a3b17f5..ffee652 100644 --- a/coordinator/ui/components/unit_flow.lua +++ b/coordinator/ui/components/unit_flow.lua @@ -132,7 +132,7 @@ local function make(parent, x, y, wide, unit) local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=turbine,y=3,text=util.trinary(unit.num_turbines>1,"GENERATORS","GENERATOR"),alignment=TEXT_ALIGN.CENTER,height=1} - TextBox{parent=root,x=_wide(93,79),y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=lg_gray} + TextBox{parent=root,x=_wide(93,79),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} for i = 1, unit.num_turbines do local ry = 1 + (2 * (i - 1)) + prv_yo From 504ee0594f04cf22f5be63cb5536fddff186df70 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 22:56:51 -0400 Subject: [PATCH 15/22] #315 switch off dynamic tank fill mode if emergency coolant is required --- supervisor/facility.lua | 62 +++++++++++++++++++++++++++++----------- supervisor/unit.lua | 4 +++ supervisor/unitlogic.lua | 12 ++++++++ 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/supervisor/facility.lua b/supervisor/facility.lua index d03ad94..641f0c5 100644 --- a/supervisor/facility.lua +++ b/supervisor/facility.lua @@ -1,22 +1,27 @@ -local const = require("scada-common.constants") -local log = require("scada-common.log") -local rsio = require("scada-common.rsio") -local types = require("scada-common.types") -local util = require("scada-common.util") +local const = require("scada-common.constants") +local log = require("scada-common.log") +local rsio = require("scada-common.rsio") +local types = require("scada-common.types") +local util = require("scada-common.util") -local unit = require("supervisor.unit") +local unit = require("supervisor.unit") -local rsctl = require("supervisor.session.rsctl") +local qtypes = require("supervisor.session.rtu.qtypes") -local PROCESS = types.PROCESS -local PROCESS_NAMES = types.PROCESS_NAMES -local PRIO = types.ALARM_PRIORITY -local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE -local WASTE = types.WASTE_PRODUCT -local WASTE_MODE = types.WASTE_MODE +local rsctl = require("supervisor.session.rsctl") + +local PROCESS = types.PROCESS +local PROCESS_NAMES = types.PROCESS_NAMES +local PRIO = types.ALARM_PRIORITY +local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE +local CONTAINER_MODE = types.CONTAINER_MODE +local WASTE = types.WASTE_PRODUCT +local WASTE_MODE = types.WASTE_MODE local IO = rsio.IO +local DTV_RTU_S_DATA = qtypes.DTV_RTU_S_DATA + -- 7.14 kJ per blade for 1 mB of fissile fuel
-- 2856 FE per blade per 1 mB, 285.6 FE per blade per 0.1 mB (minimum) local POWER_PER_BLADE = util.joules_to_fe(7140) @@ -732,24 +737,47 @@ function facility.new(num_reactors, cooling_conf) self.io_ctl.digital_write(IO.F_ALARM, has_alarm) end - ----------------------------- - -- Update Waste Processing -- - ----------------------------- + ---------------- + -- Unit Tasks -- + ---------------- local insufficent_po_rate = false + local need_emcool = false + for i = 1, #self.units do local u = self.units[i] ---@type reactor_unit + + -- update auto waste processing if u.get_control_inf().waste_mode == WASTE_MODE.AUTO then if (u.get_sna_rate() * 10.0) < u.get_burn_rate() then insufficent_po_rate = true - break end end + + -- check if unit activated emergency coolant & uses facility tanks + if (cooling_conf.fac_tank_mode > 0) and u.is_emer_cool_tripped() and (cooling_conf.fac_tank_defs[i] == 2) then + need_emcool = true + end end + -- update waste product if self.waste_product == WASTE.PLUTONIUM or (self.pu_fallback and insufficent_po_rate) then self.current_waste_product = WASTE.PLUTONIUM else self.current_waste_product = self.waste_product end + + -- make sure dynamic tanks are allowing outflow if required + -- set all, rather than trying to determine which is for which (simpler & safer) + -- there should be no need for any to be in fill only mode + if need_emcool then + for i = 1, #self.tanks do + local session = self.tanks[i] ---@type unit_session + local tank = session.get_db() ---@type dynamicv_session_db + + if tank.state.container_mode == CONTAINER_MODE.FILL then + session.get_cmd_queue().push_data(DTV_RTU_S_DATA.SET_CONT_MODE, CONTAINER_MODE.BOTH) + end + end + end end -- call the update function of all units in the facility
diff --git a/supervisor/unit.lua b/supervisor/unit.lua index cae748d..1d3dd60 100644 --- a/supervisor/unit.lua +++ b/supervisor/unit.lua @@ -733,6 +733,10 @@ function unit.new(reactor_id, num_boilers, num_turbines) return false end + -- check if emergency coolant activation has been tripped + ---@nodiscard + function public.is_emer_cool_tripped() return self.emcool_opened end + -- get build properties of machines -- -- filter options diff --git a/supervisor/unitlogic.lua b/supervisor/unitlogic.lua index 6003ac8..080f529 100644 --- a/supervisor/unitlogic.lua +++ b/supervisor/unitlogic.lua @@ -10,11 +10,13 @@ local qtypes = require("supervisor.session.rtu.qtypes") local RPS_TRIP_CAUSE = types.RPS_TRIP_CAUSE local TRI_FAIL = types.TRI_FAIL +local CONTAINER_MODE = types.CONTAINER_MODE local DUMPING_MODE = types.DUMPING_MODE local PRIO = types.ALARM_PRIORITY local ALARM_STATE = types.ALARM_STATE local TBV_RTU_S_DATA = qtypes.TBV_RTU_S_DATA +local DTV_RTU_S_DATA = qtypes.DTV_RTU_S_DATA local IO = rsio.IO @@ -826,6 +828,16 @@ function logic.handle_redstone(self) end end + -- make sure dynamic tanks are allowing outflow + for i = 1, #self.tanks do + local session = self.tanks[i] ---@type unit_session + local tank = session.get_db() ---@type dynamicv_session_db + + if tank.state.container_mode == CONTAINER_MODE.FILL then + session.get_cmd_queue().push_data(DTV_RTU_S_DATA.SET_CONT_MODE, CONTAINER_MODE.BOTH) + end + end + if self.db.annunciator.EmergencyCoolant > 1 and not self.emcool_opened then log.info(util.c("UNIT ", self.r_id, " emergency coolant valve opened")) log.info(util.c("UNIT ", self.r_id, " turbines set to dump excess steam")) From d1799205650957c44f63e5669c444ecd54a33222 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 23:23:23 -0400 Subject: [PATCH 16/22] #232 option to disable flow view screen for legacy setups --- coordinator/config.lua | 5 +++- coordinator/coordinator.lua | 43 ++++++++++++++------------- coordinator/iocontrol.lua | 18 ++++++++--- coordinator/renderer.lua | 22 ++++++++++++-- coordinator/startup.lua | 7 ++++- coordinator/ui/layout/front_panel.lua | 3 ++ 6 files changed, 69 insertions(+), 29 deletions(-) diff --git a/coordinator/config.lua b/coordinator/config.lua index 7ea6ea2..66edbc7 100644 --- a/coordinator/config.lua +++ b/coordinator/config.lua @@ -26,6 +26,9 @@ config.SOUNDER_VOLUME = 1.0 -- true for 24 hour time on main view screen config.TIME_24_HOUR = true +-- disable flow view (for legacy layouts) +config.DISABLE_FLOW_VIEW = false + -- log path config.LOG_PATH = "/log.txt" -- log mode @@ -33,6 +36,6 @@ config.LOG_PATH = "/log.txt" -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 -- true to log verbose debug messages -config.LOG_DEBUG = false +config.LOG_DEBUG = true return config diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 36636bc..93f39b1 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -49,8 +49,9 @@ end -- configure monitor layout ---@param num_units integer number of units expected +---@param disable_flow_view boolean disable flow view (legacy) ---@return boolean success, monitors_struct? monitors -function coordinator.configure_monitors(num_units) +function coordinator.configure_monitors(num_units, disable_flow_view) ---@class monitors_struct local monitors = { primary = nil, @@ -72,7 +73,7 @@ function coordinator.configure_monitors(num_units) end -- we need a certain number of monitors (1 per unit + 1 primary display + 1 flow display) - local num_displays_needed = num_units + 2 + local num_displays_needed = num_units + util.trinary(disable_flow_view, 1, 2) if #names < num_displays_needed then local message = "not enough monitors connected (need " .. num_displays_needed .. ")" println(message) @@ -125,27 +126,29 @@ function coordinator.configure_monitors(num_units) -- FLOW MONITOR DISPLAY -- -------------------------- - local iface_flow_display = settings.get("FLOW_DISPLAY") ---@type boolean|string|nil + if not disable_flow_view then + local iface_flow_display = settings.get("FLOW_DISPLAY") ---@type boolean|string|nil - if not util.table_contains(names, iface_flow_display) then - println("flow monitor display is not connected") - local response = dialog.ask_y_n("would you like to change it", true) - if response == false then return false end - iface_flow_display = nil + if not util.table_contains(names, iface_flow_display) then + println("flow monitor display is not connected") + local response = dialog.ask_y_n("would you like to change it", true) + if response == false then return false end + iface_flow_display = nil + end + + while iface_flow_display == nil and #available > 0 do + iface_flow_display = ask_monitor(available) + end + + if type(iface_flow_display) ~= "string" then return false end + + settings.set("FLOW_DISPLAY", iface_flow_display) + util.filter_table(available, function (x) return x ~= iface_flow_display end) + + monitors.flow = ppm.get_periph(iface_flow_display) + monitors.flow_name = iface_flow_display end - while iface_flow_display == nil and #available > 0 do - iface_flow_display = ask_monitor(available) - end - - if type(iface_flow_display) ~= "string" then return false end - - settings.set("FLOW_DISPLAY", iface_flow_display) - util.filter_table(available, function (x) return x ~= iface_flow_display end) - - monitors.flow = ppm.get_periph(iface_flow_display) - monitors.flow_name = iface_flow_display - ------------------- -- UNIT DISPLAYS -- ------------------- diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index e4ff9b5..577b8e6 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -338,11 +338,21 @@ function iocontrol.fp_has_speaker(has_speaker) io.fp.ps.publish("has_speaker", h function iocontrol.fp_link_state(state) io.fp.ps.publish("link_state", state) end -- report monitor connection state ----@param id integer unit ID or 0 for main +---@param id string|integer unit ID for unit monitor, "main" for main monitor, or "flow" for flow monitor function iocontrol.fp_monitor_state(id, connected) - local name = "main_monitor" - if id > 0 then name = "unit_monitor_" .. id end - io.fp.ps.publish(name, connected) + local name = nil + + if id == "main" then + name = "main_monitor" + elseif id == "flow" then + name = "flow_monitor" + elseif type(id) == "number" then + name = "unit_monitor_" .. id + end + + if name ~= nil then + io.fp.ps.publish(name, connected) + end end -- report PKT firmware version and PKT session connection state diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index b4709b6..0ab0174 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -32,7 +32,8 @@ local engine = { main_display = nil, ---@type graphics_element|nil flow_display = nil, ---@type graphics_element|nil unit_displays = {} - } + }, + disable_flow_view = false } -- init a display to the "default", but set text scale to 0.5 @@ -50,13 +51,20 @@ local function _init_display(monitor) end end +-- disable the flow view +---@param disable boolean +function renderer.legacy_disable_flow_view(disable) + engine.disable_flow_view = disable +end + -- link to the monitor peripherals ---@param monitors monitors_struct function renderer.set_displays(monitors) engine.monitors = monitors -- report to front panel as connected - iocontrol.fp_monitor_state(0, true) + iocontrol.fp_monitor_state("main", engine.monitors.primary ~= nil) + iocontrol.fp_monitor_state("flow", engine.monitors.flow ~= nil) for i = 1, #engine.monitors.unit_displays do iocontrol.fp_monitor_state(i, true) end end @@ -64,7 +72,7 @@ end function renderer.init_displays() -- init primary and flow monitors _init_display(engine.monitors.primary) - _init_display(engine.monitors.flow) + if not engine.disable_flow_view then _init_display(engine.monitors.flow) end -- init unit displays for _, monitor in ipairs(engine.monitors.unit_displays) do @@ -91,6 +99,14 @@ function renderer.validate_main_display_width() return w == 164 end +-- check flow display width +---@nodiscard +---@return boolean width_okay +function renderer.validate_flow_display_width() + local w, _ = engine.monitors.flow.getSize() + return w == 164 +end + -- check display sizes ---@nodiscard ---@return boolean valid all unit display dimensions OK diff --git a/coordinator/startup.lua b/coordinator/startup.lua index f22326b..0633eba 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -84,7 +84,7 @@ local function main() iocontrol.init_fp(COORDINATOR_VERSION, comms.version) -- setup monitors - local configured, monitors = coordinator.configure_monitors(config.NUM_UNITS) + local configured, monitors = coordinator.configure_monitors(config.NUM_UNITS, config.DISABLE_FLOW_VIEW == true) if not configured or monitors == nil then println("startup> monitor setup failed") log.fatal("monitor configuration failed") @@ -92,6 +92,7 @@ local function main() end -- init renderer + renderer.legacy_disable_flow_view(config.DISABLE_FLOW_VIEW == true) renderer.set_displays(monitors) renderer.init_displays() @@ -99,6 +100,10 @@ local function main() println("startup> main display must be 8 blocks wide") log.fatal("main display not wide enough") return + elseif (config.DISABLE_FLOW_VIEW ~= true) and not renderer.validate_flow_display_width() then + println("startup> flow display must be 8 blocks wide") + log.fatal("flow display not wide enough") + return elseif not renderer.validate_unit_display_sizes() then println("startup> one or more unit display dimensions incorrect; they must be 4x4 blocks") log.fatal("unit display dimensions incorrect") diff --git a/coordinator/ui/layout/front_panel.lua b/coordinator/ui/layout/front_panel.lua index 207e213..11fe172 100644 --- a/coordinator/ui/layout/front_panel.lua +++ b/coordinator/ui/layout/front_panel.lua @@ -73,6 +73,9 @@ local function init(panel, num_units) local main_monitor = LED{parent=monitors,label="MAIN MONITOR",colors=cpair(colors.green,colors.green_off)} main_monitor.register(ps, "main_monitor", main_monitor.update) + local flow_monitor = LED{parent=monitors,label="FLOW MONITOR",colors=cpair(colors.green,colors.green_off)} + flow_monitor.register(ps, "flow_monitor", flow_monitor.update) + monitors.line_break() for i = 1, num_units do From 8c5289867cdc11eb7da008e3b18845541b658e0d Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 23:28:48 -0400 Subject: [PATCH 17/22] #232 updated coordinator monitor disconnect/reconnect handling for changes --- coordinator/renderer.lua | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index 0ab0174..d64ca98 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -263,7 +263,18 @@ function renderer.handle_disconnect(device) engine.monitors.primary = nil engine.ui.main_display = nil - iocontrol.fp_monitor_state(0, false) + iocontrol.fp_monitor_state("main", false) + elseif engine.monitors.flow == device then + if engine.ui.flow_display ~= nil then + -- delete element tree and clear root UI elements + engine.ui.flow_display.delete() + end + + is_used = true + engine.monitors.flow = nil + engine.ui.flow_display = nil + + iocontrol.fp_monitor_state("flow", false) else for idx, monitor in pairs(engine.monitors.unit_displays) do if monitor == device then @@ -311,7 +322,18 @@ function renderer.handle_reconnect(name, device) engine.dmesg_window.redraw() end - iocontrol.fp_monitor_state(0, true) + iocontrol.fp_monitor_state("main", true) + elseif engine.monitors.flow_name == name then + is_used = true + _init_display(device) + engine.monitors.flow = device + + if engine.ui_ready and (engine.ui.flow_display == nil) then + engine.ui.flow_display = DisplayBox{window=device,fg_bg=style.root} + flow_view(engine.ui.flow_display) + end + + iocontrol.fp_monitor_state("flow", true) else for idx, monitor in ipairs(engine.monitors.unit_name_map) do if monitor == name then From b93c6b7c6e7d01f985dba323b4e0710be21ba557 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 20 Aug 2023 23:53:49 -0400 Subject: [PATCH 18/22] fixes per luacheck --- graphics/elements/pipenet.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphics/elements/pipenet.lua b/graphics/elements/pipenet.lua index 599f1f1..fe57757 100644 --- a/graphics/elements/pipenet.lua +++ b/graphics/elements/pipenet.lua @@ -216,7 +216,7 @@ local function pipenet(args) for x = 1, args.width do for y = 1, args.height do local entry = map[x][y] ---@type _pipe_map_entry|false - local char = "" + local char local invert = false if entry ~= false then @@ -246,7 +246,7 @@ local function pipenet(args) if check(x + 1, y) then -- if right char = util.trinary(entry.atr, "\x93", "\x9c") invert = entry.atr - else -- not right + else -- not right char = util.trinary(entry.atr, "\x93", "\x94") invert = entry.atr end From fd2abad5cfee02a04c68796e0fdfc5fcc7498072 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 21 Aug 2023 21:35:32 -0400 Subject: [PATCH 19/22] changed some green/red indicators to be green/gray for contrast --- coordinator/ui/components/process_ctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coordinator/ui/components/process_ctl.lua b/coordinator/ui/components/process_ctl.lua index 22d1e22..9e01d6d 100644 --- a/coordinator/ui/components/process_ctl.lua +++ b/coordinator/ui/components/process_ctl.lua @@ -58,7 +58,7 @@ local function new_view(root, x, y) facility.scram_ack = scram.on_response facility.ack_alarms_ack = ack_a.on_response - local all_ok = IndicatorLight{parent=main,y=5,label="Unit Systems Online",colors=cpair(colors.green,colors.red)} + local all_ok = IndicatorLight{parent=main,y=5,label="Unit Systems Online",colors=ind_grn} local rad_mon = TriIndicatorLight{parent=main,label="Radiation Monitor",c1=colors.gray,c2=colors.yellow,c3=colors.green} local ind_mat = IndicatorLight{parent=main,label="Induction Matrix",colors=ind_grn} local sps = IndicatorLight{parent=main,label="SPS Connected",colors=ind_grn} @@ -70,7 +70,7 @@ local function new_view(root, x, y) main.line_break() - local auto_ready = IndicatorLight{parent=main,label="Configured Units Ready",colors=cpair(colors.green,colors.red)} + local auto_ready = IndicatorLight{parent=main,label="Configured Units Ready",colors=ind_grn} local auto_act = IndicatorLight{parent=main,label="Process Active",colors=ind_grn} local auto_ramp = IndicatorLight{parent=main,label="Process Ramping",colors=ind_wht,flash=true,period=period.BLINK_250_MS} local auto_sat = IndicatorLight{parent=main,label="Min/Max Burn Rate",colors=ind_yel} From 6e51e70b62fe26c421f7d97ae9c1ca83f03a7143 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 21 Aug 2023 21:37:56 -0400 Subject: [PATCH 20/22] #232 cleanup and fixes --- coordinator/config.lua | 2 +- coordinator/iocontrol.lua | 4 ++-- coordinator/ui/layout/flow_view.lua | 8 ++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/coordinator/config.lua b/coordinator/config.lua index 66edbc7..bdf01e2 100644 --- a/coordinator/config.lua +++ b/coordinator/config.lua @@ -36,6 +36,6 @@ config.LOG_PATH = "/log.txt" -- 1 = NEW (replaces existing file on start) config.LOG_MODE = 0 -- true to log verbose debug messages -config.LOG_DEBUG = true +config.LOG_DEBUG = false return config diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 4149500..78cd0e2 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -193,7 +193,7 @@ function iocontrol.init(conf, comms) io.facility.tank_list = tank_list end - -- create facility tanks + -- create facility tank tables for i = 1, #io.facility.tank_list do if io.facility.tank_list[i] == 2 then table.insert(io.facility.tank_ps_tbl, psil.create()) @@ -307,7 +307,7 @@ function iocontrol.init(conf, comms) end -- create tank tables - if entry.has_tank then + if io.facility.tank_defs[i] == 1 then table.insert(entry.tank_ps_tbl, psil.create()) table.insert(entry.tank_data_tbl, {}) end diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 5577604..292095c 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -247,7 +247,9 @@ local function init(main) PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray} + ----------------- -- tank valves -- + ----------------- local next_f_id = 1 @@ -265,7 +267,9 @@ local function init(main) end end + ------------------- -- dynamic tanks -- + ------------------- for i = 1, #tank_list do if tank_list[i] > 0 then @@ -324,7 +328,9 @@ local function init(main) end end + --------- -- SPS -- + --------- local sps = Div{parent=main,x=140,y=3,height=12} @@ -347,7 +353,9 @@ local function init(main) sps_rate.register(facility.sps_ps_tbl[1], "process_rate", function (r) sps_rate.update(r * 1000) end) + ---------------- -- statistics -- + ---------------- TextBox{parent=main,x=145,y=16,text="PROC. WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} local pr_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=bw_fg_bg} From 99a0b0a55a8c818343e44abc150c247fec622088 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 21 Aug 2023 21:44:15 -0400 Subject: [PATCH 21/22] #232 documentation and refactor --- coordinator/ui/layout/flow_view.lua | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 292095c..9d5ca67 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -56,9 +56,13 @@ local function init(main) local water_pipes = {} -- get the y offset for this unit index + ---@param idx integer unit index local function y_ofs(idx) return ((idx - 1) * 20) end - local function calc_fdef(start_idx, end_idx) + -- 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 + local function find_fdef(start_idx, end_idx) local first, last = 4, 0 for i = start_idx, end_idx do if tank_defs[i] == 2 then @@ -103,7 +107,7 @@ local function init(main) if facility.tank_mode == 1 then -- (1) 1 total facility tank (A A A A) - local first_fdef, last_fdef = calc_fdef(1, #tank_defs) + local first_fdef, last_fdef = find_fdef(1, #tank_defs) for i = 1, #tank_defs do local y = y_ofs(i) @@ -119,7 +123,7 @@ local function init(main) end elseif facility.tank_mode == 2 then -- (2) 2 total facility tanks (A A A B) - local first_fdef, last_fdef = calc_fdef(1, math.min(3, #tank_defs)) + local first_fdef, last_fdef = find_fdef(1, math.min(3, #tank_defs)) for i = 1, #tank_defs do local y = y_ofs(i) @@ -152,7 +156,7 @@ local function init(main) end elseif facility.tank_mode == 4 then -- (4) 2 total facility tanks (A B B B) - local first_fdef, last_fdef = calc_fdef(2, #tank_defs) + local first_fdef, last_fdef = find_fdef(2, #tank_defs) for i = 1, #tank_defs do local y = y_ofs(i) @@ -172,7 +176,7 @@ local function init(main) end elseif facility.tank_mode == 5 then -- (5) 3 total facility tanks (A A B C) - local first_fdef, last_fdef = calc_fdef(1, math.min(2, #tank_defs)) + local first_fdef, last_fdef = find_fdef(1, math.min(2, #tank_defs)) for i = 1, #tank_defs do local y = y_ofs(i) @@ -192,7 +196,7 @@ local function init(main) end elseif facility.tank_mode == 6 then -- (6) 3 total facility tanks (A B B C) - local first_fdef, last_fdef = calc_fdef(2, math.min(3, #tank_defs)) + local first_fdef, last_fdef = find_fdef(2, math.min(3, #tank_defs)) for i = 1, #tank_defs do local y = y_ofs(i) @@ -212,7 +216,7 @@ local function init(main) end elseif facility.tank_mode == 7 then -- (7) 3 total facility tanks (A B C C) - local first_fdef, last_fdef = calc_fdef(3, #tank_defs) + local first_fdef, last_fdef = find_fdef(3, #tank_defs) for i = 1, #tank_defs do local y = y_ofs(i) From c78f7e173a1f8ed2be09455eef69e876df3c06e8 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 21 Aug 2023 21:53:31 -0400 Subject: [PATCH 22/22] #232 cleanup, changed antimatter rate to be integer on main display --- coordinator/ui/components/process_ctl.lua | 2 +- coordinator/ui/components/unit_detail.lua | 4 ++-- coordinator/ui/layout/flow_view.lua | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coordinator/ui/components/process_ctl.lua b/coordinator/ui/components/process_ctl.lua index 9e01d6d..a8a46e7 100644 --- a/coordinator/ui/components/process_ctl.lua +++ b/coordinator/ui/components/process_ctl.lua @@ -346,7 +346,7 @@ local function new_view(root, x, y) local po_rate = DataIndicator{parent=rect,x=2,label="",unit="mB/t",format="%12.2f",value=0,lu_colors=lu_cpair,fg_bg=bw_fg_bg,width=17} TextBox{parent=rect,x=2,y=17,text="Antimatter Rate",height=1,width=17,fg_bg=style.label} - local am_rate = DataIndicator{parent=rect,x=2,label="",unit="\xb5B/t",format="%12.2f",value=0,lu_colors=lu_cpair,fg_bg=bw_fg_bg,width=17} + local am_rate = DataIndicator{parent=rect,x=2,label="",unit="\xb5B/t",format="%12d",value=0,lu_colors=lu_cpair,fg_bg=bw_fg_bg,width=17} pu_rate.register(facility.ps, "pu_rate", pu_rate.update) po_rate.register(facility.ps, "po_rate", po_rate.update) diff --git a/coordinator/ui/components/unit_detail.lua b/coordinator/ui/components/unit_detail.lua index 877feda..985e577 100644 --- a/coordinator/ui/components/unit_detail.lua +++ b/coordinator/ui/components/unit_detail.lua @@ -348,7 +348,7 @@ local function init(parent, id) TextBox{parent=burn_control,x=9,y=2,text="mB/t"} local set_burn = function () unit.set_burn(burn_rate.get_value()) end - local set_burn_btn = PushButton{parent=burn_control,x=14,y=2,text="SET",min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),dis_fg_bg=dis_colors,callback=set_burn} + local set_burn_btn = PushButton{parent=burn_control,x=14,y=2,text="SET",min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=style.wh_gray,dis_fg_bg=dis_colors,callback=set_burn} burn_rate.register(u_ps, "burn_rate", burn_rate.set_value) burn_rate.register(u_ps, "max_burn", burn_rate.set_max) @@ -480,7 +480,7 @@ local function init(parent, id) auto_div.line_break() local function set_group() unit.set_group(group.get_value() - 1) end - local set_grp_btn = PushButton{parent=auto_div,text="SET",x=4,min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),dis_fg_bg=cpair(colors.gray,colors.white),callback=set_group} + local set_grp_btn = PushButton{parent=auto_div,text="SET",x=4,min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=style.wh_gray,dis_fg_bg=cpair(colors.gray,colors.white),callback=set_group} auto_div.line_break() diff --git a/coordinator/ui/layout/flow_view.lua b/coordinator/ui/layout/flow_view.lua index 9d5ca67..54b14b5 100644 --- a/coordinator/ui/layout/flow_view.lua +++ b/coordinator/ui/layout/flow_view.lua @@ -289,7 +289,7 @@ local function init(main) local tank = Div{parent=main,x=3,y=7+y_offset,width=20,height=14} TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} - TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.white,colors.gray)} + TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.wh_gray} local tank_box = Rectangle{parent=tank,border=border(1,colors.gray,true),width=20,height=12}