From 6f3405949d6127e9320e8fbaaaf18cb58eb8dc9f Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 10 Sep 2022 10:42:56 -0400 Subject: [PATCH] #88 hold on rendering unit detail view until we get a status, added waiting animation --- coordinator/renderer.lua | 3 +- coordinator/ui/components/unit_detail.lua | 298 +++++++++++++++++++++ coordinator/ui/components/unit_waiting.lua | 33 +++ coordinator/ui/layout/unit_view.lua | 295 ++------------------ graphics/elements/animations/waiting.lua | 94 +++++++ scada-common/tcallbackdsp.lua | 5 - 6 files changed, 444 insertions(+), 284 deletions(-) create mode 100644 coordinator/ui/components/unit_detail.lua create mode 100644 coordinator/ui/components/unit_waiting.lua create mode 100644 graphics/elements/animations/waiting.lua diff --git a/coordinator/renderer.lua b/coordinator/renderer.lua index db874ae..11ffa1e 100644 --- a/coordinator/renderer.lua +++ b/coordinator/renderer.lua @@ -2,9 +2,10 @@ local log = require("scada-common.log") local iocontrol = require("coordinator.iocontrol") +local style = require("coordinator.ui.style") + local main_view = require("coordinator.ui.layout.main_view") local unit_view = require("coordinator.ui.layout.unit_view") -local style = require("coordinator.ui.style") local renderer = {} diff --git a/coordinator/ui/components/unit_detail.lua b/coordinator/ui/components/unit_detail.lua new file mode 100644 index 0000000..1572880 --- /dev/null +++ b/coordinator/ui/components/unit_detail.lua @@ -0,0 +1,298 @@ +-- +-- Reactor Unit SCADA Coordinator GUI +-- + +local tcallbackdsp = require("scada-common.tcallbackdsp") + +local iocontrol = require("coordinator.iocontrol") + +local style = require("coordinator.ui.style") + +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +local TextBox = require("graphics.elements.textbox") +local ColorMap = require("graphics.elements.colormap") + +local CoreMap = require("graphics.elements.indicators.coremap") +local DataIndicator = require("graphics.elements.indicators.data") +local IndicatorLight = require("graphics.elements.indicators.light") +local TriIndicatorLight = require("graphics.elements.indicators.trilight") + +local MultiButton = require("graphics.elements.controls.multi_button") +local PushButton = require("graphics.elements.controls.push_button") +local SCRAMButton = require("graphics.elements.controls.scram_button") +local StartButton = require("graphics.elements.controls.start_button") +local SpinboxNumeric = require("graphics.elements.controls.spinbox_numeric") + +local TEXT_ALIGN = core.graphics.TEXT_ALIGN + +local cpair = core.graphics.cpair + +-- create a unit view +---@param parent graphics_element parent +---@param id integer +local function init(parent, id) + local unit = iocontrol.get_db().units[id] ---@type ioctl_entry + local r_ps = unit.reactor_ps + local b_ps = unit.boiler_ps_tbl + local t_ps = unit.turbine_ps_tbl + + TextBox{parent=parent,text="Reactor Unit #" .. id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} + + local scram_fg_bg = cpair(colors.white, colors.gray) + local lu_cpair = cpair(colors.gray, colors.gray) + + -- main stats and core map -- + + ---@todo need to be checking actual reactor dimensions somehow + local core_map = CoreMap{parent=parent,x=2,y=3,reactor_l=18,reactor_w=18} + r_ps.subscribe("temp", core_map.update) + + local stat_fg_bg = cpair(colors.black,colors.white) + + TextBox{parent=parent,x=21,y=3,text="Core Temp",height=1,fg_bg=style.label} + local core_temp = DataIndicator{parent=parent,x=21,label="",format="%9.2f",value=0,unit="K",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} + r_ps.subscribe("temp", core_temp.update) + parent.line_break() + + TextBox{parent=parent,x=21,text="Burn Rate",height=1,width=12,fg_bg=style.label} + local act_burn_r = DataIndicator{parent=parent,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} + r_ps.subscribe("act_burn_rate", act_burn_r.update) + parent.line_break() + + TextBox{parent=parent,x=21,text="Commanded Burn Rate",height=2,width=12,fg_bg=style.label} + local burn_r = DataIndicator{parent=parent,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} + r_ps.subscribe("burn_rate", burn_r.update) + parent.line_break() + + TextBox{parent=parent,x=21,text="Heating Rate",height=1,width=12,fg_bg=style.label} + local heating_r = DataIndicator{parent=parent,x=21,label="",format="%11.0f",value=0,unit="",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} + r_ps.subscribe("heating_rate", heating_r.update) + parent.line_break() + + TextBox{parent=parent,x=21,text="Containment Integrity",height=2,width=12,fg_bg=style.label} + local integ = DataIndicator{parent=parent,x=21,label="",format="%9.0f",value=100,unit="%",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} + r_ps.subscribe("damage", function (x) integ.update(1.0 - (x / 100.0)) end) + parent.line_break() + + -- TextBox{parent=main,text="FL",x=21,y=19,height=1,width=2,fg_bg=style.label} + -- TextBox{parent=main,text="WS",x=24,y=19,height=1,width=2,fg_bg=style.label} + -- TextBox{parent=main,text="CL",x=28,y=19,height=1,width=2,fg_bg=style.label} + -- TextBox{parent=main,text="HC",x=31,y=19,height=1,width=2,fg_bg=style.label} + + -- local fuel = VerticalBar{parent=main,x=21,y=12,fg_bg=cpair(colors.black,colors.gray),height=6,width=2} + -- local waste = VerticalBar{parent=main,x=24,y=12,fg_bg=cpair(colors.brown,colors.gray),height=6,width=2} + -- local ccool = VerticalBar{parent=main,x=28,y=12,fg_bg=cpair(colors.lightBlue,colors.gray),height=6,width=2} + -- local hcool = VerticalBar{parent=main,x=31,y=12,fg_bg=cpair(colors.orange,colors.gray),height=6,width=2} + + -- annunciator -- + + local annunciator = Div{parent=parent,x=34,y=3} + + -- annunciator colors per IAEA-TECDOC-812 recommendations + + -- connectivity/basic state + 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 r_active = IndicatorLight{parent=annunciator,label="Active",colors=cpair(colors.green,colors.gray)} + ---@todo auto control as info sent here + local r_auto = IndicatorLight{parent=annunciator,label="Auto Control",colors=cpair(colors.blue,colors.gray)} + + r_ps.subscribe("PLCOnline", plc_online.update) + r_ps.subscribe("PLCHeartbeat", plc_hbeat.update) + r_ps.subscribe("status", r_active.update) + + annunciator.line_break() + + -- annunciator fields + 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_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_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="High Startup Rate",colors=cpair(colors.yellow,colors.gray)} + + r_ps.subscribe("ReactorSCRAM", r_scram.update) + r_ps.subscribe("ManualReactorSCRAM", r_mscrm.update) + r_ps.subscribe("RCPTrip", r_rtrip.update) + r_ps.subscribe("RCSFlowLow", r_cflow.update) + r_ps.subscribe("ReactorTempHigh", r_temp.update) + r_ps.subscribe("ReactorHighDeltaT", r_rhdt.update) + r_ps.subscribe("FuelInputRateLow", r_firl.update) + r_ps.subscribe("WasteLineOcclusion", r_wloc.update) + r_ps.subscribe("HighStartupRate", r_hsrt.update) + + annunciator.line_break() + + -- RPS + local rps_trp = IndicatorLight{parent=annunciator,label="RPS Trip",colors=cpair(colors.red,colors.gray)} + local rps_dmg = IndicatorLight{parent=annunciator,label="Damage Critical",colors=cpair(colors.yellow,colors.gray)} + local rps_exh = IndicatorLight{parent=annunciator,label="Excess Heated Coolant",colors=cpair(colors.yellow,colors.gray)} + local rps_exw = IndicatorLight{parent=annunciator,label="Excess Waste",colors=cpair(colors.yellow,colors.gray)} + local rps_tmp = IndicatorLight{parent=annunciator,label="High Core Temp",colors=cpair(colors.yellow,colors.gray)} + local rps_nof = IndicatorLight{parent=annunciator,label="No Fuel",colors=cpair(colors.yellow,colors.gray)} + local rps_noc = IndicatorLight{parent=annunciator,label="No Coolant",colors=cpair(colors.yellow,colors.gray)} + local rps_flt = IndicatorLight{parent=annunciator,label="PPM Fault",colors=cpair(colors.yellow,colors.gray)} + local rps_tmo = IndicatorLight{parent=annunciator,label="Timeout",colors=cpair(colors.yellow,colors.gray)} + + r_ps.subscribe("rps_tripped", rps_trp.update) + r_ps.subscribe("dmg_crit", rps_dmg.update) + r_ps.subscribe("ex_hcool", rps_exh.update) + r_ps.subscribe("ex_waste", rps_exw.update) + r_ps.subscribe("high_temp", rps_tmp.update) + r_ps.subscribe("no_fuel", rps_nof.update) + r_ps.subscribe("no_cool", rps_noc.update) + r_ps.subscribe("fault", rps_flt.update) + r_ps.subscribe("timeout", rps_tmo.update) + + annunciator.line_break() + + -- cooling + local c_brm = IndicatorLight{parent=annunciator,label="Boil Rate Mismatch",colors=cpair(colors.yellow,colors.gray)} + local c_cfm = IndicatorLight{parent=annunciator,label="Coolant Feed Mismatch",colors=cpair(colors.yellow,colors.gray)} + local c_sfm = IndicatorLight{parent=annunciator,label="Steam Feed Mismatch",colors=cpair(colors.yellow,colors.gray)} + local c_mwrf = IndicatorLight{parent=annunciator,label="Max Water Return Feed",colors=cpair(colors.yellow,colors.gray)} + local c_tbnt = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} + + r_ps.subscribe("BoilRateMismatch", c_brm.update) + r_ps.subscribe("CoolantFeedMismatch", c_cfm.update) + r_ps.subscribe("SteamFeedMismatch", c_sfm.update) + r_ps.subscribe("MaxWaterReturnFeed", c_mwrf.update) + r_ps.subscribe("TurbineTrip", c_tbnt.update) + + annunciator.line_break() + + -- machine-specific indicators + if unit.num_boilers > 0 then + TextBox{parent=parent,x=32,y=34,text="B1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local b1_hr = IndicatorLight{parent=annunciator,label="Heating Rate Low",colors=cpair(colors.yellow,colors.gray)} + b_ps[1].subscribe("HeatingRateLow", b1_hr.update) + end + if unit.num_boilers > 1 then + TextBox{parent=parent,x=32,text="B2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local b2_hr = IndicatorLight{parent=annunciator,label="Heating Rate Low",colors=cpair(colors.yellow,colors.gray)} + b_ps[2].subscribe("HeatingRateLow", b2_hr.update) + end + + if unit.num_boilers > 0 then + parent.line_break() + annunciator.line_break() + end + + TextBox{parent=parent,x=32,text="T1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t1_sdo = TriIndicatorLight{parent=annunciator,label="Steam Dump Open",c1=colors.gray,c2=colors.yellow,c3=colors.red} + t_ps[1].subscribe("SteamDumpOpen", t1_sdo.update) + + TextBox{parent=parent,x=32,text="T1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t1_tos = IndicatorLight{parent=annunciator,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} + t_ps[1].subscribe("TurbineOverSpeed", t1_tos.update) + + TextBox{parent=parent,x=32,text="T1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t1_trp = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} + t_ps[1].subscribe("TurbineTrip", t1_trp.update) + + parent.line_break() + annunciator.line_break() + + if unit.num_turbines > 1 then + TextBox{parent=parent,x=32,text="T2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t2_sdo = TriIndicatorLight{parent=annunciator,label="Steam Dump Open",c1=colors.gray,c2=colors.yellow,c3=colors.red} + t_ps[2].subscribe("SteamDumpOpen", t2_sdo.update) + + TextBox{parent=parent,x=32,text="T2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t2_tos = IndicatorLight{parent=annunciator,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} + t_ps[2].subscribe("TurbineOverSpeed", t2_tos.update) + + TextBox{parent=parent,x=32,text="T2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t2_trp = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} + t_ps[2].subscribe("TurbineTrip", t2_trp.update) + + parent.line_break() + annunciator.line_break() + end + + if unit.num_turbines > 2 then + TextBox{parent=parent,x=32,text="T3",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t3_sdo = TriIndicatorLight{parent=annunciator,label="Steam Dump Open",c1=colors.gray,c2=colors.yellow,c3=colors.red} + t_ps[3].subscribe("SteamDumpOpen", t3_sdo.update) + + TextBox{parent=parent,x=32,text="T3",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t3_tos = IndicatorLight{parent=annunciator,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} + t_ps[3].subscribe("TurbineOverSpeed", t3_tos.update) + + TextBox{parent=parent,x=32,text="T3",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} + local t3_trp = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} + t_ps[3].subscribe("TurbineTrip", t3_trp.update) + + annunciator.line_break() + end + + ---@todo radiation monitor + IndicatorLight{parent=annunciator,label="Radiation Monitor",colors=cpair(colors.green,colors.gray)} + IndicatorLight{parent=annunciator,label="Radiation Alarm",colors=cpair(colors.red,colors.gray)} + DataIndicator{parent=parent,x=34,y=51,label="",format="%10.1f",value=0,unit="mSv/h",lu_colors=lu_cpair,width=18,fg_bg=stat_fg_bg} + + -- reactor controls -- + + StartButton{parent=parent,x=12,y=44,callback=unit.start,fg_bg=scram_fg_bg} + SCRAMButton{parent=parent,x=22,y=44,callback=unit.scram,fg_bg=scram_fg_bg} + + local burn_control = Div{parent=parent,x=12,y=40,width=19,height=3,fg_bg=cpair(colors.gray,colors.white)} + local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=cpair(colors.black,colors.white)} + TextBox{parent=burn_control,x=9,y=2,text="mB/t"} + local set_burn = function () unit.set_burn(burn_rate.get_value()) end + 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),callback=set_burn} + + local opts = { + { + text = "Auto", + fg_bg = cpair(colors.black, colors.lightGray), + active_fg_bg = cpair(colors.white, colors.gray) + }, + { + text = "Pu", + fg_bg = cpair(colors.black, colors.lightGray), + active_fg_bg = cpair(colors.black, colors.lime) + }, + { + text = "Po", + fg_bg = cpair(colors.black, colors.lightGray), + active_fg_bg = cpair(colors.black, colors.cyan) + }, + { + text = "AM", + fg_bg = cpair(colors.black, colors.lightGray), + active_fg_bg = cpair(colors.black, colors.purple) + } + } + + ---@todo waste selection + local waste_sel_f = function (s) print("waste: " .. s) end + local waste_sel = Div{parent=parent,x=2,y=48,width=29,height=2,fg_bg=cpair(colors.black, colors.white)} + + MultiButton{parent=waste_sel,x=1,y=1,options=opts,callback=waste_sel_f,min_width=6,fg_bg=cpair(colors.black, colors.white)} + TextBox{parent=waste_sel,text="Waste Processing",alignment=TEXT_ALIGN.CENTER,x=1,y=1,height=1} + + ---@fixme test code + parent.line_break() + ColorMap{parent=parent,x=2,y=51} + + ---@fixme test code + local rps = true + local function _test_toggle() + rps_trp.update(rps) + rps = not rps + tcallbackdsp.dispatch(0.25, _test_toggle) + end + + ---@fixme test code + tcallbackdsp.dispatch(0.25, _test_toggle) + + return parent +end + +return init diff --git a/coordinator/ui/components/unit_waiting.lua b/coordinator/ui/components/unit_waiting.lua new file mode 100644 index 0000000..f7fd7ec --- /dev/null +++ b/coordinator/ui/components/unit_waiting.lua @@ -0,0 +1,33 @@ +-- +-- Reactor Unit SCADA Coordinator GUI +-- + +local style = require("coordinator.ui.style") + +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +local TextBox = require("graphics.elements.textbox") + +local WaitingAnim = require("graphics.elements.animations.waiting") + +local TEXT_ALIGN = core.graphics.TEXT_ALIGN + +local cpair = core.graphics.cpair + +-- create a unit waiting view +---@param parent graphics_element parent +---@param y integer y offset +local function init(parent, y) + -- bounding box div + local root = Div{parent=parent,x=1,y=y,height=5} + + local waiting_x = math.floor(parent.width() / 2) - 2 + + TextBox{parent=root,text="Waiting for status...",alignment=TEXT_ALIGN.CENTER,y=1,height=1,fg_bg=cpair(colors.black,style.root.bkg)} + WaitingAnim{parent=root,x=waiting_x,y=3,fg_bg=cpair(colors.blue,style.root.bkg)} + + return root +end + +return init diff --git a/coordinator/ui/layout/unit_view.lua b/coordinator/ui/layout/unit_view.lua index 52fcbce..c2ae072 100644 --- a/coordinator/ui/layout/unit_view.lua +++ b/coordinator/ui/layout/unit_view.lua @@ -2,34 +2,18 @@ -- Reactor Unit SCADA Coordinator GUI -- -local tcallbackdsp = require("scada-common.tcallbackdsp") +local tcallbackdsp = require("scada-common.tcallbackdsp") -local iocontrol = require("coordinator.iocontrol") +local iocontrol = require("coordinator.iocontrol") -local style = require("coordinator.ui.style") +local style = require("coordinator.ui.style") -local core = require("graphics.core") +local unit_wait = require("coordinator.ui.components.unit_waiting") +local unit_detail = require("coordinator.ui.components.unit_detail") -local DisplayBox = require("graphics.elements.displaybox") -local Div = require("graphics.elements.div") -local TextBox = require("graphics.elements.textbox") -local ColorMap = require("graphics.elements.colormap") +local core = require("graphics.core") -local CoreMap = require("graphics.elements.indicators.coremap") -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 TriIndicatorLight = require("graphics.elements.indicators.trilight") -local VerticalBar = require("graphics.elements.indicators.vbar") - -local MultiButton = require("graphics.elements.controls.multi_button") -local PushButton = require("graphics.elements.controls.push_button") -local SCRAMButton = require("graphics.elements.controls.scram_button") -local StartButton = require("graphics.elements.controls.start_button") -local SpinboxNumeric = require("graphics.elements.controls.spinbox_numeric") - -local TEXT_ALIGN = core.graphics.TEXT_ALIGN +local DisplayBox = require("graphics.elements.displaybox") local cpair = core.graphics.cpair local border = core.graphics.border @@ -38,266 +22,21 @@ local border = core.graphics.border ---@param monitor table ---@param id integer local function init(monitor, id) - local unit = iocontrol.get_db().units[id] ---@type ioctl_entry - local r_ps = unit.reactor_ps - local b_ps = unit.boiler_ps_tbl - local t_ps = unit.turbine_ps_tbl - local main = DisplayBox{window=monitor,fg_bg=style.root} - TextBox{parent=main,text="Reactor Unit #" .. id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} + unit_wait(main, 20) - local scram_fg_bg = cpair(colors.white, colors.gray) - local lu_cpair = cpair(colors.gray, colors.gray) - - -- main stats and core map -- - - ---@todo need to be checking actual reactor dimensions somehow - local core_map = CoreMap{parent=main,x=2,y=3,reactor_l=18,reactor_w=18} - r_ps.subscribe("temp", core_map.update) - - local stat_fg_bg = cpair(colors.black,colors.white) - - TextBox{parent=main,x=21,y=3,text="Core Temp",height=1,fg_bg=style.label} - local core_temp = DataIndicator{parent=main,x=21,label="",format="%9.2f",value=0,unit="K",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} - r_ps.subscribe("temp", core_temp.update) - main.line_break() - - TextBox{parent=main,x=21,text="Burn Rate",height=1,width=12,fg_bg=style.label} - local act_burn_r = DataIndicator{parent=main,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} - r_ps.subscribe("act_burn_rate", act_burn_r.update) - main.line_break() - - TextBox{parent=main,x=21,text="Commanded Burn Rate",height=2,width=12,fg_bg=style.label} - local burn_r = DataIndicator{parent=main,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} - r_ps.subscribe("burn_rate", burn_r.update) - main.line_break() - - TextBox{parent=main,x=21,text="Heating Rate",height=1,width=12,fg_bg=style.label} - local heating_r = DataIndicator{parent=main,x=21,label="",format="%11.0f",value=0,unit="",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} - r_ps.subscribe("heating_rate", heating_r.update) - main.line_break() - - TextBox{parent=main,x=21,text="Containment Integrity",height=2,width=12,fg_bg=style.label} - local integ = DataIndicator{parent=main,x=21,label="",format="%9.0f",value=100,unit="%",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg} - r_ps.subscribe("damage", function (x) integ.update(1.0 - (x / 100.0)) end) - main.line_break() - - -- TextBox{parent=main,text="FL",x=21,y=19,height=1,width=2,fg_bg=style.label} - -- TextBox{parent=main,text="WS",x=24,y=19,height=1,width=2,fg_bg=style.label} - -- TextBox{parent=main,text="CL",x=28,y=19,height=1,width=2,fg_bg=style.label} - -- TextBox{parent=main,text="HC",x=31,y=19,height=1,width=2,fg_bg=style.label} - - -- local fuel = VerticalBar{parent=main,x=21,y=12,fg_bg=cpair(colors.black,colors.gray),height=6,width=2} - -- local waste = VerticalBar{parent=main,x=24,y=12,fg_bg=cpair(colors.brown,colors.gray),height=6,width=2} - -- local ccool = VerticalBar{parent=main,x=28,y=12,fg_bg=cpair(colors.lightBlue,colors.gray),height=6,width=2} - -- local hcool = VerticalBar{parent=main,x=31,y=12,fg_bg=cpair(colors.orange,colors.gray),height=6,width=2} - - -- annunciator -- - - local annunciator = Div{parent=main,x=34,y=3} - - -- annunciator colors per IAEA-TECDOC-812 recommendations - - -- connectivity/basic state - 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 r_active = IndicatorLight{parent=annunciator,label="Active",colors=cpair(colors.green,colors.gray)} - ---@todo auto control as info sent here - local r_auto = IndicatorLight{parent=annunciator,label="Auto Control",colors=cpair(colors.blue,colors.gray)} - - r_ps.subscribe("PLCOnline", plc_online.update) - r_ps.subscribe("PLCHeartbeat", plc_hbeat.update) - r_ps.subscribe("status", r_active.update) - - annunciator.line_break() - - -- annunciator fields - 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_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_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="High Startup Rate",colors=cpair(colors.yellow,colors.gray)} - - r_ps.subscribe("ReactorSCRAM", r_scram.update) - r_ps.subscribe("ManualReactorSCRAM", r_mscrm.update) - r_ps.subscribe("RCPTrip", r_rtrip.update) - r_ps.subscribe("RCSFlowLow", r_cflow.update) - r_ps.subscribe("ReactorTempHigh", r_temp.update) - r_ps.subscribe("ReactorHighDeltaT", r_rhdt.update) - r_ps.subscribe("FuelInputRateLow", r_firl.update) - r_ps.subscribe("WasteLineOcclusion", r_wloc.update) - r_ps.subscribe("HighStartupRate", r_hsrt.update) - - annunciator.line_break() - - -- RPS - local rps_trp = IndicatorLight{parent=annunciator,label="RPS Trip",colors=cpair(colors.red,colors.gray)} - local rps_dmg = IndicatorLight{parent=annunciator,label="Damage Critical",colors=cpair(colors.yellow,colors.gray)} - local rps_exh = IndicatorLight{parent=annunciator,label="Excess Heated Coolant",colors=cpair(colors.yellow,colors.gray)} - local rps_exw = IndicatorLight{parent=annunciator,label="Excess Waste",colors=cpair(colors.yellow,colors.gray)} - local rps_tmp = IndicatorLight{parent=annunciator,label="High Core Temp",colors=cpair(colors.yellow,colors.gray)} - local rps_nof = IndicatorLight{parent=annunciator,label="No Fuel",colors=cpair(colors.yellow,colors.gray)} - local rps_noc = IndicatorLight{parent=annunciator,label="No Coolant",colors=cpair(colors.yellow,colors.gray)} - local rps_flt = IndicatorLight{parent=annunciator,label="PPM Fault",colors=cpair(colors.yellow,colors.gray)} - local rps_tmo = IndicatorLight{parent=annunciator,label="Timeout",colors=cpair(colors.yellow,colors.gray)} - - r_ps.subscribe("rps_tripped", rps_trp.update) - r_ps.subscribe("dmg_crit", rps_dmg.update) - r_ps.subscribe("ex_hcool", rps_exh.update) - r_ps.subscribe("ex_waste", rps_exw.update) - r_ps.subscribe("high_temp", rps_tmp.update) - r_ps.subscribe("no_fuel", rps_nof.update) - r_ps.subscribe("no_cool", rps_noc.update) - r_ps.subscribe("fault", rps_flt.update) - r_ps.subscribe("timeout", rps_tmo.update) - - annunciator.line_break() - - -- cooling - local c_brm = IndicatorLight{parent=annunciator,label="Boil Rate Mismatch",colors=cpair(colors.yellow,colors.gray)} - local c_cfm = IndicatorLight{parent=annunciator,label="Coolant Feed Mismatch",colors=cpair(colors.yellow,colors.gray)} - local c_sfm = IndicatorLight{parent=annunciator,label="Steam Feed Mismatch",colors=cpair(colors.yellow,colors.gray)} - local c_mwrf = IndicatorLight{parent=annunciator,label="Max Water Return Feed",colors=cpair(colors.yellow,colors.gray)} - local c_tbnt = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} - - r_ps.subscribe("BoilRateMismatch", c_brm.update) - r_ps.subscribe("CoolantFeedMismatch", c_cfm.update) - r_ps.subscribe("SteamFeedMismatch", c_sfm.update) - r_ps.subscribe("MaxWaterReturnFeed", c_mwrf.update) - r_ps.subscribe("TurbineTrip", c_tbnt.update) - - annunciator.line_break() - - -- machine-specific indicators - if unit.num_boilers > 0 then - TextBox{parent=main,x=32,y=34,text="B1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local b1_hr = IndicatorLight{parent=annunciator,label="Heating Rate Low",colors=cpair(colors.yellow,colors.gray)} - b_ps[1].subscribe("HeatingRateLow", b1_hr.update) - end - if unit.num_boilers > 1 then - TextBox{parent=main,x=32,text="B2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local b2_hr = IndicatorLight{parent=annunciator,label="Heating Rate Low",colors=cpair(colors.yellow,colors.gray)} - b_ps[2].subscribe("HeatingRateLow", b2_hr.update) + -- block waiting for initial status + local function show_view() + local unit = iocontrol.get_db().units[id] ---@type ioctl_entry + if unit.reactor_data.last_status_update ~= nil then + unit_detail(main, id) + else + tcallbackdsp.dispatch(1, show_view) + end end - if unit.num_boilers > 0 then - main.line_break() - annunciator.line_break() - end - - TextBox{parent=main,x=32,text="T1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t1_sdo = TriIndicatorLight{parent=annunciator,label="Steam Dump Open",c1=colors.gray,c2=colors.yellow,c3=colors.red} - t_ps[1].subscribe("SteamDumpOpen", t1_sdo.update) - - TextBox{parent=main,x=32,text="T1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t1_tos = IndicatorLight{parent=annunciator,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} - t_ps[1].subscribe("TurbineOverSpeed", t1_tos.update) - - TextBox{parent=main,x=32,text="T1",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t1_trp = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} - t_ps[1].subscribe("TurbineTrip", t1_trp.update) - - main.line_break() - annunciator.line_break() - - if unit.num_turbines > 1 then - TextBox{parent=main,x=32,text="T2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t2_sdo = TriIndicatorLight{parent=annunciator,label="Steam Dump Open",c1=colors.gray,c2=colors.yellow,c3=colors.red} - t_ps[2].subscribe("SteamDumpOpen", t2_sdo.update) - - TextBox{parent=main,x=32,text="T2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t2_tos = IndicatorLight{parent=annunciator,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} - t_ps[2].subscribe("TurbineOverSpeed", t2_tos.update) - - TextBox{parent=main,x=32,text="T2",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t2_trp = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} - t_ps[2].subscribe("TurbineTrip", t2_trp.update) - - main.line_break() - annunciator.line_break() - end - - if unit.num_turbines > 2 then - TextBox{parent=main,x=32,text="T3",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t3_sdo = TriIndicatorLight{parent=annunciator,label="Steam Dump Open",c1=colors.gray,c2=colors.yellow,c3=colors.red} - t_ps[3].subscribe("SteamDumpOpen", t3_sdo.update) - - TextBox{parent=main,x=32,text="T3",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t3_tos = IndicatorLight{parent=annunciator,label="Turbine Over Speed",colors=cpair(colors.red,colors.gray)} - t_ps[3].subscribe("TurbineOverSpeed", t3_tos.update) - - TextBox{parent=main,x=32,text="T3",width=2,height=1,fg_bg=cpair(colors.black, colors.white)} - local t3_trp = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)} - t_ps[3].subscribe("TurbineTrip", t3_trp.update) - - annunciator.line_break() - end - - ---@todo radiation monitor - IndicatorLight{parent=annunciator,label="Radiation Monitor",colors=cpair(colors.green,colors.gray)} - IndicatorLight{parent=annunciator,label="Radiation Alarm",colors=cpair(colors.red,colors.gray)} - DataIndicator{parent=main,x=34,y=51,label="",format="%10.1f",value=0,unit="mSv/h",lu_colors=lu_cpair,width=18,fg_bg=stat_fg_bg} - - -- reactor controls -- - - StartButton{parent=main,x=12,y=44,callback=unit.start,fg_bg=scram_fg_bg} - SCRAMButton{parent=main,x=22,y=44,callback=unit.scram,fg_bg=scram_fg_bg} - - local burn_control = Div{parent=main,x=12,y=40,width=19,height=3,fg_bg=cpair(colors.gray,colors.white)} - local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=cpair(colors.black,colors.white)} - TextBox{parent=burn_control,x=9,y=2,text="mB/t"} - local set_burn = function () unit.set_burn(burn_rate.get_value()) end - 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),callback=set_burn} - - local opts = { - { - text = "Auto", - fg_bg = cpair(colors.black, colors.lightGray), - active_fg_bg = cpair(colors.white, colors.gray) - }, - { - text = "Pu", - fg_bg = cpair(colors.black, colors.lightGray), - active_fg_bg = cpair(colors.black, colors.lime) - }, - { - text = "Po", - fg_bg = cpair(colors.black, colors.lightGray), - active_fg_bg = cpair(colors.black, colors.cyan) - }, - { - text = "AM", - fg_bg = cpair(colors.black, colors.lightGray), - active_fg_bg = cpair(colors.black, colors.purple) - } - } - - ---@todo waste selection - local waste_sel_f = function (s) print("waste: " .. s) end - local waste_sel = Div{parent=main,x=2,y=48,width=29,height=2,fg_bg=cpair(colors.black, colors.white)} - - MultiButton{parent=waste_sel,x=1,y=1,options=opts,callback=waste_sel_f,min_width=6,fg_bg=cpair(colors.black, colors.white)} - TextBox{parent=waste_sel,text="Waste Processing",alignment=TEXT_ALIGN.CENTER,x=1,y=1,height=1} - - ---@fixme test code - main.line_break() - ColorMap{parent=main,x=2,y=51} - - ---@fixme test code - local rps = true - local function _test_toggle() - rps_trp.update(rps) - rps = not rps - tcallbackdsp.dispatch(0.25, _test_toggle) - end - - ---@fixme test code - tcallbackdsp.dispatch(0.25, _test_toggle) + tcallbackdsp.dispatch(1, show_view) return main end diff --git a/graphics/elements/animations/waiting.lua b/graphics/elements/animations/waiting.lua new file mode 100644 index 0000000..938d43b --- /dev/null +++ b/graphics/elements/animations/waiting.lua @@ -0,0 +1,94 @@ +-- Loading/Waiting Animation Graphics Element + +local tcd = require("scada-common.tcallbackdsp") + +local element = require("graphics.element") + +---@class waiting_args +---@field parent graphics_element +---@field id? string element id +---@field x? integer 1 if omitted +---@field y? integer 1 if omitted +---@field fg_bg? cpair foreground/background colors + +-- new waiting animation element +---@param args waiting_args +---@return graphics_element element, element_id id +local function waiting(args) + local state = 0 + + args.width = 4 + args.height = 3 + + -- create new graphics element base object + local e = element.new(args) + + local blit_fg = e.fg_bg.blit_fgd + local blit_bg = e.fg_bg.blit_bkg + local blit_fg_2x = e.fg_bg.blit_fgd .. e.fg_bg.blit_fgd + local blit_bg_2x = e.fg_bg.blit_bkg .. e.fg_bg.blit_bkg + + local function update() + print("updated waiting") + e.window.clear() + + if state >= 0 and state < 7 then + -- top + e.window.setCursorPos(1 + math.floor(state / 2), 1) + if state % 2 == 0 then + e.window.blit("\x8f", blit_fg, blit_bg) + else + e.window.blit("\x8a\x85", blit_fg_2x, blit_bg_2x) + end + + -- bottom + e.window.setCursorPos(4 - math.ceil(state / 2), 3) + if state % 2 == 0 then + e.window.blit("\x8f", blit_fg, blit_bg) + else + e.window.blit("\x8a\x85", blit_fg_2x, blit_bg_2x) + end + else + local st = state - 7 + + -- right + if st % 3 == 0 then + e.window.setCursorPos(4, 1 + math.floor(st / 3)) + e.window.blit("\x83", blit_bg, blit_fg) + elseif st % 3 == 1 then + e.window.setCursorPos(4, 1 + math.floor(st / 3)) + e.window.blit("\x8f", blit_bg, blit_fg) + e.window.setCursorPos(4, 2 + math.floor(st / 3)) + e.window.blit("\x83", blit_fg, blit_bg) + else + e.window.setCursorPos(4, 2 + math.floor(st / 3)) + e.window.blit("\x8f", blit_fg, blit_bg) + end + + -- left + if st % 3 == 0 then + e.window.setCursorPos(1, 3 - math.floor(st / 3)) + e.window.blit("\x83", blit_fg, blit_bg) + e.window.setCursorPos(1, 2 - math.floor(st / 3)) + e.window.blit("\x8f", blit_bg, blit_fg) + elseif st % 3 == 1 then + e.window.setCursorPos(1, 2 - math.floor(st / 3)) + e.window.blit("\x83", blit_bg, blit_fg) + else + e.window.setCursorPos(1, 2 - math.floor(st / 3)) + e.window.blit("\x8f", blit_fg, blit_bg) + end + end + + state = state + 1 + if state >= 12 then state = 0 end + + tcd.dispatch(0.5, update) + end + + update() + + return e.get() +end + +return waiting diff --git a/scada-common/tcallbackdsp.lua b/scada-common/tcallbackdsp.lua index 5cb24e3..65b8ec9 100644 --- a/scada-common/tcallbackdsp.lua +++ b/scada-common/tcallbackdsp.lua @@ -2,9 +2,6 @@ -- Timer Callback Dispatcher -- -local log = require("scada-common.log") -local util = require("scada-common.util") - local tcallbackdsp = {} local registry = {} @@ -13,7 +10,6 @@ local registry = {} ---@param time number seconds ---@param f function callback function function tcallbackdsp.dispatch(time, f) - log.debug(util.c("TCD: dispatching ", f, " for call in ", time, " seconds")) ---@diagnostic disable-next-line: undefined-field registry[os.startTimer(time)] = { callback = f } end @@ -22,7 +18,6 @@ end ---@param event integer timer event timer ID function tcallbackdsp.handle(event) if registry[event] ~= nil then - log.debug(util.c("TCD: executing callback ", registry[event].callback, " for timer ", event)) registry[event].callback() registry[event] = nil end