From a268a770f2d017f72daa1670060300b29faa1b18 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 22 May 2024 21:55:59 +0000 Subject: [PATCH 01/44] #200 pocket alarm/status informational display, ECAM style --- coordinator/iocontrol.lua | 7 +- coordinator/session/pocket.lua | 4 +- pocket/iocontrol.lua | 512 +++++++++++++++++++++++---------- pocket/pocket.lua | 2 +- pocket/ui/main.lua | 2 +- pocket/ui/pages/unit_page.lua | 30 +- supervisor/unit.lua | 6 +- 7 files changed, 400 insertions(+), 163 deletions(-) diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index bf73e1f..08fad13 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -247,6 +247,9 @@ function iocontrol.init(conf, comms, temp_scale) waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM, waste_product = types.WASTE_PRODUCT.PLUTONIUM, + last_rate_change_ms = 0, + turbine_flow_stable = false, + -- auto control group a_group = 0, @@ -1214,9 +1217,11 @@ function iocontrol.update_unit_statuses(statuses) local unit_state = status[5] if type(unit_state) == "table" then - if #unit_state == 6 then + if #unit_state == 8 then unit.waste_mode = unit_state[5] unit.waste_product = unit_state[6] + unit.last_rate_change_ms = unit_state[7] + unit.turbine_flow_stable = unit_state[8] unit.unit_ps.publish("U_StatusLine1", unit_state[1]) unit.unit_ps.publish("U_StatusLine2", unit_state[2]) diff --git a/coordinator/session/pocket.lua b/coordinator/session/pocket.lua index 09ec15c..cd03fc1 100644 --- a/coordinator/session/pocket.lua +++ b/coordinator/session/pocket.lua @@ -152,7 +152,9 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout) u.reactor_data, u.boiler_data_tbl, u.turbine_data_tbl, - u.tank_data_tbl + u.tank_data_tbl, + u.last_rate_change_ms, + u.turbine_flow_stable } _send(CRDN_TYPE.API_GET_UNIT, data) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index f6cbadb..b88fb39 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -2,6 +2,7 @@ -- I/O Control for Pocket Integration with Supervisor & Coordinator -- +local const = require("scada-common.constants") local log = require("scada-common.log") local psil = require("scada-common.psil") local types = require("scada-common.types") @@ -454,6 +455,9 @@ function iocontrol.init_fac(conf, temp_scale) waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM, waste_product = types.WASTE_PRODUCT.PLUTONIUM, + last_rate_change_ms = 0, + turbine_flow_stable = false, + -- auto control group a_group = 0, @@ -585,186 +589,384 @@ function iocontrol.record_facility_data(data) return valid end +local function tripped(state) return state == ALARM_STATE.TRIPPED or state == ALARM_STATE.ACKED end + -- update unit status data from API_GET_UNIT ---@param data table function iocontrol.record_unit_data(data) - if type(data[1]) == "number" and io.units[data[1]] then - local unit = io.units[data[1]] ---@type pioctl_unit + local unit = io.units[data[1]] ---@type pioctl_unit - unit.connected = data[2] - unit.rtu_hw = data[3] - unit.alarms = data[4] + unit.connected = data[2] + unit.rtu_hw = data[3] + unit.alarms = data[4] - --#region Annunciator + --#region Annunciator - unit.annunciator = data[5] + unit.annunciator = data[5] - local rcs_disconn, rcs_warn, rcs_hazard = false, false, false + local rcs_disconn, rcs_warn, rcs_hazard = false, false, false - for key, val in pairs(unit.annunciator) do - if key == "BoilerOnline" or key == "TurbineOnline" then - -- split up online arrays - local every = true - for id = 1, #val do - every = every and val[id] - unit.boiler_ps_tbl[id].publish(key, val[id]) - end + for key, val in pairs(unit.annunciator) do + if key == "BoilerOnline" or key == "TurbineOnline" then + -- split up online arrays + local every = true + for id = 1, #val do + every = every and val[id] + unit.boiler_ps_tbl[id].publish(key, val[id]) + end - if not every then rcs_disconn = true end + if not every then rcs_disconn = true end - unit.unit_ps.publish("U_" .. key, every) - elseif key == "HeatingRateLow" or key == "WaterLevelLow" then - -- split up array for all boilers - local any = false - for id = 1, #val do - any = any or val[id] - unit.boiler_ps_tbl[id].publish(key, val[id]) - end + unit.unit_ps.publish("U_" .. key, every) + elseif key == "HeatingRateLow" or key == "WaterLevelLow" then + -- split up array for all boilers + local any = false + for id = 1, #val do + any = any or val[id] + unit.boiler_ps_tbl[id].publish(key, val[id]) + end - if key == "HeatingRateLow" and any then - rcs_warn = true - elseif key == "WaterLevelLow" and any then - rcs_hazard = true - end + if key == "HeatingRateLow" and any then + rcs_warn = true + elseif key == "WaterLevelLow" and any then + rcs_hazard = true + end - unit.unit_ps.publish("U_" .. key, any) - elseif key == "SteamDumpOpen" or key == "TurbineOverSpeed" or key == "GeneratorTrip" or key == "TurbineTrip" then - -- split up array for all turbines - local any = false - for id = 1, #val do - any = any or val[id] - unit.turbine_ps_tbl[id].publish(key, val[id]) - end + unit.unit_ps.publish("U_" .. key, any) + elseif key == "SteamDumpOpen" or key == "TurbineOverSpeed" or key == "GeneratorTrip" or key == "TurbineTrip" then + -- split up array for all turbines + local any = false + for id = 1, #val do + any = any or val[id] + unit.turbine_ps_tbl[id].publish(key, val[id]) + end - if key == "GeneratorTrip" and any then - rcs_warn = true - elseif (key == "TurbineOverSpeed" or key == "TurbineTrip") and any then - rcs_hazard = true - end + if key == "GeneratorTrip" and any then + rcs_warn = true + elseif (key == "TurbineOverSpeed" or key == "TurbineTrip") and any then + rcs_hazard = true + end - unit.unit_ps.publish("U_" .. key, any) + unit.unit_ps.publish("U_" .. key, any) + else + -- non-table fields + unit.unit_ps.publish(key, val) + end + end + + local anc = unit.annunciator + rcs_hazard = rcs_hazard or anc.RCPTrip + rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or + anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch or anc.MaxWaterReturnFeed + + local rcs_status = 4 + if rcs_hazard then + rcs_status = 2 + elseif rcs_warn then + rcs_status = 3 + elseif rcs_disconn then + rcs_status = 1 + end + + unit.unit_ps.publish("U_RCS", rcs_status) + + --#endregion + + --#region Reactor Data + + unit.reactor_data = data[6] + + local control_status = 1 + local reactor_status = 1 + local rps_status = 1 + + if unit.connected then + -- update RPS status + if unit.reactor_data.rps_tripped then + control_status = 2 + rps_status = util.trinary(unit.reactor_data.rps_trip_cause == "manual", 3, 2) + else rps_status = 4 end + + -- update reactor/control status + if unit.reactor_data.mek_status.status then + reactor_status = 4 + control_status = util.trinary(unit.annunciator.AutoControl, 4, 3) + else + if unit.reactor_data.no_reactor then + reactor_status = 2 + elseif not unit.reactor_data.formed or unit.reactor_data.rps_status.force_dis then + reactor_status = 3 else - -- non-table fields + reactor_status = 4 + end + end + + for key, val in pairs(unit.reactor_data) do + if key ~= "rps_status" and key ~= "mek_struct" and key ~= "mek_status" then unit.unit_ps.publish(key, val) end end - local anc = unit.annunciator - rcs_hazard = rcs_hazard or anc.RCPTrip - rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or - anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch or anc.MaxWaterReturnFeed - - local rcs_status = 4 - if rcs_hazard then - rcs_status = 2 - elseif rcs_warn then - rcs_status = 3 - elseif rcs_disconn then - rcs_status = 1 - end - - unit.unit_ps.publish("U_RCS", rcs_status) - - --#endregion - - --#region Reactor Data - - unit.reactor_data = data[6] - - local control_status = 1 - local reactor_status = 1 - local rps_status = 1 - - if unit.connected then - -- update RPS status - if unit.reactor_data.rps_tripped then - control_status = 2 - rps_status = util.trinary(unit.reactor_data.rps_trip_cause == "manual", 3, 2) - else rps_status = 4 end - - -- update reactor/control status - if unit.reactor_data.mek_status.status then - reactor_status = 4 - control_status = util.trinary(unit.annunciator.AutoControl, 4, 3) - else - if unit.reactor_data.no_reactor then - reactor_status = 2 - elseif not unit.reactor_data.formed or unit.reactor_data.rps_status.force_dis then - reactor_status = 3 - else - reactor_status = 4 - end - end - - for key, val in pairs(unit.reactor_data) do - if key ~= "rps_status" and key ~= "mek_struct" and key ~= "mek_status" then - unit.unit_ps.publish(key, val) - end - end - - if type(unit.reactor_data.rps_status) == "table" then - for key, val in pairs(unit.reactor_data.rps_status) do - unit.unit_ps.publish(key, val) - end - end - - if type(unit.reactor_data.mek_status) == "table" then - for key, val in pairs(unit.reactor_data.mek_status) do - unit.unit_ps.publish(key, val) - end + if type(unit.reactor_data.rps_status) == "table" then + for key, val in pairs(unit.reactor_data.rps_status) do + unit.unit_ps.publish(key, val) end end - unit.unit_ps.publish("U_ControlStatus", control_status) - unit.unit_ps.publish("U_ReactorStatus", reactor_status) - unit.unit_ps.publish("U_RPS", rps_status) - - --#endregion - - unit.boiler_data_tbl = data[7] - - for id = 1, #unit.boiler_data_tbl do - local boiler = unit.boiler_data_tbl[id] ---@type boilerv_session_db - local ps = unit.boiler_ps_tbl[id] ---@type psil - - local boiler_status = 1 - - if unit.rtu_hw.boilers[id].connected then - if unit.rtu_hw.boilers[id].faulted then - boiler_status = 3 - elseif boiler.formed then - boiler_status = 4 - else - boiler_status = 2 - end + if type(unit.reactor_data.mek_status) == "table" then + for key, val in pairs(unit.reactor_data.mek_status) do + unit.unit_ps.publish(key, val) end - - ps.publish("BoilerStatus", boiler_status) end - - unit.turbine_data_tbl = data[8] - - for id = 1, #unit.turbine_data_tbl do - local turbine = unit.turbine_data_tbl[id] ---@type turbinev_session_db - local ps = unit.turbine_ps_tbl[id] ---@type psil - - local turbine_status = 1 - - if unit.rtu_hw.turbines[id].connected then - if unit.rtu_hw.turbines[id].faulted then - turbine_status = 3 - elseif turbine.formed then - turbine_status = 4 - else - turbine_status = 2 - end - end - - ps.publish("TurbineStatus", turbine_status) - end - - unit.tank_data_tbl = data[9] end + + unit.unit_ps.publish("U_ControlStatus", control_status) + unit.unit_ps.publish("U_ReactorStatus", reactor_status) + unit.unit_ps.publish("U_RPS", rps_status) + + --#endregion + + --#region RTU Devices + + unit.boiler_data_tbl = data[7] + + for id = 1, #unit.boiler_data_tbl do + local boiler = unit.boiler_data_tbl[id] ---@type boilerv_session_db + local ps = unit.boiler_ps_tbl[id] ---@type psil + + local boiler_status = 1 + + if unit.rtu_hw.boilers[id].connected then + if unit.rtu_hw.boilers[id].faulted then + boiler_status = 3 + elseif boiler.formed then + boiler_status = 4 + else + boiler_status = 2 + end + end + + ps.publish("BoilerStatus", boiler_status) + end + + unit.turbine_data_tbl = data[8] + + for id = 1, #unit.turbine_data_tbl do + local turbine = unit.turbine_data_tbl[id] ---@type turbinev_session_db + local ps = unit.turbine_ps_tbl[id] ---@type psil + + local turbine_status = 1 + + if unit.rtu_hw.turbines[id].connected then + if unit.rtu_hw.turbines[id].faulted then + turbine_status = 3 + elseif turbine.formed then + turbine_status = 4 + else + turbine_status = 2 + end + end + + ps.publish("TurbineStatus", turbine_status) + end + + unit.tank_data_tbl = data[9] + + --#endregion + + --#region Advanced Alarm Information Display + + local ecam = {} -- aviation reference :) back to VATSIM I go... + + if tripped(unit.alarms[ALARM.ContainmentBreach]) then + local items = { + { text = "REACTOR EXPLOSION", color = colors.white }, + { text = "WEAR HAZMAT SUIT", color = colors.blue } + } + + table.insert(ecam, { color = colors.red, text = "CONTAINMENT BREACH", help = "ContainmentBreach", items = items }) + end + + if tripped(unit.alarms[ALARM.ContainmentRadiation]) then + local items = { + { text = "RADIATION DETECTED", color = colors.white }, + { text = "WEAR HAZMAT SUIT", color = colors.blue }, + { text = "RESOLVE LEAK", color = colors.blue }, + { text = "AWAIT SAFE LEVELS", color = colors.white } + } + + table.insert(ecam, { color = colors.red, text = "RADIATION LEAK", help = "ContainmentRadiation", items = items }) + end + + if tripped(unit.alarms[ALARM.CriticalDamage]) then + local items = { + { text = "MELTDOWN IMMINENT", color = colors.white }, + { text = "CHECK PLC", color = colors.blue } + } + + table.insert(ecam, { color = colors.red, text = "RCT DAMAGE CRITICAL", help = "CriticalDamage", items = items }) + end + + if tripped(unit.alarms[ALARM.ReactorLost]) then + local items = { + { text = "REACTOR OFF-LINE", color = colors.white }, + { text = "CHECK PLC", color = colors.blue } + } + + table.insert(ecam, { color = colors.red, text = "REACTOR CONN LOST", help = "ReactorLost", items = items }) + end + + if tripped(unit.alarms[ALARM.ReactorDamage]) then + local items = { + { text = "REACTOR DAMAGED", color = colors.white }, + { text = "CHECK RCS", color = colors.blue }, + { text = "AWAIT DMG REDUCED", color = colors.blue } + } + + table.insert(ecam, { color = colors.red, text = "REACTOR DAMAGE", help = "ReactorDamage", items = items }) + end + + if tripped(unit.alarms[ALARM.ReactorOverTemp]) then + local items = { + { text = "AOA DAMAGE TEMP", color = colors.white }, + { text = "CHECK RCS", color = colors.blue }, + { text = "AWAIT COOLDOWN", color = colors.blue } + } + + table.insert(ecam, { color = colors.red, text = "REACTOR OVER TEMP", help = "ReactorOverTemp", items = items }) + end + + if tripped(unit.alarms[ALARM.ReactorHighTemp]) then + local items = { + { text = "OVER EXPECTED TEMP", color = colors.white }, + { text = "CHECK RCS", color = colors.blue } + } + + table.insert(ecam, { color = colors.yellow, text = "REACTOR HIGH TEMP", help = "ReactorHighTemp", items = items}) + end + + if tripped(unit.alarms[ALARM.ReactorWasteLeak]) then + local items = { + { text = "CHECK WASTE OUTPUT", color = colors.blue }, + { text = "DO NOT ENABLE RCT" } + } + + table.insert(ecam, { color = colors.red, text = "REACTOR WASTE LEAK", help = "ReactorWasteLeak", items = items}) + end + + if tripped(unit.alarms[ALARM.ReactorHighWaste]) then + local items = {{ text = "CHECK WASTE OUTPUT", color = colors.white }} + table.insert(ecam, { color = colors.yellow, text = "REACTOR WASTE HIGH", help = "ReactorHighWaste", items = items}) + end + + if tripped(unit.alarms[ALARM.RPSTransient]) then + local items = {} + local stat = unit.reactor_data.rps_status + + local function insert(cond, key, text, color) if cond[key] then table.insert(items, { text = text, help = key, color = color }) end end + + table.insert(items, { text = "REACTOR SCRAMMED", color = colors.white }) + insert(stat, "high_dmg", "HIGH DAMAGE", colors.red) + insert(stat, "high_temp", "HIGH TEMPERATURE", colors.red) + insert(stat, "low_cool", "CRIT LOW COOLANT") + insert(stat, "ex_waste", "EXCESS WASTE") + insert(stat, "ex_hcool", "EXCESS HEATED COOL") + insert(stat, "no_fuel", "NO FUEL") + insert(stat, "fault", "HARDWARE FAULT") + insert(stat, "timeout", "SUPERVISOR DISCONN") + insert(stat, "manual", "MANUAL SCRAM", colors.white) + insert(stat, "automatic", "AUTOMATIC SCRAM") + insert(stat, "sys_fail", "NOT FORMED", colors.red) + insert(stat, "force_dis", "FORCE DISABLED", colors.red) + + table.insert(ecam, { color = colors.yellow, text = "RPS TRANSIENT", help = "RPSTransient", items = items}) + end + + if tripped(unit.alarms[ALARM.RCSTransient]) then + local items = {} + local annunc = unit.annunciator + + local function insert(cond, key, text, color) + if cond == true or (type(cond) == "table" and cond[key]) then table.insert(items, { text = text, help = key, color = color }) end + end + + insert(annunc, "RCPTrip", "RCP TRIP", colors.red) + insert(annunc, "CoolantLevelLow", "LOW COOLANT") + + if unit.num_boilers == 0 then + if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then + insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH") + end + + if unit.turbine_flow_stable then + insert(annunc, "RCSFlowLow", "RCS FLOW LOW") + insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH") + insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH") + end + else + if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then + insert(annunc, "RCSFlowLow", "RCS FLOW LOW") + insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH") + insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH") + end + + if unit.turbine_flow_stable then + insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH") + end + end + + insert(annunc, "MaxWaterReturnFeed", "MAX WTR RTRN FEED") + + for k, v in ipairs(annunc.WaterLevelLow) do insert(v, "WaterLevelLow", "BOILER " .. k .. " WTR LOW", colors.red) end + for k, v in ipairs(annunc.HeatingRateLow) do insert(v, "HeatingRateLow", "BOILER " .. k .. " HEAT RATE") end + for k, v in ipairs(annunc.TurbineOverSpeed) do insert(v, "TurbineOverSpeed", "TURBINE " .. k .. " OVERSPD", colors.red) end + for k, v in ipairs(annunc.GeneratorTrip) do insert(v, "GeneratorTrip", "TURBINE " .. k .. " GEN TRIP") end + + table.insert(ecam, { color = colors.yellow, text = "RCS TRANSIENT", help = "RCSTransient", items = items}) + end + + if tripped(unit.alarms[ALARM.TurbineTrip]) then + local items = {} + + for k, v in ipairs(unit.annunciator.TurbineTrip) do + if v then table.insert(items, { text = "TURBINE " .. k .. " TRIP", help = "TurbineTrip" }) end + end + + table.insert(items, { text = "CHECK ENERGY OUT", color = colors.blue }) + + table.insert(ecam, { color = colors.red, text = "TURBINE TRIP", help = "TurbineTripAlarm", items = items}) + end + + if not (tripped(unit.alarms[ALARM.ReactorLost]) or unit.connected) then + local items = {{ text = "CHECK PLC", color = colors.blue }} + table.insert(ecam, { color = colors.yellow, text = "REACTOR OFF-LINE", items = items }) + end + + for k, v in ipairs(unit.annunciator.BoilerOnline) do + if not v then + local items = {{ text = "CHECK RTU", color = colors.blue }} + table.insert(ecam, { color = colors.yellow, text = "BOILER " .. k .. " OFF-LINE", items = items}) + end + end + + for k, v in ipairs(unit.annunciator.TurbineOnline) do + if not v then + local items = {{ text = "CHECK RTU", color = colors.blue }} + table.insert(ecam, { color = colors.yellow, text = "TURBINE " .. k .. " OFF-LINE", items = items}) + end + end + + -- if no alarms, put some basic status messages in + if #ecam == 0 then + table.insert(ecam, { color = colors.green, text = "REACTOR " .. util.trinary(unit.reactor_data.mek_status.status, "NOMINAL", "IDLE"), items = {}}) + + local plural = util.trinary(unit.num_turbines > 1, "S", "") + table.insert(ecam, { color = colors.green, text = "TURBINE" .. plural .. util.trinary(unit.turbine_flow_stable, " STABLE", " STABILIZING"), items = {}}) + end + + unit.unit_ps.publish("U_ECAM", textutils.serialize(ecam)) + + --#endregion end -- get the IO controller database diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 0482386..a0f3f5a 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -324,7 +324,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) iocontrol.record_facility_data(packet.data) end elseif packet.type == CRDN_TYPE.API_GET_UNIT then - if _check_length(packet, 9) then + if _check_length(packet, 11) and type(packet.data[1]) == "number" and iocontrol.get_db().units[packet.data[1]] then iocontrol.record_unit_data(packet.data) end else _fail_type(packet) end diff --git a/pocket/ui/main.lua b/pocket/ui/main.lua index 87960f2..392efa2 100644 --- a/pocket/ui/main.lua +++ b/pocket/ui/main.lua @@ -38,7 +38,7 @@ local function init(main) local db = iocontrol.get_db() -- window header message - TextBox{parent=main,y=1,text="WIP ALPHA APP S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header} + TextBox{parent=main,y=1,text="WIP ALPHA APP S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header} local svr_conn = SignalBar{parent=main,y=1,x=22,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)} local crd_conn = SignalBar{parent=main,y=1,x=26,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)} diff --git a/pocket/ui/pages/unit_page.lua b/pocket/ui/pages/unit_page.lua index da123e0..8291be5 100644 --- a/pocket/ui/pages/unit_page.lua +++ b/pocket/ui/pages/unit_page.lua @@ -10,6 +10,7 @@ local iocontrol = require("pocket.iocontrol") local core = require("graphics.core") local Div = require("graphics.elements.div") +local ListBox = require("graphics.elements.listbox") local MultiPane = require("graphics.elements.multipane") local TextBox = require("graphics.elements.textbox") @@ -196,9 +197,34 @@ local function new_view(root) nav_links[i].alarm = alm_page.nav_to - TextBox{parent=alm_div,y=1,text="Unit Alarms",height=1,alignment=ALIGN.CENTER} + TextBox{parent=alm_div,y=1,text="ECAM :)",height=1,alignment=ALIGN.CENTER} - TextBox{parent=alm_div,y=3,text="work in progress",height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.black)} + local ecam_disp = ListBox{parent=alm_div,x=2,y=3,scroll_height=500,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + ecam_disp.register(u_ps, "U_ECAM", function (data) + local ecam = textutils.unserialize(data) + + ecam_disp.remove_all() + for _, entry in ipairs(ecam) do + local div = Div{parent=ecam_disp,fg_bg=cpair(entry.color,colors.black)} + local text = TextBox{parent=div,text=entry.text} + + if entry.help then + PushButton{parent=div,x=20,y=text.get_y(),text="?",callback=function()db.nav.open_help(entry.help)end,fg_bg=cpair(colors.gray,colors.black)} + end + + for _, item in ipairs(entry.items) do + local fg_bg = nil + if item.color then fg_bg = cpair(item.color, colors.black) end + + text = TextBox{parent=div,x=3,text=item.text,fg_bg=fg_bg} + + if item.help then + PushButton{parent=div,x=20,y=text.get_y(),text="?",callback=function()db.nav.open_help(item.help)end,fg_bg=cpair(colors.gray,colors.black)} + end + end + end + end) --#endregion diff --git a/supervisor/unit.lua b/supervisor/unit.lua index 8747b61..bf3e8b1 100644 --- a/supervisor/unit.lua +++ b/supervisor/unit.lua @@ -164,7 +164,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle) ReactorDamage = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorDamage, tier = PRIO.EMERGENCY }, -- reactor >1200K ReactorOverTemp = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorOverTemp, tier = PRIO.URGENT }, - -- reactor >=1150K + -- reactor >= computed high temp limit ReactorHighTemp = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 1, id = ALARM.ReactorHighTemp, tier = PRIO.TIMELY }, -- waste = 100% ReactorWasteLeak = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorWasteLeak, tier = PRIO.EMERGENCY }, @@ -976,7 +976,9 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle) self.db.control.ready, self.db.control.degraded, self.db.control.waste_mode, - self.waste_product + self.waste_product, + self.last_rate_change_ms, + self.turbine_flow_stable } end From 0e81391144103b82ac71a697ab38360da38582ce Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 22 May 2024 21:45:52 -0400 Subject: [PATCH 02/44] #200 fixes to alarm/info display --- pocket/iocontrol.lua | 2 +- pocket/pocket.lua | 2 +- pocket/ui/pages/unit_page.lua | 27 ++++++++++++++++----------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 4500daa..485628f 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -786,7 +786,7 @@ function iocontrol.record_unit_data(data) if tripped(unit.alarms[ALARM.ContainmentBreach]) then local items = { - { text = "REACTOR EXPLOSION", color = colors.white }, + { text = "REACTOR MELTDOWN", color = colors.white }, { text = "WEAR HAZMAT SUIT", color = colors.blue } } diff --git a/pocket/pocket.lua b/pocket/pocket.lua index a0f3f5a..f330ab2 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -274,7 +274,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) local ok = util.trinary(max == nil, packet.length == length, packet.length >= length and packet.length <= (max or 0)) if not ok then local fmt = "[comms] RX_PACKET{r_chan=%d,proto=%d,type=%d}: packet length mismatch -> expect %d != actual %d" - log.debug(util.sprintf(fmt, packet.scada_frame.remote_channel(), packet.scada_frame.protocol(), packet.type)) + log.debug(util.sprintf(fmt, packet.scada_frame.remote_channel(), packet.scada_frame.protocol(), packet.type, length, packet.scada_frame.length())) end return ok end diff --git a/pocket/ui/pages/unit_page.lua b/pocket/ui/pages/unit_page.lua index 8291be5..f6ad149 100644 --- a/pocket/ui/pages/unit_page.lua +++ b/pocket/ui/pages/unit_page.lua @@ -97,7 +97,7 @@ local function new_view(root) end local function load() - local page_div = Div{parent=main,x=2,y=2,width=main.get_width()-2} + local page_div = Div{parent=main,y=2,width=main.get_width()} local panes = {} @@ -125,7 +125,8 @@ local function new_view(root) end for i = 1, db.facility.num_units do - local u_div = panes[i] ---@type graphics_element + local u_pane = panes[i] + local u_div = Div{parent=u_pane,x=2,width=main.get_width()-2} local unit = db.units[i] ---@type pioctl_unit local u_ps = unit.unit_ps @@ -197,7 +198,7 @@ local function new_view(root) nav_links[i].alarm = alm_page.nav_to - TextBox{parent=alm_div,y=1,text="ECAM :)",height=1,alignment=ALIGN.CENTER} + TextBox{parent=alm_div,y=1,text="Status Info Display",height=1,alignment=ALIGN.CENTER} local ecam_disp = ListBox{parent=alm_div,x=2,y=3,scroll_height=500,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} @@ -206,23 +207,25 @@ local function new_view(root) ecam_disp.remove_all() for _, entry in ipairs(ecam) do - local div = Div{parent=ecam_disp,fg_bg=cpair(entry.color,colors.black)} - local text = TextBox{parent=div,text=entry.text} + local div = Div{parent=ecam_disp,height=1+#entry.items,fg_bg=cpair(entry.color,colors.black)} + local text = TextBox{parent=div,height=1,text=entry.text} if entry.help then - PushButton{parent=div,x=20,y=text.get_y(),text="?",callback=function()db.nav.open_help(entry.help)end,fg_bg=cpair(colors.gray,colors.black)} + PushButton{parent=div,x=21,y=text.get_y(),text="?",callback=function()db.nav.open_help(entry.help)end,fg_bg=cpair(colors.gray,colors.black)} end for _, item in ipairs(entry.items) do local fg_bg = nil if item.color then fg_bg = cpair(item.color, colors.black) end - text = TextBox{parent=div,x=3,text=item.text,fg_bg=fg_bg} + text = TextBox{parent=div,x=3,height=1,text=item.text,fg_bg=fg_bg} if item.help then - PushButton{parent=div,x=20,y=text.get_y(),text="?",callback=function()db.nav.open_help(item.help)end,fg_bg=cpair(colors.gray,colors.black)} + PushButton{parent=div,x=21,y=text.get_y(),text="?",callback=function()db.nav.open_help(item.help)end,fg_bg=cpair(colors.gray,colors.black)} end end + + ecam_disp.line_break() end end) @@ -230,7 +233,8 @@ local function new_view(root) --#region RPS Tab - local rps_div = Div{parent=page_div} + local rps_pane = Div{parent=page_div} + local rps_div = Div{parent=rps_pane,x=2,width=main.get_width()-2} table.insert(panes, rps_div) local rps_page = app.new_page(u_page, #panes) @@ -274,8 +278,9 @@ local function new_view(root) --#region RCS Tab - local rcs_div = Div{parent=page_div} - table.insert(panes, rcs_div) + local rcs_pane = Div{parent=page_div} + local rcs_div = Div{parent=rcs_pane,x=2,width=main.get_width()-2} + table.insert(panes, rcs_pane) local rcs_page = app.new_page(u_page, #panes) rcs_page.tasks = { update } From e6d6353d059ea7548c429969006fd36c9ccfcd79 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 27 May 2024 19:31:24 -0400 Subject: [PATCH 03/44] added temperature units to pocket and to common types --- coordinator/configure.lua | 5 +-- coordinator/iocontrol.lua | 15 +++++---- coordinator/startup.lua | 2 +- pocket/configure.lua | 61 ++++++++++++++++++++++++++--------- pocket/iocontrol.lua | 15 +++++---- pocket/pocket.lua | 8 +++-- pocket/startup.lua | 2 +- pocket/ui/pages/unit_page.lua | 4 +-- scada-common/types.lua | 22 +++++++++++++ scada-common/util.lua | 2 +- 10 files changed, 97 insertions(+), 39 deletions(-) diff --git a/coordinator/configure.lua b/coordinator/configure.lua index e763a27..f57f11e 100644 --- a/coordinator/configure.lua +++ b/coordinator/configure.lua @@ -7,6 +7,7 @@ local log = require("scada-common.log") local network = require("scada-common.network") local ppm = require("scada-common.ppm") local tcd = require("scada-common.tcd") +local types = require("scada-common.types") local util = require("scada-common.util") local themes = require("graphics.themes") @@ -756,7 +757,7 @@ local function config_view(display) local clock_fmt = RadioButton{parent=crd_c_1,x=1,y=5,default=util.trinary(ini_cfg.Time24Hour,1,2),options={"24-Hour","12-Hour"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} TextBox{parent=crd_c_1,x=1,y=8,height=1,text="Temperature Scale"} - local temp_scale = RadioButton{parent=crd_c_1,x=1,y=9,default=ini_cfg.TempScale,options={"Kelvin","Celsius","Fahrenheit","Rankine"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + local temp_scale = RadioButton{parent=crd_c_1,x=1,y=9,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} local function submit_ui_opts() tmp_cfg.Time24Hour = clock_fmt.get_value() == 1 @@ -1356,7 +1357,7 @@ local function config_view(display) if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") elseif f[1] == "TempScale" then - if raw == 1 then val = "Kelvin" elseif raw == 2 then val = "Celsius" elseif raw == 3 then val = "Fahrenheit" elseif raw == 4 then val = "Rankine" end + val = types.TEMP_SCALE_NAMES[raw] elseif f[1] == "MainTheme" then val = util.strval(themes.ui_theme_name(raw)) elseif f[1] == "FrontPanelTheme" then diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 08fad13..5d8428f 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -14,6 +14,8 @@ local pgi = require("coordinator.ui.pgi") local ALARM_STATE = types.ALARM_STATE local PROCESS = types.PROCESS +local TEMP_SCALE = types.TEMP_SCALE +local TEMP_UNITS = types.TEMP_SCALE_UNITS -- nominal RTT is ping (0ms to 10ms usually) + 500ms for CRD main loop tick local WARN_RTT = 1000 -- 2x as long as expected w/ 0 ping @@ -47,17 +49,16 @@ end -- initialize the coordinator IO controller ---@param conf facility_conf configuration ---@param comms coord_comms comms reference ----@param temp_scale integer temperature unit (1 = K, 2 = C, 3 = F, 4 = R) +---@param temp_scale TEMP_SCALE temperature unit function iocontrol.init(conf, comms, temp_scale) + io.temp_label = TEMP_UNITS[temp_scale] + -- temperature unit label and conversion function (from Kelvin) - if temp_scale == 2 then - io.temp_label = "\xb0C" + if temp_scale == TEMP_SCALE.CELSIUS then io.temp_convert = function (t) return t - 273.15 end - elseif temp_scale == 3 then - io.temp_label = "\xb0F" + elseif temp_scale == TEMP_SCALE.FAHRENHEIT then io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end - elseif temp_scale == 4 then - io.temp_label = "\xb0R" + elseif temp_scale == TEMP_SCALE.RANKINE then io.temp_convert = function (t) return 1.8 * t end else io.temp_label = "K" diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 0b8a06c..f152bc8 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer") local sounder = require("coordinator.sounder") local threads = require("coordinator.threads") -local COORDINATOR_VERSION = "v1.4.6" +local COORDINATOR_VERSION = "v1.4.7" local CHUNK_LOAD_DELAY_S = 30.0 diff --git a/pocket/configure.lua b/pocket/configure.lua index 80f91bc..d16309c 100644 --- a/pocket/configure.lua +++ b/pocket/configure.lua @@ -3,7 +3,7 @@ -- local log = require("scada-common.log") -local tcd = require("scada-common.tcd") +local types = require("scada-common.types") local util = require("scada-common.util") local core = require("graphics.core") @@ -32,7 +32,9 @@ local CENTER = core.ALIGN.CENTER local RIGHT = core.ALIGN.RIGHT -- changes to the config data/format to let the user know -local changes = {} +local changes = { + { "v0.9.2", { "Added temperature scale options" } } +} ---@class pkt_configurator local configurator = {} @@ -73,6 +75,7 @@ local tool_ctl = { ---@class pkt_config local tmp_cfg = { + TempScale = 1, SVR_Channel = nil, ---@type integer CRD_Channel = nil, ---@type integer PKT_Channel = nil, ---@type integer @@ -91,6 +94,7 @@ local settings_cfg = {} -- all settings fields, their nice names, and their default values local fields = { + { "TempScale", "Temperature Scale", 1 }, { "SVR_Channel", "SVR Channel", 16240 }, { "CRD_Channel", "CRD Channel", 16243 }, { "PKT_Channel", "PKT Channel", 16244 }, @@ -126,12 +130,13 @@ local function config_view(display) local root_pane_div = Div{parent=display,x=1,y=2} local main_page = Div{parent=root_pane_div,x=1,y=1} + local ui_cfg = Div{parent=root_pane_div,x=1,y=1} local net_cfg = Div{parent=root_pane_div,x=1,y=1} local log_cfg = Div{parent=root_pane_div,x=1,y=1} local summary = Div{parent=root_pane_div,x=1,y=1} local changelog = Div{parent=root_pane_div,x=1,y=1} - local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,log_cfg,summary,changelog}} + local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,ui_cfg,net_cfg,log_cfg,summary,changelog}} -- Main Page @@ -148,7 +153,7 @@ local function config_view(display) tool_ctl.viewing_config = true tool_ctl.gen_summary(settings_cfg) tool_ctl.settings_apply.hide(true) - main_pane.set_value(4) + main_pane.set_value(5) end if fs.exists("/pocket/config.lua") then @@ -162,7 +167,28 @@ local function config_view(display) if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end PushButton{parent=main_page,x=2,y=18,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg} - PushButton{parent=main_page,x=14,y=18,min_width=12,text="Change Log",callback=function()main_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=main_page,x=14,y=18,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#region Pocket UI + + local ui_c_1 = Div{parent=ui_cfg,x=2,y=4,width=24} + + TextBox{parent=ui_cfg,x=1,y=2,height=1,text=" Pocket UI",fg_bg=cpair(colors.black,colors.lime)} + + TextBox{parent=ui_c_1,x=1,y=1,height=3,text="You may use the options below to customize formats."} + + TextBox{parent=ui_c_1,x=1,y=5,height=1,text="Temperature Scale"} + local temp_scale = RadioButton{parent=ui_c_1,x=1,y=6,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime} + + local function submit_ui_opts() + tmp_cfg.TempScale = temp_scale.get_value() + main_pane.set_value(3) + end + + PushButton{parent=ui_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=ui_c_1,x=19,y=15,text="Next \x1a",callback=submit_ui_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + + --#endregion --#region Network @@ -201,7 +227,7 @@ local function config_view(display) else chan_err.show() end end - PushButton{parent=net_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=net_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=net_c_1,x=19,y=15,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} TextBox{parent=net_c_2,x=1,y=1,height=1,text="Set connection timeout."} @@ -268,7 +294,7 @@ local function config_view(display) local v = key.get_value() if string.len(v) == 0 or string.len(v) >= 8 then tmp_cfg.AuthKey = key.get_value() - main_pane.set_value(3) + main_pane.set_value(4) key_err.hide(true) else key_err.show() end end @@ -306,11 +332,11 @@ local function config_view(display) tool_ctl.viewing_config = false tool_ctl.importing_legacy = false tool_ctl.settings_apply.show() - main_pane.set_value(4) + main_pane.set_value(5) else path_err.show() end end - PushButton{parent=log_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} + PushButton{parent=log_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} PushButton{parent=log_c_1,x=19,y=15,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg} --#endregion @@ -335,7 +361,7 @@ local function config_view(display) tool_ctl.importing_legacy = false tool_ctl.settings_apply.show() else - main_pane.set_value(3) + main_pane.set_value(4) end end @@ -444,7 +470,7 @@ local function config_view(display) tool_ctl.gen_summary(tmp_cfg) sum_pane.set_value(1) - main_pane.set_value(4) + main_pane.set_value(5) tool_ctl.importing_legacy = true end @@ -473,8 +499,13 @@ local function config_view(display) local raw = cfg[f[1]] local val = util.strval(raw) - if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) - elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") end + if f[1] == "AuthKey" then + val = string.rep("*", string.len(val)) + elseif f[1] == "LogMode" then + val = util.trinary(raw == log.MODE.APPEND, "append", "replace") + elseif f[1] == "TempScale" then + val = types.TEMP_SCALE_NAMES[raw] + end if val == "nil" then val = "" end @@ -532,9 +563,7 @@ function configurator.configure(ask_config) local event, param1, param2, param3 = util.pull_event() -- handle event - if event == "timer" then - tcd.handle(param1) - elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then + if event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then local m_e = core.events.new_mouse_event(event, param1, param2, param3) if m_e then display.handle_mouse(m_e) end elseif event == "char" or event == "key" or event == "key_up" then diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 485628f..331a863 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -10,6 +10,8 @@ local util = require("scada-common.util") local ALARM = types.ALARM local ALARM_STATE = types.ALARM_STATE +local TEMP_SCALE = types.TEMP_SCALE +local TEMP_UNITS = types.TEMP_SCALE_UNITS ---@todo nominal trip time is ping (0ms to 10ms usually) local WARN_TT = 40 @@ -268,17 +270,16 @@ end -- initialize facility-dependent components of pocket iocontrol ---@param conf facility_conf configuration ----@param temp_scale 1|2|3|4 temperature unit (1 = K, 2 = C, 3 = F, 4 = R) +---@param temp_scale TEMP_SCALE temperature unit function iocontrol.init_fac(conf, temp_scale) + io.temp_label = TEMP_UNITS[temp_scale] + -- temperature unit label and conversion function (from Kelvin) - if temp_scale == 2 then - io.temp_label = "\xb0C" + if temp_scale == TEMP_SCALE.CELSIUS then io.temp_convert = function (t) return t - 273.15 end - elseif temp_scale == 3 then - io.temp_label = "\xb0F" + elseif temp_scale == TEMP_SCALE.FAHRENHEIT then io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end - elseif temp_scale == 4 then - io.temp_label = "\xb0R" + elseif temp_scale == TEMP_SCALE.RANKINE then io.temp_convert = function (t) return 1.8 * t end else io.temp_label = "K" diff --git a/pocket/pocket.lua b/pocket/pocket.lua index f330ab2..88aafb3 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -23,6 +23,8 @@ pocket.config = config function pocket.load_config() if not settings.load("/pocket.settings") then return false end + config.TempScale = settings.get("TempScale") + config.SVR_Channel = settings.get("SVR_Channel") config.CRD_Channel = settings.get("CRD_Channel") config.PKT_Channel = settings.get("PKT_Channel") @@ -36,6 +38,9 @@ function pocket.load_config() local cfv = util.new_validator() + cfv.assert_type_int(config.TempScale) + cfv.assert_range(config.TempScale, 1, 4) + cfv.assert_channel(config.SVR_Channel) cfv.assert_channel(config.CRD_Channel) cfv.assert_channel(config.PKT_Channel) @@ -371,8 +376,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) -- get configuration local conf = { num_units = fac_config[1], cooling = fac_config[2] } - ---@todo unit options - iocontrol.init_fac(conf, 1) + iocontrol.init_fac(conf, config.TempScale) log.info("coordinator connection established") self.establish_delay_counter = 0 diff --git a/pocket/startup.lua b/pocket/startup.lua index 8ba8d3e..c9f8c8d 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -18,7 +18,7 @@ local iocontrol = require("pocket.iocontrol") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "v0.9.1-alpha" +local POCKET_VERSION = "v0.9.2-alpha" local println = util.println local println_ts = util.println_ts diff --git a/pocket/ui/pages/unit_page.lua b/pocket/ui/pages/unit_page.lua index f6ad149..222d18a 100644 --- a/pocket/ui/pages/unit_page.lua +++ b/pocket/ui/pages/unit_page.lua @@ -155,12 +155,12 @@ local function new_view(root) local text_fg = cpair(colors.white, colors._INHERIT) local rate = DataIndicator{parent=u_div,y=5,lu_colors=lu_col,label="Rate",unit="mB/t",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} - local temp = DataIndicator{parent=u_div,lu_colors=lu_col,label="Temp",unit="K",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} + local temp = DataIndicator{parent=u_div,lu_colors=lu_col,label="Temp",unit=db.temp_label,format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} local ctrl = IconIndicator{parent=u_div,x=1,y=8,label="Control State",states=mode_states} rate.register(u_ps, "act_burn_rate", rate.update) - temp.register(u_ps, "temp", temp.update) + temp.register(u_ps, "temp", function (t) temp.update(db.temp_convert(t)) end) ctrl.register(u_ps, "U_ControlStatus", ctrl.update) u_div.line_break() diff --git a/scada-common/types.lua b/scada-common/types.lua index d6fc7af..aeeca16 100644 --- a/scada-common/types.lua +++ b/scada-common/types.lua @@ -74,6 +74,28 @@ function types.new_zero_coordinate() return { x = 0, y = 0, z = 0 } end -- ENUMERATION TYPES -- --#region +---@enum TEMP_SCALE +types.TEMP_SCALE = { + KELVIN = 1, + CELSIUS = 2, + FAHRENHEIT = 3, + RANKINE = 4 +} + +types.TEMP_SCALE_NAMES = { + "Kelvin", + "Celsius", + "Fahrenheit", + "Rankine" +} + +types.TEMP_SCALE_UNITS = { + "K", + "\xb0C", + "\xb0F", + "\xb0R" +} + ---@enum PANEL_LINK_STATE types.PANEL_LINK_STATE = { LINKED = 1, diff --git a/scada-common/util.lua b/scada-common/util.lua index 0fe636d..d29a85e 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -22,7 +22,7 @@ local t_pack = table.pack local util = {} -- scada-common version -util.version = "1.3.0" +util.version = "1.3.1" util.TICK_TIME_S = 0.05 util.TICK_TIME_MS = 50 From 946c28c9292334b05bb982ebb555d295fa49acaa Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 27 May 2024 23:49:53 -0400 Subject: [PATCH 04/44] record additional reactor unit data --- pocket/iocontrol.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 331a863..7bf559f 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -779,6 +779,9 @@ function iocontrol.record_unit_data(data) unit.tank_data_tbl = data[9] + unit.last_rate_change_ms = data[10] + unit.turbine_flow_stable = data[11] + --#endregion --#region Advanced Alarm Information Display From 30c921565879d6d4d042bea5bc11837148c7c275 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 27 May 2024 23:53:35 -0400 Subject: [PATCH 05/44] #202 pocket reactor view --- pocket/ui/pages/unit_page.lua | 137 ++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 7 deletions(-) diff --git a/pocket/ui/pages/unit_page.lua b/pocket/ui/pages/unit_page.lua index 222d18a..4f576fb 100644 --- a/pocket/ui/pages/unit_page.lua +++ b/pocket/ui/pages/unit_page.lua @@ -2,6 +2,7 @@ -- Unit Overview Page -- +local types = require("scada-common.types") local util = require("scada-common.util") -- local log = require("scada-common.log") @@ -17,7 +18,7 @@ local TextBox = require("graphics.elements.textbox") local DataIndicator = require("graphics.elements.indicators.data") local IconIndicator = require("graphics.elements.indicators.icon") -- local RadIndicator = require("graphics.elements.indicators.rad") --- local VerticalBar = require("graphics.elements.indicators.vbar") +local VerticalBar = require("graphics.elements.indicators.vbar") local PushButton = require("graphics.elements.controls.push_button") @@ -69,7 +70,7 @@ local function new_view(root) local btn_fg_bg = cpair(colors.yellow, colors.black) local btn_active = cpair(colors.white, colors.black) - -- local label = cpair(colors.lightGray, colors.black) + local label = cpair(colors.lightGray, colors.black) local nav_links = {} @@ -81,7 +82,7 @@ local function new_view(root) { label = "U-" .. id, color = core.cpair(colors.black, colors.yellow), callback = function () app.switcher(id) end }, { label = " \x13 ", color = core.cpair(colors.black, colors.red), callback = nav_links[id].alarm }, { label = "RPS", tall = true, color = core.cpair(colors.black, colors.cyan), callback = nav_links[id].rps }, - -- { label = " R ", color = core.cpair(colors.black, colors.lightGray), callback = function () end }, + { label = " R ", color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].reactor }, { label = "RCS", tall = true, color = core.cpair(colors.black, colors.blue), callback = nav_links[id].rcs }, } @@ -154,7 +155,7 @@ local function new_view(root) local lu_col = cpair(colors.lightGray, colors.lightGray) local text_fg = cpair(colors.white, colors._INHERIT) - local rate = DataIndicator{parent=u_div,y=5,lu_colors=lu_col,label="Rate",unit="mB/t",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} + local rate = DataIndicator{parent=u_div,y=5,lu_colors=lu_col,label="Burn",unit="mB/t",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} local temp = DataIndicator{parent=u_div,lu_colors=lu_col,label="Temp",unit=db.temp_label,format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} local ctrl = IconIndicator{parent=u_div,x=1,y=8,label="Control State",states=mode_states} @@ -195,12 +196,11 @@ local function new_view(root) local alm_page = app.new_page(u_page, #panes) alm_page.tasks = { update } - nav_links[i].alarm = alm_page.nav_to TextBox{parent=alm_div,y=1,text="Status Info Display",height=1,alignment=ALIGN.CENTER} - local ecam_disp = ListBox{parent=alm_div,x=2,y=3,scroll_height=500,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local ecam_disp = ListBox{parent=alm_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} ecam_disp.register(u_ps, "U_ECAM", function (data) local ecam = textutils.unserialize(data) @@ -239,7 +239,6 @@ local function new_view(root) local rps_page = app.new_page(u_page, #panes) rps_page.tasks = { update } - nav_links[i].rps = rps_page.nav_to TextBox{parent=rps_div,y=1,text="Protection System",height=1,alignment=ALIGN.CENTER} @@ -276,6 +275,130 @@ local function new_view(root) --#endregion + --#region Reactor Tab + + local rct_pane = Div{parent=page_div} + local rct_div = Div{parent=rct_pane,x=2,width=main.get_width()-2} + table.insert(panes, rct_div) + + local rct_page = app.new_page(u_page, #panes) + rct_page.tasks = { update } + nav_links[i].reactor = rct_page.nav_to + + TextBox{parent=rct_div,y=1,text="Fission Reactor",height=1,alignment=ALIGN.CENTER} + + local fuel = VerticalBar{parent=rct_div,x=1,y=4,fg_bg=cpair(colors.lightGray,colors.gray),height=5,width=1} + local ccool = VerticalBar{parent=rct_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1} + local hcool = VerticalBar{parent=rct_div,x=19,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1} + local waste = VerticalBar{parent=rct_div,x=21,y=4,fg_bg=cpair(colors.brown,colors.gray),height=5,width=1} + + TextBox{parent=rct_div,text="F",x=1,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=rct_div,text="C",x=3,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=rct_div,text="H",x=19,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=rct_div,text="W",x=21,y=3,width=1,height=1,fg_bg=label} + + fuel.register(u_ps, "fuel_fill", fuel.update) + ccool.register(u_ps, "ccool_fill", ccool.update) + hcool.register(u_ps, "hcool_fill", hcool.update) + waste.register(u_ps, "waste_fill", waste.update) + + ccool.register(u_ps, "ccool_type", function (type) + if type == types.FLUID.SODIUM then + ccool.recolor(cpair(colors.lightBlue, colors.gray)) + else + ccool.recolor(cpair(colors.blue, colors.gray)) + end + end) + + hcool.register(u_ps, "hcool_type", function (type) + if type == types.FLUID.SUPERHEATED_SODIUM then + hcool.recolor(cpair(colors.orange, colors.gray)) + else + hcool.recolor(cpair(colors.white, colors.gray)) + end + end) + + TextBox{parent=rct_div,text="Burn Rate",x=5,y=5,width=13,height=1,fg_bg=label} + local burn_rate = DataIndicator{parent=rct_div,x=5,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%8.2f",value=1024.99,commas=true,width=13,fg_bg=text_fg} + TextBox{parent=rct_div,text="Temperature",x=5,y=7,width=13,height=1,fg_bg=label} + local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10) + local core_temp = DataIndicator{parent=rct_div,x=5,y=8,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=17802.03,commas=true,width=13,fg_bg=text_fg} + + local r_state = IconIndicator{parent=rct_div,x=7,y=3,label="State",states=mode_states} + + burn_rate.register(u_ps, "act_burn_rate", burn_rate.update) + core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end) + r_state.register(u_ps, "U_ControlStatus", r_state.update) + + local r_temp = IconIndicator{parent=rct_div,y=10,label="Reactor Temp. Hi",states=red_ind_s} + local r_rhdt = IconIndicator{parent=rct_div,label="Hi Delta Temp.",states=yel_ind_s} + local r_firl = IconIndicator{parent=rct_div,label="Fuel Rate Lo",states=yel_ind_s} + local r_wloc = IconIndicator{parent=rct_div,label="Waste Line Occl.",states=yel_ind_s} + local r_hsrt = IconIndicator{parent=rct_div,label="Hi Startup Rate",states=yel_ind_s} + + r_temp.register(u_ps, "ReactorTempHigh", r_temp.update) + r_rhdt.register(u_ps, "ReactorHighDeltaT", r_rhdt.update) + r_firl.register(u_ps, "FuelInputRateLow", r_firl.update) + r_wloc.register(u_ps, "WasteLineOcclusion", r_wloc.update) + r_hsrt.register(u_ps, "HighStartupRate", r_hsrt.update) + + TextBox{parent=rct_div,text="HR",x=1,y=16,width=4,height=1,fg_bg=label} + local heating_r = DataIndicator{parent=rct_div,x=6,y=16,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=text_fg} + TextBox{parent=rct_div,text="DMG",x=1,y=17,width=4,height=1,fg_bg=label} + local damage_p = DataIndicator{parent=rct_div,x=6,y=17,lu_colors=lu_col,label="",unit="%",format="%11.2f",value=0,width=16,fg_bg=text_fg} + + heating_r.register(u_ps, "heating_rate", heating_r.update) + damage_p.register(u_ps, "damage", damage_p.update) + + local rct_ext_div = Div{parent=rct_pane,x=2,width=main.get_width()-2} + table.insert(panes, rct_ext_div) + + local rct_ext_page = app.new_page(rct_page, #panes) + rct_ext_page.tasks = { update } + + PushButton{parent=rct_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_ext_page.nav_to} + PushButton{parent=rct_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_page.nav_to} + + TextBox{parent=rct_ext_div,y=1,text="More Reactor Info",height=1,alignment=ALIGN.CENTER} + + TextBox{parent=rct_ext_div,text="Fuel Tank",x=1,y=3,width=9,height=1,fg_bg=label} + local fuel_p = DataIndicator{parent=rct_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local fuel_amnt = DataIndicator{parent=rct_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + fuel_p.register(u_ps, "fuel_fill", function (x) fuel_p.update(x * 100) end) + fuel_amnt.register(u_ps, "fuel", fuel_amnt.update) + + TextBox{parent=rct_ext_div,text="Cool Coolant",x=1,y=6,width=12,height=1,fg_bg=label} + local cooled_p = DataIndicator{parent=rct_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local ccool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + cooled_p.register(u_ps, "ccool_fill", function (x) cooled_p.update(x * 100) end) + ccool_amnt.register(u_ps, "ccool_amnt", ccool_amnt.update) + + TextBox{parent=rct_ext_div,text="Hot Coolant",x=1,y=9,width=12,height=1,fg_bg=label} + local heated_p = DataIndicator{parent=rct_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local hcool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + heated_p.register(u_ps, "hcool_fill", function (x) heated_p.update(x * 100) end) + hcool_amnt.register(u_ps, "hcool_amnt", hcool_amnt.update) + + TextBox{parent=rct_ext_div,text="Waste Tank",x=1,y=12,width=10,height=1,fg_bg=label} + local waste_p = DataIndicator{parent=rct_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local waste_amnt = DataIndicator{parent=rct_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + waste_p.register(u_ps, "waste_fill", function (x) waste_p.update(x * 100) end) + waste_amnt.register(u_ps, "waste", waste_amnt.update) + + TextBox{parent=rct_ext_div,text="Boil Eff.",x=1,y=15,width=9,height=1,fg_bg=label} + TextBox{parent=rct_ext_div,text="Env. Loss",x=1,y=16,width=9,height=1,fg_bg=label} + local boil_eff = DataIndicator{parent=rct_ext_div,x=11,y=15,lu_colors=lu_col,label="",unit="%",format="%9.2f",value=0,width=11,fg_bg=text_fg} + local env_loss = DataIndicator{parent=rct_ext_div,x=11,y=16,lu_colors=lu_col,label="",unit="",format="%11.8f",value=0,width=11,fg_bg=text_fg} + + boil_eff.register(u_ps, "boil_eff", function (x) boil_eff.update(x * 100) end) + env_loss.register(u_ps, "env_loss", env_loss.update) + + --#endregion + --#region RCS Tab local rcs_pane = Div{parent=page_div} From 3181ab96f165d9ab46cd75cc05d06cf90ba9c993 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 29 May 2024 22:08:36 -0400 Subject: [PATCH 06/44] #206 work on boiler view and reorganized app code --- pocket/iocontrol.lua | 10 + .../ui/{pages/unit_page.lua => apps/unit.lua} | 188 +++--------------- pocket/ui/main.lua | 4 +- pocket/ui/pages/unit_boiler.lua | 79 ++++++++ pocket/ui/pages/unit_reactor.lua | 159 +++++++++++++++ pocket/ui/style.lua | 38 +++- 6 files changed, 320 insertions(+), 158 deletions(-) rename pocket/ui/{pages/unit_page.lua => apps/unit.lua} (58%) create mode 100644 pocket/ui/pages/unit_boiler.lua create mode 100644 pocket/ui/pages/unit_reactor.lua diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 7bf559f..c74c6a7 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -592,6 +592,14 @@ end local function tripped(state) return state == ALARM_STATE.TRIPPED or state == ALARM_STATE.ACKED end +local function _record_multiblock_status(faulted, data, ps) + ps.publish("formed", data.formed) + ps.publish("faulted", faulted) + + for key, val in pairs(data.state) do ps.publish(key, val) end + for key, val in pairs(data.tanks) do ps.publish(key, val) end +end + -- update unit status data from API_GET_UNIT ---@param data table function iocontrol.record_unit_data(data) @@ -751,6 +759,8 @@ function iocontrol.record_unit_data(data) else boiler_status = 2 end + + _record_multiblock_status(unit.rtu_hw.boilers[id].faulted, boiler, ps) end ps.publish("BoilerStatus", boiler_status) diff --git a/pocket/ui/pages/unit_page.lua b/pocket/ui/apps/unit.lua similarity index 58% rename from pocket/ui/pages/unit_page.lua rename to pocket/ui/apps/unit.lua index 4f576fb..51c27b8 100644 --- a/pocket/ui/pages/unit_page.lua +++ b/pocket/ui/apps/unit.lua @@ -2,12 +2,16 @@ -- Unit Overview Page -- -local types = require("scada-common.types") local util = require("scada-common.util") -- local log = require("scada-common.log") local iocontrol = require("pocket.iocontrol") +local style = require("pocket.ui.style") + +local boiler = require("pocket.ui.pages.unit_boiler") +local reactor = require("pocket.ui.pages.unit_reactor") + local core = require("graphics.core") local Div = require("graphics.elements.div") @@ -18,26 +22,20 @@ local TextBox = require("graphics.elements.textbox") local DataIndicator = require("graphics.elements.indicators.data") local IconIndicator = require("graphics.elements.indicators.icon") -- local RadIndicator = require("graphics.elements.indicators.rad") -local VerticalBar = require("graphics.elements.indicators.vbar") +-- local VerticalBar = require("graphics.elements.indicators.vbar") local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair -local basic_states = { - { color = cpair(colors.black, colors.lightGray), symbol = "\x07" }, - { color = cpair(colors.black, colors.red), symbol = "-" }, - { color = cpair(colors.black, colors.yellow), symbol = "\x1e" }, - { color = cpair(colors.black, colors.green), symbol = "+" } -} - -local mode_states = { - { color = cpair(colors.black, colors.lightGray), symbol = "\x07" }, - { color = cpair(colors.black, colors.red), symbol = "-" }, - { color = cpair(colors.black, colors.green), symbol = "+" }, - { color = cpair(colors.black, colors.purple), symbol = "A" } -} +-- local label = style.label +local lu_col = style.label_unit_pair +local text_fg = style.text_fg +local basic_states = style.icon_states.basic_states +local mode_states = style.icon_states.mode_states +local red_ind_s = style.icon_states.red_ind_s +local yel_ind_s = style.icon_states.yel_ind_s local emc_ind_s = { { color = cpair(colors.black, colors.gray), symbol = "-" }, @@ -45,16 +43,6 @@ local emc_ind_s = { { color = cpair(colors.black, colors.green), symbol = "+" } } -local red_ind_s = { - { color = cpair(colors.black, colors.lightGray), symbol = "+" }, - { color = cpair(colors.black, colors.red), symbol = "-" } -} - -local yel_ind_s = { - { color = cpair(colors.black, colors.lightGray), symbol = "+" }, - { color = cpair(colors.black, colors.yellow), symbol = "-" } -} - -- new unit page view ---@param root graphics_element parent local function new_view(root) @@ -70,12 +58,11 @@ local function new_view(root) local btn_fg_bg = cpair(colors.yellow, colors.black) local btn_active = cpair(colors.white, colors.black) - local label = cpair(colors.lightGray, colors.black) local nav_links = {} local function set_sidebar(id) - -- local unit = db.units[id] ---@type pioctl_unit + local unit = db.units[id] ---@type pioctl_unit local list = { { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, @@ -86,13 +73,13 @@ local function new_view(root) { label = "RCS", tall = true, color = core.cpair(colors.black, colors.blue), callback = nav_links[id].rcs }, } - -- for i = 1, unit.num_boilers do - -- table.insert(list, { label = "B-" .. i, color = core.cpair(colors.black, colors.lightBlue), callback = function () end }) - -- end + for i = 1, unit.num_boilers do + table.insert(list, { label = "B-" .. i, color = core.cpair(colors.black, colors.lightBlue), callback = nav_links[id].boiler[i] }) + end - -- for i = 1, unit.num_turbines do - -- table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.white), callback = function () end }) - -- end + for i = 1, unit.num_turbines do + table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.white), callback = function () end }) + end app.set_sidebar(list) end @@ -152,9 +139,6 @@ local function new_view(root) local type = util.trinary(unit.num_boilers > 0, "Sodium Cooled Reactor", "Boiling Water Reactor") TextBox{parent=u_div,y=3,text=type,height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.black)} - local lu_col = cpair(colors.lightGray, colors.lightGray) - local text_fg = cpair(colors.white, colors._INHERIT) - local rate = DataIndicator{parent=u_div,y=5,lu_colors=lu_col,label="Burn",unit="mB/t",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} local temp = DataIndicator{parent=u_div,lu_colors=lu_col,label="Temp",unit=db.temp_label,format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg} @@ -277,125 +261,7 @@ local function new_view(root) --#region Reactor Tab - local rct_pane = Div{parent=page_div} - local rct_div = Div{parent=rct_pane,x=2,width=main.get_width()-2} - table.insert(panes, rct_div) - - local rct_page = app.new_page(u_page, #panes) - rct_page.tasks = { update } - nav_links[i].reactor = rct_page.nav_to - - TextBox{parent=rct_div,y=1,text="Fission Reactor",height=1,alignment=ALIGN.CENTER} - - local fuel = VerticalBar{parent=rct_div,x=1,y=4,fg_bg=cpair(colors.lightGray,colors.gray),height=5,width=1} - local ccool = VerticalBar{parent=rct_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1} - local hcool = VerticalBar{parent=rct_div,x=19,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1} - local waste = VerticalBar{parent=rct_div,x=21,y=4,fg_bg=cpair(colors.brown,colors.gray),height=5,width=1} - - TextBox{parent=rct_div,text="F",x=1,y=3,width=1,height=1,fg_bg=label} - TextBox{parent=rct_div,text="C",x=3,y=3,width=1,height=1,fg_bg=label} - TextBox{parent=rct_div,text="H",x=19,y=3,width=1,height=1,fg_bg=label} - TextBox{parent=rct_div,text="W",x=21,y=3,width=1,height=1,fg_bg=label} - - fuel.register(u_ps, "fuel_fill", fuel.update) - ccool.register(u_ps, "ccool_fill", ccool.update) - hcool.register(u_ps, "hcool_fill", hcool.update) - waste.register(u_ps, "waste_fill", waste.update) - - ccool.register(u_ps, "ccool_type", function (type) - if type == types.FLUID.SODIUM then - ccool.recolor(cpair(colors.lightBlue, colors.gray)) - else - ccool.recolor(cpair(colors.blue, colors.gray)) - end - end) - - hcool.register(u_ps, "hcool_type", function (type) - if type == types.FLUID.SUPERHEATED_SODIUM then - hcool.recolor(cpair(colors.orange, colors.gray)) - else - hcool.recolor(cpair(colors.white, colors.gray)) - end - end) - - TextBox{parent=rct_div,text="Burn Rate",x=5,y=5,width=13,height=1,fg_bg=label} - local burn_rate = DataIndicator{parent=rct_div,x=5,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%8.2f",value=1024.99,commas=true,width=13,fg_bg=text_fg} - TextBox{parent=rct_div,text="Temperature",x=5,y=7,width=13,height=1,fg_bg=label} - local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10) - local core_temp = DataIndicator{parent=rct_div,x=5,y=8,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=17802.03,commas=true,width=13,fg_bg=text_fg} - - local r_state = IconIndicator{parent=rct_div,x=7,y=3,label="State",states=mode_states} - - burn_rate.register(u_ps, "act_burn_rate", burn_rate.update) - core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end) - r_state.register(u_ps, "U_ControlStatus", r_state.update) - - local r_temp = IconIndicator{parent=rct_div,y=10,label="Reactor Temp. Hi",states=red_ind_s} - local r_rhdt = IconIndicator{parent=rct_div,label="Hi Delta Temp.",states=yel_ind_s} - local r_firl = IconIndicator{parent=rct_div,label="Fuel Rate Lo",states=yel_ind_s} - local r_wloc = IconIndicator{parent=rct_div,label="Waste Line Occl.",states=yel_ind_s} - local r_hsrt = IconIndicator{parent=rct_div,label="Hi Startup Rate",states=yel_ind_s} - - r_temp.register(u_ps, "ReactorTempHigh", r_temp.update) - r_rhdt.register(u_ps, "ReactorHighDeltaT", r_rhdt.update) - r_firl.register(u_ps, "FuelInputRateLow", r_firl.update) - r_wloc.register(u_ps, "WasteLineOcclusion", r_wloc.update) - r_hsrt.register(u_ps, "HighStartupRate", r_hsrt.update) - - TextBox{parent=rct_div,text="HR",x=1,y=16,width=4,height=1,fg_bg=label} - local heating_r = DataIndicator{parent=rct_div,x=6,y=16,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=text_fg} - TextBox{parent=rct_div,text="DMG",x=1,y=17,width=4,height=1,fg_bg=label} - local damage_p = DataIndicator{parent=rct_div,x=6,y=17,lu_colors=lu_col,label="",unit="%",format="%11.2f",value=0,width=16,fg_bg=text_fg} - - heating_r.register(u_ps, "heating_rate", heating_r.update) - damage_p.register(u_ps, "damage", damage_p.update) - - local rct_ext_div = Div{parent=rct_pane,x=2,width=main.get_width()-2} - table.insert(panes, rct_ext_div) - - local rct_ext_page = app.new_page(rct_page, #panes) - rct_ext_page.tasks = { update } - - PushButton{parent=rct_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_ext_page.nav_to} - PushButton{parent=rct_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_page.nav_to} - - TextBox{parent=rct_ext_div,y=1,text="More Reactor Info",height=1,alignment=ALIGN.CENTER} - - TextBox{parent=rct_ext_div,text="Fuel Tank",x=1,y=3,width=9,height=1,fg_bg=label} - local fuel_p = DataIndicator{parent=rct_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local fuel_amnt = DataIndicator{parent=rct_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} - - fuel_p.register(u_ps, "fuel_fill", function (x) fuel_p.update(x * 100) end) - fuel_amnt.register(u_ps, "fuel", fuel_amnt.update) - - TextBox{parent=rct_ext_div,text="Cool Coolant",x=1,y=6,width=12,height=1,fg_bg=label} - local cooled_p = DataIndicator{parent=rct_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local ccool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} - - cooled_p.register(u_ps, "ccool_fill", function (x) cooled_p.update(x * 100) end) - ccool_amnt.register(u_ps, "ccool_amnt", ccool_amnt.update) - - TextBox{parent=rct_ext_div,text="Hot Coolant",x=1,y=9,width=12,height=1,fg_bg=label} - local heated_p = DataIndicator{parent=rct_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local hcool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} - - heated_p.register(u_ps, "hcool_fill", function (x) heated_p.update(x * 100) end) - hcool_amnt.register(u_ps, "hcool_amnt", hcool_amnt.update) - - TextBox{parent=rct_ext_div,text="Waste Tank",x=1,y=12,width=10,height=1,fg_bg=label} - local waste_p = DataIndicator{parent=rct_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local waste_amnt = DataIndicator{parent=rct_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} - - waste_p.register(u_ps, "waste_fill", function (x) waste_p.update(x * 100) end) - waste_amnt.register(u_ps, "waste", waste_amnt.update) - - TextBox{parent=rct_ext_div,text="Boil Eff.",x=1,y=15,width=9,height=1,fg_bg=label} - TextBox{parent=rct_ext_div,text="Env. Loss",x=1,y=16,width=9,height=1,fg_bg=label} - local boil_eff = DataIndicator{parent=rct_ext_div,x=11,y=15,lu_colors=lu_col,label="",unit="%",format="%9.2f",value=0,width=11,fg_bg=text_fg} - local env_loss = DataIndicator{parent=rct_ext_div,x=11,y=16,lu_colors=lu_col,label="",unit="",format="%11.8f",value=0,width=11,fg_bg=text_fg} - - boil_eff.register(u_ps, "boil_eff", function (x) boil_eff.update(x * 100) end) - env_loss.register(u_ps, "env_loss", env_loss.update) + nav_links[i].reactor = reactor(app, u_page, panes, page_div, u_ps, update) --#endregion @@ -458,6 +324,18 @@ local function new_view(root) ttrip.register(u_ps, "U_TurbineTrip", ttrip.update) --#endregion + + --#region Boiler Tabs + + local blr_pane = Div{parent=page_div} + nav_links[i].boiler = {} + + for b_id = 1, unit.num_boilers do + local ps = unit.boiler_ps_tbl[b_id] + nav_links[i].boiler[b_id] = boiler(app, u_page, panes, blr_pane, b_id, ps, update) + end + + --#endregion end -- setup multipane diff --git a/pocket/ui/main.lua b/pocket/ui/main.lua index 392efa2..eaecc1e 100644 --- a/pocket/ui/main.lua +++ b/pocket/ui/main.lua @@ -7,11 +7,11 @@ local iocontrol = require("pocket.iocontrol") local diag_apps = require("pocket.ui.apps.diag_apps") local dummy_app = require("pocket.ui.apps.dummy_app") local sys_apps = require("pocket.ui.apps.sys_apps") +local unit_app = require("pocket.ui.apps.unit") local conn_waiting = require("pocket.ui.components.conn_waiting") local home_page = require("pocket.ui.pages.home_page") -local unit_page = require("pocket.ui.pages.unit_page") local style = require("pocket.ui.style") @@ -72,8 +72,8 @@ local function init(main) local page_div = Div{parent=main_pane,x=4,y=1} home_page(page_div) - unit_page(page_div) + unit_app(page_div) diag_apps(page_div) sys_apps(page_div) dummy_app(page_div) diff --git a/pocket/ui/pages/unit_boiler.lua b/pocket/ui/pages/unit_boiler.lua new file mode 100644 index 0000000..9f8ee62 --- /dev/null +++ b/pocket/ui/pages/unit_boiler.lua @@ -0,0 +1,79 @@ +local types = require("scada-common.types") +local util = require("scada-common.util") + +local iocontrol = require("pocket.iocontrol") + +local style = require("pocket.ui.style") + +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +local TextBox = require("graphics.elements.textbox") + +local DataIndicator = require("graphics.elements.indicators.data") +local IconIndicator = require("graphics.elements.indicators.icon") +local VerticalBar = require("graphics.elements.indicators.vbar") + +local PushButton = require("graphics.elements.controls.push_button") + +local ALIGN = core.ALIGN +local cpair = core.cpair + +local label = style.label +local lu_col = style.label_unit_pair +local text_fg = style.text_fg +local basic_states = style.icon_states.basic_states +local red_ind_s = style.icon_states.red_ind_s +local yel_ind_s = style.icon_states.yel_ind_s + +-- create a boiler view in the unit app +---@param app pocket_app +---@param u_page nav_tree_page +---@param panes table +---@param blr_pane graphics_element +---@param b_id integer boiler ID +---@param ps psil +---@param update function +return function (app, u_page, panes, blr_pane, b_id, ps, update) + local db = iocontrol.get_db() + + local blr_div = Div{parent=blr_pane,x=2,width=blr_pane.get_width()-2} + table.insert(panes, blr_div) + + local blr_page = app.new_page(u_page, #panes) + blr_page.tasks = { update } + + TextBox{parent=blr_div,y=1,text="Boiler "..b_id,height=1,alignment=ALIGN.CENTER} + + local hcool = VerticalBar{parent=blr_div,x=1,y=4,fg_bg=cpair(colors.orange,colors.gray),height=5,width=1} + local water = VerticalBar{parent=blr_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1} + local steam = VerticalBar{parent=blr_div,x=19,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1} + local ccool = VerticalBar{parent=blr_div,x=21,y=4,fg_bg=cpair(colors.lightBlue,colors.gray),height=5,width=1} + + TextBox{parent=blr_div,text="H",x=1,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=blr_div,text="W",x=3,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=blr_div,text="S",x=19,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=blr_div,text="C",x=21,y=3,width=1,height=1,fg_bg=label} + + hcool.register(ps, "hcool_fill", hcool.update) + water.register(ps, "water_fill", water.update) + steam.register(ps, "steam_fill", steam.update) + ccool.register(ps, "ccool_fill", ccool.update) + + TextBox{parent=blr_div,text="Temperature",x=5,y=5,width=13,height=1,fg_bg=label} + local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10) + local temp = DataIndicator{parent=blr_div,x=5,y=6,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=17802.03,commas=true,width=13,fg_bg=text_fg} + + local state = IconIndicator{parent=blr_div,x=7,y=3,label="State",states=basic_states} + + temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end) + state.register(ps, "BoilerStatus", state.update) + + local b_wll = IconIndicator{parent=blr_div,y=10,label="Water Level Lo",states=red_ind_s} + local b_hr = IconIndicator{parent=blr_div,label="Heating Rate Lo",states=yel_ind_s} + + b_wll.register(ps, "WaterLevelLow", b_wll.update) + b_hr.register(ps, "HeatingRateLow", b_hr.update) + + return blr_page.nav_to +end diff --git a/pocket/ui/pages/unit_reactor.lua b/pocket/ui/pages/unit_reactor.lua new file mode 100644 index 0000000..8fe6213 --- /dev/null +++ b/pocket/ui/pages/unit_reactor.lua @@ -0,0 +1,159 @@ +local types = require("scada-common.types") +local util = require("scada-common.util") + +local iocontrol = require("pocket.iocontrol") + +local style = require("pocket.ui.style") + +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +local TextBox = require("graphics.elements.textbox") + +local DataIndicator = require("graphics.elements.indicators.data") +local IconIndicator = require("graphics.elements.indicators.icon") +local VerticalBar = require("graphics.elements.indicators.vbar") + +local PushButton = require("graphics.elements.controls.push_button") + +local ALIGN = core.ALIGN +local cpair = core.cpair + +local label = style.label +local lu_col = style.label_unit_pair +local text_fg = style.text_fg +local mode_states = style.icon_states.mode_states +local red_ind_s = style.icon_states.red_ind_s +local yel_ind_s = style.icon_states.yel_ind_s + +-- create a reactor view in the unit app +---@param app pocket_app +---@param u_page nav_tree_page +---@param panes table +---@param page_div graphics_element +---@param u_ps psil +---@param update function +return function (app, u_page, panes, page_div, u_ps, update) + local db = iocontrol.get_db() + + local rct_pane = Div{parent=page_div} + local rct_div = Div{parent=rct_pane,x=2,width=page_div.get_width()-2} + table.insert(panes, rct_div) + + local rct_page = app.new_page(u_page, #panes) + rct_page.tasks = { update } + + TextBox{parent=rct_div,y=1,text="Fission Reactor",height=1,alignment=ALIGN.CENTER} + + local fuel = VerticalBar{parent=rct_div,x=1,y=4,fg_bg=cpair(colors.lightGray,colors.gray),height=5,width=1} + local ccool = VerticalBar{parent=rct_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1} + local hcool = VerticalBar{parent=rct_div,x=19,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1} + local waste = VerticalBar{parent=rct_div,x=21,y=4,fg_bg=cpair(colors.brown,colors.gray),height=5,width=1} + + TextBox{parent=rct_div,text="F",x=1,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=rct_div,text="C",x=3,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=rct_div,text="H",x=19,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=rct_div,text="W",x=21,y=3,width=1,height=1,fg_bg=label} + + fuel.register(u_ps, "fuel_fill", fuel.update) + ccool.register(u_ps, "ccool_fill", ccool.update) + hcool.register(u_ps, "hcool_fill", hcool.update) + waste.register(u_ps, "waste_fill", waste.update) + + ccool.register(u_ps, "ccool_type", function (type) + if type == types.FLUID.SODIUM then + ccool.recolor(cpair(colors.lightBlue, colors.gray)) + else + ccool.recolor(cpair(colors.blue, colors.gray)) + end + end) + + hcool.register(u_ps, "hcool_type", function (type) + if type == types.FLUID.SUPERHEATED_SODIUM then + hcool.recolor(cpair(colors.orange, colors.gray)) + else + hcool.recolor(cpair(colors.white, colors.gray)) + end + end) + + TextBox{parent=rct_div,text="Burn Rate",x=5,y=5,width=13,height=1,fg_bg=label} + local burn_rate = DataIndicator{parent=rct_div,x=5,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%8.2f",value=0,commas=true,width=13,fg_bg=text_fg} + TextBox{parent=rct_div,text="Temperature",x=5,y=7,width=13,height=1,fg_bg=label} + local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10) + local core_temp = DataIndicator{parent=rct_div,x=5,y=8,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=0,commas=true,width=13,fg_bg=text_fg} + + local state = IconIndicator{parent=rct_div,x=7,y=3,label="State",states=mode_states} + + burn_rate.register(u_ps, "act_burn_rate", burn_rate.update) + core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end) + state.register(u_ps, "U_ControlStatus", state.update) + + local r_temp = IconIndicator{parent=rct_div,y=10,label="Reactor Temp. Hi",states=red_ind_s} + local r_rhdt = IconIndicator{parent=rct_div,label="Hi Delta Temp.",states=yel_ind_s} + local r_firl = IconIndicator{parent=rct_div,label="Fuel Rate Lo",states=yel_ind_s} + local r_wloc = IconIndicator{parent=rct_div,label="Waste Line Occl.",states=yel_ind_s} + local r_hsrt = IconIndicator{parent=rct_div,label="Hi Startup Rate",states=yel_ind_s} + + r_temp.register(u_ps, "ReactorTempHigh", r_temp.update) + r_rhdt.register(u_ps, "ReactorHighDeltaT", r_rhdt.update) + r_firl.register(u_ps, "FuelInputRateLow", r_firl.update) + r_wloc.register(u_ps, "WasteLineOcclusion", r_wloc.update) + r_hsrt.register(u_ps, "HighStartupRate", r_hsrt.update) + + TextBox{parent=rct_div,text="HR",x=1,y=16,width=4,height=1,fg_bg=label} + local heating_r = DataIndicator{parent=rct_div,x=6,y=16,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=text_fg} + TextBox{parent=rct_div,text="DMG",x=1,y=17,width=4,height=1,fg_bg=label} + local damage_p = DataIndicator{parent=rct_div,x=6,y=17,lu_colors=lu_col,label="",unit="%",format="%11.2f",value=0,width=16,fg_bg=text_fg} + + heating_r.register(u_ps, "heating_rate", heating_r.update) + damage_p.register(u_ps, "damage", damage_p.update) + + local rct_ext_div = Div{parent=rct_pane,x=2,width=page_div.get_width()-2} + table.insert(panes, rct_ext_div) + + local rct_ext_page = app.new_page(rct_page, #panes) + rct_ext_page.tasks = { update } + + PushButton{parent=rct_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_ext_page.nav_to} + PushButton{parent=rct_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_page.nav_to} + + TextBox{parent=rct_ext_div,y=1,text="More Reactor Info",height=1,alignment=ALIGN.CENTER} + + TextBox{parent=rct_ext_div,text="Fuel Tank",x=1,y=3,width=9,height=1,fg_bg=label} + local fuel_p = DataIndicator{parent=rct_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local fuel_amnt = DataIndicator{parent=rct_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + fuel_p.register(u_ps, "fuel_fill", function (x) fuel_p.update(x * 100) end) + fuel_amnt.register(u_ps, "fuel", fuel_amnt.update) + + TextBox{parent=rct_ext_div,text="Cool Coolant",x=1,y=6,width=12,height=1,fg_bg=label} + local cooled_p = DataIndicator{parent=rct_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local ccool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + cooled_p.register(u_ps, "ccool_fill", function (x) cooled_p.update(x * 100) end) + ccool_amnt.register(u_ps, "ccool_amnt", ccool_amnt.update) + + TextBox{parent=rct_ext_div,text="Hot Coolant",x=1,y=9,width=12,height=1,fg_bg=label} + local heated_p = DataIndicator{parent=rct_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local hcool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + heated_p.register(u_ps, "hcool_fill", function (x) heated_p.update(x * 100) end) + hcool_amnt.register(u_ps, "hcool_amnt", hcool_amnt.update) + + TextBox{parent=rct_ext_div,text="Waste Tank",x=1,y=12,width=10,height=1,fg_bg=label} + local waste_p = DataIndicator{parent=rct_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local waste_amnt = DataIndicator{parent=rct_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + waste_p.register(u_ps, "waste_fill", function (x) waste_p.update(x * 100) end) + waste_amnt.register(u_ps, "waste", waste_amnt.update) + + TextBox{parent=rct_ext_div,text="Boil Eff.",x=1,y=15,width=9,height=1,fg_bg=label} + TextBox{parent=rct_ext_div,text="Env. Loss",x=1,y=16,width=9,height=1,fg_bg=label} + local boil_eff = DataIndicator{parent=rct_ext_div,x=11,y=15,lu_colors=lu_col,label="",unit="%",format="%9.2f",value=0,width=11,fg_bg=text_fg} + local env_loss = DataIndicator{parent=rct_ext_div,x=11,y=16,lu_colors=lu_col,label="",unit="",format="%11.8f",value=0,width=11,fg_bg=text_fg} + + boil_eff.register(u_ps, "boil_eff", function (x) boil_eff.update(x * 100) end) + env_loss.register(u_ps, "env_loss", env_loss.update) + + return rct_page.nav_to +end diff --git a/pocket/ui/style.lua b/pocket/ui/style.lua index 2fb7526..f91d7b8 100644 --- a/pocket/ui/style.lua +++ b/pocket/ui/style.lua @@ -12,7 +12,9 @@ local cpair = core.cpair style.root = cpair(colors.white, colors.black) style.header = cpair(colors.white, colors.gray) -style.label = cpair(colors.gray, colors.lightGray) +style.text_fg = cpair(colors.white, colors._INHERIT) +style.label = cpair(colors.lightGray, colors.black) +style.label_unit_pair = cpair(colors.lightGray, colors.lightGray) style.colors = { { c = colors.red, hex = 0xdf4949 }, @@ -33,6 +35,40 @@ style.colors = { -- { c = colors.brown, hex = 0x7f664c } } +local states = {} + +states.basic_states = { + { color = cpair(colors.black, colors.lightGray), symbol = "\x07" }, + { color = cpair(colors.black, colors.red), symbol = "-" }, + { color = cpair(colors.black, colors.yellow), symbol = "\x1e" }, + { color = cpair(colors.black, colors.green), symbol = "+" } +} + +states.mode_states = { + { color = cpair(colors.black, colors.lightGray), symbol = "\x07" }, + { color = cpair(colors.black, colors.red), symbol = "-" }, + { color = cpair(colors.black, colors.green), symbol = "+" }, + { color = cpair(colors.black, colors.purple), symbol = "A" } +} + +states.emc_ind_s = { + { color = cpair(colors.black, colors.gray), symbol = "-" }, + { color = cpair(colors.black, colors.white), symbol = "\x07" }, + { color = cpair(colors.black, colors.green), symbol = "+" } +} + +states.red_ind_s = { + { color = cpair(colors.black, colors.lightGray), symbol = "+" }, + { color = cpair(colors.black, colors.red), symbol = "-" } +} + +states.yel_ind_s = { + { color = cpair(colors.black, colors.lightGray), symbol = "+" }, + { color = cpair(colors.black, colors.yellow), symbol = "-" } +} + +style.icon_states = states + -- MAIN LAYOUT -- style.reactor = { From 0fa0324940f93e8dc942fcfb8a2f94b80288383c Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 31 May 2024 18:16:04 -0400 Subject: [PATCH 07/44] #206 pocket boiler view --- pocket/iocontrol.lua | 10 ++++++ pocket/ui/pages/unit_boiler.lua | 62 ++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index c74c6a7..83b807c 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -750,20 +750,30 @@ function iocontrol.record_unit_data(data) local ps = unit.boiler_ps_tbl[id] ---@type psil local boiler_status = 1 + local computed_status = 1 if unit.rtu_hw.boilers[id].connected then if unit.rtu_hw.boilers[id].faulted then boiler_status = 3 + computed_status = 3 elseif boiler.formed then boiler_status = 4 + + if boiler.state.boil_rate > 0 then + computed_status = 5 + else + computed_status = 4 + end else boiler_status = 2 + computed_status = 2 end _record_multiblock_status(unit.rtu_hw.boilers[id].faulted, boiler, ps) end ps.publish("BoilerStatus", boiler_status) + ps.publish("BoilerStateStatus", computed_status) end unit.turbine_data_tbl = data[8] diff --git a/pocket/ui/pages/unit_boiler.lua b/pocket/ui/pages/unit_boiler.lua index 9f8ee62..37f4715 100644 --- a/pocket/ui/pages/unit_boiler.lua +++ b/pocket/ui/pages/unit_boiler.lua @@ -11,6 +11,7 @@ local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") local DataIndicator = require("graphics.elements.indicators.data") +local StateIndicator = require("graphics.elements.indicators.state") local IconIndicator = require("graphics.elements.indicators.icon") local VerticalBar = require("graphics.elements.indicators.vbar") @@ -22,7 +23,6 @@ local cpair = core.cpair local label = style.label local lu_col = style.label_unit_pair local text_fg = style.text_fg -local basic_states = style.icon_states.basic_states local red_ind_s = style.icon_states.red_ind_s local yel_ind_s = style.icon_states.yel_ind_s @@ -43,7 +43,9 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update) local blr_page = app.new_page(u_page, #panes) blr_page.tasks = { update } - TextBox{parent=blr_div,y=1,text="Boiler "..b_id,height=1,alignment=ALIGN.CENTER} + TextBox{parent=blr_div,y=1,text="Boiler "..b_id,width=8,height=1} + local status = StateIndicator{parent=blr_div,x=10,y=1,states=style.boiler.states,value=1,min_width=12} + status.register(ps, "BoilerStateStatus", status.update) local hcool = VerticalBar{parent=blr_div,x=1,y=4,fg_bg=cpair(colors.orange,colors.gray),height=5,width=1} local water = VerticalBar{parent=blr_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1} @@ -64,10 +66,7 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update) local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10) local temp = DataIndicator{parent=blr_div,x=5,y=6,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=17802.03,commas=true,width=13,fg_bg=text_fg} - local state = IconIndicator{parent=blr_div,x=7,y=3,label="State",states=basic_states} - temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end) - state.register(ps, "BoilerStatus", state.update) local b_wll = IconIndicator{parent=blr_div,y=10,label="Water Level Lo",states=red_ind_s} local b_hr = IconIndicator{parent=blr_div,label="Heating Rate Lo",states=yel_ind_s} @@ -75,5 +74,58 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update) b_wll.register(ps, "WaterLevelLow", b_wll.update) b_hr.register(ps, "HeatingRateLow", b_hr.update) + TextBox{parent=blr_div,text="Boil Rate",x=1,y=13,width=12,height=1,fg_bg=label} + local boil_r = DataIndicator{parent=blr_div,x=6,y=14,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=text_fg} + + boil_r.register(ps, "boil_rate", boil_r.update) + + local blr_ext_div = Div{parent=blr_pane,x=2,width=blr_pane.get_width()-2} + table.insert(panes, blr_ext_div) + + local blr_ext_page = app.new_page(blr_page, #panes) + blr_ext_page.tasks = { update } + + PushButton{parent=blr_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=blr_ext_page.nav_to} + PushButton{parent=blr_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=blr_page.nav_to} + + TextBox{parent=blr_ext_div,y=1,text="More Boiler Info",height=1,alignment=ALIGN.CENTER} + + local function update_amount(indicator) + return function (x) indicator.update(x.amount) end + end + + TextBox{parent=blr_ext_div,text="Hot Coolant",x=1,y=3,width=12,height=1,fg_bg=label} + local heated_p = DataIndicator{parent=blr_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local hcool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + heated_p.register(ps, "hcool_fill", function (x) heated_p.update(x * 100) end) + hcool_amnt.register(ps, "hcool", update_amount(hcool_amnt)) + + TextBox{parent=blr_ext_div,text="Water Tank",x=1,y=6,width=9,height=1,fg_bg=label} + local fuel_p = DataIndicator{parent=blr_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local fuel_amnt = DataIndicator{parent=blr_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + fuel_p.register(ps, "water_fill", function (x) fuel_p.update(x * 100) end) + fuel_amnt.register(ps, "water", update_amount(fuel_amnt)) + + TextBox{parent=blr_ext_div,text="Steam Tank",x=1,y=9,width=10,height=1,fg_bg=label} + local steam_p = DataIndicator{parent=blr_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local steam_amnt = DataIndicator{parent=blr_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + steam_p.register(ps, "steam_fill", function (x) steam_p.update(x * 100) end) + steam_amnt.register(ps, "steam", update_amount(steam_amnt)) + + TextBox{parent=blr_ext_div,text="Cool Coolant",x=1,y=12,width=12,height=1,fg_bg=label} + local cooled_p = DataIndicator{parent=blr_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local ccool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + cooled_p.register(ps, "ccool_fill", function (x) cooled_p.update(x * 100) end) + ccool_amnt.register(ps, "ccool", update_amount(ccool_amnt)) + + TextBox{parent=blr_ext_div,text="Env. Loss",x=1,y=15,width=9,height=1,fg_bg=label} + local env_loss = DataIndicator{parent=blr_ext_div,x=11,y=15,lu_colors=lu_col,label="",unit="",format="%11.8f",value=0,width=11,fg_bg=text_fg} + + env_loss.register(ps, "env_loss", env_loss.update) + return blr_page.nav_to end From ac2d189c1a6850da00d2cb4a9febbf727f2b19db Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 31 May 2024 19:25:36 -0400 Subject: [PATCH 08/44] reactor and boiler view fixes --- pocket/ui/pages/unit_boiler.lua | 10 +++++----- pocket/ui/pages/unit_reactor.lua | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pocket/ui/pages/unit_boiler.lua b/pocket/ui/pages/unit_boiler.lua index 37f4715..c161248 100644 --- a/pocket/ui/pages/unit_boiler.lua +++ b/pocket/ui/pages/unit_boiler.lua @@ -64,7 +64,7 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update) TextBox{parent=blr_div,text="Temperature",x=5,y=5,width=13,height=1,fg_bg=label} local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10) - local temp = DataIndicator{parent=blr_div,x=5,y=6,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=17802.03,commas=true,width=13,fg_bg=text_fg} + local temp = DataIndicator{parent=blr_div,x=5,y=6,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=0,commas=true,width=13,fg_bg=text_fg} temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end) @@ -96,28 +96,28 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update) TextBox{parent=blr_ext_div,text="Hot Coolant",x=1,y=3,width=12,height=1,fg_bg=label} local heated_p = DataIndicator{parent=blr_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local hcool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local hcool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} heated_p.register(ps, "hcool_fill", function (x) heated_p.update(x * 100) end) hcool_amnt.register(ps, "hcool", update_amount(hcool_amnt)) TextBox{parent=blr_ext_div,text="Water Tank",x=1,y=6,width=9,height=1,fg_bg=label} local fuel_p = DataIndicator{parent=blr_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local fuel_amnt = DataIndicator{parent=blr_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local fuel_amnt = DataIndicator{parent=blr_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} fuel_p.register(ps, "water_fill", function (x) fuel_p.update(x * 100) end) fuel_amnt.register(ps, "water", update_amount(fuel_amnt)) TextBox{parent=blr_ext_div,text="Steam Tank",x=1,y=9,width=10,height=1,fg_bg=label} local steam_p = DataIndicator{parent=blr_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local steam_amnt = DataIndicator{parent=blr_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local steam_amnt = DataIndicator{parent=blr_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} steam_p.register(ps, "steam_fill", function (x) steam_p.update(x * 100) end) steam_amnt.register(ps, "steam", update_amount(steam_amnt)) TextBox{parent=blr_ext_div,text="Cool Coolant",x=1,y=12,width=12,height=1,fg_bg=label} local cooled_p = DataIndicator{parent=blr_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local ccool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local ccool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} cooled_p.register(ps, "ccool_fill", function (x) cooled_p.update(x * 100) end) ccool_amnt.register(ps, "ccool", update_amount(ccool_amnt)) diff --git a/pocket/ui/pages/unit_reactor.lua b/pocket/ui/pages/unit_reactor.lua index 8fe6213..a6fa011 100644 --- a/pocket/ui/pages/unit_reactor.lua +++ b/pocket/ui/pages/unit_reactor.lua @@ -121,28 +121,28 @@ return function (app, u_page, panes, page_div, u_ps, update) TextBox{parent=rct_ext_div,text="Fuel Tank",x=1,y=3,width=9,height=1,fg_bg=label} local fuel_p = DataIndicator{parent=rct_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local fuel_amnt = DataIndicator{parent=rct_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local fuel_amnt = DataIndicator{parent=rct_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} fuel_p.register(u_ps, "fuel_fill", function (x) fuel_p.update(x * 100) end) fuel_amnt.register(u_ps, "fuel", fuel_amnt.update) TextBox{parent=rct_ext_div,text="Cool Coolant",x=1,y=6,width=12,height=1,fg_bg=label} local cooled_p = DataIndicator{parent=rct_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local ccool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local ccool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} cooled_p.register(u_ps, "ccool_fill", function (x) cooled_p.update(x * 100) end) ccool_amnt.register(u_ps, "ccool_amnt", ccool_amnt.update) TextBox{parent=rct_ext_div,text="Hot Coolant",x=1,y=9,width=12,height=1,fg_bg=label} local heated_p = DataIndicator{parent=rct_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local hcool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local hcool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} heated_p.register(u_ps, "hcool_fill", function (x) heated_p.update(x * 100) end) hcool_amnt.register(u_ps, "hcool_amnt", hcool_amnt.update) TextBox{parent=rct_ext_div,text="Waste Tank",x=1,y=12,width=10,height=1,fg_bg=label} local waste_p = DataIndicator{parent=rct_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} - local waste_amnt = DataIndicator{parent=rct_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB/t",format="%16.0f",value=0,commas=true,width=21,fg_bg=text_fg} + local waste_amnt = DataIndicator{parent=rct_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} waste_p.register(u_ps, "waste_fill", function (x) waste_p.update(x * 100) end) waste_amnt.register(u_ps, "waste", waste_amnt.update) From be6c3755a4b2660ec45fe5f36dfffec3f55b10bb Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 1 Jun 2024 00:50:19 -0400 Subject: [PATCH 09/44] #207 pocket turbine view --- pocket/iocontrol.lua | 14 ++++ pocket/ui/apps/unit.lua | 17 ++++- pocket/ui/pages/unit_boiler.lua | 12 ++-- pocket/ui/pages/unit_turbine.lua | 117 +++++++++++++++++++++++++++++++ pocket/ui/style.lua | 6 ++ scada-common/util.lua | 20 +++++- supervisor/startup.lua | 2 +- supervisor/unitlogic.lua | 17 +---- 8 files changed, 179 insertions(+), 26 deletions(-) create mode 100644 pocket/ui/pages/unit_turbine.lua diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 83b807c..c3ecc35 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -783,18 +783,32 @@ function iocontrol.record_unit_data(data) local ps = unit.turbine_ps_tbl[id] ---@type psil local turbine_status = 1 + local computed_status = 1 if unit.rtu_hw.turbines[id].connected then if unit.rtu_hw.turbines[id].faulted then turbine_status = 3 + computed_status = 3 elseif turbine.formed then turbine_status = 4 + + if turbine.tanks.energy_fill >= 0.99 then + computed_status = 6 + elseif turbine.state.flow_rate < 100 then + computed_status = 4 + else + computed_status = 5 + end else turbine_status = 2 + computed_status = 2 end + + _record_multiblock_status(unit.rtu_hw.turbines[id].faulted, turbine, ps) end ps.publish("TurbineStatus", turbine_status) + ps.publish("TurbineStateStatus", computed_status) end unit.tank_data_tbl = data[9] diff --git a/pocket/ui/apps/unit.lua b/pocket/ui/apps/unit.lua index 51c27b8..a6f28b3 100644 --- a/pocket/ui/apps/unit.lua +++ b/pocket/ui/apps/unit.lua @@ -11,6 +11,7 @@ local style = require("pocket.ui.style") local boiler = require("pocket.ui.pages.unit_boiler") local reactor = require("pocket.ui.pages.unit_reactor") +local turbine = require("pocket.ui.pages.unit_turbine") local core = require("graphics.core") @@ -74,11 +75,11 @@ local function new_view(root) } for i = 1, unit.num_boilers do - table.insert(list, { label = "B-" .. i, color = core.cpair(colors.black, colors.lightBlue), callback = nav_links[id].boiler[i] }) + table.insert(list, { label = "B-" .. i, color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].boiler[i] }) end for i = 1, unit.num_turbines do - table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.white), callback = function () end }) + table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].turbine[i] }) end app.set_sidebar(list) @@ -336,6 +337,18 @@ local function new_view(root) end --#endregion + + --#region Turbine Tabs + + local tbn_pane = Div{parent=page_div} + nav_links[i].turbine = {} + + for t_id = 1, unit.num_turbines do + local ps = unit.turbine_ps_tbl[t_id] + nav_links[i].turbine[t_id] = turbine(app, u_page, panes, tbn_pane, i, t_id, ps, update) + end + + --#endregion end -- setup multipane diff --git a/pocket/ui/pages/unit_boiler.lua b/pocket/ui/pages/unit_boiler.lua index c161248..e15ae40 100644 --- a/pocket/ui/pages/unit_boiler.lua +++ b/pocket/ui/pages/unit_boiler.lua @@ -20,11 +20,11 @@ local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair -local label = style.label -local lu_col = style.label_unit_pair -local text_fg = style.text_fg -local red_ind_s = style.icon_states.red_ind_s -local yel_ind_s = style.icon_states.yel_ind_s +local label = style.label +local lu_col = style.label_unit_pair +local text_fg = style.text_fg +local red_ind_s = style.icon_states.red_ind_s +local yel_ind_s = style.icon_states.yel_ind_s -- create a boiler view in the unit app ---@param app pocket_app @@ -43,7 +43,7 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update) local blr_page = app.new_page(u_page, #panes) blr_page.tasks = { update } - TextBox{parent=blr_div,y=1,text="Boiler "..b_id,width=8,height=1} + TextBox{parent=blr_div,y=1,text="BLR #"..b_id,width=8,height=1} local status = StateIndicator{parent=blr_div,x=10,y=1,states=style.boiler.states,value=1,min_width=12} status.register(ps, "BoilerStateStatus", status.update) diff --git a/pocket/ui/pages/unit_turbine.lua b/pocket/ui/pages/unit_turbine.lua new file mode 100644 index 0000000..b4d9a2e --- /dev/null +++ b/pocket/ui/pages/unit_turbine.lua @@ -0,0 +1,117 @@ +local util = require("scada-common.util") + +local iocontrol = require("pocket.iocontrol") + +local style = require("pocket.ui.style") + +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +local TextBox = require("graphics.elements.textbox") + +local DataIndicator = require("graphics.elements.indicators.data") +local IconIndicator = require("graphics.elements.indicators.icon") +local PowerIndicator = require("graphics.elements.indicators.power") +local StateIndicator = require("graphics.elements.indicators.state") +local VerticalBar = require("graphics.elements.indicators.vbar") + +local PushButton = require("graphics.elements.controls.push_button") + +local ALIGN = core.ALIGN +local cpair = core.cpair + +local label = style.label +local lu_col = style.label_unit_pair +local text_fg = style.text_fg +local tri_ind_s = style.icon_states.tri_ind_s +local red_ind_s = style.icon_states.red_ind_s +local yel_ind_s = style.icon_states.yel_ind_s + +-- create a turbine view in the unit app +---@param app pocket_app +---@param u_page nav_tree_page +---@param panes table +---@param tbn_pane graphics_element +---@param u_id integer unit ID +---@param t_id integer turbine ID +---@param ps psil +---@param update function +return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, update) + local db = iocontrol.get_db() + + local tbn_div = Div{parent=tbn_pane,x=2,width=tbn_pane.get_width()-2} + table.insert(panes, tbn_div) + + local tbn_page = app.new_page(u_page, #panes) + tbn_page.tasks = { update } + + TextBox{parent=tbn_div,y=1,text="TRBN #"..t_id,width=8,height=1} + local status = StateIndicator{parent=tbn_div,x=10,y=1,states=style.turbine.states,value=1,min_width=12} + status.register(ps, "TurbineStateStatus", status.update) + + local steam = VerticalBar{parent=tbn_div,x=1,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1} + local ccool = VerticalBar{parent=tbn_div,x=21,y=4,fg_bg=cpair(colors.green,colors.gray),height=5,width=1} + + TextBox{parent=tbn_div,text="S",x=1,y=3,width=1,height=1,fg_bg=label} + TextBox{parent=tbn_div,text="E",x=21,y=3,width=1,height=1,fg_bg=label} + + steam.register(ps, "steam_fill", steam.update) + ccool.register(ps, "energy_fill", ccool.update) + + TextBox{parent=tbn_div,text="Production",x=3,y=3,width=17,height=1,fg_bg=label} + local prod_rate = PowerIndicator{parent=tbn_div,x=3,y=4,lu_colors=lu_col,label="",format="%11.2f",value=0,rate=true,width=17,fg_bg=text_fg} + TextBox{parent=tbn_div,text="Flow Rate",x=3,y=5,width=17,height=1,fg_bg=label} + local flow_rate = DataIndicator{parent=tbn_div,x=3,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=17,fg_bg=text_fg} + TextBox{parent=tbn_div,text="Steam Input Rate",x=3,y=7,width=17,height=1,fg_bg=label} + local input_rate = DataIndicator{parent=tbn_div,x=3,y=8,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=17,fg_bg=text_fg} + + prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end) + flow_rate.register(ps, "flow_rate", flow_rate.update) + input_rate.register(ps, "steam_input_rate", input_rate.update) + + local t_sdo = IconIndicator{parent=tbn_div,y=10,label="Steam Dumping",states=tri_ind_s} + local t_tos = IconIndicator{parent=tbn_div,label="Over Speed",states=red_ind_s} + local t_gtrp = IconIndicator{parent=tbn_div,label="Generator Trip",states=yel_ind_s} + local t_trp = IconIndicator{parent=tbn_div,label="Turbine Trip",states=red_ind_s} + + t_sdo.register(ps, "SteamDumpOpen", t_sdo.update) + t_tos.register(ps, "TurbineOverSpeed", t_tos.update) + t_gtrp.register(ps, "GeneratorTrip", t_gtrp.update) + t_trp.register(ps, "TurbineTrip", t_trp.update) + + + local tbn_ext_div = Div{parent=tbn_pane,x=2,width=tbn_pane.get_width()-2} + table.insert(panes, tbn_ext_div) + + local tbn_ext_page = app.new_page(tbn_page, #panes) + tbn_ext_page.tasks = { update } + + PushButton{parent=tbn_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=tbn_ext_page.nav_to} + PushButton{parent=tbn_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=tbn_page.nav_to} + + TextBox{parent=tbn_ext_div,y=1,text="More Turbine Info",height=1,alignment=ALIGN.CENTER} + + TextBox{parent=tbn_ext_div,text="Steam Tank",x=1,y=3,width=10,height=1,fg_bg=label} + local steam_p = DataIndicator{parent=tbn_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local steam_amnt = DataIndicator{parent=tbn_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg} + + steam_p.register(ps, "steam_fill", function (x) steam_p.update(x * 100) end) + steam_amnt.register(ps, "steam", function (x) steam_amnt.update(x.amount) end) + + TextBox{parent=tbn_ext_div,text="Energy Fill",x=1,y=6,width=12,height=1,fg_bg=label} + local charge_p = DataIndicator{parent=tbn_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg} + local charge_amnt = PowerIndicator{parent=tbn_ext_div,x=1,y=7,lu_colors=lu_col,label="",format="%17.4f",value=0,width=21,fg_bg=text_fg} + + charge_p.register(ps, "energy_fill", function (x) charge_p.update(x * 100) end) + charge_amnt.register(ps, "energy", charge_amnt.update) + + TextBox{parent=tbn_ext_div,text="Rotation Rate",x=1,y=9,width=13,height=1,fg_bg=label} + local rotation = DataIndicator{parent=tbn_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="",format="%21.12f",value=0,width=21,fg_bg=text_fg} + + rotation.register(ps, "steam", function () + local status, result = pcall(function () return util.turbine_rotation(db.units[u_id].turbine_data_tbl[t_id]) end) + if status then rotation.update(result) end + end) + + return tbn_page.nav_to +end diff --git a/pocket/ui/style.lua b/pocket/ui/style.lua index f91d7b8..ad4a231 100644 --- a/pocket/ui/style.lua +++ b/pocket/ui/style.lua @@ -57,6 +57,12 @@ states.emc_ind_s = { { color = cpair(colors.black, colors.green), symbol = "+" } } +states.tri_ind_s = { + { color = cpair(colors.black, colors.lightGray), symbol = "+" }, + { color = cpair(colors.black, colors.yellow), symbol = "\x1e" }, + { color = cpair(colors.black, colors.red), symbol = "-" } +} + states.red_ind_s = { { color = cpair(colors.black, colors.lightGray), symbol = "+" }, { color = cpair(colors.black, colors.red), symbol = "-" } diff --git a/scada-common/util.lua b/scada-common/util.lua index d29a85e..344ffd8 100644 --- a/scada-common/util.lua +++ b/scada-common/util.lua @@ -4,6 +4,8 @@ local cc_strings = require("cc.strings") +local const = require("scada-common.constants") + local math = math local string = string local table = table @@ -368,7 +370,7 @@ end --#endregion ---#region MEKANISM POWER +--#region MEKANISM MATH -- convert Joules to FE ---@nodiscard @@ -434,6 +436,22 @@ function util.power_format(fe, combine_label, format) end end +-- compute Mekanism's rotation rate for a turbine +---@nodiscard +---@param turbine turbinev_session_db turbine data +function util.turbine_rotation(turbine) + local build = turbine.build + + local inner_vol = build.steam_cap / const.mek.TURBINE_GAS_PER_TANK + local disp_rate = (build.dispersers * const.mek.TURBINE_DISPERSER_FLOW) * inner_vol + local vent_rate = build.vents * const.mek.TURBINE_VENT_FLOW + + local max_rate = math.min(disp_rate, vent_rate) + local flow = math.min(max_rate, turbine.tanks.steam.amount) + + return (flow * (turbine.tanks.steam.amount / build.steam_cap)) / max_rate +end + --#endregion --#region UTILITY CLASSES diff --git a/supervisor/startup.lua b/supervisor/startup.lua index f83e973..89c38d8 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 = "v1.3.11" +local SUPERVISOR_VERSION = "v1.3.12" local println = util.println local println_ts = util.println_ts diff --git a/supervisor/unitlogic.lua b/supervisor/unitlogic.lua index 3fe2ebc..20b00ff 100644 --- a/supervisor/unitlogic.lua +++ b/supervisor/unitlogic.lua @@ -39,21 +39,6 @@ local ALARM_LIMS = const.ALARM_LIMITS ---@class unit_logic_extension local logic = {} --- compute Mekanism's rotation rate for a turbine ----@param turbine turbinev_session_db -local function turbine_rotation(turbine) - local build = turbine.build - - local inner_vol = build.steam_cap / const.mek.TURBINE_GAS_PER_TANK - local disp_rate = (build.dispersers * const.mek.TURBINE_DISPERSER_FLOW) * inner_vol - local vent_rate = build.vents * const.mek.TURBINE_VENT_FLOW - - local max_rate = math.min(disp_rate, vent_rate) - local flow = math.min(max_rate, turbine.tanks.steam.amount) - - return (flow * (turbine.tanks.steam.amount / build.steam_cap)) / max_rate -end - -- update the annunciator ---@param self _unit_self function logic.update_annunciator(self) @@ -333,7 +318,7 @@ function logic.update_annunciator(self) local last = self.turbine_stability_data[i] if (not self.turbine_flow_stable) and (turbine.state.steam_input_rate > 0) then - local rotation = turbine_rotation(turbine) + local rotation = util.turbine_rotation(turbine) local rotation_stable = false -- see if data updated, and if so, check rotation speed change From c1c49ea3fbe736ad0d0dfd7709b2ebdf3953d55f Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 2 Jun 2024 16:06:32 -0400 Subject: [PATCH 10/44] #200 unit app updates --- pocket/iocontrol.lua | 19 ++++++++++++++-- pocket/ui/pages/unit_reactor.lua | 38 ++++++++++++++++---------------- pocket/ui/style.lua | 4 ++-- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index c3ecc35..f46532c 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -693,24 +693,38 @@ function iocontrol.record_unit_data(data) local control_status = 1 local reactor_status = 1 + local reactor_state = 1 local rps_status = 1 if unit.connected then -- update RPS status if unit.reactor_data.rps_tripped then control_status = 2 - rps_status = util.trinary(unit.reactor_data.rps_trip_cause == "manual", 3, 2) + + if unit.reactor_data.rps_trip_cause == "manual" then + reactor_state = 4 -- disabled + rps_status = 3 + else + reactor_state = 6 -- SCRAM + rps_status = 2 + end else rps_status = 4 end -- update reactor/control status if unit.reactor_data.mek_status.status then reactor_status = 4 + reactor_state = 5 -- running control_status = util.trinary(unit.annunciator.AutoControl, 4, 3) else if unit.reactor_data.no_reactor then reactor_status = 2 - elseif not unit.reactor_data.formed or unit.reactor_data.rps_status.force_dis then + reactor_state = 3 -- faulted + elseif not unit.reactor_data.formed then reactor_status = 3 + reactor_state = 2 -- not formed + elseif unit.reactor_data.rps_status.force_dis then + reactor_status = 3 + reactor_state = 7 -- force disabled else reactor_status = 4 end @@ -737,6 +751,7 @@ function iocontrol.record_unit_data(data) unit.unit_ps.publish("U_ControlStatus", control_status) unit.unit_ps.publish("U_ReactorStatus", reactor_status) + unit.unit_ps.publish("U_ReactorStateStatus", reactor_state) unit.unit_ps.publish("U_RPS", rps_status) --#endregion diff --git a/pocket/ui/pages/unit_reactor.lua b/pocket/ui/pages/unit_reactor.lua index a6fa011..6990e92 100644 --- a/pocket/ui/pages/unit_reactor.lua +++ b/pocket/ui/pages/unit_reactor.lua @@ -1,20 +1,21 @@ -local types = require("scada-common.types") -local util = require("scada-common.util") +local types = require("scada-common.types") +local util = require("scada-common.util") -local iocontrol = require("pocket.iocontrol") +local iocontrol = require("pocket.iocontrol") -local style = require("pocket.ui.style") +local style = require("pocket.ui.style") -local core = require("graphics.core") +local core = require("graphics.core") -local Div = require("graphics.elements.div") -local TextBox = require("graphics.elements.textbox") +local Div = require("graphics.elements.div") +local TextBox = require("graphics.elements.textbox") -local DataIndicator = require("graphics.elements.indicators.data") -local IconIndicator = require("graphics.elements.indicators.icon") -local VerticalBar = require("graphics.elements.indicators.vbar") +local DataIndicator = require("graphics.elements.indicators.data") +local StateIndicator = require("graphics.elements.indicators.state") +local IconIndicator = require("graphics.elements.indicators.icon") +local VerticalBar = require("graphics.elements.indicators.vbar") -local PushButton = require("graphics.elements.controls.push_button") +local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair @@ -43,7 +44,9 @@ return function (app, u_page, panes, page_div, u_ps, update) local rct_page = app.new_page(u_page, #panes) rct_page.tasks = { update } - TextBox{parent=rct_div,y=1,text="Fission Reactor",height=1,alignment=ALIGN.CENTER} + TextBox{parent=rct_div,y=1,text="Reactor",width=8,height=1} + local status = StateIndicator{parent=rct_div,x=10,y=1,states=style.reactor.states,value=1,min_width=12} + status.register(u_ps, "U_ReactorStateStatus", status.update) local fuel = VerticalBar{parent=rct_div,x=1,y=4,fg_bg=cpair(colors.lightGray,colors.gray),height=5,width=1} local ccool = VerticalBar{parent=rct_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1} @@ -76,17 +79,14 @@ return function (app, u_page, panes, page_div, u_ps, update) end end) - TextBox{parent=rct_div,text="Burn Rate",x=5,y=5,width=13,height=1,fg_bg=label} - local burn_rate = DataIndicator{parent=rct_div,x=5,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%8.2f",value=0,commas=true,width=13,fg_bg=text_fg} - TextBox{parent=rct_div,text="Temperature",x=5,y=7,width=13,height=1,fg_bg=label} + TextBox{parent=rct_div,text="Burn Rate",x=5,y=4,width=13,height=1,fg_bg=label} + local burn_rate = DataIndicator{parent=rct_div,x=5,y=5,lu_colors=lu_col,label="",unit="mB/t",format="%8.2f",value=0,commas=true,width=13,fg_bg=text_fg} + TextBox{parent=rct_div,text="Temperature",x=5,y=6,width=13,height=1,fg_bg=label} local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10) - local core_temp = DataIndicator{parent=rct_div,x=5,y=8,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=0,commas=true,width=13,fg_bg=text_fg} - - local state = IconIndicator{parent=rct_div,x=7,y=3,label="State",states=mode_states} + local core_temp = DataIndicator{parent=rct_div,x=5,y=7,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=0,commas=true,width=13,fg_bg=text_fg} burn_rate.register(u_ps, "act_burn_rate", burn_rate.update) core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end) - state.register(u_ps, "U_ControlStatus", state.update) local r_temp = IconIndicator{parent=rct_div,y=10,label="Reactor Temp. Hi",states=red_ind_s} local r_rhdt = IconIndicator{parent=rct_div,label="Hi Delta Temp.",states=yel_ind_s} diff --git a/pocket/ui/style.lua b/pocket/ui/style.lua index ad4a231..dc26755 100644 --- a/pocket/ui/style.lua +++ b/pocket/ui/style.lua @@ -82,7 +82,7 @@ style.reactor = { states = { { color = cpair(colors.black, colors.yellow), - text = "PLC OFF-LINE" + text = "OFF-LINE" }, { color = cpair(colors.black, colors.orange), @@ -106,7 +106,7 @@ style.reactor = { }, { color = cpair(colors.black, colors.red), - text = "FORCE DISABLED" + text = "FORCE DSBL" } } } From db901129f9b8821c1cff6a3178d0f91e96064232 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sun, 2 Jun 2024 22:00:42 -0400 Subject: [PATCH 11/44] #200 status display updates --- pocket/iocontrol.lua | 64 +++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index f46532c..e65ea03 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -837,49 +837,41 @@ function iocontrol.record_unit_data(data) local ecam = {} -- aviation reference :) back to VATSIM I go... - if tripped(unit.alarms[ALARM.ContainmentBreach]) then - local items = { - { text = "REACTOR MELTDOWN", color = colors.white }, - { text = "WEAR HAZMAT SUIT", color = colors.blue } - } + local function red(text) return { text = text, color = colors.red } end + local function white(text) return { text = text, color = colors.white } end + local function blue(text) return { text = text, color = colors.blue } end + if tripped(unit.alarms[ALARM.ContainmentBreach]) then + local items = { white("REACTOR MELTDOWN"), blue("DON HAZMAT SUIT") } table.insert(ecam, { color = colors.red, text = "CONTAINMENT BREACH", help = "ContainmentBreach", items = items }) end if tripped(unit.alarms[ALARM.ContainmentRadiation]) then local items = { - { text = "RADIATION DETECTED", color = colors.white }, - { text = "WEAR HAZMAT SUIT", color = colors.blue }, - { text = "RESOLVE LEAK", color = colors.blue }, - { text = "AWAIT SAFE LEVELS", color = colors.white } + white("RADIATION DETECTED"), + blue("DON HAZMAT SUIT"), + blue("RESOLVE LEAK"), + blue("AWAIT SAFE LEVELS") } table.insert(ecam, { color = colors.red, text = "RADIATION LEAK", help = "ContainmentRadiation", items = items }) end if tripped(unit.alarms[ALARM.CriticalDamage]) then - local items = { - { text = "MELTDOWN IMMINENT", color = colors.white }, - { text = "CHECK PLC", color = colors.blue } - } - + local items = { white("MELTDOWN IMMINENT"), blue("EVACUATE") } table.insert(ecam, { color = colors.red, text = "RCT DAMAGE CRITICAL", help = "CriticalDamage", items = items }) end if tripped(unit.alarms[ALARM.ReactorLost]) then - local items = { - { text = "REACTOR OFF-LINE", color = colors.white }, - { text = "CHECK PLC", color = colors.blue } - } - + local items = { white("REACTOR OFF-LINE"), blue("CHECK PLC") } table.insert(ecam, { color = colors.red, text = "REACTOR CONN LOST", help = "ReactorLost", items = items }) end if tripped(unit.alarms[ALARM.ReactorDamage]) then local items = { - { text = "REACTOR DAMAGED", color = colors.white }, - { text = "CHECK RCS", color = colors.blue }, - { text = "AWAIT DMG REDUCED", color = colors.blue } + white("REACTOR DAMAGED"), + blue("CHECK RCS"), + blue("AWAIT DMG REDUCED") } table.insert(ecam, { color = colors.red, text = "REACTOR DAMAGE", help = "ReactorDamage", items = items }) @@ -887,34 +879,27 @@ function iocontrol.record_unit_data(data) if tripped(unit.alarms[ALARM.ReactorOverTemp]) then local items = { - { text = "AOA DAMAGE TEMP", color = colors.white }, - { text = "CHECK RCS", color = colors.blue }, - { text = "AWAIT COOLDOWN", color = colors.blue } + white("HIT DAMAGING TEMP"), + blue("CHECK RCS"), + blue("AWAIT COOLDOWN") } table.insert(ecam, { color = colors.red, text = "REACTOR OVER TEMP", help = "ReactorOverTemp", items = items }) end if tripped(unit.alarms[ALARM.ReactorHighTemp]) then - local items = { - { text = "OVER EXPECTED TEMP", color = colors.white }, - { text = "CHECK RCS", color = colors.blue } - } - + local items = { white("OVER EXPECTED TEMP"), blue("CHECK RCS") } table.insert(ecam, { color = colors.yellow, text = "REACTOR HIGH TEMP", help = "ReactorHighTemp", items = items}) end if tripped(unit.alarms[ALARM.ReactorWasteLeak]) then - local items = { - { text = "CHECK WASTE OUTPUT", color = colors.blue }, - { text = "DO NOT ENABLE RCT" } - } + local items = { white("AT WASTE CAPACITY"), blue("CHECK WASTE OUTPUT"), red("DO NOT ENABLE RCT") } table.insert(ecam, { color = colors.red, text = "REACTOR WASTE LEAK", help = "ReactorWasteLeak", items = items}) end if tripped(unit.alarms[ALARM.ReactorHighWaste]) then - local items = {{ text = "CHECK WASTE OUTPUT", color = colors.white }} + local items = { blue("CHECK WASTE OUTPUT") } table.insert(ecam, { color = colors.yellow, text = "REACTOR WASTE HIGH", help = "ReactorHighWaste", items = items}) end @@ -991,26 +976,25 @@ function iocontrol.record_unit_data(data) if v then table.insert(items, { text = "TURBINE " .. k .. " TRIP", help = "TurbineTrip" }) end end - table.insert(items, { text = "CHECK ENERGY OUT", color = colors.blue }) - + table.insert(items, blue("CHECK ENERGY OUT")) table.insert(ecam, { color = colors.red, text = "TURBINE TRIP", help = "TurbineTripAlarm", items = items}) end if not (tripped(unit.alarms[ALARM.ReactorLost]) or unit.connected) then - local items = {{ text = "CHECK PLC", color = colors.blue }} + local items = { blue("CHECK PLC") } table.insert(ecam, { color = colors.yellow, text = "REACTOR OFF-LINE", items = items }) end for k, v in ipairs(unit.annunciator.BoilerOnline) do if not v then - local items = {{ text = "CHECK RTU", color = colors.blue }} + local items = { blue("CHECK RTU") } table.insert(ecam, { color = colors.yellow, text = "BOILER " .. k .. " OFF-LINE", items = items}) end end for k, v in ipairs(unit.annunciator.TurbineOnline) do if not v then - local items = {{ text = "CHECK RTU", color = colors.blue }} + local items = { blue("CHECK RTU") } table.insert(ecam, { color = colors.yellow, text = "TURBINE " .. k .. " OFF-LINE", items = items}) end end From a2af0d382936c157fdd2f448cd98cac31c9e0421 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 4 Jun 2024 00:21:54 +0000 Subject: [PATCH 12/44] #403 work on pocket guide app --- pocket/iocontrol.lua | 9 ++-- pocket/ui/apps/guide.lua | 108 +++++++++++++++++++++++++++++++++++++++ pocket/ui/docs/docs.lua | 55 ++++++++++++++++++++ 3 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 pocket/ui/apps/guide.lua create mode 100644 pocket/ui/docs/docs.lua diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index c74c6a7..223998a 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -34,12 +34,13 @@ local APP_ID = { ROOT = 1, -- main app page UNITS = 2, - ABOUT = 3, + GUIDE = 3, + ABOUT = 4, -- diag app page - ALARMS = 4, + ALARMS = 5, -- other - DUMMY = 5, - NUM_APPS = 5 + DUMMY = 6, + NUM_APPS = 6 } iocontrol.APP_ID = APP_ID diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua new file mode 100644 index 0000000..1ad1b3b --- /dev/null +++ b/pocket/ui/apps/guide.lua @@ -0,0 +1,108 @@ +-- +-- System Guide +-- + +-- local util = require("scada-common.util") +-- local log = require("scada-common.log") + +local iocontrol = require("pocket.iocontrol") + +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +-- local ListBox = require("graphics.elements.listbox") +local MultiPane = require("graphics.elements.multipane") +local TextBox = require("graphics.elements.textbox") + +local PushButton = require("graphics.elements.controls.push_button") + +local ALIGN = core.ALIGN +local cpair = core.cpair + +-- new system guide view +---@param root graphics_element parent +local function new_view(root) + local db = iocontrol.get_db() + + local main = Div{parent=root,x=1,y=1} + + local app = db.nav.register_app(iocontrol.APP_ID.GUIDE, main) + + TextBox{parent=main,y=2,text="Guide",height=1,alignment=ALIGN.CENTER} + TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER} + + local btn_fg_bg = cpair(colors.cyan, colors.black) + local btn_active = cpair(colors.white, colors.black) + + local list = { + { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, + { label = "Use", tall = true, color = core.cpair(colors.black, colors.purple), callback = function () app.switcher(1) end }, + { label = "UIs", tall = true, color = core.cpair(colors.black, colors.blue), callback = function () app.switcher(2) end }, + { label = "FPs", tall = true, color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(3) end } + } + + app.set_sidebar(list) + + local function load() + local page_div = Div{parent=main,y=2} + local p_width = page_div.get_width() - 2 + local sub_panes = {} + + local main_page = app.new_page(nil, 1) + local use_page = app.new_page(main_page, 2) + local uis_page = app.new_page(main_page, 3) + local fps_page = app.new_page(main_page, 4) + + local home = Div{parent=page_div,x=2,width=p_width} + + TextBox{parent=home,y=1,text="cc-mek-scada Guide",height=1,alignment=ALIGN.CENTER} + + PushButton{parent=home,y=3,text="System Usage >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=use_page.nav_to} + PushButton{parent=home,text="Operator UIs >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to} + PushButton{parent=home,text="Front Panels >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fps_page.nav_to} + + local use = Div{parent=page_div,x=2,width=p_width} + + TextBox{parent=use,y=1,text="System Usage",height=1,alignment=ALIGN.CENTER} + + PushButton{parent=use,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} + + PushButton{parent=use,y=3,text="Configuring Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=use,text="Connecting Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=use,text="Manual Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=use,text="Automatic Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=use,text="Waste Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + + local uis = Div{parent=page_div,x=2,width=p_width} + + TextBox{parent=uis,y=1,text="Operator UIs",height=1,alignment=ALIGN.CENTER} + + PushButton{parent=uis,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} + + PushButton{parent=uis,y=3,text="Annunciators >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=uis,text="Pocket UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=uis,text="Coordinator UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + + local fps = Div{parent=page_div,x=2,width=p_width} + + TextBox{parent=fps,y=1,text="Front Panels",height=1,alignment=ALIGN.CENTER} + + PushButton{parent=fps,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} + + PushButton{parent=fps,y=3,text="Common Items >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=fps,text="Reactor PLC >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=fps,text="RTU Gateway >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=fps,text="Supervisor >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=fps,text="Coordinator >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + + -- setup multipane + local u_pane = MultiPane{parent=page_div,x=1,y=1,panes={home,use,uis,fps,table.unpack(sub_panes)}} + app.set_root_pane(u_pane) + end + + app.set_on_load(load) + + return main +end + +return new_view diff --git a/pocket/ui/docs/docs.lua b/pocket/ui/docs/docs.lua new file mode 100644 index 0000000..b51c1ef --- /dev/null +++ b/pocket/ui/docs/docs.lua @@ -0,0 +1,55 @@ +local docs = {} + +local target + +local function doc(key, name, desc) + table.insert(target, { key = key, name = name, desc = desc }) +end + +docs.alarms = {} + +target = docs.alarms +doc("ContainmentBreach", "Containment Breach", "Reactor disconnected or indicated unformed while being at or above 100% damage; explosion likely occurred.") +doc("ContainmentRadiation", "Containment Radiation", "Environment detector(s) assigned to the unit have observed high levels of radiation.") +doc("ReactorLost", "Reactor Lost", "Reactor PLC has stopped communicating with the supervisor.") +doc("CriticalDamage", "Damage Critical", "Reactor damage has reached or exceeded 100%, so it may explode at any moment.") +doc("ReactorDamage", "Reactor Damage", "Reactor temperature causing increasing damage to reactor casing.") +doc("ReactorOverTemp", "Reactor Over Temp", "Reactor temperature is at or above maximum safe temperature and is now taking damage.") +doc("ReactorHighTemp", "Reactor High Temp", "Reactor temperature is above expected operating levels and may exceed maximum safe temperature soon.") +doc("ReactorWasteLeak", "Reactor Waste Leak", "The reactor is full of spent waste and will now emit radiation if additional waste is generated.") +doc("ReactorHighWaste", "Reactor High Waste", "Reactor waste levels are high and may leak soon.") +doc("RPSTransient", "RPS Transient", "Reactor protection system was activated.") +doc("RCSTransient", "RCS Transient", "Something is wrong with the reactor coolant system, check RCS indicators.") +doc("TurbineTripAlarm", "Turbine Trip", "A turbine stopped rotating, likely due to having full energy storage.") + +docs.annunc = { + unit = { + main_section = {}, + rps_section = {}, + rcs_section = {} + } +} + +target = docs.annunc.unit.main_section +doc("PLCOnline", "PLC Online", "Indicates if the fission reactor PLC is connected.") +doc("PLCHeartbeat", "PLC Heartbeat", "An indicator of status data being live. As status messages are received from the PLC, this light will turn on and off. If it gets stuck, the supervisor has stopped receiving data.") +doc("RadiationMonitor", "Radiation Monitor", "Indicates if at least once environment detector is connected and assigned to this unit.") +doc("AutoControl", "Automatic Control", "Indicates if the reactor is under the control of one of the automatic control modes.") +doc("ReactorSCRAM", "Reactor SCRAM", "Indicates if the reactor protection system is holding the reactor SCRAM'd.") +doc("ManualReactorSCRAM", "Manual Reactor SCRAM", "Indicates if the operator (you) initiated a SCRAM.") +doc("AutoReactorSCRAM", "Auto Reactor SCRAM", "Indicates if the automatic control system initiated a SCRAM. The main view screen will have an indicator as to why.") +doc("RadiationWarning", "Radiation Warning", "Indicates if radiation levels are above normal. There is likely a leak somewhere, so that should be identified and fixed.") +doc("RCPTrip", "RCP Trip", "Reactor coolant pump tripped. This is a technical concept not directly mapping to mekansim, so in this case it indicates if there is either high heated coolant or low cooled coolant causing an RPS trip. Check the coolant system if this occurs.") +doc("RCSFlowLow", "RCS Flow Low", "Indicates if the reactor coolant system flow is low. This is observed when the cooled coolant level in the reactor is dropping. This can occur while a turbine spins up, but if it persists, check that the cooling system is operating properly.") +doc("CoolantLevelLow", "Coolant Level Low", "Indicates if the reactor coolant level is lower than it should be. Check the coolant system.") +doc("ReactorTempHigh", "Reactor Temp. High", "Indicates if the reactor temperature is above expected maximum operating temperature. This is not yet damaging, but should be attended to. Check coolant system.") +doc("ReactorHighDeltaT", "Reactor High Delta T", "Indicates if the reactor temperature is climbing rapidly. This can occur when a reactor is starting up, but it is a concern if it happens uncontrolled while the burn rate is not increasing.") +doc("FuelInputRateLow", "Fuel Input Rate Low", "Indicates if the fissile fuel levels in the reactor are dropping or are very low. Ensure a steady supply of fuel is entering the reactor.") +doc("WasteLineOcclusion", "Waste Line Occlusion", "Waste levels in the reactor are increasing. Ensure your waste processing system is operating at a sufficient rate for your burn rate.") +doc("HighStartupRate", "Startup Rate High", "This is a rough calculation of if your burn rate is high enough to cause a loss of coolant. A burn rate above this is likely to cause that, but it could occur at even higher or even lower rates depending on your setup (such as pipes, water supplies, and boiler tanks).") + +target = docs.annunc.unit.rps_section +doc("rps_tripped", "RPS Trip", "Indicates if the reactor protection system has caused a SCRAM.") +doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage.") + +return docs From 4d87887709299714eb6d7439488a5f88350a5951 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 3 Jun 2024 20:52:59 -0400 Subject: [PATCH 13/44] #403 pocket guide fixes --- pocket/ui/apps/guide.lua | 19 ++++++++----------- pocket/ui/main.lua | 8 ++++++-- pocket/ui/pages/home_page.lua | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 1ad1b3b..9fd9628 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -36,9 +36,9 @@ local function new_view(root) local list = { { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, - { label = "Use", tall = true, color = core.cpair(colors.black, colors.purple), callback = function () app.switcher(1) end }, - { label = "UIs", tall = true, color = core.cpair(colors.black, colors.blue), callback = function () app.switcher(2) end }, - { label = "FPs", tall = true, color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(3) end } + { label = "Use", color = core.cpair(colors.black, colors.purple), callback = function () app.switcher(2) end }, + { label = "UIs", color = core.cpair(colors.black, colors.blue), callback = function () app.switcher(3) end }, + { label = "FPs", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(4) end } } app.set_sidebar(list) @@ -46,7 +46,6 @@ local function new_view(root) local function load() local page_div = Div{parent=main,y=2} local p_width = page_div.get_width() - 2 - local sub_panes = {} local main_page = app.new_page(nil, 1) local use_page = app.new_page(main_page, 2) @@ -54,6 +53,10 @@ local function new_view(root) local fps_page = app.new_page(main_page, 4) local home = Div{parent=page_div,x=2,width=p_width} + local use = Div{parent=page_div,x=2,width=p_width} + local uis = Div{parent=page_div,x=2,width=p_width} + local fps = Div{parent=page_div,x=2,width=p_width} + local panes = { home, use, uis, fps } TextBox{parent=home,y=1,text="cc-mek-scada Guide",height=1,alignment=ALIGN.CENTER} @@ -61,8 +64,6 @@ local function new_view(root) PushButton{parent=home,text="Operator UIs >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to} PushButton{parent=home,text="Front Panels >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fps_page.nav_to} - local use = Div{parent=page_div,x=2,width=p_width} - TextBox{parent=use,y=1,text="System Usage",height=1,alignment=ALIGN.CENTER} PushButton{parent=use,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} @@ -73,8 +74,6 @@ local function new_view(root) PushButton{parent=use,text="Automatic Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} PushButton{parent=use,text="Waste Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - local uis = Div{parent=page_div,x=2,width=p_width} - TextBox{parent=uis,y=1,text="Operator UIs",height=1,alignment=ALIGN.CENTER} PushButton{parent=uis,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} @@ -83,8 +82,6 @@ local function new_view(root) PushButton{parent=uis,text="Pocket UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} PushButton{parent=uis,text="Coordinator UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - local fps = Div{parent=page_div,x=2,width=p_width} - TextBox{parent=fps,y=1,text="Front Panels",height=1,alignment=ALIGN.CENTER} PushButton{parent=fps,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} @@ -96,7 +93,7 @@ local function new_view(root) PushButton{parent=fps,text="Coordinator >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} -- setup multipane - local u_pane = MultiPane{parent=page_div,x=1,y=1,panes={home,use,uis,fps,table.unpack(sub_panes)}} + local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes} app.set_root_pane(u_pane) end diff --git a/pocket/ui/main.lua b/pocket/ui/main.lua index eaecc1e..9f33e1f 100644 --- a/pocket/ui/main.lua +++ b/pocket/ui/main.lua @@ -2,10 +2,13 @@ -- Pocket GUI Root -- +local util = require("scada-common.util") + local iocontrol = require("pocket.iocontrol") local diag_apps = require("pocket.ui.apps.diag_apps") local dummy_app = require("pocket.ui.apps.dummy_app") +local guide_app = require("pocket.ui.apps.guide") local sys_apps = require("pocket.ui.apps.sys_apps") local unit_app = require("pocket.ui.apps.unit") @@ -74,11 +77,12 @@ local function init(main) home_page(page_div) unit_app(page_div) - diag_apps(page_div) + guide_app(page_div) sys_apps(page_div) + diag_apps(page_div) dummy_app(page_div) - assert(#db.nav.get_containers() == iocontrol.APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered") + assert(util.table_len(db.nav.get_containers()) == iocontrol.APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered") db.nav.set_pane(MultiPane{parent=page_div,x=1,y=1,panes=db.nav.get_containers()}) db.nav.set_sidebar(Sidebar{parent=main_pane,x=1,y=1,height=18,fg_bg=cpair(colors.white,colors.gray)}) diff --git a/pocket/ui/pages/home_page.lua b/pocket/ui/pages/home_page.lua index 483a881..8854614 100644 --- a/pocket/ui/pages/home_page.lua +++ b/pocket/ui/pages/home_page.lua @@ -51,7 +51,7 @@ local function new_view(root) App{parent=apps_1,x=2,y=7,text="\x17",title="Process",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.purple),active_fg_bg=active_fg_bg} App{parent=apps_1,x=9,y=7,text="\x7f",title="Waste",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.brown),active_fg_bg=active_fg_bg} App{parent=apps_1,x=16,y=7,text="\x08",title="Devices",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.lightGray),active_fg_bg=active_fg_bg} - App{parent=apps_1,x=2,y=12,text="\xb6",title="Guide",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg} + App{parent=apps_1,x=2,y=12,text="\xb6",title="Guide",callback=function()open(APP_ID.GUIDE)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg} App{parent=apps_1,x=9,y=12,text="?",title="About",callback=function()open(APP_ID.ABOUT)end,app_fg_bg=cpair(colors.black,colors.white),active_fg_bg=active_fg_bg} TextBox{parent=apps_2,text="Diagnostic Apps",x=1,y=2,height=1,alignment=ALIGN.CENTER} From 25ebf2c8c73105018ece879c2976ff36c0afa029 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 5 Jun 2024 00:31:06 +0000 Subject: [PATCH 14/44] graphics automatic constraints --- graphics/core.lua | 2 +- graphics/element.lua | 10 +++++++++- graphics/elements/rectangle.lua | 2 +- graphics/elements/textbox.lua | 14 ++++++++++++-- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/graphics/core.lua b/graphics/core.lua index 830ca69..f6a647a 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -7,7 +7,7 @@ local flasher = require("graphics.flasher") local core = {} -core.version = "2.2.4" +core.version = "2.3.0" core.flasher = flasher core.events = events diff --git a/graphics/element.lua b/graphics/element.lua index 731a22c..6f859ab 100644 --- a/graphics/element.lua +++ b/graphics/element.lua @@ -82,9 +82,10 @@ end -- a base graphics element, should not be created on its own ---@nodiscard ---@param args graphics_args arguments +---@param constraint? function apply a dimensional constraint based on proposed dimensions function(frame) -> width, height ---@param child_offset_x? integer mouse event offset x ---@param child_offset_y? integer mouse event offset y -function element.new(args, child_offset_x, child_offset_y) +function element.new(args, constraint, child_offset_x, child_offset_y) local self = { id = nil, ---@type element_id|nil is_root = args.parent == nil, @@ -226,6 +227,13 @@ function element.new(args, child_offset_x, child_offset_y) local w, h = self.p_window.getSize() f.w = math.min(f.w, w - (f.x - 1)) f.h = math.min(f.h, h - (f.y - 1)) + + if type(constraint) == "function" then + -- constrain per provided constraint function (can only get smaller than available space) + w, h = constraint(w, h) + f.w = math.min(f.w, w) + f.h = math.min(f.h, h) + end end -- check frame diff --git a/graphics/elements/rectangle.lua b/graphics/elements/rectangle.lua index 252790b..eceb9bd 100644 --- a/graphics/elements/rectangle.lua +++ b/graphics/elements/rectangle.lua @@ -45,7 +45,7 @@ local function rectangle(args) end -- create new graphics element base object - local e = element.new(args, offset_x, offset_y) + local e = element.new(args, nil, offset_x, offset_y) -- create content window for child elements e.content_window = window.create(e.window, 1 + offset_x, 1 + offset_y, e.frame.w - (2 * offset_x), e.frame.h - (2 * offset_y)) diff --git a/graphics/elements/textbox.lua b/graphics/elements/textbox.lua index 07a8736..5a02579 100644 --- a/graphics/elements/textbox.lua +++ b/graphics/elements/textbox.lua @@ -10,12 +10,13 @@ local ALIGN = core.ALIGN ---@class textbox_args ---@field text string text to show ---@field alignment? ALIGN text alignment, left by default +---@field anchor? boolean true to use this as an anchor, making it focusable ---@field parent graphics_element ---@field id? string element id ---@field x? integer 1 if omitted ---@field y? integer auto incremented if omitted ---@field width? integer parent width if omitted ----@field height? integer parent height if omitted +---@field height? integer minimum necessary height for wrapped text if omitted ---@field gframe? graphics_frame frame instead of x/y/width/height ---@field fg_bg? cpair foreground/background colors ---@field hidden? boolean true to hide on initial draw @@ -26,8 +27,17 @@ local ALIGN = core.ALIGN local function textbox(args) element.assert(type(args.text) == "string", "text is a required field") + if args.anchor == true then args.can_focus = true end + + -- regex to identify entries without a height currently: ^.*TextBox\{((?!height=).)*$ + -- provide a constraint condition to element creation to prevent an pointlessly tall text box + ---@param frame graphics_frame + local function constrain(frame) + return frame.w, math.max(args.height, math.max(1, #util.strwrap(args.text, frame.w))) + end + -- create new graphics element base object - local e = element.new(args) + local e = element.new(args, constrain) e.value = args.text From b9030d6bed192bb8f8a43831e2e20c8532840b2f Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 5 Jun 2024 00:31:29 +0000 Subject: [PATCH 15/44] #403 work on guide app --- pocket/ui/apps/guide.lua | 40 ++++++++++++++++++++++++++++++++--- pocket/ui/{docs => }/docs.lua | 15 ++++++++++++- 2 files changed, 51 insertions(+), 4 deletions(-) rename pocket/ui/{docs => }/docs.lua (89%) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 9fd9628..a6edc87 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -7,10 +7,13 @@ local iocontrol = require("pocket.iocontrol") +local docs = require("pocket.ui.docs") +local style = require("pocket.ui.style") + local core = require("graphics.core") local Div = require("graphics.elements.div") --- local ListBox = require("graphics.elements.listbox") +local ListBox = require("graphics.elements.listbox") local MultiPane = require("graphics.elements.multipane") local TextBox = require("graphics.elements.textbox") @@ -19,6 +22,10 @@ local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair +local label = style.label +-- local lu_col = style.label_unit_pair +local text_fg = style.text_fg + -- new system guide view ---@param root graphics_element parent local function new_view(root) @@ -51,18 +58,23 @@ local function new_view(root) local use_page = app.new_page(main_page, 2) local uis_page = app.new_page(main_page, 3) local fps_page = app.new_page(main_page, 4) + local gls_page = app.new_page(main_page, 5) local home = Div{parent=page_div,x=2,width=p_width} local use = Div{parent=page_div,x=2,width=p_width} local uis = Div{parent=page_div,x=2,width=p_width} local fps = Div{parent=page_div,x=2,width=p_width} - local panes = { home, use, uis, fps } + local gls = Div{parent=page_div,x=2} + local panes = { home, use, uis, fps, gls } + + local doc_map = {} TextBox{parent=home,y=1,text="cc-mek-scada Guide",height=1,alignment=ALIGN.CENTER} PushButton{parent=home,y=3,text="System Usage >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=use_page.nav_to} PushButton{parent=home,text="Operator UIs >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to} PushButton{parent=home,text="Front Panels >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fps_page.nav_to} + PushButton{parent=home,text="Glossary >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} TextBox{parent=use,y=1,text="System Usage",height=1,alignment=ALIGN.CENTER} @@ -78,7 +90,16 @@ local function new_view(root) PushButton{parent=uis,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} - PushButton{parent=uis,y=3,text="Annunciators >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + local alarms_page = app.new_page(uis_page, #panes + 1) + local alarms_div = Div{parent=page_div,x=2} + table.insert(panes, alarms_div) + + local annunc_page = app.new_page(uis_page, #panes + 1) + local annunc_div = Div{parent=page_div,x=2} + table.insert(panes, annunc_div) + + PushButton{parent=uis,y=3,text="Alarms >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=alarms_page.nav_to} + PushButton{parent=uis,text="Annunciators >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=annunc_page.nav_to} PushButton{parent=uis,text="Pocket UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} PushButton{parent=uis,text="Coordinator UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} @@ -92,6 +113,19 @@ local function new_view(root) PushButton{parent=fps,text="Supervisor >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} PushButton{parent=fps,text="Coordinator >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + TextBox{parent=gls,y=1,text="Glossary",height=1,alignment=ALIGN.CENTER} + + PushButton{parent=gls,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} + + local gls_list_box = ListBox{parent=gls,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + for i = 1, #docs.glossary do + local item = docs.glossary[i] ---@type pocket_doc_item + doc_map[item.key] = TextBox{parent=gls_list_box,text=item.name,anchor=true} + TextBox{parent=gls_list_box,text=item.desc,fg_bg=label} + gls_list_box.line_break() + end + -- setup multipane local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes} app.set_root_pane(u_pane) diff --git a/pocket/ui/docs/docs.lua b/pocket/ui/docs.lua similarity index 89% rename from pocket/ui/docs/docs.lua rename to pocket/ui/docs.lua index b51c1ef..69b31d9 100644 --- a/pocket/ui/docs/docs.lua +++ b/pocket/ui/docs.lua @@ -3,7 +3,9 @@ local docs = {} local target local function doc(key, name, desc) - table.insert(target, { key = key, name = name, desc = desc }) + ---@class pocket_doc_item + local item = { key = key, name = name, desc = desc } + table.insert(target, item) end docs.alarms = {} @@ -52,4 +54,15 @@ target = docs.annunc.unit.rps_section doc("rps_tripped", "RPS Trip", "Indicates if the reactor protection system has caused a SCRAM.") doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage.") +docs.glossary = {} + +target = docs.glossary +doc("G_Nominal", "Nominal", "") +doc("G_RCS", "RCS", "Reactor Cooling System: the combination of all machines used to cool the reactor.") +doc("G_RPS", "RPS", "Reactor Protection System: a component of the reactor PLC responsible for keeping the reactor safe.") +doc("G_Transient", "Transient", "") +doc("G_Trip", "Trip", "A checked condition has occurred, also known as 'tripped'.") + +target = docs.annunc.unit.main_section + return docs From 58fb35e85b112b4c96e0611014f4dde680e9bddd Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 5 Jun 2024 00:31:47 +0000 Subject: [PATCH 16/44] keyboard and paste support for pocket --- pocket/renderer.lua | 16 ++++++++++++++++ pocket/startup.lua | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pocket/renderer.lua b/pocket/renderer.lua index 892fc92..bc16037 100644 --- a/pocket/renderer.lua +++ b/pocket/renderer.lua @@ -92,4 +92,20 @@ function renderer.handle_mouse(event) end end +-- handle a keyboard event +---@param event key_interaction|nil +function renderer.handle_key(event) + if ui.display ~= nil and event ~= nil then + ui.display.handle_key(event) + end +end + +-- handle a paste event +---@param text string +function renderer.handle_paste(text) + if ui.display ~= nil then + ui.display.handle_paste(text) + end +end + return renderer diff --git a/pocket/startup.lua b/pocket/startup.lua index c9f8c8d..335bdc1 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -175,8 +175,14 @@ local function main() pocket_comms.handle_packet(packet) elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then - -- handle a monitor touch event + -- handle a mouse event renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) + elseif event == "char" or event == "key" or event == "key_up" then + -- handle a keyboard event + renderer.handle_key(core.events.new_key_event(event, param1, param2)) + elseif event == "paste" then + -- handle a paste event + renderer.handle_paste(param1) end -- check for termination request From 9404b50a8c6a931e629d81a6b7e947335c99fba2 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 5 Jun 2024 22:07:38 +0000 Subject: [PATCH 17/44] #403 additional work on guide app --- pocket/ui/apps/guide.lua | 67 +++++++++++++++++++++++++++++++++++----- pocket/ui/docs.lua | 43 +++++++++++++++++++------- 2 files changed, 91 insertions(+), 19 deletions(-) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index a6edc87..b4300fa 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -114,16 +114,69 @@ local function new_view(root) PushButton{parent=fps,text="Coordinator >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} TextBox{parent=gls,y=1,text="Glossary",height=1,alignment=ALIGN.CENTER} - PushButton{parent=gls,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} - local gls_list_box = ListBox{parent=gls,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local gls_abb_page = app.new_page(gls_page, #panes + 1) + local gls_abb_div = Div{parent=page_div,x=2} + table.insert(panes, gls_abb_div) + TextBox{parent=gls_abb_div,y=1,text="Abbreviations",height=1,alignment=ALIGN.CENTER} + PushButton{parent=gls_abb_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} - for i = 1, #docs.glossary do - local item = docs.glossary[i] ---@type pocket_doc_item - doc_map[item.key] = TextBox{parent=gls_list_box,text=item.name,anchor=true} - TextBox{parent=gls_list_box,text=item.desc,fg_bg=label} - gls_list_box.line_break() + local gls_abb_view_page = app.new_page(gls_abb_page, #panes + 1) + local gls_abb_view_div = Div{parent=page_div,x=2} + table.insert(panes, gls_abb_view_div) + TextBox{parent=gls_abb_view_div,y=1,text="Abbreviations",height=1,alignment=ALIGN.CENTER} + PushButton{parent=gls_abb_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abb_page.nav_to} + + local gls_term_page = app.new_page(gls_page, #panes + 1) + local gls_term_div = Div{parent=page_div,x=2} + table.insert(panes, gls_term_div) + TextBox{parent=gls_term_div,y=1,text="Terminology",height=1,alignment=ALIGN.CENTER} + PushButton{parent=gls_term_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} + + local gls_term_view_page = app.new_page(gls_term_page, #panes + 1) + local gls_term_view_div = Div{parent=page_div,x=2} + table.insert(panes, gls_term_view_div) + TextBox{parent=gls_term_view_div,y=1,text="Terminology",height=1,alignment=ALIGN.CENTER} + PushButton{parent=gls_term_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_term_page.nav_to} + + PushButton{parent=gls,y=3,text="Abbreviations >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abb_page.nav_to} + PushButton{parent=gls,text="Terminology >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_term_page.nav_to} + + local abb_name_list = ListBox{parent=gls_abb_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local abb_def_list = ListBox{parent=gls_abb_view_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + for i = 1, #docs.glossary.abbvs do + local item = docs.glossary.abbvs[i] ---@type pocket_doc_item + + doc_map[item.key] = TextBox{parent=abb_def_list,text=item.name,anchor=true,cpair(colors.blue,colors.black)} + TextBox{parent=abb_def_list,text=item.desc,fg_bg=label} + TextBox{parent=abb_def_list,text="",fg_bg=label} + + local function view() + gls_abb_view_page.nav_to() + doc_map[item.key].focus() + end + + PushButton{parent=abb_name_list,text=item.name,fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=view} + end + + local term_name_list = ListBox{parent=gls_term_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local term_def_list = ListBox{parent=gls_term_view_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + for i = 1, #docs.glossary.terms do + local item = docs.glossary.terms[i] ---@type pocket_doc_item + + doc_map[item.key] = TextBox{parent=term_def_list,text=item.name,anchor=true,cpair(colors.blue,colors.black)} + TextBox{parent=term_def_list,text=item.desc,fg_bg=label} + TextBox{parent=term_def_list,text="",fg_bg=label} + + local function view() + gls_term_view_page.nav_to() + doc_map[item.key].focus() + end + + PushButton{parent=term_name_list,text=item.name,fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=view} end -- setup multipane diff --git a/pocket/ui/docs.lua b/pocket/ui/docs.lua index 69b31d9..d38d4ce 100644 --- a/pocket/ui/docs.lua +++ b/pocket/ui/docs.lua @@ -26,9 +26,7 @@ doc("TurbineTripAlarm", "Turbine Trip", "A turbine stopped rotating, likely due docs.annunc = { unit = { - main_section = {}, - rps_section = {}, - rcs_section = {} + main_section = {}, rps_section = {}, rcs_section = {} } } @@ -54,15 +52,36 @@ target = docs.annunc.unit.rps_section doc("rps_tripped", "RPS Trip", "Indicates if the reactor protection system has caused a SCRAM.") doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage.") -docs.glossary = {} - -target = docs.glossary -doc("G_Nominal", "Nominal", "") -doc("G_RCS", "RCS", "Reactor Cooling System: the combination of all machines used to cool the reactor.") -doc("G_RPS", "RPS", "Reactor Protection System: a component of the reactor PLC responsible for keeping the reactor safe.") -doc("G_Transient", "Transient", "") -doc("G_Trip", "Trip", "A checked condition has occurred, also known as 'tripped'.") - target = docs.annunc.unit.main_section +docs.glossary = { + abbvs = {}, terms = {} +} + +target = docs.glossary.abbvs +doc("G_ACK", "ACK", "Alarm ACKnowledge. This indicates you understand an alarm occured and would like to stop the audio tone(s).") +doc("G_CRD", "CRD", "Coordinator. Abbreviation for the coordinator computer.") +doc("G_DBG", "DBG", "Debug. Abbreviation for the debugging sessions from pocket computers found on the supervisor's front panel.") +doc("G_PKT", "PKT", "Pocket. Abbreviation for the pocket computer.") +doc("G_PLC", "PLC", "Programmable Logic Controller. A device that not only reports data and controls outputs, but also can make decisions on its own.") +doc("G_PPM", "PPM", "Protected Peripheral Manager. This is an abstraction layer created for this project that prevents peripheral calls from crashing applications.") +doc("G_RCP", "RCP", "Reactor Coolant Pump. This is from real-world terminology with water-cooled reactors, but in this system it just relates to the functioning of reactor coolant flow.") +doc("G_RCS", "RCS", "Reactor Cooling System. The combination of all machines used to cool the reactor.") +doc("G_RPS", "RPS", "Reactor Protection System. A component of the reactor PLC responsible for keeping the reactor safe.") +doc("G_RTU", "RTU", "Remote Terminal Unit. Provides monitoring to and basic output from a SCADA system, interfacing with various types of devices/controls.") +doc("G_SCADA", "SCADA", "Supervisory Control and Data Acquisition. A control systems architecture used in many different process control applications.") +doc("G_SVR", "SVR", "Supervisor. Abbreviation for the supervisory computer.") + +target = docs.glossary.term +doc("G_Fault", "Fault", "Something has gone wrong and/or failed to function.") +doc("G_FrontPanel", "Front Panel", "A basic interface on the front of a device for viewing and sometimes modifying its state. This is what you see when looking at a computer running one of the SCADA applications.") +doc("G_Nominal", "Nominal", "Normal operation. Everything operating as intended.") +doc("G_Ringback", "Ringback", "An indication that an alarm had gone off so that you are aware, even if the alarm condition is no longer met.") +doc("G_SCRAM", "SCRAM", "Emergency shut-down of a reactor by stopping the fission reactor.") +doc("G_Transient", "Transient", "A temporary change in state from normal operation. Coolant levels dropping or core temperature rising above nominal values would be examples of transients.") +doc("G_Trip", "Trip", "A checked condition has occurred, also known as 'tripped'.") +doc("G_Tripped", "Tripped", "An alarm condition has been met and is still met.") +doc("G_Tripping", "Tripping", "An alarm condition is met but has not met the minimum time before a condition is deemed a problem.") +doc("G_TurbineTrip", "Turbine Trip", "The turbine stops, which prevents heated coolant from being properly cooled. In Mekanism, this would occur when a turbine cannot generate any more energy due to filling its buffer and having no output with any storage for energy left.") + return docs From e37b8758cd12c3fe2e059b9fe0bb4e0d50698152 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 5 Jun 2024 19:38:02 -0400 Subject: [PATCH 18/44] #403 guide fixes and focusing improvements --- pocket/ui/apps/guide.lua | 26 ++++++++++++++++---------- pocket/ui/docs.lua | 6 +++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index b4300fa..1d0a0a6 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -143,40 +143,46 @@ local function new_view(root) PushButton{parent=gls,y=3,text="Abbreviations >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abb_page.nav_to} PushButton{parent=gls,text="Terminology >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_term_page.nav_to} - local abb_name_list = ListBox{parent=gls_abb_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - local abb_def_list = ListBox{parent=gls_abb_view_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local abb_name_list = ListBox{parent=gls_abb_div,x=1,y=3,scroll_height=20,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local abb_def_list = ListBox{parent=gls_abb_view_div,x=1,y=3,scroll_height=101,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + local _end = nil for i = 1, #docs.glossary.abbvs do local item = docs.glossary.abbvs[i] ---@type pocket_doc_item - doc_map[item.key] = TextBox{parent=abb_def_list,text=item.name,anchor=true,cpair(colors.blue,colors.black)} + doc_map[item.key] = TextBox{parent=abb_def_list,text=item.name,anchor=true,fg_bg=cpair(colors.blue,colors.black)} TextBox{parent=abb_def_list,text=item.desc,fg_bg=label} - TextBox{parent=abb_def_list,text="",fg_bg=label} + _end = Div{parent=abb_def_list,height=1,can_focus=true} local function view() + _end.focus() gls_abb_view_page.nav_to() doc_map[item.key].focus() end - PushButton{parent=abb_name_list,text=item.name,fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=view} + PushButton{parent=abb_name_list,text=item.name,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} end - local term_name_list = ListBox{parent=gls_term_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - local term_def_list = ListBox{parent=gls_term_view_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local term_name_list = ListBox{parent=gls_term_div,x=1,y=3,scroll_height=20,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local term_def_list = ListBox{parent=gls_term_view_div,x=1,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + local _end_b = nil for i = 1, #docs.glossary.terms do local item = docs.glossary.terms[i] ---@type pocket_doc_item - doc_map[item.key] = TextBox{parent=term_def_list,text=item.name,anchor=true,cpair(colors.blue,colors.black)} + doc_map[item.key] = TextBox{parent=term_def_list,text=item.name,anchor=true,fg_bg=cpair(colors.blue,colors.black)} TextBox{parent=term_def_list,text=item.desc,fg_bg=label} - TextBox{parent=term_def_list,text="",fg_bg=label} + _end_b = Div{parent=term_def_list,height=1,can_focus=true} local function view() + _end_b.focus() gls_term_view_page.nav_to() doc_map[item.key].focus() end - PushButton{parent=term_name_list,text=item.name,fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=view} + PushButton{parent=term_name_list,text=item.name,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} end -- setup multipane diff --git a/pocket/ui/docs.lua b/pocket/ui/docs.lua index d38d4ce..50e20fa 100644 --- a/pocket/ui/docs.lua +++ b/pocket/ui/docs.lua @@ -72,16 +72,16 @@ doc("G_RTU", "RTU", "Remote Terminal Unit. Provides monitoring to and basic outp doc("G_SCADA", "SCADA", "Supervisory Control and Data Acquisition. A control systems architecture used in many different process control applications.") doc("G_SVR", "SVR", "Supervisor. Abbreviation for the supervisory computer.") -target = docs.glossary.term +target = docs.glossary.terms doc("G_Fault", "Fault", "Something has gone wrong and/or failed to function.") doc("G_FrontPanel", "Front Panel", "A basic interface on the front of a device for viewing and sometimes modifying its state. This is what you see when looking at a computer running one of the SCADA applications.") doc("G_Nominal", "Nominal", "Normal operation. Everything operating as intended.") doc("G_Ringback", "Ringback", "An indication that an alarm had gone off so that you are aware, even if the alarm condition is no longer met.") -doc("G_SCRAM", "SCRAM", "Emergency shut-down of a reactor by stopping the fission reactor.") +doc("G_SCRAM", "SCRAM", "[Emergency] shut-down of a reactor by stopping the fission reactor. In Mekanism and here, it isn't always for an emergency.") doc("G_Transient", "Transient", "A temporary change in state from normal operation. Coolant levels dropping or core temperature rising above nominal values would be examples of transients.") doc("G_Trip", "Trip", "A checked condition has occurred, also known as 'tripped'.") doc("G_Tripped", "Tripped", "An alarm condition has been met and is still met.") doc("G_Tripping", "Tripping", "An alarm condition is met but has not met the minimum time before a condition is deemed a problem.") -doc("G_TurbineTrip", "Turbine Trip", "The turbine stops, which prevents heated coolant from being properly cooled. In Mekanism, this would occur when a turbine cannot generate any more energy due to filling its buffer and having no output with any storage for energy left.") +doc("G_TurbineTrip", "Turbine Trip", "The turbine stopped, which prevents heated coolant from being properly cooled. In Mekanism, this would occur when a turbine cannot generate any more energy due to filling its buffer and having no output with any storage for energy left.") return docs From 375b7f680ef60d386a931060613329eb55a3bf36 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 5 Jun 2024 19:38:22 -0400 Subject: [PATCH 19/44] fixes to graphics constraint logic --- graphics/element.lua | 2 +- graphics/elements/textbox.lua | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/graphics/element.lua b/graphics/element.lua index 6f859ab..d086408 100644 --- a/graphics/element.lua +++ b/graphics/element.lua @@ -230,7 +230,7 @@ function element.new(args, constraint, child_offset_x, child_offset_y) if type(constraint) == "function" then -- constrain per provided constraint function (can only get smaller than available space) - w, h = constraint(w, h) + w, h = constraint(f) f.w = math.min(f.w, w) f.h = math.min(f.h, h) end diff --git a/graphics/elements/textbox.lua b/graphics/elements/textbox.lua index 5a02579..0761471 100644 --- a/graphics/elements/textbox.lua +++ b/graphics/elements/textbox.lua @@ -33,7 +33,13 @@ local function textbox(args) -- provide a constraint condition to element creation to prevent an pointlessly tall text box ---@param frame graphics_frame local function constrain(frame) - return frame.w, math.max(args.height, math.max(1, #util.strwrap(args.text, frame.w))) + local new_height = math.max(1, #util.strwrap(args.text, frame.w)) + + if args.height then + new_height = math.max(frame.h, new_height) + end + + return frame.w, new_height end -- create new graphics element base object From 5e70e4131e50637dad5aba0c35cf6c3cca351999 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 7 Jun 2024 02:30:55 +0000 Subject: [PATCH 20/44] #403 more work on guide and help linking --- pocket/iocontrol.lua | 16 +++- pocket/ui/apps/guide.lua | 125 ++++++++++-------------------- pocket/ui/docs.lua | 27 ++++++- pocket/ui/pages/guide_section.lua | 58 ++++++++++++++ 4 files changed, 137 insertions(+), 89 deletions(-) create mode 100644 pocket/ui/pages/guide_section.lua diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 67ea84c..9ff06a4 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -64,6 +64,7 @@ function iocontrol.alloc_nav() pane = nil, ---@type graphics_element apps = {}, containers = {}, + help_map = {}, cur_app = APP_ID.ROOT } @@ -181,9 +182,6 @@ function iocontrol.alloc_nav() return app end - -- get a list of the app containers (usually Div elements) - function io.nav.get_containers() return self.containers end - -- open a given app ---@param app_id POCKET_APP_ID function io.nav.open_app(app_id) @@ -202,6 +200,9 @@ function iocontrol.alloc_nav() end end + -- get a list of the app containers (usually Div elements) + function io.nav.get_containers() return self.containers end + -- get the currently active page ---@return nav_tree_page function io.nav.get_current_page() @@ -218,6 +219,15 @@ function iocontrol.alloc_nav() io.nav.open_app(APP_ID.ROOT) end end + + function io.nav.open_help(key) + io.nav.open_app(APP_ID.GUIDE) + + local load = self.help_map[key] + if load then load() end + end + + function io.nav.link_help(map) self.help_map = map end end -- initialize facility-independent components of pocket iocontrol diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 1d0a0a6..5d5b4ed 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -10,6 +10,8 @@ local iocontrol = require("pocket.iocontrol") local docs = require("pocket.ui.docs") local style = require("pocket.ui.style") +local guide_section = require("pocket.ui.pages.guide_section") + local core = require("graphics.core") local Div = require("graphics.elements.div") @@ -40,6 +42,7 @@ local function new_view(root) local btn_fg_bg = cpair(colors.cyan, colors.black) local btn_active = cpair(colors.white, colors.black) + local btn_disable = cpair(colors.gray, colors.black) local list = { { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, @@ -68,6 +71,10 @@ local function new_view(root) local panes = { home, use, uis, fps, gls } local doc_map = {} + local search_map = {} + + ---@class _guide_section_constructor_data + local sect_construct_data = { app, page_div, panes, doc_map, search_map, btn_fg_bg, btn_active } TextBox{parent=home,y=1,text="cc-mek-scada Guide",height=1,alignment=ALIGN.CENTER} @@ -77,117 +84,65 @@ local function new_view(root) PushButton{parent=home,text="Glossary >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} TextBox{parent=use,y=1,text="System Usage",height=1,alignment=ALIGN.CENTER} - PushButton{parent=use,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} - PushButton{parent=use,y=3,text="Configuring Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=use,text="Connecting Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=use,text="Manual Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=use,text="Automatic Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=use,text="Waste Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=use,y=3,text="Configuring Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=use,text="Connecting Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=use,text="Manual Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=use,text="Automatic Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=use,text="Waste Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() TextBox{parent=uis,y=1,text="Operator UIs",height=1,alignment=ALIGN.CENTER} - PushButton{parent=uis,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} - local alarms_page = app.new_page(uis_page, #panes + 1) - local alarms_div = Div{parent=page_div,x=2} - table.insert(panes, alarms_div) - local annunc_page = app.new_page(uis_page, #panes + 1) local annunc_div = Div{parent=page_div,x=2} table.insert(panes, annunc_div) + local alarms_page = guide_section(sect_construct_data, uis_page, "Alarms", docs.alarms) + PushButton{parent=uis,y=3,text="Alarms >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=alarms_page.nav_to} PushButton{parent=uis,text="Annunciators >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=annunc_page.nav_to} - PushButton{parent=uis,text="Pocket UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=uis,text="Coordinator UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=uis,text="Pocket UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=uis,text="Coordinator UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + + TextBox{parent=annunc_div,y=1,text="Annunciators",height=1,alignment=ALIGN.CENTER} + PushButton{parent=annunc_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to} + + local unit_gen_page = guide_section(sect_construct_data, annunc_page, "Unit General", docs.annunc.unit.main_section) + local unit_rps_page = guide_section(sect_construct_data, annunc_page, "Unit RPS", docs.annunc.unit.rps_section) + local unit_rcs_page = guide_section(sect_construct_data, annunc_page, "Unit RCS", docs.annunc.unit.rcs_section) + + PushButton{parent=annunc_div,y=3,text="Unit General >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_gen_page.nav_to} + PushButton{parent=annunc_div,text="Unit RPS >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_rps_page.nav_to} + PushButton{parent=annunc_div,text="Unit RCS >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_rcs_page.nav_to} + PushButton{parent=annunc_div,text="Facility General >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=annunc_div,text="Waste & Valves >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() TextBox{parent=fps,y=1,text="Front Panels",height=1,alignment=ALIGN.CENTER} - PushButton{parent=fps,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} - PushButton{parent=fps,y=3,text="Common Items >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=fps,text="Reactor PLC >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=fps,text="RTU Gateway >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=fps,text="Supervisor >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} - PushButton{parent=fps,text="Coordinator >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + PushButton{parent=fps,y=3,text="Common Items >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=fps,text="Reactor PLC >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=fps,text="RTU Gateway >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=fps,text="Supervisor >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() + PushButton{parent=fps,text="Coordinator >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable() TextBox{parent=gls,y=1,text="Glossary",height=1,alignment=ALIGN.CENTER} PushButton{parent=gls,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} - local gls_abb_page = app.new_page(gls_page, #panes + 1) - local gls_abb_div = Div{parent=page_div,x=2} - table.insert(panes, gls_abb_div) - TextBox{parent=gls_abb_div,y=1,text="Abbreviations",height=1,alignment=ALIGN.CENTER} - PushButton{parent=gls_abb_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} + local gls_abbv_page = guide_section(sect_construct_data, gls_page, "Abbreviations", docs.glossary.abbvs) + local gls_term_page = guide_section(sect_construct_data, gls_page, "Terminology", docs.glossary.terms) - local gls_abb_view_page = app.new_page(gls_abb_page, #panes + 1) - local gls_abb_view_div = Div{parent=page_div,x=2} - table.insert(panes, gls_abb_view_div) - TextBox{parent=gls_abb_view_div,y=1,text="Abbreviations",height=1,alignment=ALIGN.CENTER} - PushButton{parent=gls_abb_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abb_page.nav_to} - - local gls_term_page = app.new_page(gls_page, #panes + 1) - local gls_term_div = Div{parent=page_div,x=2} - table.insert(panes, gls_term_div) - TextBox{parent=gls_term_div,y=1,text="Terminology",height=1,alignment=ALIGN.CENTER} - PushButton{parent=gls_term_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} - - local gls_term_view_page = app.new_page(gls_term_page, #panes + 1) - local gls_term_view_div = Div{parent=page_div,x=2} - table.insert(panes, gls_term_view_div) - TextBox{parent=gls_term_view_div,y=1,text="Terminology",height=1,alignment=ALIGN.CENTER} - PushButton{parent=gls_term_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_term_page.nav_to} - - PushButton{parent=gls,y=3,text="Abbreviations >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abb_page.nav_to} + PushButton{parent=gls,y=3,text="Abbreviations >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abbv_page.nav_to} PushButton{parent=gls,text="Terminology >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_term_page.nav_to} - local abb_name_list = ListBox{parent=gls_abb_div,x=1,y=3,scroll_height=20,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - local abb_def_list = ListBox{parent=gls_abb_view_div,x=1,y=3,scroll_height=101,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - - local _end = nil - - for i = 1, #docs.glossary.abbvs do - local item = docs.glossary.abbvs[i] ---@type pocket_doc_item - - doc_map[item.key] = TextBox{parent=abb_def_list,text=item.name,anchor=true,fg_bg=cpair(colors.blue,colors.black)} - TextBox{parent=abb_def_list,text=item.desc,fg_bg=label} - _end = Div{parent=abb_def_list,height=1,can_focus=true} - - local function view() - _end.focus() - gls_abb_view_page.nav_to() - doc_map[item.key].focus() - end - - PushButton{parent=abb_name_list,text=item.name,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} - end - - local term_name_list = ListBox{parent=gls_term_div,x=1,y=3,scroll_height=20,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - local term_def_list = ListBox{parent=gls_term_view_div,x=1,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - - local _end_b = nil - - for i = 1, #docs.glossary.terms do - local item = docs.glossary.terms[i] ---@type pocket_doc_item - - doc_map[item.key] = TextBox{parent=term_def_list,text=item.name,anchor=true,fg_bg=cpair(colors.blue,colors.black)} - TextBox{parent=term_def_list,text=item.desc,fg_bg=label} - _end_b = Div{parent=term_def_list,height=1,can_focus=true} - - local function view() - _end_b.focus() - gls_term_view_page.nav_to() - doc_map[item.key].focus() - end - - PushButton{parent=term_name_list,text=item.name,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} - end - -- setup multipane local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes} app.set_root_pane(u_pane) + + -- link help resources + db.nav.link_help(doc_map) end app.set_on_load(load) diff --git a/pocket/ui/docs.lua b/pocket/ui/docs.lua index 50e20fa..24090ec 100644 --- a/pocket/ui/docs.lua +++ b/pocket/ui/docs.lua @@ -50,9 +50,32 @@ doc("HighStartupRate", "Startup Rate High", "This is a rough calculation of if y target = docs.annunc.unit.rps_section doc("rps_tripped", "RPS Trip", "Indicates if the reactor protection system has caused a SCRAM.") +doc("manual", "Manual Reactor SCRAM", "Indicates if the operator (you) tripped the RPS by pressing SCRAM.") +doc("automatic", "Auto Reactor SCRAM", "Indicates if the automatic control system tripped the RPS.") +doc("sys_fail", "System Failure", "Indicates if the RPS tripped due to the reactor not being formed.") doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage.") +doc("ex_waste", "Excess Waste", "Indicates if the RPS tripped due to very high waste levels.") +doc("ex_hcool", "Excess Heated Coolant", "Indicates if the RPS tripped due to very high waste levels.") +doc("high_temp", "Temperature High", "Indicates if the RPS tripped due to reaching damaging temperatures.") +doc("low_cool", "Coolant Level Low Low", "Indicates if the RPS tripped due to very low coolant levels that result in the temperature uncontrollably rising.") +doc("no_fuel", "No Fuel", "Indicates if the RPS tripped due to no fuel being available.") +doc("fault", "PPM Fault", "Indicates if the RPS tripped due to a peripheral access fault.") +doc("timeout", "Connection Timeout", "Indicates if the RPS tripped due to losing connection with the supervisory computer.") +doc("sys_fail", "System Failure", "Indicates if the RPS tripped due to the reactor not being formed.") -target = docs.annunc.unit.main_section +target = docs.annunc.unit.rcs_section +doc("RCSFault", "RCS Hardware Fault", "Indicates if one or more of the RCS devices have a peripheral fault.") +doc("EmergencyCoolant", "Emergency Coolant", "Off if no emergency coolant redstone is configured, white when it is configured but not in use, and green/blue when it is activated.") +doc("CoolantFeedMismatch", "Coolant Feed Mismatch", "The coolant system is accumulating heated coolant or losing cooled coolant, likely due to one of the machines not keeping up with the needs of the reactor.") +doc("BoilRateMismatch", "Boil Rate Mismatch", "The total heating rate of the reactor is more than 4% off from the steam input rate of the turbines OR for sodium setups, the boiler boil rates are more than 4% off from the steam input rate of the turbines.") +doc("SteamFeedMismatch", "Steam Feed Mismatch", "There is an above tolerance difference between turbine flow and steam input rates or the reactor/boilers are gaining steam or losing water.") +doc("MaxWaterReturnFeed", "Max Water Return Feed", "The turbines are condensing the max rate of water that they can per the structure build.") +doc("WaterLevelLow", "Water Level Low", "The water level in the boiler is low.") +doc("HeatingRateLow", "Heating Rate Low", "The boiler is not hot enough to boil water, but it is receiving heated coolant.") +doc("SteamDumpOpen", "Steam Relief Valve Open", "This turns yellow if the turbine is set to dumping excess and red if it is set to dumping all. 'Relief Valve' in this case is that setting allowing the venting of steam.") +doc("TurbineOverSpeed", "Turbine Over Speed", "The turbine is at steam capacity, but not tripped. You may need more turbines if they can't keep up.") +doc("GeneratorTrip", "Generator Trip", "The turbine is no longer outputting power due to it having nowhere to go. Likely due to full power storage.") +doc("TurbineTrip", "Turbine Trip", "The turbine has reached its maximum power charge and has stopped rotating, and as a result stopped cooling steam to water.") docs.glossary = { abbvs = {}, terms = {} @@ -62,6 +85,7 @@ target = docs.glossary.abbvs doc("G_ACK", "ACK", "Alarm ACKnowledge. This indicates you understand an alarm occured and would like to stop the audio tone(s).") doc("G_CRD", "CRD", "Coordinator. Abbreviation for the coordinator computer.") doc("G_DBG", "DBG", "Debug. Abbreviation for the debugging sessions from pocket computers found on the supervisor's front panel.") +doc("G_FP", "FP", "Front Panel. See Terminology.") doc("G_PKT", "PKT", "Pocket. Abbreviation for the pocket computer.") doc("G_PLC", "PLC", "Programmable Logic Controller. A device that not only reports data and controls outputs, but also can make decisions on its own.") doc("G_PPM", "PPM", "Protected Peripheral Manager. This is an abstraction layer created for this project that prevents peripheral calls from crashing applications.") @@ -71,6 +95,7 @@ doc("G_RPS", "RPS", "Reactor Protection System. A component of the reactor PLC r doc("G_RTU", "RTU", "Remote Terminal Unit. Provides monitoring to and basic output from a SCADA system, interfacing with various types of devices/controls.") doc("G_SCADA", "SCADA", "Supervisory Control and Data Acquisition. A control systems architecture used in many different process control applications.") doc("G_SVR", "SVR", "Supervisor. Abbreviation for the supervisory computer.") +doc("G_UI", "UI", "User Interface.") target = docs.glossary.terms doc("G_Fault", "Fault", "Something has gone wrong and/or failed to function.") diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua new file mode 100644 index 0000000..344f6c7 --- /dev/null +++ b/pocket/ui/pages/guide_section.lua @@ -0,0 +1,58 @@ +local core = require("graphics.core") + +local Div = require("graphics.elements.div") +local ListBox = require("graphics.elements.listbox") +local TextBox = require("graphics.elements.textbox") + +local PushButton = require("graphics.elements.controls.push_button") + +local ALIGN = core.ALIGN +local cpair = core.cpair + +-- new guide documentation section +---@param data _guide_section_constructor_data +---@param base_page nav_tree_page +---@param title string +---@param items table +---@return nav_tree_page +return function (data, base_page, title, items) + local app, page_div, panes, doc_map, search_map, btn_fg_bg, btn_active = table.unpack(data) + + local section_page = app.new_page(base_page, #panes + 1) + local gls_term_div = Div{parent=page_div,x=2} + table.insert(panes, gls_term_div) + TextBox{parent=gls_term_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} + PushButton{parent=gls_term_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=base_page.nav_to} + + local gls_term_view_page = app.new_page(section_page, #panes + 1) + local gls_term_view_div = Div{parent=page_div,x=2} + table.insert(panes, gls_term_view_div) + TextBox{parent=gls_term_view_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} + PushButton{parent=gls_term_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=section_page.nav_to} + + local name_list = ListBox{parent=gls_term_div,x=1,y=3,scroll_height=30,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local def_list = ListBox{parent=gls_term_view_div,x=1,y=3,scroll_height=120,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + local _end = nil + + for i = 1, #items do + local item = items[i] ---@type pocket_doc_item + + local anchor = TextBox{parent=def_list,text=item.name,anchor=true,fg_bg=cpair(colors.blue,colors.black)} + TextBox{parent=def_list,text=item.desc} + _end = Div{parent=def_list,height=1,can_focus=true} + + local function view() + _end.focus() + gls_term_view_page.nav_to() + anchor.focus() + end + + doc_map[item.key] = view + search_map[item.name] = view + + PushButton{parent=name_list,text=item.name,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} + end + + return section_page +end From b457edbc71ea4b174a1c099e1a787f1256f5f425 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 6 Jun 2024 22:54:26 -0400 Subject: [PATCH 21/44] #403 variable sizing on listbox heights for sections --- pocket/ui/apps/guide.lua | 30 +++++++++++++++--------------- pocket/ui/pages/guide_section.lua | 5 +++-- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 5d5b4ed..f30d927 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -5,21 +5,21 @@ -- local util = require("scada-common.util") -- local log = require("scada-common.log") -local iocontrol = require("pocket.iocontrol") +local iocontrol = require("pocket.iocontrol") -local docs = require("pocket.ui.docs") -local style = require("pocket.ui.style") +local docs = require("pocket.ui.docs") +local style = require("pocket.ui.style") local guide_section = require("pocket.ui.pages.guide_section") -local core = require("graphics.core") +local core = require("graphics.core") -local Div = require("graphics.elements.div") -local ListBox = require("graphics.elements.listbox") -local MultiPane = require("graphics.elements.multipane") -local TextBox = require("graphics.elements.textbox") +local Div = require("graphics.elements.div") +local ListBox = require("graphics.elements.listbox") +local MultiPane = require("graphics.elements.multipane") +local TextBox = require("graphics.elements.textbox") -local PushButton = require("graphics.elements.controls.push_button") +local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair @@ -99,7 +99,7 @@ local function new_view(root) local annunc_div = Div{parent=page_div,x=2} table.insert(panes, annunc_div) - local alarms_page = guide_section(sect_construct_data, uis_page, "Alarms", docs.alarms) + local alarms_page = guide_section(sect_construct_data, uis_page, "Alarms", docs.alarms, 100) PushButton{parent=uis,y=3,text="Alarms >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=alarms_page.nav_to} PushButton{parent=uis,text="Annunciators >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=annunc_page.nav_to} @@ -109,9 +109,9 @@ local function new_view(root) TextBox{parent=annunc_div,y=1,text="Annunciators",height=1,alignment=ALIGN.CENTER} PushButton{parent=annunc_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to} - local unit_gen_page = guide_section(sect_construct_data, annunc_page, "Unit General", docs.annunc.unit.main_section) - local unit_rps_page = guide_section(sect_construct_data, annunc_page, "Unit RPS", docs.annunc.unit.rps_section) - local unit_rcs_page = guide_section(sect_construct_data, annunc_page, "Unit RCS", docs.annunc.unit.rcs_section) + local unit_gen_page = guide_section(sect_construct_data, annunc_page, "Unit General", docs.annunc.unit.main_section, 200) + local unit_rps_page = guide_section(sect_construct_data, annunc_page, "Unit RPS", docs.annunc.unit.rps_section, 100) + local unit_rcs_page = guide_section(sect_construct_data, annunc_page, "Unit RCS", docs.annunc.unit.rcs_section, 100) PushButton{parent=annunc_div,y=3,text="Unit General >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_gen_page.nav_to} PushButton{parent=annunc_div,text="Unit RPS >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_rps_page.nav_to} @@ -131,8 +131,8 @@ local function new_view(root) TextBox{parent=gls,y=1,text="Glossary",height=1,alignment=ALIGN.CENTER} PushButton{parent=gls,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} - local gls_abbv_page = guide_section(sect_construct_data, gls_page, "Abbreviations", docs.glossary.abbvs) - local gls_term_page = guide_section(sect_construct_data, gls_page, "Terminology", docs.glossary.terms) + local gls_abbv_page = guide_section(sect_construct_data, gls_page, "Abbreviations", docs.glossary.abbvs, 120) + local gls_term_page = guide_section(sect_construct_data, gls_page, "Terminology", docs.glossary.terms, 100) PushButton{parent=gls,y=3,text="Abbreviations >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abbv_page.nav_to} PushButton{parent=gls,text="Terminology >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_term_page.nav_to} diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index 344f6c7..b788a2a 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -14,8 +14,9 @@ local cpair = core.cpair ---@param base_page nav_tree_page ---@param title string ---@param items table +---@param scroll_height integer ---@return nav_tree_page -return function (data, base_page, title, items) +return function (data, base_page, title, items, scroll_height) local app, page_div, panes, doc_map, search_map, btn_fg_bg, btn_active = table.unpack(data) local section_page = app.new_page(base_page, #panes + 1) @@ -31,7 +32,7 @@ return function (data, base_page, title, items) PushButton{parent=gls_term_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=section_page.nav_to} local name_list = ListBox{parent=gls_term_div,x=1,y=3,scroll_height=30,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - local def_list = ListBox{parent=gls_term_view_div,x=1,y=3,scroll_height=120,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local def_list = ListBox{parent=gls_term_view_div,x=1,y=3,scroll_height=scroll_height,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} local _end = nil From e88e1afcc47460ee0be2c9e214f71b006fecc83d Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 6 Jun 2024 23:05:22 -0400 Subject: [PATCH 22/44] #403 started work on guide searching --- pocket/ui/apps/guide.lua | 30 ++++++++++++++++++++++-------- pocket/ui/pages/guide_section.lua | 20 ++++++++++---------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index f30d927..07f22d1 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -6,6 +6,7 @@ -- local log = require("scada-common.log") local iocontrol = require("pocket.iocontrol") +local TextField = require("graphics.elements.form.text_field") local docs = require("pocket.ui.docs") local style = require("pocket.ui.style") @@ -46,9 +47,10 @@ local function new_view(root) local list = { { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, - { label = "Use", color = core.cpair(colors.black, colors.purple), callback = function () app.switcher(2) end }, - { label = "UIs", color = core.cpair(colors.black, colors.blue), callback = function () app.switcher(3) end }, - { label = "FPs", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(4) end } + { label = "\x14_?", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(2) end }, + -- { label = "Use", color = core.cpair(colors.black, colors.purple), callback = function () app.switcher(2) end }, + -- { label = "UIs", color = core.cpair(colors.black, colors.blue), callback = function () app.switcher(3) end }, + -- { label = "FPs", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(4) end } } app.set_sidebar(list) @@ -58,17 +60,19 @@ local function new_view(root) local p_width = page_div.get_width() - 2 local main_page = app.new_page(nil, 1) - local use_page = app.new_page(main_page, 2) - local uis_page = app.new_page(main_page, 3) - local fps_page = app.new_page(main_page, 4) - local gls_page = app.new_page(main_page, 5) + local search_page = app.new_page(main_page, 2) + local use_page = app.new_page(main_page, 3) + local uis_page = app.new_page(main_page, 4) + local fps_page = app.new_page(main_page, 5) + local gls_page = app.new_page(main_page, 6) local home = Div{parent=page_div,x=2,width=p_width} + local search = Div{parent=page_div,x=2} local use = Div{parent=page_div,x=2,width=p_width} local uis = Div{parent=page_div,x=2,width=p_width} local fps = Div{parent=page_div,x=2,width=p_width} local gls = Div{parent=page_div,x=2} - local panes = { home, use, uis, fps, gls } + local panes = { home, search, use, uis, fps, gls } local doc_map = {} local search_map = {} @@ -83,6 +87,16 @@ local function new_view(root) PushButton{parent=home,text="Front Panels >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fps_page.nav_to} PushButton{parent=home,text="Glossary >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} + TextBox{parent=search,y=1,text="Search",height=1,alignment=ALIGN.CENTER} + PushButton{parent=search,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} + + TextField{parent=search,x=1,y=3,width=18,fg_bg=cpair(colors.white,colors.gray)} + PushButton{parent=search,x=20,y=3,text="GO",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + + local search_results = ListBox{parent=search,x=1,y=5,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + TextBox{parent=search_results,text="Click 'GO' to search..."} + TextBox{parent=use,y=1,text="System Usage",height=1,alignment=ALIGN.CENTER} PushButton{parent=use,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index b788a2a..dbb7348 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -20,19 +20,19 @@ return function (data, base_page, title, items, scroll_height) local app, page_div, panes, doc_map, search_map, btn_fg_bg, btn_active = table.unpack(data) local section_page = app.new_page(base_page, #panes + 1) - local gls_term_div = Div{parent=page_div,x=2} - table.insert(panes, gls_term_div) - TextBox{parent=gls_term_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} - PushButton{parent=gls_term_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=base_page.nav_to} + local section_div = Div{parent=page_div,x=2} + table.insert(panes, section_div) + TextBox{parent=section_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} + PushButton{parent=section_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=base_page.nav_to} local gls_term_view_page = app.new_page(section_page, #panes + 1) - local gls_term_view_div = Div{parent=page_div,x=2} - table.insert(panes, gls_term_view_div) - TextBox{parent=gls_term_view_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} - PushButton{parent=gls_term_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=section_page.nav_to} + local section_view_div = Div{parent=page_div,x=2} + table.insert(panes, section_view_div) + TextBox{parent=section_view_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} + PushButton{parent=section_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=section_page.nav_to} - local name_list = ListBox{parent=gls_term_div,x=1,y=3,scroll_height=30,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - local def_list = ListBox{parent=gls_term_view_div,x=1,y=3,scroll_height=scroll_height,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local name_list = ListBox{parent=section_div,x=1,y=3,scroll_height=30,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local def_list = ListBox{parent=section_view_div,x=1,y=3,scroll_height=scroll_height,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} local _end = nil From 83ba6e3961501ab4dd74a3db6d24266ff54a3773 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 6 Jun 2024 23:10:19 -0400 Subject: [PATCH 23/44] #403 updated search navigation --- pocket/ui/apps/guide.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 07f22d1..84d506d 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -47,10 +47,8 @@ local function new_view(root) local list = { { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, - { label = "\x14_?", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(2) end }, - -- { label = "Use", color = core.cpair(colors.black, colors.purple), callback = function () app.switcher(2) end }, - -- { label = "UIs", color = core.cpair(colors.black, colors.blue), callback = function () app.switcher(3) end }, - -- { label = "FPs", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(4) end } + { label = " \x14 ", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(1) end }, + { label = "__?", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(2) end } } app.set_sidebar(list) @@ -66,7 +64,7 @@ local function new_view(root) local fps_page = app.new_page(main_page, 5) local gls_page = app.new_page(main_page, 6) - local home = Div{parent=page_div,x=2,width=p_width} + local home = Div{parent=page_div,x=2} local search = Div{parent=page_div,x=2} local use = Div{parent=page_div,x=2,width=p_width} local uis = Div{parent=page_div,x=2,width=p_width} @@ -82,13 +80,13 @@ local function new_view(root) TextBox{parent=home,y=1,text="cc-mek-scada Guide",height=1,alignment=ALIGN.CENTER} - PushButton{parent=home,y=3,text="System Usage >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=use_page.nav_to} + PushButton{parent=home,y=3,text="Search >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=search_page.nav_to} + PushButton{parent=home,y=5,text="System Usage >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=use_page.nav_to} PushButton{parent=home,text="Operator UIs >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to} PushButton{parent=home,text="Front Panels >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fps_page.nav_to} PushButton{parent=home,text="Glossary >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to} TextBox{parent=search,y=1,text="Search",height=1,alignment=ALIGN.CENTER} - PushButton{parent=search,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} TextField{parent=search,x=1,y=3,width=18,fg_bg=cpair(colors.white,colors.gray)} PushButton{parent=search,x=20,y=3,text="GO",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} From 09c44a6969998e6011821b55bf87b3a2b226b9e4 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 12 Jun 2024 19:59:19 -0400 Subject: [PATCH 24/44] multi-line push button and keep focus on keyboard select --- graphics/element.lua | 2 +- graphics/elements/controls/push_button.lua | 45 ++++++++++++++-------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/graphics/element.lua b/graphics/element.lua index d086408..7475dc1 100644 --- a/graphics/element.lua +++ b/graphics/element.lua @@ -200,7 +200,7 @@ function element.new(args, constraint, child_offset_x, child_offset_y) ---@param next_y integer next line if no y was provided function protected.prepare_template(offset_x, offset_y, next_y) -- don't auto incrememnt y if inheriting height, that would cause an assertion - next_y = util.trinary(args.height == nil, 1, next_y) + next_y = util.trinary(args.height == nil and constraint == nil, 1, next_y) -- record offsets in case there is a reposition self.offset_x = offset_x diff --git a/graphics/elements/controls/push_button.lua b/graphics/elements/controls/push_button.lua index 88c8a5d..f060901 100644 --- a/graphics/elements/controls/push_button.lua +++ b/graphics/elements/controls/push_button.lua @@ -1,6 +1,7 @@ -- Button Graphics Element local tcd = require("scada-common.tcd") +local util = require("scada-common.util") local core = require("graphics.core") local element = require("graphics.element") @@ -21,7 +22,6 @@ local KEY_CLICK = core.events.KEY_CLICK ---@field id? string element id ---@field x? integer 1 if omitted ---@field y? integer auto incremented if omitted ----@field height? integer parent height if omitted ---@field fg_bg? cpair foreground/background colors ---@field hidden? boolean true to hide on initial draw @@ -38,29 +38,40 @@ local function push_button(args) -- set automatic settings args.can_focus = true - args.height = 1 args.min_width = args.min_width or 0 args.width = math.max(text_width, args.min_width) - -- create new graphics element base object - local e = element.new(args) - - local h_pad = 1 - local v_pad = math.floor(e.frame.h / 2) + 1 - - if alignment == ALIGN.CENTER then - h_pad = math.floor((e.frame.w - text_width) / 2) + 1 - elseif alignment == ALIGN.RIGHT then - h_pad = (e.frame.w - text_width) + 1 + -- provide a constraint condition to element creation to prefer a single line button + ---@param frame graphics_frame + local function constrain(frame) + return frame.w, math.max(1, #util.strwrap(args.text, frame.w)) end + -- create new graphics element base object + local e = element.new(args, constrain) + + local text_lines = util.strwrap(args.text, e.frame.w) + -- draw the button function e.redraw() e.window.clear() - -- write the button text - e.w_set_cur(h_pad, v_pad) - e.w_write(args.text) + for i = 1, #text_lines do + if i > e.frame.h then break end + + local len = string.len(text_lines[i]) + + -- use cursor position to align this line + if alignment == ALIGN.CENTER then + e.w_set_cur(math.floor((e.frame.w - len) / 2) + 1, i) + elseif alignment == ALIGN.RIGHT then + e.w_set_cur((e.frame.w - len) + 1, i) + else + e.w_set_cur(1, i) + end + + e.w_write(text_lines[i]) + end end -- draw the button as pressed (if active_fg_bg set) @@ -109,7 +120,9 @@ local function push_button(args) if event.type == KEY_CLICK.DOWN then if event.key == keys.space or event.key == keys.enter or event.key == keys.numPadEnter then args.callback() - e.defocus() + -- visualize click without unfocusing + show_unpressed() + if args.active_fg_bg ~= nil then tcd.dispatch(0.25, show_pressed) end end end end From 356caf7b4d4fecb65686ae3faa8281e446866a69 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 12 Jun 2024 20:01:31 -0400 Subject: [PATCH 25/44] #403 guide searching --- pocket/ui/apps/guide.lua | 61 +++++++++++++++++++++++++++---- pocket/ui/docs.lua | 2 +- pocket/ui/pages/guide_section.lua | 6 +-- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 84d506d..35868d8 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -2,11 +2,11 @@ -- System Guide -- --- local util = require("scada-common.util") +local util = require("scada-common.util") -- local log = require("scada-common.log") local iocontrol = require("pocket.iocontrol") -local TextField = require("graphics.elements.form.text_field") +local TextField = require("graphics.elements.form.text_field") local docs = require("pocket.ui.docs") local style = require("pocket.ui.style") @@ -73,10 +73,10 @@ local function new_view(root) local panes = { home, search, use, uis, fps, gls } local doc_map = {} - local search_map = {} + local search_db = {} ---@class _guide_section_constructor_data - local sect_construct_data = { app, page_div, panes, doc_map, search_map, btn_fg_bg, btn_active } + local sect_construct_data = { app, page_div, panes, doc_map, search_db, btn_fg_bg, btn_active } TextBox{parent=home,y=1,text="cc-mek-scada Guide",height=1,alignment=ALIGN.CENTER} @@ -88,10 +88,57 @@ local function new_view(root) TextBox{parent=search,y=1,text="Search",height=1,alignment=ALIGN.CENTER} - TextField{parent=search,x=1,y=3,width=18,fg_bg=cpair(colors.white,colors.gray)} - PushButton{parent=search,x=20,y=3,text="GO",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()end} + local query_field = TextField{parent=search,x=1,y=3,width=18,fg_bg=cpair(colors.white,colors.gray)} - local search_results = ListBox{parent=search,x=1,y=5,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + local func_ref = {} + + PushButton{parent=search,x=20,y=3,text="GO",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()func_ref.run_search()end} + + local search_results = ListBox{parent=search,x=1,y=5,scroll_height=200,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} + + function func_ref.run_search() + local query = string.lower(query_field.get_value()) + local s_results = { {}, {}, {} } + + search_results.remove_all() + + if string.len(query) < 3 then + TextBox{parent=search_results,text=util.trinary(string.len(query)==0,"Click 'GO' to search...","Search requires at least 3 characters.")} + return + end + + for _, entry in ipairs(search_db) do + local s_start, _ = string.find(entry[1], query, 1, true) + + if s_start == nil then + elseif s_start == 1 then + -- best match, start of key + table.insert(s_results[1], entry) + elseif string.sub(query, s_start - 1, s_start) == " " then + -- start of word, good match + table.insert(s_results[2], entry) + else + -- basic match in content + table.insert(s_results[3], entry) + end + end + + local empty = true + + for tier = 1, 3 do + for idx = 1, #s_results[tier] do + local entry = s_results[tier][idx] + TextBox{parent=search_results,text=entry[3].." >",fg_bg=cpair(colors.gray,colors.black)} + PushButton{parent=search_results,text=entry[2],alignment=ALIGN.LEFT,fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=entry[4]} + + empty = false + end + end + + if empty then + TextBox{parent=search_results,text="No results found."} + end + end TextBox{parent=search_results,text="Click 'GO' to search..."} diff --git a/pocket/ui/docs.lua b/pocket/ui/docs.lua index 24090ec..936b2e8 100644 --- a/pocket/ui/docs.lua +++ b/pocket/ui/docs.lua @@ -55,7 +55,7 @@ doc("automatic", "Auto Reactor SCRAM", "Indicates if the automatic control syste doc("sys_fail", "System Failure", "Indicates if the RPS tripped due to the reactor not being formed.") doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage.") doc("ex_waste", "Excess Waste", "Indicates if the RPS tripped due to very high waste levels.") -doc("ex_hcool", "Excess Heated Coolant", "Indicates if the RPS tripped due to very high waste levels.") +doc("ex_hcool", "Excess Heated Coolant", "Indicates if the RPS tripped due to very high heated coolant levels.") doc("high_temp", "Temperature High", "Indicates if the RPS tripped due to reaching damaging temperatures.") doc("low_cool", "Coolant Level Low Low", "Indicates if the RPS tripped due to very low coolant levels that result in the temperature uncontrollably rising.") doc("no_fuel", "No Fuel", "Indicates if the RPS tripped due to no fuel being available.") diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index dbb7348..1cbc8ec 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -17,7 +17,7 @@ local cpair = core.cpair ---@param scroll_height integer ---@return nav_tree_page return function (data, base_page, title, items, scroll_height) - local app, page_div, panes, doc_map, search_map, btn_fg_bg, btn_active = table.unpack(data) + local app, page_div, panes, doc_map, search_db, btn_fg_bg, btn_active = table.unpack(data) local section_page = app.new_page(base_page, #panes + 1) local section_div = Div{parent=page_div,x=2} @@ -50,9 +50,9 @@ return function (data, base_page, title, items, scroll_height) end doc_map[item.key] = view - search_map[item.name] = view + table.insert(search_db, { string.lower(item.name), item.name, title, view }) - PushButton{parent=name_list,text=item.name,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} + PushButton{parent=name_list,text=item.name,alignment=ALIGN.LEFT,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} end return section_page From 95b93eb795f52861cd0a9f40d030d76578119844 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 12 Jun 2024 20:10:16 -0400 Subject: [PATCH 26/44] #403 nav up button sends back to prior app if open_help was used --- pocket/iocontrol.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 9ff06a4..96bc0a8 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -65,6 +65,7 @@ function iocontrol.alloc_nav() apps = {}, containers = {}, help_map = {}, + help_return = nil, cur_app = APP_ID.ROOT } @@ -185,6 +186,9 @@ function iocontrol.alloc_nav() -- open a given app ---@param app_id POCKET_APP_ID function io.nav.open_app(app_id) + -- reset help return on navigating out of an app + if app_id == APP_ID.ROOT then self.help_return = nil end + local app = self.apps[app_id] ---@type pocket_app if app then if not app.loaded then app.load() end @@ -211,6 +215,13 @@ function iocontrol.alloc_nav() -- attempt to navigate up function io.nav.nav_up() + -- return out of help if opened with open_help + if self.help_return then + io.nav.open_app(self.help_return) + self.help_return = nil + return + end + local app = self.apps[self.cur_app] ---@type pocket_app log.debug("attempting app nav up for app " .. self.cur_app) @@ -220,7 +231,10 @@ function iocontrol.alloc_nav() end end + -- open the help app, to show the reference for a key function io.nav.open_help(key) + self.help_return = self.cur_app + io.nav.open_app(APP_ID.GUIDE) local load = self.help_map[key] From ff8ae5e6094e18107c2902664bea012743807f34 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 12 Jun 2024 20:17:31 -0400 Subject: [PATCH 27/44] #200 updated status info display fields --- pocket/iocontrol.lua | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 96bc0a8..5020399 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -894,9 +894,7 @@ function iocontrol.record_unit_data(data) if tripped(unit.alarms[ALARM.ReactorDamage]) then local items = { - white("REACTOR DAMAGED"), - blue("CHECK RCS"), - blue("AWAIT DMG REDUCED") + white("REACTOR DAMAGED"), blue("CHECK RCS"), blue("AWAIT DMG REDUCED") } table.insert(ecam, { color = colors.red, text = "REACTOR DAMAGE", help = "ReactorDamage", items = items }) @@ -904,9 +902,7 @@ function iocontrol.record_unit_data(data) if tripped(unit.alarms[ALARM.ReactorOverTemp]) then local items = { - white("HIT DAMAGING TEMP"), - blue("CHECK RCS"), - blue("AWAIT COOLDOWN") + white("DAMAGING TEMP"), blue("CHECK RCS"), blue("AWAIT COOLDOWN") } table.insert(ecam, { color = colors.red, text = "REACTOR OVER TEMP", help = "ReactorOverTemp", items = items }) @@ -918,7 +914,7 @@ function iocontrol.record_unit_data(data) end if tripped(unit.alarms[ALARM.ReactorWasteLeak]) then - local items = { white("AT WASTE CAPACITY"), blue("CHECK WASTE OUTPUT"), red("DO NOT ENABLE RCT") } + local items = { white("AT WASTE CAPACITY"), blue("CHECK WASTE OUTPUT"), blue("KEEP RCT DISABLED") } table.insert(ecam, { color = colors.red, text = "REACTOR WASTE LEAK", help = "ReactorWasteLeak", items = items}) end @@ -934,7 +930,7 @@ function iocontrol.record_unit_data(data) local function insert(cond, key, text, color) if cond[key] then table.insert(items, { text = text, help = key, color = color }) end end - table.insert(items, { text = "REACTOR SCRAMMED", color = colors.white }) + table.insert(items, white("REACTOR SCRAMMED")) insert(stat, "high_dmg", "HIGH DAMAGE", colors.red) insert(stat, "high_temp", "HIGH TEMPERATURE", colors.red) insert(stat, "low_cool", "CRIT LOW COOLANT") @@ -947,6 +943,8 @@ function iocontrol.record_unit_data(data) insert(stat, "automatic", "AUTOMATIC SCRAM") insert(stat, "sys_fail", "NOT FORMED", colors.red) insert(stat, "force_dis", "FORCE DISABLED", colors.red) + table.insert(items, blue("RESOLVE PROBLEM")) + table.insert(items, blue("RESET RPS")) table.insert(ecam, { color = colors.yellow, text = "RPS TRANSIENT", help = "RPSTransient", items = items}) end @@ -959,6 +957,8 @@ function iocontrol.record_unit_data(data) if cond == true or (type(cond) == "table" and cond[key]) then table.insert(items, { text = text, help = key, color = color }) end end + table.insert(items, white("COOLANT PROBLEM")) + insert(annunc, "RCPTrip", "RCP TRIP", colors.red) insert(annunc, "CoolantLevelLow", "LOW COOLANT") @@ -986,10 +986,8 @@ function iocontrol.record_unit_data(data) insert(annunc, "MaxWaterReturnFeed", "MAX WTR RTRN FEED") - for k, v in ipairs(annunc.WaterLevelLow) do insert(v, "WaterLevelLow", "BOILER " .. k .. " WTR LOW", colors.red) end - for k, v in ipairs(annunc.HeatingRateLow) do insert(v, "HeatingRateLow", "BOILER " .. k .. " HEAT RATE") end - for k, v in ipairs(annunc.TurbineOverSpeed) do insert(v, "TurbineOverSpeed", "TURBINE " .. k .. " OVERSPD", colors.red) end - for k, v in ipairs(annunc.GeneratorTrip) do insert(v, "GeneratorTrip", "TURBINE " .. k .. " GEN TRIP") end + + table.insert(items, blue("CHECK COOLING SYS")) table.insert(ecam, { color = colors.yellow, text = "RCS TRANSIENT", help = "RCSTransient", items = items}) end From f8a5dd9c321db2b3985b7d9e3d9bec639d05ffef Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 12 Jun 2024 20:20:30 -0400 Subject: [PATCH 28/44] #200 additional fields for info display --- pocket/iocontrol.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 5020399..f48ce25 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -986,6 +986,10 @@ function iocontrol.record_unit_data(data) insert(annunc, "MaxWaterReturnFeed", "MAX WTR RTRN FEED") + for k, v in ipairs(annunc.WaterLevelLow) do insert(v, "WaterLevelLow", "BOILER " .. k .. " WTR LOW", colors.red) end + for k, v in ipairs(annunc.HeatingRateLow) do insert(v, "HeatingRateLow", "BOILER " .. k .. " HEAT RATE") end + for k, v in ipairs(annunc.TurbineOverSpeed) do insert(v, "TurbineOverSpeed", "TURBINE " .. k .. " OVERSPD", colors.red) end + for k, v in ipairs(annunc.GeneratorTrip) do insert(v, "GeneratorTrip", "TURBINE " .. k .. " GEN TRIP") end table.insert(items, blue("CHECK COOLING SYS")) From 5848c2ac1a3898b19803c9524407f6d9f9089bc7 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 12 Jun 2024 20:22:41 -0400 Subject: [PATCH 29/44] test code for debugging --- pocket/iocontrol.lua | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index f48ce25..669891b 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -862,10 +862,31 @@ function iocontrol.record_unit_data(data) local ecam = {} -- aviation reference :) back to VATSIM I go... - local function red(text) return { text = text, color = colors.red } end + -- local function red(text) return { text = text, color = colors.red } end local function white(text) return { text = text, color = colors.white } end local function blue(text) return { text = text, color = colors.blue } end + -- unit.reactor_data.rps_status = { + -- high_dmg = false, + -- high_temp = false, + -- low_cool = false, + -- ex_waste = false, + -- ex_hcool = false, + -- no_fuel = false, + -- fault = false, + -- timeout = false, + -- manual = false, + -- automatic = false, + -- sys_fail = false, + -- force_dis = false + -- } + + -- if unit.reactor_data.rps_status then + -- for k, v in pairs(unit.alarms) do + -- unit.alarms[k] = ALARM_STATE.TRIPPED + -- end + -- end + if tripped(unit.alarms[ALARM.ContainmentBreach]) then local items = { white("REACTOR MELTDOWN"), blue("DON HAZMAT SUIT") } table.insert(ecam, { color = colors.red, text = "CONTAINMENT BREACH", help = "ContainmentBreach", items = items }) @@ -928,6 +949,10 @@ function iocontrol.record_unit_data(data) local items = {} local stat = unit.reactor_data.rps_status + -- for k, _ in pairs(stat) do + -- stat[k] = true + -- end + local function insert(cond, key, text, color) if cond[key] then table.insert(items, { text = text, help = key, color = color }) end end table.insert(items, white("REACTOR SCRAMMED")) @@ -953,6 +978,15 @@ function iocontrol.record_unit_data(data) local items = {} local annunc = unit.annunciator + -- for k, v in pairs(annunc) do + -- if type(v) == "boolean" then annunc[k] = true end + -- if type(v) == "table" then + -- for a, _ in pairs(v) do + -- v[a] = true + -- end + -- end + -- end + local function insert(cond, key, text, color) if cond == true or (type(cond) == "table" and cond[key]) then table.insert(items, { text = text, help = key, color = color }) end end From e851a5275f5e5a6a12e9197fc8dc53f31a23b756 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Thu, 13 Jun 2024 16:45:44 +0000 Subject: [PATCH 30/44] #496 pocket threading --- coordinator/threads.lua | 2 +- pocket/startup.lua | 154 ++++++++++++---------------- pocket/threads.lua | 216 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+), 91 deletions(-) create mode 100644 pocket/threads.lua diff --git a/coordinator/threads.lua b/coordinator/threads.lua index e7c2d8c..20ba7ae 100644 --- a/coordinator/threads.lua +++ b/coordinator/threads.lua @@ -244,7 +244,7 @@ function threads.thread__main(smem) return public end --- coordinator renderer thread, tasked with long duration re-draws +-- coordinator renderer thread, tasked with long duration draws ---@nodiscard ---@param smem crd_shared_memory function threads.thread__render(smem) diff --git a/pocket/startup.lua b/pocket/startup.lua index 335bdc1..0c08061 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -6,19 +6,18 @@ require("/initenv").init_env() local crash = require("scada-common.crash") local log = require("scada-common.log") +local mqueue = require("scada-common.mqueue") local network = require("scada-common.network") local ppm = require("scada-common.ppm") -local tcd = require("scada-common.tcd") local util = require("scada-common.util") -local core = require("graphics.core") - local configure = require("pocket.configure") local iocontrol = require("pocket.iocontrol") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") +local threads = require("pocket.threads") -local POCKET_VERSION = "v0.9.2-alpha" +local POCKET_VERSION = "v0.10.0-alpha" local println = util.println local println_ts = util.println_ts @@ -71,6 +70,44 @@ local function main() -- record version for GUI iocontrol.get_db().version = POCKET_VERSION + ---------------------------------------- + -- memory allocation + ---------------------------------------- + + -- shared memory across threads + ---@class pkt_shared_memory + local __shared_memory = { + -- pocket system state flags + ---@class pkt_state + pkt_state = { + ui_ok = false, + shutdown = false + }, + + -- core pocket devices + pkt_dev = { + modem = ppm.get_wireless_modem() + }, + + -- system objects + pkt_sys = { + nic = nil, ---@type nic + pocket_comms = nil, ---@type pocket_comms + sv_wd = nil, ---@type watchdog + api_wd = nil ---@type watchdog + }, + + -- message queues + q = { + mq_render = mqueue.new() + } + } + + local smem_dev = __shared_memory.pkt_dev + local smem_sys = __shared_memory.pkt_sys + + local pkt_state = __shared_memory.pkt_state + ---------------------------------------- -- setup communications & clocks ---------------------------------------- @@ -83,118 +120,55 @@ local function main() iocontrol.report_link_state(iocontrol.LINK_STATE.UNLINKED) -- get the communications modem - local modem = ppm.get_wireless_modem() - if modem == nil then + if smem_dev.modem == nil then println("startup> wireless modem not found: please craft the pocket computer with a wireless modem") log.fatal("startup> no wireless modem on startup") return end -- create connection watchdogs - local conn_wd = { - sv = util.new_watchdog(config.ConnTimeout), - api = util.new_watchdog(config.ConnTimeout) - } - - conn_wd.sv.cancel() - conn_wd.api.cancel() - + smem_sys.sv_wd = util.new_watchdog(config.ConnTimeout) + smem_sys.sv_wd.cancel() + smem_sys.api_wd = util.new_watchdog(config.ConnTimeout) + smem_sys.api_wd.cancel() log.debug("startup> conn watchdogs created") -- create network interface then setup comms - local nic = network.nic(modem) - local pocket_comms = pocket.comms(POCKET_VERSION, nic, conn_wd.sv, conn_wd.api) + smem_sys.nic = network.nic(smem_dev.modem) + smem_sys.pocket_comms = pocket.comms(POCKET_VERSION, smem_sys.nic, smem_sys.sv_wd, smem_sys.api_wd) log.debug("startup> comms init") - -- base loop clock (2Hz, 10 ticks) - local MAIN_CLOCK = 0.5 - local loop_clock = util.new_clock(MAIN_CLOCK) - -- init I/O control - iocontrol.init_core(pocket_comms) + iocontrol.init_core(smem_sys.pocket_comms) ---------------------------------------- -- start the UI ---------------------------------------- - local ui_ok, message = renderer.try_start_ui() - if not ui_ok then - println(util.c("UI error: ", message)) - log.error(util.c("startup> GUI render failed with error ", message)) - else - -- start clock - loop_clock.start() + local ui_message + pkt_state.ui_ok, ui_message = renderer.try_start_ui() + if not pkt_state.ui_ok then + println(util.c("UI error: ", ui_message)) + log.error(util.c("startup> GUI render failed with error ", ui_message)) end ---------------------------------------- - -- main event loop + -- start system ---------------------------------------- - if ui_ok then - -- start connection watchdogs - conn_wd.sv.feed() - conn_wd.api.feed() - log.debug("startup> conn watchdogs started") + if pkt_state.ui_ok then + -- init threads + local main_thread = threads.thread__main(__shared_memory) + local render_thread = threads.thread__render(__shared_memory) - local io_db = iocontrol.get_db() - local nav = io_db.nav + log.info("startup> completed") - -- main event loop - while true do - local event, param1, param2, param3, param4, param5 = util.pull_event() - - -- handle event - if event == "timer" then - if loop_clock.is_clock(param1) then - -- main loop tick - - -- relink if necessary - pocket_comms.link_update() - - -- update any tasks for the active page - local page_tasks = nav.get_current_page().tasks - for i = 1, #page_tasks do page_tasks[i]() end - - loop_clock.start() - elseif conn_wd.sv.is_timer(param1) then - -- supervisor watchdog timeout - log.info("supervisor server timeout") - pocket_comms.close_sv() - elseif conn_wd.api.is_timer(param1) then - -- coordinator watchdog timeout - log.info("coordinator api server timeout") - pocket_comms.close_api() - else - -- a non-clock/main watchdog timer event - -- notify timer callback dispatcher - tcd.handle(param1) - end - elseif event == "modem_message" then - -- got a packet - local packet = pocket_comms.parse_packet(param1, param2, param3, param4, param5) - pocket_comms.handle_packet(packet) - elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or - event == "double_click" then - -- handle a mouse event - renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) - elseif event == "char" or event == "key" or event == "key_up" then - -- handle a keyboard event - renderer.handle_key(core.events.new_key_event(event, param1, param2)) - elseif event == "paste" then - -- handle a paste event - renderer.handle_paste(param1) - end - - -- check for termination request - if event == "terminate" or ppm.should_terminate() then - log.info("terminate requested, closing server connections...") - pocket_comms.close() - log.info("connections closed") - break - end - end + -- run threads + parallel.waitForAll(main_thread.p_exec, render_thread.p_exec) renderer.close_ui() + else + println_ts("UI creation failed") end println_ts("exited") diff --git a/pocket/threads.lua b/pocket/threads.lua new file mode 100644 index 0000000..22a4576 --- /dev/null +++ b/pocket/threads.lua @@ -0,0 +1,216 @@ +local log = require("scada-common.log") +local mqueue = require("scada-common.mqueue") +local ppm = require("scada-common.ppm") +local tcd = require("scada-common.tcd") +local util = require("scada-common.util") + +local iocontrol = require("pocket.iocontrol") +local renderer = require("pocket.renderer") + +local core = require("graphics.core") + +local threads = {} + +local MAIN_CLOCK = 0.5 -- (2Hz, 10 ticks) +local RENDER_SLEEP = 100 -- (100ms, 2 ticks) + +local MQ__RENDER_CMD = { + UNLOAD_SV_APPS = 1, + UNLOAD_API_APPS = 2 +} + +local MQ__RENDER_DATA = { + LOAD_APP = 1 +} + +-- main thread +---@nodiscard +---@param smem pkt_shared_memory +function threads.thread__main(smem) + ---@class parallel_thread + local public = {} + + -- execute thread + function public.exec() + log.debug("main thread start") + + local loop_clock = util.new_clock(MAIN_CLOCK) + + -- start clock + loop_clock.start() + + -- load in from shared memory + local pkt_state = smem.pkt_state + local pocket_comms = smem.pkt_sys.pocket_comms + local sv_wd = smem.pkt_sys.sv_wd + local api_wd = smem.pkt_sys.api_wd + + -- start connection watchdogs + sv_wd.feed() + api_wd.feed() + log.debug("startup> conn watchdogs started") + + local io_db = iocontrol.get_db() + local nav = io_db.nav + + -- event loop + while true do + local event, param1, param2, param3, param4, param5 = util.pull_event() + + -- handle event + if event == "timer" then + if loop_clock.is_clock(param1) then + -- main loop tick + + -- relink if necessary + pocket_comms.link_update() + + -- update any tasks for the active page + local page_tasks = nav.get_current_page().tasks + for i = 1, #page_tasks do page_tasks[i]() end + + loop_clock.start() + elseif sv_wd.is_timer(param1) then + -- supervisor watchdog timeout + log.info("supervisor server timeout") + pocket_comms.close_sv() + elseif api_wd.is_timer(param1) then + -- coordinator watchdog timeout + log.info("coordinator api server timeout") + pocket_comms.close_api() + else + -- a non-clock/main watchdog timer event + -- notify timer callback dispatcher + tcd.handle(param1) + end + elseif event == "modem_message" then + -- got a packet + local packet = pocket_comms.parse_packet(param1, param2, param3, param4, param5) + pocket_comms.handle_packet(packet) + elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or + event == "double_click" then + -- handle a mouse event + renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) + elseif event == "char" or event == "key" or event == "key_up" then + -- handle a keyboard event + renderer.handle_key(core.events.new_key_event(event, param1, param2)) + elseif event == "paste" then + -- handle a paste event + renderer.handle_paste(param1) + end + + -- check for termination request or UI crash + if event == "terminate" or ppm.should_terminate() then + log.info("terminate requested, main thread exiting") + pkt_state.shutdown = true + elseif not pkt_state.ui_ok then + pkt_state.shutdown = true + log.info("terminating due to fatal UI error") + end + + if pkt_state.shutdown then + log.info("closing server connections...") + pocket_comms.close() + log.info("connections closed") + break + end + end + end + + -- execute the thread in a protected mode, retrying it on return if not shutting down + function public.p_exec() + local pkt_state = smem.pkt_state + + while not pkt_state.shutdown do + local status, result = pcall(public.exec) + if status == false then + log.fatal(util.strval(result)) + end + + -- if status is true, then we are probably exiting, so this won't matter + -- this thread cannot be slept because it will miss events (namely "terminate") + if not pkt_state.shutdown then + log.info("main thread restarting now...") + end + end + end + + return public +end + +-- pocket renderer thread, tasked with long duration draws +---@nodiscard +---@param smem pkt_shared_memory +function threads.thread__render(smem) + ---@class parallel_thread + local public = {} + + -- execute thread + function public.exec() + log.debug("render thread start") + + -- load in from shared memory + local pkt_state = smem.pkt_state + local render_queue = smem.q.mq_render + + local last_update = util.time() + + -- thread loop + while true do + -- check for messages in the message queue + while render_queue.ready() and not pkt_state.shutdown do + local msg = render_queue.pop() + + if msg ~= nil then + if msg.qtype == mqueue.TYPE.COMMAND then + -- received a command + if msg.message == MQ__RENDER_CMD.UNLOAD_SV_APPS then + elseif msg.message == MQ__RENDER_CMD.UNLOAD_API_APPS then + end + elseif msg.qtype == mqueue.TYPE.DATA then + -- received data + local cmd = msg.message ---@type queue_data + + if cmd.key == MQ__RENDER_DATA.LOAD_APP then + end + elseif msg.qtype == mqueue.TYPE.PACKET then + -- received a packet + end + end + + -- quick yield + util.nop() + end + + -- check for termination request + if pkt_state.shutdown then + log.info("render thread exiting") + break + end + + -- delay before next check + last_update = util.adaptive_delay(RENDER_SLEEP, last_update) + end + end + + -- execute the thread in a protected mode, retrying it on return if not shutting down + function public.p_exec() + local pkt_state = smem.pkt_state + + while not pkt_state.shutdown do + local status, result = pcall(public.exec) + if status == false then + log.fatal(util.strval(result)) + end + + if not pkt_state.shutdown then + log.info("render thread restarting in 5 seconds...") + util.psleep(5) + end + end + end + + return public +end + +return threads From 38457cfbbc69bfb3f57ccf41130db0ee5b88a501 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 13 Jun 2024 20:34:39 -0400 Subject: [PATCH 31/44] enforce pocket computer requirement --- pocket/startup.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pocket/startup.lua b/pocket/startup.lua index 0c08061..6ce61a2 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -2,6 +2,9 @@ -- SCADA System Access on a Pocket Computer -- +---@diagnostic disable-next-line: undefined-global +local _is_pocket_env = pocket or periphemu + require("/initenv").init_env() local crash = require("scada-common.crash") @@ -22,6 +25,12 @@ local POCKET_VERSION = "v0.10.0-alpha" local println = util.println local println_ts = util.println_ts +-- check environment (allows Pocket or CraftOS-PC) +if not _is_pocket_env then + println("You can only use this application on a pocket computer.") + return +end + ---------------------------------------- -- get configuration ---------------------------------------- From def5b49327386622c065e682a367fe9d5511b27f Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 13 Jun 2024 21:43:56 -0400 Subject: [PATCH 32/44] #496 threaded app loading --- pocket/iocontrol.lua | 216 +--------------------------- pocket/pocket.lua | 231 ++++++++++++++++++++++++++++++ pocket/startup.lua | 10 +- pocket/threads.lua | 29 ++-- pocket/ui/apps/diag_apps.lua | 6 +- pocket/ui/apps/dummy_app.lua | 5 +- pocket/ui/apps/guide.lua | 37 +++-- pocket/ui/apps/sys_apps.lua | 5 +- pocket/ui/apps/unit.lua | 60 +++++--- pocket/ui/docs.lua | 2 +- pocket/ui/main.lua | 12 +- pocket/ui/pages/guide_section.lua | 6 + pocket/ui/pages/home_page.lua | 8 +- 13 files changed, 352 insertions(+), 275 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 669891b..1d2b77c 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -29,225 +29,17 @@ local LINK_STATE = { iocontrol.LINK_STATE = LINK_STATE ----@enum POCKET_APP_ID -local APP_ID = { - ROOT = 1, - -- main app page - UNITS = 2, - GUIDE = 3, - ABOUT = 4, - -- diag app page - ALARMS = 5, - -- other - DUMMY = 6, - NUM_APPS = 6 -} - -iocontrol.APP_ID = APP_ID - ---@class pocket_ioctl local io = { version = "unknown", ps = psil.create() } ----@class nav_tree_page ----@field _p nav_tree_page|nil page's parent ----@field _c table page's children ----@field nav_to function function to navigate to this page ----@field switcher function|nil function to switch between children ----@field tasks table tasks to run while viewing this page - --- allocate the page navigation system -function iocontrol.alloc_nav() - local self = { - pane = nil, ---@type graphics_element - apps = {}, - containers = {}, - help_map = {}, - help_return = nil, - cur_app = APP_ID.ROOT - } - - self.cur_page = self.root - - ---@class pocket_nav - io.nav = {} - - -- set the root pane element to switch between apps with - ---@param root_pane graphics_element - function io.nav.set_pane(root_pane) - self.pane = root_pane - end - - function io.nav.set_sidebar(sidebar) - self.sidebar = sidebar - end - - -- register an app - ---@param app_id POCKET_APP_ID app ID - ---@param container graphics_element element that contains this app (usually a Div) - ---@param pane graphics_element? multipane if this is a simple paned app, then nav_to must be a number - function io.nav.register_app(app_id, container, pane) - ---@class pocket_app - local app = { - loaded = false, - load = nil, - cur_page = nil, ---@type nav_tree_page - pane = pane, - paned_pages = {}, - sidebar_items = {} - } - - app.load = function () app.loaded = true end - - -- delayed set of the pane if it wasn't ready at the start - ---@param root_pane graphics_element multipane - function app.set_root_pane(root_pane) - app.pane = root_pane - end - - function app.set_sidebar(items) - app.sidebar_items = items - if self.sidebar then self.sidebar.update(items) end - end - - -- function to run on initial load into memory - ---@param on_load function callback - function app.set_on_load(on_load) - app.load = function () - on_load() - app.loaded = true - end - end - - -- if a pane was provided, this will switch between numbered pages - ---@param idx integer page index - function app.switcher(idx) - if app.paned_pages[idx] then - app.paned_pages[idx].nav_to() - end - end - - -- create a new page entry in the app's page navigation tree - ---@param parent nav_tree_page? a parent page or nil to set this as the root - ---@param nav_to function|integer function to navigate to this page or pane index - ---@return nav_tree_page new_page this new page - function app.new_page(parent, nav_to) - ---@type nav_tree_page - local page = { _p = parent, _c = {}, nav_to = function () end, switcher = function () end, tasks = {} } - - if parent == nil and app.cur_page == nil then - app.cur_page = page - end - - if type(nav_to) == "number" then - app.paned_pages[nav_to] = page - - function page.nav_to() - app.cur_page = page - if app.pane then app.pane.set_value(nav_to) end - end - else - function page.nav_to() - app.cur_page = page - nav_to() - end - end - - -- switch between children - ---@param id integer child ID - function page.switcher(id) if page._c[id] then page._c[id].nav_to() end end - - if parent ~= nil then - table.insert(page._p._c, page) - end - - return page - end - - -- get the currently active page - function app.get_current_page() return app.cur_page end - - -- attempt to navigate up the tree - ---@return boolean success true if successfully navigated up - function app.nav_up() - local parent = app.cur_page._p - if parent then parent.nav_to() end - return parent ~= nil - end - - self.apps[app_id] = app - self.containers[app_id] = container - - return app - end - - -- open a given app - ---@param app_id POCKET_APP_ID - function io.nav.open_app(app_id) - -- reset help return on navigating out of an app - if app_id == APP_ID.ROOT then self.help_return = nil end - - local app = self.apps[app_id] ---@type pocket_app - if app then - if not app.loaded then app.load() end - - self.cur_app = app_id - self.pane.set_value(app_id) - - if #app.sidebar_items > 0 then - self.sidebar.update(app.sidebar_items) - end - else - log.debug("tried to open unknown app") - end - end - - -- get a list of the app containers (usually Div elements) - function io.nav.get_containers() return self.containers end - - -- get the currently active page - ---@return nav_tree_page - function io.nav.get_current_page() - return self.apps[self.cur_app].get_current_page() - end - - -- attempt to navigate up - function io.nav.nav_up() - -- return out of help if opened with open_help - if self.help_return then - io.nav.open_app(self.help_return) - self.help_return = nil - return - end - - local app = self.apps[self.cur_app] ---@type pocket_app - log.debug("attempting app nav up for app " .. self.cur_app) - - if not app.nav_up() then - log.debug("internal app nav up failed, going to home screen") - io.nav.open_app(APP_ID.ROOT) - end - end - - -- open the help app, to show the reference for a key - function io.nav.open_help(key) - self.help_return = self.cur_app - - io.nav.open_app(APP_ID.GUIDE) - - local load = self.help_map[key] - if load then load() end - end - - function io.nav.link_help(map) self.help_map = map end -end - -- initialize facility-independent components of pocket iocontrol ---@param comms pocket_comms -function iocontrol.init_core(comms) - iocontrol.alloc_nav() +---@param nav pocket_nav +function iocontrol.init_core(comms, nav) + io.nav = nav ---@class pocket_ioctl_diag io.diag = {} @@ -858,7 +650,7 @@ function iocontrol.record_unit_data(data) --#endregion - --#region Advanced Alarm Information Display + --#region Status Information Display local ecam = {} -- aviation reference :) back to VATSIM I go... diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 88aafb3..6cc66d1 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -14,6 +14,18 @@ local LINK_STATE = iocontrol.LINK_STATE local pocket = {} +local MQ__RENDER_CMD = { + UNLOAD_SV_APPS = 1, + UNLOAD_API_APPS = 2 +} + +local MQ__RENDER_DATA = { + LOAD_APP = 1 +} + +pocket.MQ__RENDER_CMD = MQ__RENDER_CMD +pocket.MQ__RENDER_DATA = MQ__RENDER_DATA + ---@type pkt_config local config = {} @@ -63,6 +75,225 @@ function pocket.load_config() return cfv.valid() end +---@enum POCKET_APP_ID +local APP_ID = { + ROOT = 1, + -- main app pages + UNITS = 2, + GUIDE = 3, + ABOUT = 4, + -- diag app page + ALARMS = 5, + -- other + DUMMY = 6, + NUM_APPS = 6 +} + +pocket.APP_ID = APP_ID + +---@class nav_tree_page +---@field _p nav_tree_page|nil page's parent +---@field _c table page's children +---@field nav_to function function to navigate to this page +---@field switcher function|nil function to switch between children +---@field tasks table tasks to run while viewing this page + +-- allocate the page navigation system +---@param render_queue mqueue +function pocket.init_nav(render_queue) + local self = { + pane = nil, ---@type graphics_element + apps = {}, + containers = {}, + help_map = {}, + help_return = nil, + cur_app = APP_ID.ROOT + } + + self.cur_page = self.root + + ---@class pocket_nav + local nav = {} + + -- set the root pane element to switch between apps with + ---@param root_pane graphics_element + function nav.set_pane(root_pane) + self.pane = root_pane + end + + function nav.set_sidebar(sidebar) + self.sidebar = sidebar + end + + -- register an app + ---@param app_id POCKET_APP_ID app ID + ---@param container graphics_element element that contains this app (usually a Div) + ---@param pane graphics_element? multipane if this is a simple paned app, then nav_to must be a number + function nav.register_app(app_id, container, pane) + ---@class pocket_app + local app = { + loaded = false, + load = nil, + cur_page = nil, ---@type nav_tree_page + pane = pane, + paned_pages = {}, + sidebar_items = {} + } + + app.load = function () app.loaded = true end + + -- delayed set of the pane if it wasn't ready at the start + ---@param root_pane graphics_element multipane + function app.set_root_pane(root_pane) + app.pane = root_pane + end + + function app.set_sidebar(items) + app.sidebar_items = items + if self.sidebar then self.sidebar.update(items) end + end + + -- function to run on initial load into memory + ---@param on_load function callback + function app.set_on_load(on_load) + app.load = function () + on_load() + app.loaded = true + end + end + + -- if a pane was provided, this will switch between numbered pages + ---@param idx integer page index + function app.switcher(idx) + if app.paned_pages[idx] then + app.paned_pages[idx].nav_to() + end + end + + -- create a new page entry in the app's page navigation tree + ---@param parent nav_tree_page? a parent page or nil to set this as the root + ---@param nav_to function|integer function to navigate to this page or pane index + ---@return nav_tree_page new_page this new page + function app.new_page(parent, nav_to) + ---@type nav_tree_page + local page = { _p = parent, _c = {}, nav_to = function () end, switcher = function () end, tasks = {} } + + if parent == nil and app.cur_page == nil then + app.cur_page = page + end + + if type(nav_to) == "number" then + app.paned_pages[nav_to] = page + + function page.nav_to() + app.cur_page = page + if app.pane then app.pane.set_value(nav_to) end + end + else + function page.nav_to() + app.cur_page = page + nav_to() + end + end + + -- switch between children + ---@param id integer child ID + function page.switcher(id) if page._c[id] then page._c[id].nav_to() end end + + if parent ~= nil then + table.insert(page._p._c, page) + end + + return page + end + + -- get the currently active page + function app.get_current_page() return app.cur_page end + + -- attempt to navigate up the tree + ---@return boolean success true if successfully navigated up + function app.nav_up() + local parent = app.cur_page._p + if parent then parent.nav_to() end + return parent ~= nil + end + + self.apps[app_id] = app + self.containers[app_id] = container + + return app + end + + -- open a given app + ---@param app_id POCKET_APP_ID + function nav.open_app(app_id) + -- reset help return on navigating out of an app + if app_id == APP_ID.ROOT then self.help_return = nil end + + local app = self.apps[app_id] ---@type pocket_app + if app then + if not app.loaded then render_queue.push_data(MQ__RENDER_DATA.LOAD_APP, app_id) end + + self.cur_app = app_id + self.pane.set_value(app_id) + + if #app.sidebar_items > 0 then + self.sidebar.update(app.sidebar_items) + end + else + log.debug("tried to open unknown app") + end + end + + -- load a given app + ---@param app_id POCKET_APP_ID + function nav.load_app(app_id) + self.apps[app_id].load() + end + + -- get a list of the app containers (usually Div elements) + function nav.get_containers() return self.containers end + + -- get the currently active page + ---@return nav_tree_page + function nav.get_current_page() + return self.apps[self.cur_app].get_current_page() + end + + -- attempt to navigate up + function nav.nav_up() + -- return out of help if opened with open_help + if self.help_return then + nav.open_app(self.help_return) + self.help_return = nil + return + end + + local app = self.apps[self.cur_app] ---@type pocket_app + log.debug("attempting app nav up for app " .. self.cur_app) + + if not app.nav_up() then + log.debug("internal app nav up failed, going to home screen") + nav.open_app(APP_ID.ROOT) + end + end + + -- open the help app, to show the reference for a key + function nav.open_help(key) + self.help_return = self.cur_app + + nav.open_app(APP_ID.GUIDE) + + local load = self.help_map[key] + if load then load() end + end + + -- link the help map from the guide app + function nav.link_help(map) self.help_map = map end + + return nav +end + -- pocket coordinator + supervisor communications ---@nodiscard ---@param version string pocket version diff --git a/pocket/startup.lua b/pocket/startup.lua index 6ce61a2..a55d376 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -103,7 +103,8 @@ local function main() nic = nil, ---@type nic pocket_comms = nil, ---@type pocket_comms sv_wd = nil, ---@type watchdog - api_wd = nil ---@type watchdog + api_wd = nil, ---@type watchdog + nav = nil ---@type pocket_nav }, -- message queues @@ -118,7 +119,7 @@ local function main() local pkt_state = __shared_memory.pkt_state ---------------------------------------- - -- setup communications & clocks + -- setup system ---------------------------------------- -- message authentication init @@ -147,8 +148,9 @@ local function main() smem_sys.pocket_comms = pocket.comms(POCKET_VERSION, smem_sys.nic, smem_sys.sv_wd, smem_sys.api_wd) log.debug("startup> comms init") - -- init I/O control - iocontrol.init_core(smem_sys.pocket_comms) + -- init nav and I/O handler + smem_sys.nav = pocket.init_nav(__shared_memory.q.mq_render) + iocontrol.init_core(smem_sys.pocket_comms, smem_sys.nav) ---------------------------------------- -- start the UI diff --git a/pocket/threads.lua b/pocket/threads.lua index 22a4576..6c241f7 100644 --- a/pocket/threads.lua +++ b/pocket/threads.lua @@ -4,7 +4,7 @@ local ppm = require("scada-common.ppm") local tcd = require("scada-common.tcd") local util = require("scada-common.util") -local iocontrol = require("pocket.iocontrol") +local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") local core = require("graphics.core") @@ -14,14 +14,8 @@ local threads = {} local MAIN_CLOCK = 0.5 -- (2Hz, 10 ticks) local RENDER_SLEEP = 100 -- (100ms, 2 ticks) -local MQ__RENDER_CMD = { - UNLOAD_SV_APPS = 1, - UNLOAD_API_APPS = 2 -} - -local MQ__RENDER_DATA = { - LOAD_APP = 1 -} +local MQ__RENDER_CMD = pocket.MQ__RENDER_CMD +local MQ__RENDER_DATA = pocket.MQ__RENDER_DATA -- main thread ---@nodiscard @@ -44,15 +38,13 @@ function threads.thread__main(smem) local pocket_comms = smem.pkt_sys.pocket_comms local sv_wd = smem.pkt_sys.sv_wd local api_wd = smem.pkt_sys.api_wd + local nav = smem.pkt_sys.nav -- start connection watchdogs sv_wd.feed() api_wd.feed() log.debug("startup> conn watchdogs started") - local io_db = iocontrol.get_db() - local nav = io_db.nav - -- event loop while true do local event, param1, param2, param3, param4, param5 = util.pull_event() @@ -152,9 +144,12 @@ function threads.thread__render(smem) -- load in from shared memory local pkt_state = smem.pkt_state local render_queue = smem.q.mq_render + local nav = smem.pkt_sys.nav local last_update = util.time() + local ui_message + -- thread loop while true do -- check for messages in the message queue @@ -172,6 +167,16 @@ function threads.thread__render(smem) local cmd = msg.message ---@type queue_data if cmd.key == MQ__RENDER_DATA.LOAD_APP then + log.debug("RENDER: load app " .. cmd.val) + + local draw_start = util.time_ms() + + pkt_state.ui_ok, ui_message = pcall(function () nav.load_app(cmd.val) end) + if not pkt_state.ui_ok then + log.fatal(util.c("RENDER: app load failed with error ", ui_message)) + else + log.debug("RENDER: app loaded in " .. (util.time_ms() - draw_start) .. "ms") + end end elseif msg.qtype == mqueue.TYPE.PACKET then -- received a packet diff --git a/pocket/ui/apps/diag_apps.lua b/pocket/ui/apps/diag_apps.lua index ec8b5e6..be63e95 100644 --- a/pocket/ui/apps/diag_apps.lua +++ b/pocket/ui/apps/diag_apps.lua @@ -3,6 +3,7 @@ -- local iocontrol = require("pocket.iocontrol") +local pocket = require("pocket.pocket") local core = require("graphics.core") @@ -15,9 +16,10 @@ local Checkbox = require("graphics.elements.controls.checkbox") local PushButton = require("graphics.elements.controls.push_button") local SwitchButton = require("graphics.elements.controls.switch_button") +local ALIGN = core.ALIGN local cpair = core.cpair -local ALIGN = core.ALIGN +local APP_ID = pocket.APP_ID -- create diagnostic app pages ---@param root graphics_element parent @@ -30,7 +32,7 @@ local function create_pages(root) local alarm_test = Div{parent=root,x=1,y=1} - local alarm_app = db.nav.register_app(iocontrol.APP_ID.ALARMS, alarm_test) + local alarm_app = db.nav.register_app(APP_ID.ALARMS, alarm_test) local page = alarm_app.new_page(nil, function () end) page.tasks = { db.diag.tone_test.get_tone_states } diff --git a/pocket/ui/apps/dummy_app.lua b/pocket/ui/apps/dummy_app.lua index d7845a7..6e92493 100644 --- a/pocket/ui/apps/dummy_app.lua +++ b/pocket/ui/apps/dummy_app.lua @@ -3,12 +3,15 @@ -- local iocontrol = require("pocket.iocontrol") +local pocket = require("pocket.pocket") local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") +local APP_ID = pocket.APP_ID + -- create placeholder app page ---@param root graphics_element parent local function create_pages(root) @@ -16,7 +19,7 @@ local function create_pages(root) local main = Div{parent=root,x=1,y=1} - db.nav.register_app(iocontrol.APP_ID.DUMMY, main).new_page(nil, function () end) + db.nav.register_app(APP_ID.DUMMY, main).new_page(nil, function () end) TextBox{parent=main,text="This app is not implemented yet.",x=1,y=2,alignment=core.ALIGN.CENTER} diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 35868d8..61e5884 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -3,13 +3,12 @@ -- local util = require("scada-common.util") --- local log = require("scada-common.log") local iocontrol = require("pocket.iocontrol") -local TextField = require("graphics.elements.form.text_field") +local pocket = require("pocket.pocket") local docs = require("pocket.ui.docs") -local style = require("pocket.ui.style") +-- local style = require("pocket.ui.style") local guide_section = require("pocket.ui.pages.guide_section") @@ -20,33 +19,44 @@ local ListBox = require("graphics.elements.listbox") local MultiPane = require("graphics.elements.multipane") local TextBox = require("graphics.elements.textbox") +local WaitingAnim = require("graphics.elements.animations.waiting") + local PushButton = require("graphics.elements.controls.push_button") +local TextField = require("graphics.elements.form.text_field") + local ALIGN = core.ALIGN local cpair = core.cpair -local label = style.label --- local lu_col = style.label_unit_pair -local text_fg = style.text_fg +local APP_ID = pocket.APP_ID + +-- local label = style.label +-- local lu_col = style.label_unit_pair +-- local text_fg = style.text_fg -- new system guide view ---@param root graphics_element parent local function new_view(root) local db = iocontrol.get_db() - local main = Div{parent=root,x=1,y=1} + local frame = Div{parent=root,x=1,y=1} - local app = db.nav.register_app(iocontrol.APP_ID.GUIDE, main) + local app = db.nav.register_app(APP_ID.GUIDE, frame) - TextBox{parent=main,y=2,text="Guide",height=1,alignment=ALIGN.CENTER} - TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER} + local load_div = Div{parent=frame,x=1,y=1} + local main = Div{parent=frame,x=1,y=1} + + TextBox{parent=load_div,y=12,text="Loading...",height=1,alignment=ALIGN.CENTER} + WaitingAnim{parent=load_div,x=math.floor(main.get_width()/2)-1,y=8,fg_bg=cpair(colors.cyan,colors._INHERIT)} + + local load_pane = MultiPane{parent=main,x=1,y=1,panes={load_div,main}} local btn_fg_bg = cpair(colors.cyan, colors.black) local btn_active = cpair(colors.white, colors.black) local btn_disable = cpair(colors.gray, colors.black) local list = { - { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, + { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }, { label = " \x14 ", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(1) end }, { label = "__?", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(2) end } } @@ -142,6 +152,8 @@ local function new_view(root) TextBox{parent=search_results,text="Click 'GO' to search..."} + util.nop() + TextBox{parent=use,y=1,text="System Usage",height=1,alignment=ALIGN.CENTER} PushButton{parent=use,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to} @@ -202,6 +214,9 @@ local function new_view(root) -- link help resources db.nav.link_help(doc_map) + + -- done, show the app + load_pane.set_value(2) end app.set_on_load(load) diff --git a/pocket/ui/apps/sys_apps.lua b/pocket/ui/apps/sys_apps.lua index aa20a8d..85f8ee0 100644 --- a/pocket/ui/apps/sys_apps.lua +++ b/pocket/ui/apps/sys_apps.lua @@ -19,9 +19,10 @@ local TextBox = require("graphics.elements.textbox") local PushButton = require("graphics.elements.controls.push_button") +local ALIGN = core.ALIGN local cpair = core.cpair -local ALIGN = core.ALIGN +local APP_ID = pocket.APP_ID -- create system app pages ---@param root graphics_element parent @@ -34,7 +35,7 @@ local function create_pages(root) local about_root = Div{parent=root,x=1,y=1} - local about_app = db.nav.register_app(iocontrol.APP_ID.ABOUT, about_root) + local about_app = db.nav.register_app(APP_ID.ABOUT, about_root) local about_page = about_app.new_page(nil, 1) local nt_page = about_app.new_page(about_page, 2) diff --git a/pocket/ui/apps/unit.lua b/pocket/ui/apps/unit.lua index a6f28b3..6c39785 100644 --- a/pocket/ui/apps/unit.lua +++ b/pocket/ui/apps/unit.lua @@ -2,34 +2,39 @@ -- Unit Overview Page -- -local util = require("scada-common.util") --- local log = require("scada-common.log") +local util = require("scada-common.util") +-- local log = require("scada-common.log") -local iocontrol = require("pocket.iocontrol") +local iocontrol = require("pocket.iocontrol") +local pocket = require("pocket.pocket") -local style = require("pocket.ui.style") +local style = require("pocket.ui.style") -local boiler = require("pocket.ui.pages.unit_boiler") -local reactor = require("pocket.ui.pages.unit_reactor") -local turbine = require("pocket.ui.pages.unit_turbine") +local boiler = require("pocket.ui.pages.unit_boiler") +local reactor = require("pocket.ui.pages.unit_reactor") +local turbine = require("pocket.ui.pages.unit_turbine") -local core = require("graphics.core") +local core = require("graphics.core") -local Div = require("graphics.elements.div") -local ListBox = require("graphics.elements.listbox") -local MultiPane = require("graphics.elements.multipane") -local TextBox = require("graphics.elements.textbox") +local Div = require("graphics.elements.div") +local ListBox = require("graphics.elements.listbox") +local MultiPane = require("graphics.elements.multipane") +local TextBox = require("graphics.elements.textbox") -local DataIndicator = require("graphics.elements.indicators.data") -local IconIndicator = require("graphics.elements.indicators.icon") --- local RadIndicator = require("graphics.elements.indicators.rad") --- local VerticalBar = require("graphics.elements.indicators.vbar") +local WaitingAnim = require("graphics.elements.animations.waiting") -local PushButton = require("graphics.elements.controls.push_button") +local DataIndicator = require("graphics.elements.indicators.data") +local IconIndicator = require("graphics.elements.indicators.icon") +-- local RadIndicator = require("graphics.elements.indicators.rad") +-- local VerticalBar = require("graphics.elements.indicators.vbar") + +local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair +local APP_ID = pocket.APP_ID + -- local label = style.label local lu_col = style.label_unit_pair local text_fg = style.text_fg @@ -49,11 +54,17 @@ local emc_ind_s = { local function new_view(root) local db = iocontrol.get_db() - local main = Div{parent=root,x=1,y=1} + local frame = Div{parent=root,x=1,y=1} - local app = db.nav.register_app(iocontrol.APP_ID.UNITS, main) + local app = db.nav.register_app(APP_ID.UNITS, frame) - TextBox{parent=main,y=2,text="Units App",height=1,alignment=ALIGN.CENTER} + local load_div = Div{parent=frame,x=1,y=1} + local main = Div{parent=frame,x=1,y=1} + + TextBox{parent=load_div,y=12,text="Loading...",height=1,alignment=ALIGN.CENTER} + WaitingAnim{parent=load_div,x=math.floor(main.get_width()/2)-1,y=8,fg_bg=cpair(colors.yellow,colors._INHERIT)} + + local load_pane = MultiPane{parent=main,x=1,y=1,panes={load_div,main}} TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER} @@ -66,7 +77,7 @@ local function new_view(root) local unit = db.units[id] ---@type pioctl_unit local list = { - { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end }, + { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }, { label = "U-" .. id, color = core.cpair(colors.black, colors.yellow), callback = function () app.switcher(id) end }, { label = " \x13 ", color = core.cpair(colors.black, colors.red), callback = nav_links[id].alarm }, { label = "RPS", tall = true, color = core.cpair(colors.black, colors.cyan), callback = nav_links[id].rps }, @@ -174,6 +185,8 @@ local function new_view(root) --#endregion + util.nop() + --#region Alarms Tab local alm_div = Div{parent=page_div} @@ -349,6 +362,8 @@ local function new_view(root) end --#endregion + + util.nop() end -- setup multipane @@ -356,6 +371,9 @@ local function new_view(root) app.set_root_pane(u_pane) set_sidebar(active_unit) + + -- done, show the app + load_pane.set_value(2) end app.set_on_load(load) diff --git a/pocket/ui/docs.lua b/pocket/ui/docs.lua index 936b2e8..eed9777 100644 --- a/pocket/ui/docs.lua +++ b/pocket/ui/docs.lua @@ -102,7 +102,7 @@ doc("G_Fault", "Fault", "Something has gone wrong and/or failed to function.") doc("G_FrontPanel", "Front Panel", "A basic interface on the front of a device for viewing and sometimes modifying its state. This is what you see when looking at a computer running one of the SCADA applications.") doc("G_Nominal", "Nominal", "Normal operation. Everything operating as intended.") doc("G_Ringback", "Ringback", "An indication that an alarm had gone off so that you are aware, even if the alarm condition is no longer met.") -doc("G_SCRAM", "SCRAM", "[Emergency] shut-down of a reactor by stopping the fission reactor. In Mekanism and here, it isn't always for an emergency.") +doc("G_SCRAM", "SCRAM", "[Emergency] shut-down of a reactor by stopping the fission. In Mekanism and here, it isn't always for an emergency.") doc("G_Transient", "Transient", "A temporary change in state from normal operation. Coolant levels dropping or core temperature rising above nominal values would be examples of transients.") doc("G_Trip", "Trip", "A checked condition has occurred, also known as 'tripped'.") doc("G_Tripped", "Tripped", "An alarm condition has been met and is still met.") diff --git a/pocket/ui/main.lua b/pocket/ui/main.lua index 9f33e1f..83e5716 100644 --- a/pocket/ui/main.lua +++ b/pocket/ui/main.lua @@ -5,6 +5,7 @@ local util = require("scada-common.util") local iocontrol = require("pocket.iocontrol") +local pocket = require("pocket.pocket") local diag_apps = require("pocket.ui.apps.diag_apps") local dummy_app = require("pocket.ui.apps.dummy_app") @@ -29,11 +30,12 @@ local Sidebar = require("graphics.elements.controls.sidebar") local SignalBar = require("graphics.elements.indicators.signal") +local ALIGN = core.ALIGN +local cpair = core.cpair + local LINK_STATE = iocontrol.LINK_STATE -local ALIGN = core.ALIGN - -local cpair = core.cpair +local APP_ID = pocket.APP_ID -- create new main view ---@param main graphics_element main displaybox @@ -82,14 +84,14 @@ local function init(main) diag_apps(page_div) dummy_app(page_div) - assert(util.table_len(db.nav.get_containers()) == iocontrol.APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered") + assert(util.table_len(db.nav.get_containers()) == APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered") db.nav.set_pane(MultiPane{parent=page_div,x=1,y=1,panes=db.nav.get_containers()}) db.nav.set_sidebar(Sidebar{parent=main_pane,x=1,y=1,height=18,fg_bg=cpair(colors.white,colors.gray)}) PushButton{parent=main_pane,x=1,y=19,text="\x1b",min_width=3,fg_bg=cpair(colors.white,colors.gray),active_fg_bg=cpair(colors.gray,colors.black),callback=db.nav.nav_up} - db.nav.open_app(iocontrol.APP_ID.ROOT) + db.nav.open_app(APP_ID.ROOT) --#endregion end diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index 1cbc8ec..a9e9596 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -1,3 +1,5 @@ +local util = require("scada-common.util") + local core = require("graphics.core") local Div = require("graphics.elements.div") @@ -53,7 +55,11 @@ return function (data, base_page, title, items, scroll_height) table.insert(search_db, { string.lower(item.name), item.name, title, view }) PushButton{parent=name_list,text=item.name,alignment=ALIGN.LEFT,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} + + if i % 12 == 0 then util.nop() end end + util.nop() + return section_page end diff --git a/pocket/ui/pages/home_page.lua b/pocket/ui/pages/home_page.lua index 8854614..4b7fded 100644 --- a/pocket/ui/pages/home_page.lua +++ b/pocket/ui/pages/home_page.lua @@ -3,6 +3,7 @@ -- local iocontrol = require("pocket.iocontrol") +local pocket = require("pocket.pocket") local core = require("graphics.core") @@ -12,11 +13,10 @@ local TextBox = require("graphics.elements.textbox") local App = require("graphics.elements.controls.app") +local ALIGN = core.ALIGN local cpair = core.cpair -local APP_ID = iocontrol.APP_ID - -local ALIGN = core.ALIGN +local APP_ID = pocket.APP_ID -- new home page view ---@param root graphics_element parent @@ -25,7 +25,7 @@ local function new_view(root) local main = Div{parent=root,x=1,y=1,height=19} - local app = db.nav.register_app(iocontrol.APP_ID.ROOT, main) + local app = db.nav.register_app(APP_ID.ROOT, main) local apps_1 = Div{parent=main,x=1,y=1,height=15} local apps_2 = Div{parent=main,x=1,y=1,height=15} From 0b97d4d4b0b655324b37f0d0c1cf30e4fffce573 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 13 Jun 2024 21:52:13 -0400 Subject: [PATCH 33/44] updated header message --- pocket/ui/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pocket/ui/main.lua b/pocket/ui/main.lua index 83e5716..40b1104 100644 --- a/pocket/ui/main.lua +++ b/pocket/ui/main.lua @@ -43,7 +43,7 @@ local function init(main) local db = iocontrol.get_db() -- window header message - TextBox{parent=main,y=1,text="WIP ALPHA APP S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header} + TextBox{parent=main,y=1,text="EARLY ACCESS ALPHA S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header} local svr_conn = SignalBar{parent=main,y=1,x=22,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)} local crd_conn = SignalBar{parent=main,y=1,x=26,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)} From 00cacd6d0af79013f10a65000a1e86eec74df11c Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 14 Jun 2024 16:10:04 +0000 Subject: [PATCH 34/44] #497 unload apps when required connections are lost --- pocket/pocket.lua | 63 +++++++++++++++++++++++++++++++++------- pocket/startup.lua | 7 +++-- pocket/threads.lua | 16 +++++----- pocket/ui/apps/guide.lua | 36 ++++++++++++++++++----- pocket/ui/apps/unit.lua | 26 ++++++++++++++--- 5 files changed, 114 insertions(+), 34 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 6cc66d1..ab3783a 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -102,7 +102,8 @@ pocket.APP_ID = APP_ID ---@param render_queue mqueue function pocket.init_nav(render_queue) local self = { - pane = nil, ---@type graphics_element + pane = nil, ---@type graphics_element + sidebar = nil, ---@type graphics_element apps = {}, containers = {}, help_map = {}, @@ -117,23 +118,22 @@ function pocket.init_nav(render_queue) -- set the root pane element to switch between apps with ---@param root_pane graphics_element - function nav.set_pane(root_pane) - self.pane = root_pane - end + function nav.set_pane(root_pane) self.pane = root_pane end - function nav.set_sidebar(sidebar) - self.sidebar = sidebar - end + -- link sidebar element + ---@param sidebar graphics_element + function nav.set_sidebar(sidebar) self.sidebar = sidebar end -- register an app ---@param app_id POCKET_APP_ID app ID ---@param container graphics_element element that contains this app (usually a Div) ---@param pane graphics_element? multipane if this is a simple paned app, then nav_to must be a number - function nav.register_app(app_id, container, pane) + ---@param require_sv boolean? true or false/nil otherwise to specifiy if this app should be unloaded when the supervisor connection is lost + ---@param require_api boolean? true or false/nil otherwise to specifiy if this app should be unloaded when the api connection is lost + function nav.register_app(app_id, container, pane, require_sv, require_api) ---@class pocket_app local app = { loaded = false, - load = nil, cur_page = nil, ---@type nav_tree_page pane = pane, paned_pages = {}, @@ -141,6 +141,11 @@ function pocket.init_nav(render_queue) } app.load = function () app.loaded = true end + app.unload = function () app.loaded = false end + + -- check which connections this requires + ---@return boolean requires_sv, boolean requires_api + function app.check_requires() return require_sv or false, require_api or false end -- delayed set of the pane if it wasn't ready at the start ---@param root_pane graphics_element multipane @@ -155,13 +160,22 @@ function pocket.init_nav(render_queue) -- function to run on initial load into memory ---@param on_load function callback - function app.set_on_load(on_load) + function app.set_load(on_load) app.load = function () on_load() app.loaded = true end end + -- function to run to close out the app + ---@param on_unload function callback + function app.set_unload(on_unload) + app.unload = function () + on_unload() + app.loaded = false + end + end + -- if a pane was provided, this will switch between numbered pages ---@param idx integer page index function app.switcher(idx) @@ -207,6 +221,12 @@ function pocket.init_nav(render_queue) return page end + -- delete paned pages and clear the current page + function app.delete_pages() + app.paned_pages = {} + app.cur_page = nil + end + -- get the currently active page function app.get_current_page() return app.cur_page end @@ -251,6 +271,22 @@ function pocket.init_nav(render_queue) self.apps[app_id].load() end + -- unload api-dependent apps + function nav.unload_api() + for _, app in pairs(self.apps) do + local _, api = app.check_requires() + if app.loaded and api then app.unload() end + end + end + + -- unload supervisor-dependent apps + function nav.unload_sv() + for _, app in pairs(self.apps) do + local sv, _ = app.check_requires() + if app.loaded and sv then app.unload() end + end + end + -- get a list of the app containers (usually Div elements) function nav.get_containers() return self.containers end @@ -300,7 +336,8 @@ end ---@param nic nic network interface device ---@param sv_watchdog watchdog ---@param api_watchdog watchdog -function pocket.comms(version, nic, sv_watchdog, api_watchdog) +---@param nav pocket_nav +function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav) local self = { sv = { linked = false, @@ -399,6 +436,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) -- close connection to the supervisor function public.close_sv() sv_watchdog.cancel() + nav.unload_sv() self.sv.linked = false self.sv.r_seq_num = nil self.sv.addr = comms.BROADCAST @@ -408,6 +446,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) -- close connection to coordinator API server function public.close_api() api_watchdog.cancel() + nav.unload_api() self.api.linked = false self.api.r_seq_num = nil self.api.addr = comms.BROADCAST @@ -589,6 +628,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) elseif packet.type == MGMT_TYPE.CLOSE then -- handle session close api_watchdog.cancel() + nav.unload_api() self.api.linked = false self.api.r_seq_num = nil self.api.addr = comms.BROADCAST @@ -694,6 +734,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog) elseif packet.type == MGMT_TYPE.CLOSE then -- handle session close sv_watchdog.cancel() + nav.unload_sv() self.sv.linked = false self.sv.r_seq_num = nil self.sv.addr = comms.BROADCAST diff --git a/pocket/startup.lua b/pocket/startup.lua index a55d376..0d2be64 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -122,6 +122,8 @@ local function main() -- setup system ---------------------------------------- + smem_sys.nav = pocket.init_nav(__shared_memory.q.mq_render) + -- message authentication init if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then network.init_mac(config.AuthKey) @@ -145,11 +147,10 @@ local function main() -- create network interface then setup comms smem_sys.nic = network.nic(smem_dev.modem) - smem_sys.pocket_comms = pocket.comms(POCKET_VERSION, smem_sys.nic, smem_sys.sv_wd, smem_sys.api_wd) + smem_sys.pocket_comms = pocket.comms(POCKET_VERSION, smem_sys.nic, smem_sys.sv_wd, smem_sys.api_wd, smem_sys.nav) log.debug("startup> comms init") - -- init nav and I/O handler - smem_sys.nav = pocket.init_nav(__shared_memory.q.mq_render) + -- init I/O control iocontrol.init_core(smem_sys.pocket_comms, smem_sys.nav) ---------------------------------------- diff --git a/pocket/threads.lua b/pocket/threads.lua index 6c241f7..5e05940 100644 --- a/pocket/threads.lua +++ b/pocket/threads.lua @@ -1,13 +1,13 @@ -local log = require("scada-common.log") -local mqueue = require("scada-common.mqueue") -local ppm = require("scada-common.ppm") -local tcd = require("scada-common.tcd") -local util = require("scada-common.util") +local log = require("scada-common.log") +local mqueue = require("scada-common.mqueue") +local ppm = require("scada-common.ppm") +local tcd = require("scada-common.tcd") +local util = require("scada-common.util") -local pocket = require("pocket.pocket") -local renderer = require("pocket.renderer") +local pocket = require("pocket.pocket") +local renderer = require("pocket.renderer") -local core = require("graphics.core") +local core = require("graphics.core") local threads = {} diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 61e5884..2fea06a 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -55,16 +55,21 @@ local function new_view(root) local btn_active = cpair(colors.white, colors.black) local btn_disable = cpair(colors.gray, colors.black) - local list = { - { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }, - { label = " \x14 ", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(1) end }, - { label = "__?", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(2) end } - } + app.set_sidebar({{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }}) - app.set_sidebar(list) + local page_div = nil ---@type nil|graphics_element + -- load the app (create the elements) local function load() - local page_div = Div{parent=main,y=2} + local list = { + { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }, + { label = " \x14 ", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(1) end }, + { label = "__?", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(2) end } + } + + app.set_sidebar(list) + + page_div = Div{parent=main,y=2} local p_width = page_div.get_width() - 2 local main_page = app.new_page(nil, 1) @@ -219,7 +224,22 @@ local function new_view(root) load_pane.set_value(2) end - app.set_on_load(load) + -- delete the elements and switch back to the loading screen + local function unload() + if page_div then + page_div.delete() + page_div = nil + end + + app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } }) + app.delete_pages() + + -- show loading screen + load_pane.set_value(1) + end + + app.set_load(load) + app.set_unload(unload) return main end diff --git a/pocket/ui/apps/unit.lua b/pocket/ui/apps/unit.lua index 6c39785..8a11d88 100644 --- a/pocket/ui/apps/unit.lua +++ b/pocket/ui/apps/unit.lua @@ -56,7 +56,7 @@ local function new_view(root) local frame = Div{parent=root,x=1,y=1} - local app = db.nav.register_app(APP_ID.UNITS, frame) + local app = db.nav.register_app(APP_ID.UNITS, frame, nil, false, true) local load_div = Div{parent=frame,x=1,y=1} local main = Div{parent=frame,x=1,y=1} @@ -66,13 +66,15 @@ local function new_view(root) local load_pane = MultiPane{parent=main,x=1,y=1,panes={load_div,main}} - TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER} + app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } }) local btn_fg_bg = cpair(colors.yellow, colors.black) local btn_active = cpair(colors.white, colors.black) local nav_links = {} + local page_div = nil ---@type nil|graphics_element + -- set sidebar to display unit-specific fields based on a specified unit local function set_sidebar(id) local unit = db.units[id] ---@type pioctl_unit @@ -96,8 +98,9 @@ local function new_view(root) app.set_sidebar(list) end + -- load the app (create the elements) local function load() - local page_div = Div{parent=main,y=2,width=main.get_width()} + page_div = Div{parent=main,y=2,width=main.get_width()} local panes = {} @@ -376,7 +379,22 @@ local function new_view(root) load_pane.set_value(2) end - app.set_on_load(load) + -- delete the elements and switch back to the loading screen + local function unload() + if page_div then + page_div.delete() + page_div = nil + end + + app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } }) + app.delete_pages() + + -- show loading screen + load_pane.set_value(1) + end + + app.set_load(load) + app.set_unload(unload) return main end From 697a3d6f6b7275dfd3532b66bb6e7b5263cd0f33 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 14 Jun 2024 16:14:24 +0000 Subject: [PATCH 35/44] luacheck fixes --- pocket/iocontrol.lua | 2 +- pocket/startup.lua | 2 +- pocket/ui/pages/guide_section.lua | 2 +- pocket/ui/pages/unit_reactor.lua | 11 +++++------ pocket/ui/pages/unit_turbine.lua | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 1d2b77c..3d52e68 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -3,7 +3,7 @@ -- local const = require("scada-common.constants") -local log = require("scada-common.log") +-- local log = require("scada-common.log") local psil = require("scada-common.psil") local types = require("scada-common.types") local util = require("scada-common.util") diff --git a/pocket/startup.lua b/pocket/startup.lua index 0d2be64..33832da 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -3,7 +3,7 @@ -- ---@diagnostic disable-next-line: undefined-global -local _is_pocket_env = pocket or periphemu +local _is_pocket_env = pocket ~= nil or periphemu ~= nil require("/initenv").init_env() diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index a9e9596..63e2333 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -36,7 +36,7 @@ return function (data, base_page, title, items, scroll_height) local name_list = ListBox{parent=section_div,x=1,y=3,scroll_height=30,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} local def_list = ListBox{parent=section_view_div,x=1,y=3,scroll_height=scroll_height,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)} - local _end = nil + local _end for i = 1, #items do local item = items[i] ---@type pocket_doc_item diff --git a/pocket/ui/pages/unit_reactor.lua b/pocket/ui/pages/unit_reactor.lua index 6990e92..7446d78 100644 --- a/pocket/ui/pages/unit_reactor.lua +++ b/pocket/ui/pages/unit_reactor.lua @@ -20,12 +20,11 @@ local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair -local label = style.label -local lu_col = style.label_unit_pair -local text_fg = style.text_fg -local mode_states = style.icon_states.mode_states -local red_ind_s = style.icon_states.red_ind_s -local yel_ind_s = style.icon_states.yel_ind_s +local label = style.label +local lu_col = style.label_unit_pair +local text_fg = style.text_fg +local red_ind_s = style.icon_states.red_ind_s +local yel_ind_s = style.icon_states.yel_ind_s -- create a reactor view in the unit app ---@param app pocket_app diff --git a/pocket/ui/pages/unit_turbine.lua b/pocket/ui/pages/unit_turbine.lua index b4d9a2e..5a7763b 100644 --- a/pocket/ui/pages/unit_turbine.lua +++ b/pocket/ui/pages/unit_turbine.lua @@ -109,8 +109,8 @@ return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, update) local rotation = DataIndicator{parent=tbn_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="",format="%21.12f",value=0,width=21,fg_bg=text_fg} rotation.register(ps, "steam", function () - local status, result = pcall(function () return util.turbine_rotation(db.units[u_id].turbine_data_tbl[t_id]) end) - if status then rotation.update(result) end + local ok, result = pcall(function () return util.turbine_rotation(db.units[u_id].turbine_data_tbl[t_id]) end) + if ok then rotation.update(result) end end) return tbn_page.nav_to From fb1f85a626c25e553fabf1c66f43c78d2def3f66 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 14 Jun 2024 16:16:41 +0000 Subject: [PATCH 36/44] possible luacheck suppression --- pocket/startup.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pocket/startup.lua b/pocket/startup.lua index 33832da..f88c2e6 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -2,8 +2,11 @@ -- SCADA System Access on a Pocket Computer -- + +-- luacheck: no undefined ---@diagnostic disable-next-line: undefined-global -local _is_pocket_env = pocket ~= nil or periphemu ~= nil +local _is_pocket_env = pocket or periphemu +-- luacheck: undefined require("/initenv").init_env() From 14736e414fd99190232635542cd7eb8d8292a5d9 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 14 Jun 2024 16:20:53 +0000 Subject: [PATCH 37/44] luacheck ignore --- pocket/startup.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pocket/startup.lua b/pocket/startup.lua index f88c2e6..03eefec 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -2,11 +2,8 @@ -- SCADA System Access on a Pocket Computer -- - --- luacheck: no undefined ---@diagnostic disable-next-line: undefined-global -local _is_pocket_env = pocket or periphemu --- luacheck: undefined +local _is_pocket_env = pocket or periphemu -- luacheck: ignore pocket require("/initenv").init_env() From c66ad44adb7c103d66f30a67e3aba700649cdc45 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 14 Jun 2024 16:32:25 +0000 Subject: [PATCH 38/44] pocket cleanup --- pocket/pocket.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index ab3783a..f32ef1a 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -127,9 +127,9 @@ function pocket.init_nav(render_queue) -- register an app ---@param app_id POCKET_APP_ID app ID ---@param container graphics_element element that contains this app (usually a Div) - ---@param pane graphics_element? multipane if this is a simple paned app, then nav_to must be a number - ---@param require_sv boolean? true or false/nil otherwise to specifiy if this app should be unloaded when the supervisor connection is lost - ---@param require_api boolean? true or false/nil otherwise to specifiy if this app should be unloaded when the api connection is lost + ---@param pane? graphics_element multipane if this is a simple paned app, then nav_to must be a number + ---@param require_sv? boolean true to specifiy if this app should be unloaded when the supervisor connection is lost + ---@param require_api? boolean true to specifiy if this app should be unloaded when the api connection is lost function nav.register_app(app_id, container, pane, require_sv, require_api) ---@class pocket_app local app = { @@ -153,6 +153,8 @@ function pocket.init_nav(render_queue) app.pane = root_pane end + -- configure the sidebar + ---@param items table function app.set_sidebar(items) app.sidebar_items = items if self.sidebar then self.sidebar.update(items) end @@ -185,7 +187,7 @@ function pocket.init_nav(render_queue) end -- create a new page entry in the app's page navigation tree - ---@param parent nav_tree_page? a parent page or nil to set this as the root + ---@param parent nav_tree_page|nil a parent page or nil to set this as the root ---@param nav_to function|integer function to navigate to this page or pane index ---@return nav_tree_page new_page this new page function app.new_page(parent, nav_to) @@ -244,7 +246,7 @@ function pocket.init_nav(render_queue) return app end - -- open a given app + -- open an app ---@param app_id POCKET_APP_ID function nav.open_app(app_id) -- reset help return on navigating out of an app @@ -296,7 +298,8 @@ function pocket.init_nav(render_queue) return self.apps[self.cur_app].get_current_page() end - -- attempt to navigate up + -- attempt to navigate up within the active app, otherwise open home page
+ -- except, this will go back to a prior app if leaving the help app after open_help was used function nav.nav_up() -- return out of help if opened with open_help if self.help_return then From 87a91e309d315e46d9be4a94b7b633c4721d0d96 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 14 Jun 2024 21:09:14 +0000 Subject: [PATCH 39/44] #403 guide updates --- pocket/ui/docs.lua | 111 +++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/pocket/ui/docs.lua b/pocket/ui/docs.lua index eed9777..40507a1 100644 --- a/pocket/ui/docs.lua +++ b/pocket/ui/docs.lua @@ -8,21 +8,23 @@ local function doc(key, name, desc) table.insert(target, item) end +-- important to note in the future: The PLC should always be in a chunk with the reactor to ensure it can protect it on chunk load if you do not keep it all chunk loaded + docs.alarms = {} target = docs.alarms -doc("ContainmentBreach", "Containment Breach", "Reactor disconnected or indicated unformed while being at or above 100% damage; explosion likely occurred.") +doc("ContainmentBreach", "Containment Breach", "Reactor disconnected or indicated unformed while being at or above 100% damage; explosion assumed.") doc("ContainmentRadiation", "Containment Radiation", "Environment detector(s) assigned to the unit have observed high levels of radiation.") doc("ReactorLost", "Reactor Lost", "Reactor PLC has stopped communicating with the supervisor.") -doc("CriticalDamage", "Damage Critical", "Reactor damage has reached or exceeded 100%, so it may explode at any moment.") -doc("ReactorDamage", "Reactor Damage", "Reactor temperature causing increasing damage to reactor casing.") -doc("ReactorOverTemp", "Reactor Over Temp", "Reactor temperature is at or above maximum safe temperature and is now taking damage.") +doc("CriticalDamage", "Damage Critical", "Reactor damage has reached or exceeded 100%, so it will explode at any moment.") +doc("ReactorDamage", "Reactor Damage", "Reactor temperature causing increasing damage to the reactor casing.") +doc("ReactorOverTemp", "Reactor Over Temp", "Reactor temperature is at or above maximum safe temperature, so it is now taking damage.") doc("ReactorHighTemp", "Reactor High Temp", "Reactor temperature is above expected operating levels and may exceed maximum safe temperature soon.") -doc("ReactorWasteLeak", "Reactor Waste Leak", "The reactor is full of spent waste and will now emit radiation if additional waste is generated.") +doc("ReactorWasteLeak", "Reactor Waste Leak", "The reactor is full of spent waste so it will now emit radiation if additional waste is generated.") doc("ReactorHighWaste", "Reactor High Waste", "Reactor waste levels are high and may leak soon.") doc("RPSTransient", "RPS Transient", "Reactor protection system was activated.") -doc("RCSTransient", "RCS Transient", "Something is wrong with the reactor coolant system, check RCS indicators.") -doc("TurbineTripAlarm", "Turbine Trip", "A turbine stopped rotating, likely due to having full energy storage.") +doc("RCSTransient", "RCS Transient", "Something is wrong with the reactor coolant system, check RCS indicators for details.") +doc("TurbineTripAlarm", "Turbine Trip", "A turbine stopped rotating, likely due to having full energy storage. This will prevent cooling, so it needs to be resolved before using that unit.") docs.annunc = { unit = { @@ -31,69 +33,68 @@ docs.annunc = { } target = docs.annunc.unit.main_section -doc("PLCOnline", "PLC Online", "Indicates if the fission reactor PLC is connected.") -doc("PLCHeartbeat", "PLC Heartbeat", "An indicator of status data being live. As status messages are received from the PLC, this light will turn on and off. If it gets stuck, the supervisor has stopped receiving data.") -doc("RadiationMonitor", "Radiation Monitor", "Indicates if at least once environment detector is connected and assigned to this unit.") -doc("AutoControl", "Automatic Control", "Indicates if the reactor is under the control of one of the automatic control modes.") -doc("ReactorSCRAM", "Reactor SCRAM", "Indicates if the reactor protection system is holding the reactor SCRAM'd.") -doc("ManualReactorSCRAM", "Manual Reactor SCRAM", "Indicates if the operator (you) initiated a SCRAM.") -doc("AutoReactorSCRAM", "Auto Reactor SCRAM", "Indicates if the automatic control system initiated a SCRAM. The main view screen will have an indicator as to why.") -doc("RadiationWarning", "Radiation Warning", "Indicates if radiation levels are above normal. There is likely a leak somewhere, so that should be identified and fixed.") -doc("RCPTrip", "RCP Trip", "Reactor coolant pump tripped. This is a technical concept not directly mapping to mekansim, so in this case it indicates if there is either high heated coolant or low cooled coolant causing an RPS trip. Check the coolant system if this occurs.") -doc("RCSFlowLow", "RCS Flow Low", "Indicates if the reactor coolant system flow is low. This is observed when the cooled coolant level in the reactor is dropping. This can occur while a turbine spins up, but if it persists, check that the cooling system is operating properly.") -doc("CoolantLevelLow", "Coolant Level Low", "Indicates if the reactor coolant level is lower than it should be. Check the coolant system.") -doc("ReactorTempHigh", "Reactor Temp. High", "Indicates if the reactor temperature is above expected maximum operating temperature. This is not yet damaging, but should be attended to. Check coolant system.") -doc("ReactorHighDeltaT", "Reactor High Delta T", "Indicates if the reactor temperature is climbing rapidly. This can occur when a reactor is starting up, but it is a concern if it happens uncontrolled while the burn rate is not increasing.") -doc("FuelInputRateLow", "Fuel Input Rate Low", "Indicates if the fissile fuel levels in the reactor are dropping or are very low. Ensure a steady supply of fuel is entering the reactor.") +doc("PLCOnline", "PLC Online", "Indicates if the fission reactor PLC is connected. If it isn't, check that your PLC is on and configured properly.") +doc("PLCHeartbeat", "PLC Heartbeat", "An indicator of status data being live. As status messages are received from the PLC, this light will turn on and off. If it gets stuck, the supervisor has stopped receiving data or a screen has frozen.") +doc("RadiationMonitor", "Radiation Monitor", "On if at least one environment detector is connected and assigned to this unit.") +doc("AutoControl", "Automatic Control", "On if the reactor is under the control of one of the automatic control modes.") +doc("ReactorSCRAM", "Reactor SCRAM", "On if the reactor protection system is holding the reactor SCRAM'd.") +doc("ManualReactorSCRAM", "Manual Reactor SCRAM", "On if the operator (you) initiated a SCRAM.") +doc("AutoReactorSCRAM", "Auto Reactor SCRAM", "On if the automatic control system initiated a SCRAM. The main view screen annunciator will have an indication as to why.") +doc("RadiationWarning", "Radiation Warning", "On if radiation levels are above normal. There is likely a leak somewhere, so that should be identified and fixed. Hazmat suit recommended.") +doc("RCPTrip", "RCP Trip", "Reactor coolant pump tripped. This is a technical concept not directly mapping to Mekansim. Here, it indicates if there is either high heated coolant or low cooled coolant that caused an RPS trip. Check the coolant system if this occurs.") +doc("RCSFlowLow", "RCS Flow Low", "Indicates if the reactor coolant system flow is low. This is observed when the cooled coolant level in the reactor is dropping. This can occur while a turbine spins up, but if it persists, check that the cooling system is operating properly. This can occur with smaller boilers or when using pipes and not having enough.") +doc("CoolantLevelLow", "Coolant Level Low", "On if the reactor coolant level is lower than it should be. Check the coolant system.") +doc("ReactorTempHigh", "Reactor Temp. High", "On if the reactor temperature is above expected maximum operating temperature. This is not yet damaging, but should be attended to. Check coolant system.") +doc("ReactorHighDeltaT", "Reactor High Delta T", "On if the reactor temperature is climbing rapidly. This can occur when a reactor is starting up, but it is a concern if it happens while the burn rate is not increasing.") +doc("FuelInputRateLow", "Fuel Input Rate Low", "On if the fissile fuel levels in the reactor are dropping or very low. Ensure a steady supply of fuel is entering the reactor.") doc("WasteLineOcclusion", "Waste Line Occlusion", "Waste levels in the reactor are increasing. Ensure your waste processing system is operating at a sufficient rate for your burn rate.") -doc("HighStartupRate", "Startup Rate High", "This is a rough calculation of if your burn rate is high enough to cause a loss of coolant. A burn rate above this is likely to cause that, but it could occur at even higher or even lower rates depending on your setup (such as pipes, water supplies, and boiler tanks).") +doc("HighStartupRate", "Startup Rate High", "This is a rough calculation of if your burn rate is high enough to cause a loss of coolant on startup. A burn rate above this is likely to cause that, but it could occur at even higher or even lower rates depending on your setup (such as pipes, water supplies, and boiler tanks).") target = docs.annunc.unit.rps_section doc("rps_tripped", "RPS Trip", "Indicates if the reactor protection system has caused a SCRAM.") doc("manual", "Manual Reactor SCRAM", "Indicates if the operator (you) tripped the RPS by pressing SCRAM.") doc("automatic", "Auto Reactor SCRAM", "Indicates if the automatic control system tripped the RPS.") -doc("sys_fail", "System Failure", "Indicates if the RPS tripped due to the reactor not being formed.") -doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage.") -doc("ex_waste", "Excess Waste", "Indicates if the RPS tripped due to very high waste levels.") -doc("ex_hcool", "Excess Heated Coolant", "Indicates if the RPS tripped due to very high heated coolant levels.") -doc("high_temp", "Temperature High", "Indicates if the RPS tripped due to reaching damaging temperatures.") -doc("low_cool", "Coolant Level Low Low", "Indicates if the RPS tripped due to very low coolant levels that result in the temperature uncontrollably rising.") -doc("no_fuel", "No Fuel", "Indicates if the RPS tripped due to no fuel being available.") -doc("fault", "PPM Fault", "Indicates if the RPS tripped due to a peripheral access fault.") -doc("timeout", "Connection Timeout", "Indicates if the RPS tripped due to losing connection with the supervisory computer.") -doc("sys_fail", "System Failure", "Indicates if the RPS tripped due to the reactor not being formed.") +doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage. Await damage levels to lower.") +doc("ex_waste", "Excess Waste", "Indicates if the RPS tripped due to very high waste levels. Ensure waste processing system is keeping up.") +doc("ex_hcool", "Excess Heated Coolant", "Indicates if the RPS tripped due to very high heated coolant levels. Check that the cooling system is able to keep up with heated coolant flow.") +doc("high_temp", "Temperature High", "Indicates if the RPS tripped due to reaching damaging temperatures. Await damage levels to lower.") +doc("low_cool", "Coolant Level Low Low", "Indicates if the RPS tripped due to very low coolant levels that result in the temperature uncontrollably rising. Ensure that the cooling system can provide sufficient cooled coolant flow.") +doc("no_fuel", "No Fuel", "Indicates if the RPS tripped due to no fuel being available. Check fuel input.") +doc("fault", "PPM Fault", "Indicates if the RPS tripped due to a peripheral access fault. Something went wrong interfacing with the reactor, try restarting the PLC.") +doc("timeout", "Connection Timeout", "Indicates if the RPS tripped due to losing connection with the supervisory computer. Check that your PLC and supervisor remain chunk loaded.") +doc("sys_fail", "System Failure", "Indicates if the RPS tripped due to the reactor not being formed. Ensure that the multi-block is formed.") target = docs.annunc.unit.rcs_section -doc("RCSFault", "RCS Hardware Fault", "Indicates if one or more of the RCS devices have a peripheral fault.") -doc("EmergencyCoolant", "Emergency Coolant", "Off if no emergency coolant redstone is configured, white when it is configured but not in use, and green/blue when it is activated.") -doc("CoolantFeedMismatch", "Coolant Feed Mismatch", "The coolant system is accumulating heated coolant or losing cooled coolant, likely due to one of the machines not keeping up with the needs of the reactor.") -doc("BoilRateMismatch", "Boil Rate Mismatch", "The total heating rate of the reactor is more than 4% off from the steam input rate of the turbines OR for sodium setups, the boiler boil rates are more than 4% off from the steam input rate of the turbines.") -doc("SteamFeedMismatch", "Steam Feed Mismatch", "There is an above tolerance difference between turbine flow and steam input rates or the reactor/boilers are gaining steam or losing water.") -doc("MaxWaterReturnFeed", "Max Water Return Feed", "The turbines are condensing the max rate of water that they can per the structure build.") -doc("WaterLevelLow", "Water Level Low", "The water level in the boiler is low.") -doc("HeatingRateLow", "Heating Rate Low", "The boiler is not hot enough to boil water, but it is receiving heated coolant.") -doc("SteamDumpOpen", "Steam Relief Valve Open", "This turns yellow if the turbine is set to dumping excess and red if it is set to dumping all. 'Relief Valve' in this case is that setting allowing the venting of steam.") +doc("RCSFault", "RCS Hardware Fault", "Indicates if one or more of the RCS devices have a peripheral fault. Check that your machines are formed. If this persists, try rebooting affected RTUs.") +doc("EmergencyCoolant", "Emergency Coolant", "Off if no emergency coolant redstone is configured, white when it is configured but not in use, and green/blue when it is activated. This is based on an RTU having a redstone emergency coolant output configured for this unit.") +doc("CoolantFeedMismatch", "Coolant Feed Mismatch", "The coolant system is accumulating heated coolant or losing cooled coolant, likely due to one of the machines not keeping up with the needs of the reactor. The flow monitor can help figure out where the problem is.") +doc("BoilRateMismatch", "Boil Rate Mismatch", "The total heating rate of the reactor exceed the tolerance from the steam input rate of the turbines OR for sodium setups, the boiler boil rates exceed the tolerance from the steam input rate of the turbines. The flow monitor can help figure out where the problem is.") +doc("SteamFeedMismatch", "Steam Feed Mismatch", "There is an above tolerance difference between turbine flow and steam input rates or the reactor/boilers are gaining steam or losing water. The flow monitor can help figure out where the problem is.") +doc("MaxWaterReturnFeed", "Max Water Return Feed", "The turbines are condensing the max rate of water that they can per the structure build. If water return is insufficient, add more saturating condensers to your turbine(s).") +doc("WaterLevelLow", "Water Level Low", "The water level in the boiler is low. A larger boiler water tank may help, or you can feed additional water into the boiler from elsewhere.") +doc("HeatingRateLow", "Heating Rate Low", "The boiler is not hot enough to boil water, but it is receiving heated coolant. This is almost never a safety concern.") +doc("SteamDumpOpen", "Steam Relief Valve Open", "This turns yellow if the turbine is set to dumping excess and red if it is set to dumping [all]. 'Relief Valve' in this case is that setting allowing the venting of steam. You should never have this set to dumping [all]. Emergency coolant activation from the supervisor will automatically set it to dumping excess to ensure there is no backup of steam as water is added.") doc("TurbineOverSpeed", "Turbine Over Speed", "The turbine is at steam capacity, but not tripped. You may need more turbines if they can't keep up.") -doc("GeneratorTrip", "Generator Trip", "The turbine is no longer outputting power due to it having nowhere to go. Likely due to full power storage.") -doc("TurbineTrip", "Turbine Trip", "The turbine has reached its maximum power charge and has stopped rotating, and as a result stopped cooling steam to water.") +doc("GeneratorTrip", "Generator Trip", "The turbine is no longer outputting power due to it having nowhere to go. Likely due to full power storage. This will lead to a Turbine Trip if not addressed.") +doc("TurbineTrip", "Turbine Trip", "The turbine has reached its maximum power charge and has stopped rotating, and as a result stopped cooling steam to water. Ensure the turbine has somewhere to output power, as this is the most common cause of reactor meltdowns. However, the likelihood of a meltdown with this system in place is much lower, especially with emergency coolant helping during turbine trips.") docs.glossary = { abbvs = {}, terms = {} } target = docs.glossary.abbvs -doc("G_ACK", "ACK", "Alarm ACKnowledge. This indicates you understand an alarm occured and would like to stop the audio tone(s).") +doc("G_ACK", "ACK", "Alarm ACKnowledge. Pressing this acknowledges that you understand an alarm occurred and would like to stop the audio tone(s).") doc("G_CRD", "CRD", "Coordinator. Abbreviation for the coordinator computer.") doc("G_DBG", "DBG", "Debug. Abbreviation for the debugging sessions from pocket computers found on the supervisor's front panel.") doc("G_FP", "FP", "Front Panel. See Terminology.") doc("G_PKT", "PKT", "Pocket. Abbreviation for the pocket computer.") -doc("G_PLC", "PLC", "Programmable Logic Controller. A device that not only reports data and controls outputs, but also can make decisions on its own.") +doc("G_PLC", "PLC", "Programmable Logic Controller. A device that not only reports data and controls outputs, but can also make decisions on its own.") doc("G_PPM", "PPM", "Protected Peripheral Manager. This is an abstraction layer created for this project that prevents peripheral calls from crashing applications.") -doc("G_RCP", "RCP", "Reactor Coolant Pump. This is from real-world terminology with water-cooled reactors, but in this system it just relates to the functioning of reactor coolant flow.") -doc("G_RCS", "RCS", "Reactor Cooling System. The combination of all machines used to cool the reactor.") +doc("G_RCP", "RCP", "Reactor Coolant Pump. This is from real-world terminology with water-cooled (boiling water and pressurized water) reactors, but in this system it just reflects to the functioning of reactor coolant flow. See the annunciator page on it for more information.") +doc("G_RCS", "RCS", "Reactor Cooling System. The combination of all machines used to cool the reactor (turbines, boilers, dynamic tanks).") doc("G_RPS", "RPS", "Reactor Protection System. A component of the reactor PLC responsible for keeping the reactor safe.") -doc("G_RTU", "RTU", "Remote Terminal Unit. Provides monitoring to and basic output from a SCADA system, interfacing with various types of devices/controls.") -doc("G_SCADA", "SCADA", "Supervisory Control and Data Acquisition. A control systems architecture used in many different process control applications.") +doc("G_RTU", "RTU", "Remote Terminal Unit. Provides monitoring to and basic output from a SCADA system, interfacing with various types of devices/interfaces.") +doc("G_SCADA", "SCADA", "Supervisory Control and Data Acquisition. A control systems architecture used in a wide variety process control applications.") doc("G_SVR", "SVR", "Supervisor. Abbreviation for the supervisory computer.") doc("G_UI", "UI", "User Interface.") @@ -101,12 +102,12 @@ target = docs.glossary.terms doc("G_Fault", "Fault", "Something has gone wrong and/or failed to function.") doc("G_FrontPanel", "Front Panel", "A basic interface on the front of a device for viewing and sometimes modifying its state. This is what you see when looking at a computer running one of the SCADA applications.") doc("G_Nominal", "Nominal", "Normal operation. Everything operating as intended.") -doc("G_Ringback", "Ringback", "An indication that an alarm had gone off so that you are aware, even if the alarm condition is no longer met.") +doc("G_Ringback", "Ringback", "An indication that an alarm had gone off but is no longer having its trip condition(s) met. This is to make you are aware that it occurred.") doc("G_SCRAM", "SCRAM", "[Emergency] shut-down of a reactor by stopping the fission. In Mekanism and here, it isn't always for an emergency.") -doc("G_Transient", "Transient", "A temporary change in state from normal operation. Coolant levels dropping or core temperature rising above nominal values would be examples of transients.") -doc("G_Trip", "Trip", "A checked condition has occurred, also known as 'tripped'.") -doc("G_Tripped", "Tripped", "An alarm condition has been met and is still met.") -doc("G_Tripping", "Tripping", "An alarm condition is met but has not met the minimum time before a condition is deemed a problem.") -doc("G_TurbineTrip", "Turbine Trip", "The turbine stopped, which prevents heated coolant from being properly cooled. In Mekanism, this would occur when a turbine cannot generate any more energy due to filling its buffer and having no output with any storage for energy left.") +doc("G_Transient", "Transient", "A temporary change in state from normal operation. Coolant levels dropping or core temperature rising above nominal values are examples of transients.") +doc("G_Trip", "Trip", "A checked condition had occurred, see 'Tripped'.") +doc("G_Tripped", "Tripped", "An alarm condition has been met, and is still met.") +doc("G_Tripping", "Tripping", "Alarm condition(s) is/are met, but has/have not reached the minimum time before the condition(s) is/are deemed a problem.") +doc("G_TurbineTrip", "Turbine Trip", "The turbine stopped, which prevents heated coolant from being cooled. In Mekanism, this would occur when a turbine cannot generate any more energy due to filling its buffer and having no output with any remaining energy capacity.") return docs From 1c719ad67b45286d6b324fc2777523adb5563662 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Fri, 14 Jun 2024 21:10:42 +0000 Subject: [PATCH 40/44] cleanup for pull request --- graphics/elements/textbox.lua | 1 - pocket/iocontrol.lua | 19 +++++-------------- pocket/threads.lua | 4 ++-- pocket/ui/apps/guide.lua | 4 ++-- pocket/ui/apps/unit.lua | 7 ++----- pocket/ui/pages/guide_section.lua | 4 ++-- pocket/ui/pages/unit_boiler.lua | 26 +++++++++++++------------- pocket/ui/pages/unit_reactor.lua | 4 ++-- pocket/ui/pages/unit_turbine.lua | 11 +++++------ 9 files changed, 33 insertions(+), 47 deletions(-) diff --git a/graphics/elements/textbox.lua b/graphics/elements/textbox.lua index 0761471..2a61860 100644 --- a/graphics/elements/textbox.lua +++ b/graphics/elements/textbox.lua @@ -29,7 +29,6 @@ local function textbox(args) if args.anchor == true then args.can_focus = true end - -- regex to identify entries without a height currently: ^.*TextBox\{((?!height=).)*$ -- provide a constraint condition to element creation to prevent an pointlessly tall text box ---@param frame graphics_frame local function constrain(frame) diff --git a/pocket/iocontrol.lua b/pocket/iocontrol.lua index 3d52e68..da9c0d7 100644 --- a/pocket/iocontrol.lua +++ b/pocket/iocontrol.lua @@ -489,7 +489,7 @@ function iocontrol.record_unit_data(data) local anc = unit.annunciator rcs_hazard = rcs_hazard or anc.RCPTrip rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or - anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch or anc.MaxWaterReturnFeed + anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch local rcs_status = 4 if rcs_hazard then @@ -530,7 +530,7 @@ function iocontrol.record_unit_data(data) -- update reactor/control status if unit.reactor_data.mek_status.status then reactor_status = 4 - reactor_state = 5 -- running + reactor_state = 5 -- running control_status = util.trinary(unit.annunciator.AutoControl, 4, 3) else if unit.reactor_data.no_reactor then @@ -706,18 +706,12 @@ function iocontrol.record_unit_data(data) end if tripped(unit.alarms[ALARM.ReactorDamage]) then - local items = { - white("REACTOR DAMAGED"), blue("CHECK RCS"), blue("AWAIT DMG REDUCED") - } - + local items = { white("REACTOR DAMAGED"), blue("CHECK RCS"), blue("AWAIT DMG REDUCED") } table.insert(ecam, { color = colors.red, text = "REACTOR DAMAGE", help = "ReactorDamage", items = items }) end if tripped(unit.alarms[ALARM.ReactorOverTemp]) then - local items = { - white("DAMAGING TEMP"), blue("CHECK RCS"), blue("AWAIT COOLDOWN") - } - + local items = { white("DAMAGING TEMP"), blue("CHECK RCS"), blue("AWAIT COOLDOWN") } table.insert(ecam, { color = colors.red, text = "REACTOR OVER TEMP", help = "ReactorOverTemp", items = items }) end @@ -728,7 +722,6 @@ function iocontrol.record_unit_data(data) if tripped(unit.alarms[ALARM.ReactorWasteLeak]) then local items = { white("AT WASTE CAPACITY"), blue("CHECK WASTE OUTPUT"), blue("KEEP RCT DISABLED") } - table.insert(ecam, { color = colors.red, text = "REACTOR WASTE LEAK", help = "ReactorWasteLeak", items = items}) end @@ -741,9 +734,7 @@ function iocontrol.record_unit_data(data) local items = {} local stat = unit.reactor_data.rps_status - -- for k, _ in pairs(stat) do - -- stat[k] = true - -- end + -- for k, _ in pairs(stat) do stat[k] = true end local function insert(cond, key, text, color) if cond[key] then table.insert(items, { text = text, help = key, color = color }) end end diff --git a/pocket/threads.lua b/pocket/threads.lua index 5e05940..8cdb7ff 100644 --- a/pocket/threads.lua +++ b/pocket/threads.lua @@ -80,7 +80,7 @@ function threads.thread__main(smem) local packet = pocket_comms.parse_packet(param1, param2, param3, param4, param5) pocket_comms.handle_packet(packet) elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or - event == "double_click" then + event == "double_click" then -- handle a mouse event renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) elseif event == "char" or event == "key" or event == "key_up" then @@ -143,8 +143,8 @@ function threads.thread__render(smem) -- load in from shared memory local pkt_state = smem.pkt_state - local render_queue = smem.q.mq_render local nav = smem.pkt_sys.nav + local render_queue = smem.q.mq_render local last_update = util.time() diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index 2fea06a..bda49a7 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -84,7 +84,7 @@ local function new_view(root) local use = Div{parent=page_div,x=2,width=p_width} local uis = Div{parent=page_div,x=2,width=p_width} local fps = Div{parent=page_div,x=2,width=p_width} - local gls = Div{parent=page_div,x=2} + local gls = Div{parent=page_div,x=2,width=p_width} local panes = { home, search, use, uis, fps, gls } local doc_map = {} @@ -118,7 +118,7 @@ local function new_view(root) search_results.remove_all() if string.len(query) < 3 then - TextBox{parent=search_results,text=util.trinary(string.len(query)==0,"Click 'GO' to search...","Search requires at least 3 characters.")} + TextBox{parent=search_results,text="Search requires at least 3 characters."} return end diff --git a/pocket/ui/apps/unit.lua b/pocket/ui/apps/unit.lua index 8a11d88..1481a2b 100644 --- a/pocket/ui/apps/unit.lua +++ b/pocket/ui/apps/unit.lua @@ -3,7 +3,6 @@ -- local util = require("scada-common.util") --- local log = require("scada-common.log") local iocontrol = require("pocket.iocontrol") local pocket = require("pocket.pocket") @@ -23,12 +22,10 @@ local TextBox = require("graphics.elements.textbox") local WaitingAnim = require("graphics.elements.animations.waiting") +local PushButton = require("graphics.elements.controls.push_button") + local DataIndicator = require("graphics.elements.indicators.data") local IconIndicator = require("graphics.elements.indicators.icon") --- local RadIndicator = require("graphics.elements.indicators.rad") --- local VerticalBar = require("graphics.elements.indicators.vbar") - -local PushButton = require("graphics.elements.controls.push_button") local ALIGN = core.ALIGN local cpair = core.cpair diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index 63e2333..77941a8 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -27,7 +27,7 @@ return function (data, base_page, title, items, scroll_height) TextBox{parent=section_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} PushButton{parent=section_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=base_page.nav_to} - local gls_term_view_page = app.new_page(section_page, #panes + 1) + local view_page = app.new_page(section_page, #panes + 1) local section_view_div = Div{parent=page_div,x=2} table.insert(panes, section_view_div) TextBox{parent=section_view_div,y=1,text=title,height=1,alignment=ALIGN.CENTER} @@ -47,7 +47,7 @@ return function (data, base_page, title, items, scroll_height) local function view() _end.focus() - gls_term_view_page.nav_to() + view_page.nav_to() anchor.focus() end diff --git a/pocket/ui/pages/unit_boiler.lua b/pocket/ui/pages/unit_boiler.lua index e15ae40..86b963a 100644 --- a/pocket/ui/pages/unit_boiler.lua +++ b/pocket/ui/pages/unit_boiler.lua @@ -1,21 +1,21 @@ -local types = require("scada-common.types") -local util = require("scada-common.util") +local types = require("scada-common.types") +local util = require("scada-common.util") -local iocontrol = require("pocket.iocontrol") +local iocontrol = require("pocket.iocontrol") -local style = require("pocket.ui.style") +local style = require("pocket.ui.style") -local core = require("graphics.core") +local core = require("graphics.core") -local Div = require("graphics.elements.div") -local TextBox = require("graphics.elements.textbox") +local Div = require("graphics.elements.div") +local TextBox = require("graphics.elements.textbox") -local DataIndicator = require("graphics.elements.indicators.data") +local PushButton = require("graphics.elements.controls.push_button") + +local DataIndicator = require("graphics.elements.indicators.data") local StateIndicator = require("graphics.elements.indicators.state") -local IconIndicator = require("graphics.elements.indicators.icon") -local VerticalBar = require("graphics.elements.indicators.vbar") - -local PushButton = require("graphics.elements.controls.push_button") +local IconIndicator = require("graphics.elements.indicators.icon") +local VerticalBar = require("graphics.elements.indicators.vbar") local ALIGN = core.ALIGN local cpair = core.cpair @@ -69,7 +69,7 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update) temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end) local b_wll = IconIndicator{parent=blr_div,y=10,label="Water Level Lo",states=red_ind_s} - local b_hr = IconIndicator{parent=blr_div,label="Heating Rate Lo",states=yel_ind_s} + local b_hr = IconIndicator{parent=blr_div,label="Heating Rate Lo",states=yel_ind_s} b_wll.register(ps, "WaterLevelLow", b_wll.update) b_hr.register(ps, "HeatingRateLow", b_hr.update) diff --git a/pocket/ui/pages/unit_reactor.lua b/pocket/ui/pages/unit_reactor.lua index 7446d78..b7fb23f 100644 --- a/pocket/ui/pages/unit_reactor.lua +++ b/pocket/ui/pages/unit_reactor.lua @@ -10,13 +10,13 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") +local PushButton = require("graphics.elements.controls.push_button") + local DataIndicator = require("graphics.elements.indicators.data") local StateIndicator = require("graphics.elements.indicators.state") local IconIndicator = require("graphics.elements.indicators.icon") local VerticalBar = require("graphics.elements.indicators.vbar") -local PushButton = require("graphics.elements.controls.push_button") - local ALIGN = core.ALIGN local cpair = core.cpair diff --git a/pocket/ui/pages/unit_turbine.lua b/pocket/ui/pages/unit_turbine.lua index 5a7763b..03cd865 100644 --- a/pocket/ui/pages/unit_turbine.lua +++ b/pocket/ui/pages/unit_turbine.lua @@ -9,14 +9,14 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local TextBox = require("graphics.elements.textbox") +local PushButton = require("graphics.elements.controls.push_button") + local DataIndicator = require("graphics.elements.indicators.data") local IconIndicator = require("graphics.elements.indicators.icon") local PowerIndicator = require("graphics.elements.indicators.power") local StateIndicator = require("graphics.elements.indicators.state") local VerticalBar = require("graphics.elements.indicators.vbar") -local PushButton = require("graphics.elements.controls.push_button") - local ALIGN = core.ALIGN local cpair = core.cpair @@ -70,16 +70,15 @@ return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, update) input_rate.register(ps, "steam_input_rate", input_rate.update) local t_sdo = IconIndicator{parent=tbn_div,y=10,label="Steam Dumping",states=tri_ind_s} - local t_tos = IconIndicator{parent=tbn_div,label="Over Speed",states=red_ind_s} - local t_gtrp = IconIndicator{parent=tbn_div,label="Generator Trip",states=yel_ind_s} - local t_trp = IconIndicator{parent=tbn_div,label="Turbine Trip",states=red_ind_s} + local t_tos = IconIndicator{parent=tbn_div,label="Over Speed",states=red_ind_s} + local t_gtrp = IconIndicator{parent=tbn_div,label="Generator Trip",states=yel_ind_s} + local t_trp = IconIndicator{parent=tbn_div,label="Turbine Trip",states=red_ind_s} t_sdo.register(ps, "SteamDumpOpen", t_sdo.update) t_tos.register(ps, "TurbineOverSpeed", t_tos.update) t_gtrp.register(ps, "GeneratorTrip", t_gtrp.update) t_trp.register(ps, "TurbineTrip", t_trp.update) - local tbn_ext_div = Div{parent=tbn_pane,x=2,width=tbn_pane.get_width()-2} table.insert(panes, tbn_ext_div) From ea8f62dea63d6e2779674e1240ecb4c41889dd56 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 14 Jun 2024 17:38:45 -0400 Subject: [PATCH 41/44] #497 exit app if it is unloaded --- pocket/pocket.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pocket/pocket.lua b/pocket/pocket.lua index f32ef1a..0afc068 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -275,17 +275,23 @@ function pocket.init_nav(render_queue) -- unload api-dependent apps function nav.unload_api() - for _, app in pairs(self.apps) do + for id, app in pairs(self.apps) do local _, api = app.check_requires() - if app.loaded and api then app.unload() end + if app.loaded and api then + if id == self.cur_app then nav.open_app(APP_ID.ROOT) end + app.unload() + end end end -- unload supervisor-dependent apps function nav.unload_sv() - for _, app in pairs(self.apps) do + for id, app in pairs(self.apps) do local sv, _ = app.check_requires() - if app.loaded and sv then app.unload() end + if app.loaded and sv then + if id == self.cur_app then nav.open_app(APP_ID.ROOT) end + app.unload() + end end end From 219f02b1882cf9b46f9c980bb32161e4983fc8d0 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 14 Jun 2024 17:42:03 -0400 Subject: [PATCH 42/44] print render crash cause to user --- pocket/startup.lua | 5 +++++ pocket/threads.lua | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pocket/startup.lua b/pocket/startup.lua index 03eefec..f597779 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -90,6 +90,7 @@ local function main() ---@class pkt_state pkt_state = { ui_ok = false, + ui_error = nil, shutdown = false }, @@ -179,6 +180,10 @@ local function main() parallel.waitForAll(main_thread.p_exec, render_thread.p_exec) renderer.close_ui() + + if not pkt_state.ui_ok then + println(util.c("App crashed with error: ", pkt_state.ui_error)) + end else println_ts("UI creation failed") end diff --git a/pocket/threads.lua b/pocket/threads.lua index 8cdb7ff..32120b2 100644 --- a/pocket/threads.lua +++ b/pocket/threads.lua @@ -148,8 +148,6 @@ function threads.thread__render(smem) local last_update = util.time() - local ui_message - -- thread loop while true do -- check for messages in the message queue @@ -171,9 +169,9 @@ function threads.thread__render(smem) local draw_start = util.time_ms() - pkt_state.ui_ok, ui_message = pcall(function () nav.load_app(cmd.val) end) + pkt_state.ui_ok, pkt_state.ui_error = pcall(function () nav.load_app(cmd.val) end) if not pkt_state.ui_ok then - log.fatal(util.c("RENDER: app load failed with error ", ui_message)) + log.fatal(util.c("RENDER: app load failed with error ", pkt_state.ui_error)) else log.debug("RENDER: app loaded in " .. (util.time_ms() - draw_start) .. "ms") end From 9fe0669fda44a5813de97b58207f41249b522da8 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 14 Jun 2024 17:49:43 -0400 Subject: [PATCH 43/44] updated guide section heights and added a debug message to track height usage --- pocket/startup.lua | 2 +- pocket/ui/apps/guide.lua | 4 ++-- pocket/ui/pages/guide_section.lua | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pocket/startup.lua b/pocket/startup.lua index f597779..b56da4f 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -182,7 +182,7 @@ local function main() renderer.close_ui() if not pkt_state.ui_ok then - println(util.c("App crashed with error: ", pkt_state.ui_error)) + println(util.c("UI crashed with error: ", pkt_state.ui_error)) end else println_ts("UI creation failed") diff --git a/pocket/ui/apps/guide.lua b/pocket/ui/apps/guide.lua index bda49a7..b174805 100644 --- a/pocket/ui/apps/guide.lua +++ b/pocket/ui/apps/guide.lua @@ -185,9 +185,9 @@ local function new_view(root) TextBox{parent=annunc_div,y=1,text="Annunciators",height=1,alignment=ALIGN.CENTER} PushButton{parent=annunc_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to} - local unit_gen_page = guide_section(sect_construct_data, annunc_page, "Unit General", docs.annunc.unit.main_section, 200) + local unit_gen_page = guide_section(sect_construct_data, annunc_page, "Unit General", docs.annunc.unit.main_section, 170) local unit_rps_page = guide_section(sect_construct_data, annunc_page, "Unit RPS", docs.annunc.unit.rps_section, 100) - local unit_rcs_page = guide_section(sect_construct_data, annunc_page, "Unit RCS", docs.annunc.unit.rcs_section, 100) + local unit_rcs_page = guide_section(sect_construct_data, annunc_page, "Unit RCS", docs.annunc.unit.rcs_section, 170) PushButton{parent=annunc_div,y=3,text="Unit General >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_gen_page.nav_to} PushButton{parent=annunc_div,text="Unit RPS >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_rps_page.nav_to} diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index 77941a8..fed6e6e 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -1,3 +1,4 @@ +local log = require("scada-common.log") local util = require("scada-common.util") local core = require("graphics.core") @@ -57,8 +58,11 @@ return function (data, base_page, title, items, scroll_height) PushButton{parent=name_list,text=item.name,alignment=ALIGN.LEFT,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} if i % 12 == 0 then util.nop() end + end + log.debug("guide section " .. title .. " generated with final height ".. _end.get_y()) + util.nop() return section_page From 4a39ed9d38984dfb16236e3a357cd090836515f5 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Fri, 14 Jun 2024 17:50:16 -0400 Subject: [PATCH 44/44] removed stray newline --- pocket/ui/pages/guide_section.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/pocket/ui/pages/guide_section.lua b/pocket/ui/pages/guide_section.lua index fed6e6e..e73567b 100644 --- a/pocket/ui/pages/guide_section.lua +++ b/pocket/ui/pages/guide_section.lua @@ -58,7 +58,6 @@ return function (data, base_page, title, items, scroll_height) PushButton{parent=name_list,text=item.name,alignment=ALIGN.LEFT,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view} if i % 12 == 0 then util.nop() end - end log.debug("guide section " .. title .. " generated with final height ".. _end.get_y())