#398 #355 pocket process control UI

This commit is contained in:
Mikayla Fischler
2024-10-12 15:30:14 -04:00
parent 41843a2478
commit 38a1a4282c
6 changed files with 223 additions and 21 deletions

View File

@ -264,10 +264,8 @@ local function new_view(root, x, y)
local limits = {}
for i = 1, #rate_limits do limits[i] = rate_limits[i].get_value() end
process.save(mode.get_value(), b_target.get_value(),
db.energy_convert_to_fe(c_target.get_value()),
db.energy_convert_to_fe(g_target.get_value()),
limits)
process.save(mode.get_value(), b_target.get_value(), db.energy_convert_to_fe(c_target.get_value()),
db.energy_convert_to_fe(g_target.get_value()), limits)
end
-- start automatic control after saving process control settings

View File

@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
local core = {}
core.version = "2.4.3"
core.version = "2.4.4"
core.flasher = flasher
core.events = events

View File

@ -11,6 +11,7 @@ local KEY_CLICK = core.events.KEY_CLICK
---@field options table button options
---@field radio_colors cpair radio button colors (inner & outer)
---@field select_color color color for radio button border when selected
---@field dis_fg_bg? cpair foreground/background colors when disabled
---@field default? integer default state, defaults to options[1]
---@field min_width? integer text length + 2 if omitted
---@field callback? function function to call on touch
@ -64,6 +65,10 @@ return function (args)
local inner_color = util.trinary(e.value == i, args.radio_colors.color_b, args.radio_colors.color_a)
local outer_color = util.trinary(e.value == i, args.select_color, args.radio_colors.color_b)
if e.value == i and args.dis_fg_bg and not e.enabled then
outer_color = args.radio_colors.color_a
end
e.w_set_cur(1, i)
e.w_set_fgd(inner_color)
@ -75,9 +80,14 @@ return function (args)
e.w_write("\x95")
-- write button text
if i == focused_opt and e.is_focused() and e.enabled then
e.w_set_fgd(e.fg_bg.bkg)
e.w_set_bkg(e.fg_bg.fgd)
if args.dis_fg_bg and not e.enabled then
e.w_set_fgd(args.dis_fg_bg.fgd)
e.w_set_bkg(args.dis_fg_bg.bkg)
elseif i == focused_opt and e.is_focused() then
if e.enabled then
e.w_set_fgd(e.fg_bg.bkg)
e.w_set_bkg(e.fg_bg.fgd)
end
else
e.w_set_fgd(e.fg_bg.fgd)
e.w_set_bkg(e.fg_bg.bkg)

View File

@ -15,12 +15,13 @@ local core = require("graphics.core")
local Div = require("graphics.elements.Div")
local MultiPane = require("graphics.elements.MultiPane")
local Rectangle = require("graphics.elements.Rectangle")
local TextBox = require("graphics.elements.TextBox")
local WaitingAnim = require("graphics.elements.animations.Waiting")
local HazardButton = require("graphics.elements.controls.HazardButton")
local PushButton = require("graphics.elements.controls.PushButton")
local RadioButton = require("graphics.elements.controls.RadioButton")
local NumberField = require("graphics.elements.form.NumberField")
@ -31,12 +32,16 @@ local AUTO_GROUP = types.AUTO_GROUP
local ALIGN = core.ALIGN
local cpair = core.cpair
local border = core.border
local APP_ID = pocket.APP_ID
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 grn_ind_s = style.icon_states.grn_ind_s
local wht_ind_s = style.icon_states.wht_ind_s
local hzd_fg_bg = cpair(colors.white, colors.gray)
local dis_colors = cpair(colors.white, colors.lightGray)
@ -67,15 +72,17 @@ local function new_view(root)
-- load the app (create the elements)
local function load()
local f_ps = db.facility.ps
page_div = Div{parent=main,y=2,width=main.get_width()}
local panes = {} ---@type Div[]
-- create all page divs
-- for _ = 1, db.facility.num_units + 1 do
-- local div = Div{parent=page_div}
-- table.insert(panes, div)
-- end
for _ = 1, db.facility.num_units + 3 do
local div = Div{parent=page_div}
table.insert(panes, div)
end
local last_update = 0
-- refresh data callback, every 500ms it will re-send the query
@ -86,6 +93,10 @@ local function new_view(root)
end
end
--#region unit settings/status
local rate_limits = {}
for i = 1, db.facility.num_units do
local u_pane = panes[i]
local u_div = Div{parent=u_pane,x=2,width=main.get_width()-2}
@ -97,17 +108,186 @@ local function new_view(root)
TextBox{parent=u_div,y=1,text="Reactor Unit #"..i,alignment=ALIGN.CENTER}
u_div.line_break()
TextBox{parent=u_div,y=3,text="Auto Rate Limit",fg_bg=cpair(colors.lightGray,colors.black)}
rate_limits[i] = NumberField{parent=u_div,x=1,y=4,width=16,default=0.01,min=0.01,max_frac_digits=2,max_chars=8,allow_decimal=true,align_right=true,fg_bg=cpair(colors.white,colors.gray),dis_fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=u_div,x=18,y=4,text="mB/t",width=4,fg_bg=cpair(colors.lightGray,colors.black)}
rate_limits[i].register(unit.unit_ps, "max_burn", rate_limits[i].set_max)
rate_limits[i].register(unit.unit_ps, "burn_limit", rate_limits[i].set_value)
local ready = IconIndicator{parent=u_div,y=6,label="Auto Ready",states=grn_ind_s}
local a_stb = IconIndicator{parent=u_div,label="Auto Standby",states=wht_ind_s}
local degraded = IconIndicator{parent=u_div,label="Unit Degraded",states=red_ind_s}
ready.register(u_ps, "U_AutoReady", ready.update)
degraded.register(u_ps, "U_AutoDegraded", degraded.update)
-- update standby indicator
a_stb.register(u_ps, "status", function (active)
a_stb.update(unit.annunciator.AutoControl and (not active))
end)
a_stb.register(u_ps, "AutoControl", function (auto_active)
if auto_active then
a_stb.update(unit.reactor_data.mek_status.status == false)
else a_stb.update(false) end
end)
local function _set_group(value) process.set_group(i, value) end
local group = RadioButton{parent=u_div,y=10,options=types.AUTO_GROUP_NAMES,callback=_set_group,radio_colors=cpair(colors.lightGray,colors.gray),select_color=colors.purple,dis_fg_bg=cpair(colors.gray,colors.black)}
-- can't change group if auto is engaged regardless of if this unit is part of auto control
group.register(f_ps, "auto_active", function (auto_active)
if auto_active then group.disable() else group.enable() end
end)
group.register(u_ps, "auto_group_id", function (gid) group.set_value(gid + 1) end)
TextBox{parent=u_div,y=16,text="Assigned Group",fg_bg=style.label}
local auto_grp = TextBox{parent=u_div,text="Manual",width=11,fg_bg=cpair(colors.white,colors.gray)}
auto_grp.register(u_ps, "auto_group", auto_grp.set_value)
util.nop()
end
-- main process page
--#endregion
-- local f_pane = panes[db.facility.num_units + 1]
-- local f_div = Div{parent=f_pane,x=2,width=main.get_width()-2}
--#region process control options page
-- app.new_page(nil, db.facility.num_units + 1)
local o_pane = panes[db.facility.num_units + 2]
local o_div = Div{parent=o_pane,x=2,width=main.get_width()-2}
local opt_page = app.new_page(nil, db.facility.num_units + 2)
TextBox{parent=o_div,y=1,text="Process Options",alignment=ALIGN.CENTER}
local ctl_opts = { "Monitored Max Burn", "Combined Burn Rate", "Charge Level", "Generation Rate" }
local mode = RadioButton{parent=o_div,x=1,y=3,options=ctl_opts,callback=function()end,radio_colors=cpair(colors.lightGray,colors.gray),select_color=colors.purple}
mode.register(f_ps, "process_mode", mode.set_value)
TextBox{parent=o_div,y=9,text="Burn Rate Target",fg_bg=cpair(colors.lightGray,colors.black)}
local b_target = NumberField{parent=o_div,x=1,y=10,width=15,default=0.01,min=0.01,max_frac_digits=2,max_chars=8,allow_decimal=true,align_right=true,fg_bg=cpair(colors.white,colors.gray),dis_fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=o_div,x=17,y=10,text="mB/t",fg_bg=cpair(colors.lightGray,colors.black)}
TextBox{parent=o_div,y=12,text="Charge Level Target",fg_bg=cpair(colors.lightGray,colors.black)}
local c_target = NumberField{parent=o_div,x=1,y=13,width=15,default=0,min=0,max_chars=16,align_right=true,fg_bg=cpair(colors.white,colors.gray),dis_fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=o_div,x=17,y=13,text="M"..db.energy_label,fg_bg=cpair(colors.lightGray,colors.black)}
TextBox{parent=o_div,y=15,text="Generation Target",fg_bg=cpair(colors.lightGray,colors.black)}
local g_target = NumberField{parent=o_div,x=1,y=16,width=15,default=0,min=0,max_chars=16,align_right=true,fg_bg=cpair(colors.white,colors.gray),dis_fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=o_div,x=17,y=16,text="k"..db.energy_label.."/t",fg_bg=cpair(colors.lightGray,colors.black)}
b_target.register(f_ps, "process_burn_target", b_target.set_value)
c_target.register(f_ps, "process_charge_target", c_target.set_value)
g_target.register(f_ps, "process_gen_target", g_target.set_value)
--#endregion
--#region process control page
local c_pane = panes[db.facility.num_units + 1]
local c_div = Div{parent=c_pane,x=2,width=main.get_width()-2}
local proc_ctrl = app.new_page(nil, db.facility.num_units + 1)
TextBox{parent=c_div,y=1,text="Process Control",alignment=ALIGN.CENTER}
local u_stat = Rectangle{parent=c_div,border=border(1,colors.gray,true),thin=true,width=21,height=5,x=1,y=3,fg_bg=cpair(colors.black,colors.lightGray)}
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",alignment=ALIGN.CENTER}
local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",height=2,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.lightGray)}
stat_line_1.register(f_ps, "status_line_1", stat_line_1.set_value)
stat_line_2.register(f_ps, "status_line_2", stat_line_2.set_value)
local function _start_auto()
local limits = {}
for i = 1, #rate_limits do limits[i] = rate_limits[i].get_value() end
process.process_start(mode.get_value(), b_target.get_value(), db.energy_convert_to_fe(c_target.get_value()),
db.energy_convert_to_fe(g_target.get_value()), limits)
end
local start = HazardButton{parent=c_div,x=2,y=9,text="START",accent=colors.lightBlue,dis_colors=dis_colors,callback=_start_auto,timeout=3,fg_bg=hzd_fg_bg}
local stop = HazardButton{parent=c_div,x=13,y=9,text="STOP",accent=colors.red,dis_colors=dis_colors,callback=process.process_stop,timeout=3,fg_bg=hzd_fg_bg}
db.facility.start_ack = start.on_response
db.facility.stop_ack = stop.on_response
start.register(f_ps, "auto_ready", function (ready)
if ready and (not db.facility.auto_active) then start.enable() else start.disable() end
end)
local auto_ready = IconIndicator{parent=c_div,y=14,label="Units Ready",states=grn_ind_s}
local auto_act = IconIndicator{parent=c_div,label="Process Active",states=grn_ind_s}
local auto_ramp = IconIndicator{parent=c_div,label="Process Ramping",states=wht_ind_s}
local auto_sat = IconIndicator{parent=c_div,label="Min/Max Burn Rate",states=yel_ind_s}
auto_ready.register(f_ps, "auto_ready", auto_ready.update)
auto_act.register(f_ps, "auto_active", auto_act.update)
auto_ramp.register(f_ps, "auto_ramping", auto_ramp.update)
auto_sat.register(f_ps, "auto_saturated", auto_sat.update)
-- REGISTER_NOTE: for optimization/brevity, due to not deleting anything but the whole element tree when it comes
-- to the process control display and coordinator GUI as a whole, child elements will not directly be registered here
-- (preventing garbage collection until the parent 'proc' is deleted)
page_div.register(f_ps, "auto_active", function (active)
if active then
b_target.disable()
c_target.disable()
g_target.disable()
mode.disable()
start.disable()
for i = 1, #rate_limits do rate_limits[i].disable() end
else
b_target.enable()
c_target.enable()
g_target.enable()
mode.enable()
if db.facility.auto_ready then start.enable() end
for i = 1, #rate_limits do rate_limits[i].enable() end
end
end)
--#endregion
--#region auto-SCRAM annunciator page
local a_pane = panes[db.facility.num_units + 3]
local a_div = Div{parent=a_pane,x=2,width=main.get_width()-2}
local annunc_page = app.new_page(nil, db.facility.num_units + 3)
TextBox{parent=a_div,y=1,text="Automatic SCRAM",alignment=ALIGN.CENTER}
local auto_scram = IconIndicator{parent=a_div,y=3,label="Automatic SCRAM",states=red_ind_s}
TextBox{parent=a_div,y=5,text="Induction Matrix",fg_bg=cpair(colors.lightGray,colors.black)}
local matrix_dc = IconIndicator{parent=a_div,label="Disconnected",states=yel_ind_s}
local matrix_fill = IconIndicator{parent=a_div,label="Charge High",states=red_ind_s}
TextBox{parent=a_div,y=9,text="Assigned Units",fg_bg=cpair(colors.lightGray,colors.black)}
local unit_crit = IconIndicator{parent=a_div,label="Critical Alarm",states=red_ind_s}
TextBox{parent=a_div,y=12,text="Facility",fg_bg=cpair(colors.lightGray,colors.black)}
local fac_rad_h = IconIndicator{parent=a_div,label="Radiation High",states=red_ind_s}
TextBox{parent=a_div,y=15,text="Generation Rate Mode",fg_bg=cpair(colors.lightGray,colors.black)}
local gen_fault = IconIndicator{parent=a_div,label="Control Fault",states=yel_ind_s}
auto_scram.register(f_ps, "auto_scram", auto_scram.update)
matrix_dc.register(f_ps, "as_matrix_dc", matrix_dc.update)
matrix_fill.register(f_ps, "as_matrix_fill", matrix_fill.update)
unit_crit.register(f_ps, "as_crit_alarm", unit_crit.update)
fac_rad_h.register(f_ps, "as_radiation", fac_rad_h.update)
gen_fault.register(f_ps, "as_gen_fault", gen_fault.update)
--#endregion
-- setup multipane
local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
@ -117,8 +297,9 @@ local function new_view(root)
local list = {
{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = db.nav.go_home },
{ label = " \x17 ", color = core.cpair(colors.black, colors.orange), callback = function () app.switcher(db.facility.num_units + 1) end },
{ label = "SET", color = core.cpair(colors.black, colors.orange), callback = function () app.switcher(db.facility.num_units + 1) end }
{ label = " \x17 ", color = core.cpair(colors.black, colors.purple), callback = proc_ctrl.nav_to },
{ label = " \x13 ", color = core.cpair(colors.black, colors.red), callback = annunc_page.nav_to },
{ label = "OPT", color = core.cpair(colors.black, colors.yellow), callback = opt_page.nav_to }
}
for i = 1, db.facility.num_units do
@ -128,6 +309,7 @@ local function new_view(root)
app.set_sidebar(list)
-- done, show the app
proc_ctrl.nav_to()
load_pane.set_value(2)
end

View File

@ -12,6 +12,7 @@ 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 loader_app = require("pocket.ui.apps.loader")
local process_app = require("pocket.ui.apps.process")
local sys_apps = require("pocket.ui.apps.sys_apps")
local unit_app = require("pocket.ui.apps.unit")
@ -64,6 +65,7 @@ local function init(main)
home_page(page_div)
unit_app(page_div)
control_app(page_div)
process_app(page_div)
guide_app(page_div)
loader_app(page_div)
sys_apps(page_div)

View File

@ -73,6 +73,16 @@ states.yel_ind_s = {
{ color = cpair(colors.black, colors.yellow), symbol = "-" }
}
states.grn_ind_s = {
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
{ color = cpair(colors.black, colors.green), symbol = "+" }
}
states.wht_ind_s = {
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
{ color = cpair(colors.black, colors.white), symbol = "+" }
}
style.icon_states = states
-- MAIN LAYOUT --