#200 functioning pocket unit overview

This commit is contained in:
Mikayla Fischler 2024-05-11 19:19:52 -04:00
parent c181142f75
commit 6a8ed311f3
6 changed files with 242 additions and 58 deletions

View File

@ -228,6 +228,8 @@ function iocontrol.init(conf, comms, temp_scale)
---@class ioctl_unit
local entry = {
unit_id = i,
connected = false,
rtu_hw = { boilers = {}, turbines = {} },
num_boilers = 0,
num_turbines = 0,
@ -319,12 +321,14 @@ function iocontrol.init(conf, comms, temp_scale)
for _ = 1, conf.cooling.r_cool[i].BoilerCount do
table.insert(entry.boiler_ps_tbl, psil.create())
table.insert(entry.boiler_data_tbl, {})
table.insert(entry.rtu_hw.boilers, { connected = false, faulted = false })
end
-- create turbine tables
for _ = 1, conf.cooling.r_cool[i].TurbineCount do
table.insert(entry.turbine_ps_tbl, psil.create())
table.insert(entry.turbine_data_tbl, {})
table.insert(entry.rtu_hw.turbines, { connected = false, faulted = false })
end
-- create tank tables
@ -897,6 +901,7 @@ function iocontrol.update_unit_statuses(statuses)
end
if #reactor_status == 0 then
unit.connected = false
unit.unit_ps.publish("computed_status", 1) -- disconnected
elseif #reactor_status == 3 then
local mek_status = reactor_status[1]
@ -956,6 +961,8 @@ function iocontrol.update_unit_statuses(statuses)
unit.unit_ps.publish(key, val)
end
end
unit.connected = true
else
log.debug(log_header .. "reactor status length mismatch")
valid = false
@ -970,7 +977,10 @@ function iocontrol.update_unit_statuses(statuses)
local boil_sum = 0
for id = 1, #unit.boiler_ps_tbl do
if rtu_statuses.boilers[id] == nil then
local connected = rtu_statuses.boilers[id] ~= nil
unit.rtu_hw.boilers[id].connected = connected
if not connected then
-- disconnected
unit.boiler_ps_tbl[id].publish("computed_status", 1)
end
@ -982,6 +992,7 @@ function iocontrol.update_unit_statuses(statuses)
local ps = unit.boiler_ps_tbl[id] ---@type psil
local rtu_faulted = _record_multiblock_status(boiler, data, ps)
unit.rtu_hw.boilers[id].faulted = rtu_faulted
if rtu_faulted then
ps.publish("computed_status", 3) -- faulted
@ -1013,7 +1024,10 @@ function iocontrol.update_unit_statuses(statuses)
local flow_sum = 0
for id = 1, #unit.turbine_ps_tbl do
if rtu_statuses.turbines[id] == nil then
local connected = rtu_statuses.turbines[id] ~= nil
unit.rtu_hw.turbines[id].connected = connected
if not connected then
-- disconnected
unit.turbine_ps_tbl[id].publish("computed_status", 1)
end
@ -1025,6 +1039,7 @@ function iocontrol.update_unit_statuses(statuses)
local ps = unit.turbine_ps_tbl[id] ---@type psil
local rtu_faulted = _record_multiblock_status(turbine, data, ps)
unit.rtu_hw.turbines[id].faulted = rtu_faulted
if rtu_faulted then
ps.publish("computed_status", 3) -- faulted

View File

@ -138,21 +138,26 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
}
_send(CRDN_TYPE.API_GET_FAC, data)
elseif pkt.type == CRDN_TYPE.API_GET_UNITS then
local data = {}
elseif pkt.type == CRDN_TYPE.API_GET_UNIT then
if pkt.length == 1 and type(pkt.data[1]) == "number" then
local u = db.units[pkt.data[1]] ---@type ioctl_unit
for i = 1, #db.units do
local u = db.units[i] ---@type ioctl_unit
table.insert(data, {
u.unit_id,
u.num_boilers,
u.num_turbines,
u.num_snas,
u.has_tank
})
if u then
local data = {
u.unit_id,
u.connected,
u.rtu_hw,
u.alarms,
u.annunciator,
u.reactor_data,
u.boiler_data_tbl,
u.turbine_data_tbl,
u.tank_data_tbl
}
_send(CRDN_TYPE.API_GET_UNIT, data)
end
end
_send(CRDN_TYPE.API_GET_UNITS, data)
else
log.debug(log_header .. "handler received unsupported CRDN packet type " .. pkt.type)
end

View File

@ -87,7 +87,6 @@ function iocontrol.alloc_nav()
local app = {
loaded = false,
load = nil,
root = { _p = nil, _c = {}, nav_to = function () end, tasks = {} }, ---@type nav_tree_page
cur_page = nil, ---@type nav_tree_page
pane = pane,
paned_pages = {},
@ -132,9 +131,8 @@ function iocontrol.alloc_nav()
---@type nav_tree_page
local page = { _p = parent, _c = {}, nav_to = function () end, switcher = function () end, tasks = {} }
if parent == nil then
app.root = page
if app.cur_page == nil then app.cur_page = page end
if parent == nil and app.cur_page == nil then
app.cur_page = page
end
if type(nav_to) == "number" then
@ -259,6 +257,12 @@ function iocontrol.init_core(comms)
alarm_buttons = {},
tone_indicators = {} -- indicators to update from supervisor tone states
}
-- API access
---@class pocket_ioctl_api
io.api = {
get_unit = function (unit) comms.api__get_unit(unit) end
}
end
-- initialize facility-dependent components of pocket iocontrol
@ -431,6 +435,8 @@ function iocontrol.init_fac(conf, temp_scale)
---@class pioctl_unit
local entry = {
unit_id = i,
connected = false,
rtu_hw = {},
num_boilers = 0,
num_turbines = 0,
@ -579,6 +585,116 @@ function iocontrol.record_facility_data(data)
return valid
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
unit.connected = data[2]
unit.rtu_hw = data[3]
unit.alarms = data[4]
unit.annunciator = data[5]
unit.reactor_data = data[6]
local control_status = 1
local reactor_status = 1
local rps_status = 1
if not unit.connected then
-- disconnected
reactor_status = 1
else
-- 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
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)
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]
end
end
-- get the IO controller database
function iocontrol.get_db() return io end

View File

@ -119,6 +119,20 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
self.api.seq_num = self.api.seq_num + 1
end
-- send an API packet to the coordinator
---@param msg_type CRDN_TYPE
---@param msg table
local function _send_api(msg_type, msg)
local s_pkt = comms.scada_packet()
local pkt = comms.crdn_packet()
pkt.make(msg_type, msg)
s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.SCADA_CRDN, pkt.raw_sendable())
nic.transmit(config.CRD_Channel, config.PKT_Channel, s_pkt)
self.api.seq_num = self.api.seq_num + 1
end
-- attempt supervisor connection establishment
local function _send_sv_establish()
_send_sv(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT })
@ -215,6 +229,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
if self.sv.linked then _send_sv(MGMT_TYPE.DIAG_ALARM_SET, { id, state }) end
end
-- coordinator get unit data
function public.api__get_unit(unit)
if self.api.linked then _send_api(CRDN_TYPE.API_GET_UNIT, { unit }) end
end
-- parse a packet
---@param side string
---@param sender integer
@ -304,7 +323,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
if _check_length(packet, 11) then
iocontrol.record_facility_data(packet.data)
end
elseif packet.type == CRDN_TYPE.API_GET_UNITS then
elseif packet.type == CRDN_TYPE.API_GET_UNIT then
if _check_length(packet, 9) then
iocontrol.record_unit_data(packet.data)
end
else _fail_type(packet) end
else
log.debug("discarding coordinator SCADA_CRDN packet before linked")

View File

@ -2,13 +2,15 @@
-- Unit Overview Page
--
local iocontrol = require("pocket.iocontrol")
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 MultiPane = require("graphics.elements.multipane")
local MultiPane = require("graphics.elements.multipane")
local TextBox = require("graphics.elements.textbox")
local AlarmLight = require("graphics.elements.indicators.alight")
@ -29,6 +31,20 @@ local SpinboxNumeric = require("graphics.elements.controls.spinbox_numeric")
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" }
}
-- new unit page view
---@param root graphics_element parent
local function new_view(root)
@ -37,29 +53,41 @@ local function new_view(root)
local main = Div{parent=root,x=1,y=1}
local app = db.nav.register_app(iocontrol.APP_ID.UNITS, main)
app.new_page(nil, function () end)
TextBox{parent=main,y=2,text="Units App",height=1,alignment=ALIGN.CENTER}
TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER}
local page_div = Div{parent=main,x=2,y=2,width=main.get_width()-2}
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 function set_sidebar(unit)
app.set_sidebar({
local function set_sidebar(id)
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 = "U#" .. unit, color = core.cpair(colors.black, colors.yellow), callback = function () end },
{ label = "RPS", color = core.cpair(colors.black, colors.red), callback = function () end },
{ label = "RCS", color = core.cpair(colors.black, colors.blue), callback = function () end },
{ label = " R ", tall = true, color = core.cpair(colors.black, colors.orange), callback = function () end },
})
{ label = "U-" .. id, color = core.cpair(colors.black, colors.yellow) },
{ label = " \x13 ", color = core.cpair(colors.black, colors.red), callback = function () end },
{ label = " R ", tall = true, color = core.cpair(colors.black, colors.lightGray), callback = function () end },
{ label = "RPS", color = core.cpair(colors.black, colors.cyan), callback = function () end },
{ label = "RCS", tall = true, color = core.cpair(colors.black, colors.blue), callback = function () end },
}
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_turbines do
table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.white), callback = function () end })
end
app.set_sidebar(list)
end
local function load()
local page_div = Div{parent=main,x=2,y=2,width=main.get_width()-2}
local u_pages = {}
local active_unit = 1
@ -71,16 +99,17 @@ local function new_view(root)
end
local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=u_pages}
app.set_root_pane(u_pane)
local function prev(x)
active_unit = util.trinary(x == 1, db.facility.num_units, x - 1)
u_pane.set_value(active_unit)
app.switcher(active_unit)
set_sidebar(active_unit)
end
local function next(x)
active_unit = util.trinary(x == db.facility.num_units, 1, x + 1)
u_pane.set_value(active_unit)
app.switcher(active_unit)
set_sidebar(active_unit)
end
@ -88,6 +117,16 @@ local function new_view(root)
local u_div = u_pages[i] ---@type graphics_element
local unit = db.units[i] ---@type pioctl_unit
local last_update = 0
local function update()
if util.time_ms() - last_update >= 500 then
db.api.get_unit(i)
last_update = util.time_ms()
end
end
app.new_page(nil, i).tasks = { update }
TextBox{parent=u_div,y=1,text="Reactor Unit #"..i,height=1,alignment=ALIGN.CENTER}
PushButton{parent=u_div,x=1,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()prev(i)end}
PushButton{parent=u_div,x=21,y=1,text=">",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()next(i)end}
@ -101,46 +140,33 @@ local function new_view(root)
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 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 ctrl = IconIndicator{parent=u_div,x=1,y=8,label="Control State",states=mode_states}
ctrl.update(i+1)
rate.register(unit.unit_ps, "act_burn_rate", rate.update)
temp.register(unit.unit_ps, "temp", temp.update)
ctrl.register(unit.unit_ps, "U_ControlStatus", ctrl.update)
u_div.line_break()
local rct = IconIndicator{parent=u_div,x=1,label="Fission Reactor",states=basic_states}
local rps = IconIndicator{parent=u_div,x=1,label="Protection System",states=basic_states}
rct.register(unit.unit_ps, "U_ReactorStatus", rct.update)
rps.register(unit.unit_ps, "U_RPS", rps.update)
u_div.line_break()
local rcs = IconIndicator{parent=u_div,x=1,label="Coolant System",states=basic_states}
for b = 1, unit.num_boilers do
local blr = IconIndicator{parent=u_div,x=1,label="Boiler "..b,states=basic_states}
blr.update(b+2)
blr.register(unit.boiler_ps_tbl[b], "BoilerStatus", blr.update)
end
for t = 1, unit.num_turbines do
local trb = IconIndicator{parent=u_div,x=1,label="Turbine "..t,states=basic_states}
trb.update(t)
local tbn = IconIndicator{parent=u_div,x=1,label="Turbine "..t,states=basic_states}
tbn.register(unit.turbine_ps_tbl[t], "TurbineStatus", tbn.update)
end
rct.update(4)
rps.update(3)
rcs.update(3)
end
end

View File

@ -18,7 +18,7 @@ local comms = {}
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
comms.version = "2.5.1"
comms.api_version = "0.0.1"
comms.api_version = "0.0.2"
---@enum PROTOCOL
local PROTOCOL = {
@ -67,7 +67,7 @@ local CRDN_TYPE = {
UNIT_STATUSES = 5, -- state of each of the reactor units
UNIT_CMD = 6, -- command a reactor unit
API_GET_FAC = 7, -- API: get all the facility data
API_GET_UNITS = 8 -- API: get all the reactor unit data
API_GET_UNIT = 8 -- API: get reactor unit data
}
---@enum ESTABLISH_ACK