code cleanup and work on linking for annunciator

This commit is contained in:
Mikayla Fischler 2022-09-06 22:38:27 -04:00
parent 117784500a
commit b53d2d6694
10 changed files with 162 additions and 110 deletions

View File

@ -4,7 +4,7 @@ local ppm = require("scada-common.ppm")
local util = require("scada-common.util")
local apisessions = require("coordinator.apisessions")
local database = require("coordinator.database")
local iocontrol = require("coordinator.iocontrol")
local dialog = require("coordinator.ui.dialog")
@ -387,8 +387,8 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
table.insert(conf.defs, packet.data[i])
end
-- init database structure
database.init(conf)
-- init io controller
iocontrol.init(conf)
self.sv_linked = true
else
@ -399,7 +399,7 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
end
elseif packet.type == SCADA_CRDN_TYPES.STRUCT_BUILDS then
-- record builds
if database.populate_builds(packet.data) then
if iocontrol.populate_builds(packet.data) then
-- acknowledge receipt of builds
_send_sv(PROTOCOLS.SCADA_CRDN, SCADA_CRDN_TYPES.STRUCT_BUILDS, {})
else
@ -407,7 +407,7 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
end
elseif packet.type == SCADA_CRDN_TYPES.UNIT_STATUSES then
-- update statuses
if not database.update_statuses(packet.data) then
if not iocontrol.update_statuses(packet.data) then
log.error("received invalid unit statuses packet")
end
elseif packet.type == SCADA_CRDN_TYPES.COMMAND_UNIT then

View File

@ -1,30 +1,35 @@
local psil = require("scada-common.psil")
local log = require("scada-common.log")
local database = {}
local iocontrol = {}
database.WASTE = { Pu = 0, Po = 1, AntiMatter = 2 }
---@class ioctl
local io = {}
---@class coord_db
local db = {}
-- initialize the coordinator database
-- initialize the coordinator IO controller
---@param conf facility_conf configuration
function database.init(conf)
db.facility = {
function iocontrol.init(conf)
io.facility = {
scram = false,
num_units = conf.num_units,
ps = psil.create()
}
db.units = {}
io.units = {}
for i = 1, conf.num_units do
---@class coord_db_entry
---@class ioctl_entry
local entry = {
unit_id = i, ---@type integer
initialized = false,
waste_mode = 0,
control_state = false,
burn_rate_cmd = 0.0,
waste_control = 0,
---@fixme debug stubs to be linked into comms later?
start = function () print("UNIT " .. i .. ": start") end,
scram = function () print("UNIT " .. i .. ": SCRAM") end,
set_burn = function (rate) print("UNIT " .. i .. ": set burn rate to " .. rate) end,
reactor_ps = psil.create(),
reactor_data = {}, ---@type reactor_db
@ -48,20 +53,20 @@ function database.init(conf)
table.insert(entry.turbine_data_tbl, data)
end
table.insert(db.units, entry)
table.insert(io.units, entry)
end
end
-- populate structure builds
---@param builds table
---@return boolean valid
function database.populate_builds(builds)
if #builds ~= #db.units then
function iocontrol.populate_builds(builds)
if #builds ~= #io.units then
log.error("number of provided unit builds does not match expected number of units")
return false
else
for i = 1, #builds do
local unit = db.units[i] ---@type coord_db_entry
local unit = io.units[i] ---@type ioctl_entry
local build = builds[i]
-- reactor build
@ -110,13 +115,13 @@ end
-- update unit statuses
---@param statuses table
---@return boolean valid
function database.update_statuses(statuses)
if #statuses ~= #db.units then
function iocontrol.update_statuses(statuses)
if #statuses ~= #io.units then
log.error("number of provided unit statuses does not match expected number of units")
return false
else
for i = 1, #statuses do
local unit = db.units[i] ---@type coord_db_entry
local unit = io.units[i] ---@type ioctl_entry
local status = statuses[i]
-- reactor PLC status
@ -124,8 +129,11 @@ function database.update_statuses(statuses)
local reactor_status = status[1]
if #reactor_status == 0 then
unit.reactor_ps.publish("online", false)
unit.reactor_ps.publish("computed_status", 1) -- disconnected
else
unit.reactor_ps.publish("online", true)
local mek_status = reactor_status[1]
local rps_status = reactor_status[2]
local gen_status = reactor_status[3]
@ -167,20 +175,43 @@ function database.update_statuses(statuses)
end
end
-- annunciator
local annunciator = status[2] ---@type annunciator
for key, val in pairs(annunciator) do
if key == "TurbineTrip" then
local trips = val
local any = false
for x = 1, #trips do
any = any or trips[x]
unit.turbine_ps_tbl[x].publish(x .. "_TurbineTrip", trips[x])
end
unit.reactor_ps.publish("TurbineTrip", any)
else
unit.reactor_ps.publish(key, val)
end
end
-- RTU statuses
local rtu_statuses = status[2]
local rtu_statuses = status[3]
-- boiler statuses
for id = 1, #unit.boiler_data_tbl do
if rtu_statuses.boilers[i] == nil then
-- disconnected
unit.boiler_ps_tbl[id].publish(id .. "_online", false)
unit.boiler_ps_tbl[id].publish(id .. "_computed_status", 1)
end
end
for id, boiler in pairs(rtu_statuses.boilers) do
unit.boiler_ps_tbl[id].publish(id .. "_online", true)
unit.boiler_data_tbl[id].state = boiler[1] ---@type table
unit.boiler_data_tbl[id].tanks = boiler[2] ---@type table
@ -208,11 +239,14 @@ function database.update_statuses(statuses)
for id = 1, #unit.turbine_ps_tbl do
if rtu_statuses.turbines[i] == nil then
-- disconnected
unit.turbine_ps_tbl[id].publish(id .. "_online", false)
unit.turbine_ps_tbl[id].publish(id .. "_computed_status", 1)
end
end
for id, turbine in pairs(rtu_statuses.turbines) do
unit.turbine_ps_tbl[id].publish(id .. "_online", true)
unit.turbine_data_tbl[id].state = turbine[1] ---@type table
unit.turbine_data_tbl[id].tanks = turbine[2] ---@type table
@ -242,7 +276,7 @@ function database.update_statuses(statuses)
return true
end
-- get the database
function database.get() return db end
-- get the IO controller database
function iocontrol.get_db() return io end
return database
return iocontrol

View File

@ -1,6 +1,6 @@
local log = require("scada-common.log")
local database = require("coordinator.database")
local iocontrol = require("coordinator.iocontrol")
local main_view = require("coordinator.ui.layout.main_view")
local unit_view = require("coordinator.ui.layout.unit_view")

View File

@ -16,7 +16,7 @@ local config = require("coordinator.config")
local coordinator = require("coordinator.coordinator")
local renderer = require("coordinator.renderer")
local COORDINATOR_VERSION = "alpha-v0.4.5"
local COORDINATOR_VERSION = "alpha-v0.4.6"
local print = util.print
local println = util.println

View File

@ -14,11 +14,11 @@ local cpair = core.graphics.cpair
local border = core.graphics.border
-- new boiler view
---@param root graphics_element
---@param x integer
---@param y integer
---@param id integer
---@param ps psil
---@param root graphics_element parent
---@param x integer top left x
---@param y integer top left y
---@param id integer device index
---@param ps psil ps interface
local function new_view(root, x, y, id, ps)
local tag = id .. "_"

View File

@ -15,11 +15,12 @@ local TEXT_ALIGN = core.graphics.TEXT_ALIGN
local cpair = core.graphics.cpair
local border = core.graphics.border
---@param root graphics_element
---@param x integer
---@param y integer
---@param data reactor_db
---@param ps psil
-- create new reactor view
---@param root graphics_element parent
---@param x integer top left x
---@param y integer top left y
---@param data reactor_db reactor data
---@param ps psil ps interface
local function new_view(root, x, y, data, ps)
local reactor = Rectangle{parent=root,border=border(1, colors.gray, true),width=30,height=7,x=x,y=y}

View File

@ -14,11 +14,11 @@ local cpair = core.graphics.cpair
local border = core.graphics.border
-- new turbine view
---@param root graphics_element
---@param x integer
---@param y integer
---@param id integer
---@param ps psil
---@param root graphics_element parent
---@param x integer top left x
---@param y integer top left y
---@param id integer device index
---@param ps psil ps interface
local function new_view(root, x, y, id, ps)
local tag = id .. "_"

View File

@ -20,10 +20,11 @@ local cpair = core.graphics.cpair
local border = core.graphics.border
local pipe = core.graphics.pipe
---@param parent graphics_element
---@param x integer
---@param y integer
---@param unit coord_db_entry
-- make a new unit overview window
---@param parent graphics_element parent
---@param x integer top left x
---@param y integer top left y
---@param unit ioctl_entry unit database entry
local function make(parent, x, y, unit)
local height = 0
local num_boilers = #unit.boiler_data_tbl

View File

@ -2,7 +2,7 @@
-- Main SCADA Coordinator GUI
--
local database = require("coordinator.database")
local iocontrol = require("coordinator.iocontrol")
local style = require("coordinator.ui.style")
@ -15,13 +15,15 @@ local TextBox = require("graphics.elements.textbox")
local TEXT_ALIGN = core.graphics.TEXT_ALIGN
-- create new main view
---@param monitor table main viewscreen
local function init(monitor)
local main = DisplayBox{window=monitor,fg_bg=style.root}
-- window header message
TextBox{parent=main,text="Nuclear Generation Facility SCADA Coordinator",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header}
local db = database.get()
local db = iocontrol.get_db()
local uo_1, uo_2, uo_3, uo_4 ---@type graphics_element

View File

@ -2,11 +2,14 @@
-- Reactor Unit SCADA Coordinator GUI
--
local core = require("graphics.core")
local tcallbackdsp = require("scada-common.tcallbackdsp")
local iocontrol = require("coordinator.iocontrol")
local style = require("coordinator.ui.style")
local core = require("graphics.core")
local DisplayBox = require("graphics.elements.displaybox")
local Div = require("graphics.elements.div")
local TextBox = require("graphics.elements.textbox")
@ -30,7 +33,14 @@ local TEXT_ALIGN = core.graphics.TEXT_ALIGN
local cpair = core.graphics.cpair
local border = core.graphics.border
-- create a unit view
---@param monitor table
---@param id integer
local function init(monitor, id)
local unit = iocontrol.get_db().units[id] ---@type ioctl_entry
local r_ps = unit.reactor_ps
local r_data = unit.reactor_data
local main = DisplayBox{window=monitor,fg_bg=style.root}
TextBox{parent=main,text="Reactor Unit #" .. id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header}
@ -38,41 +48,37 @@ local function init(monitor, id)
local scram_fg_bg = cpair(colors.white, colors.gray)
local lu_cpair = cpair(colors.gray, colors.gray)
---@fixme test code
local t = 300
if id == 1 then
t = 340
elseif id == 2 then
t = 340
elseif id == 3 then
t = 300
elseif id == 4 then
t = 300
end
-- main stats and core map --
--@todo need to be checking actual reactor dimensions somehow
local core_map = CoreMap{parent=main,x=2,y=3,reactor_l=18,reactor_w=18}
core_map.update(t)
r_ps.subscribe("temp", core_map.update)
local stat_fg_bg = cpair(colors.black,colors.white)
TextBox{parent=main,x=21,y=3,text="Core Temp",height=1,fg_bg=style.label}
DataIndicator{parent=main,x=21,label="",format="%9.2f",value=300,unit="K",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
local core_temp = DataIndicator{parent=main,x=21,label="",format="%9.2f",value=0,unit="K",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
r_ps.subscribe("temp", core_temp.update)
main.line_break()
TextBox{parent=main,x=21,text="Burn Rate",height=1,width=12,fg_bg=style.label}
DataIndicator{parent=main,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
local act_burn_r = DataIndicator{parent=main,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
r_ps.subscribe("act_burn_rate", act_burn_r.update)
main.line_break()
TextBox{parent=main,x=21,text="Commanded Burn Rate",height=2,width=12,fg_bg=style.label}
DataIndicator{parent=main,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
local burn_r = DataIndicator{parent=main,x=21,label="",format="%6.1f",value=0,unit="mB/t",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
r_ps.subscribe("burn_rate", burn_r.update)
main.line_break()
TextBox{parent=main,x=21,text="Heating Rate",height=1,width=12,fg_bg=style.label}
DataIndicator{parent=main,x=21,label="",format="%11.0f",value=0,unit="",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
local heating_r = DataIndicator{parent=main,x=21,label="",format="%11.0f",value=0,unit="",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
r_ps.subscribe("heating_rate", heating_r.update)
main.line_break()
TextBox{parent=main,x=21,text="Containment Integrity",height=2,width=12,fg_bg=style.label}
DataIndicator{parent=main,x=21,label="",format="%9.0f",value=100,unit="%",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
local integ = DataIndicator{parent=main,x=21,label="",format="%9.0f",value=100,unit="%",lu_colors=lu_cpair,width=12,fg_bg=stat_fg_bg}
r_ps.subscribe("damage", function (x) integ.update(1.0 - (x / 100.0)) end)
main.line_break()
-- TextBox{parent=main,text="FL",x=21,y=19,height=1,width=2,fg_bg=style.label}
@ -85,11 +91,7 @@ local function init(monitor, id)
-- local ccool = VerticalBar{parent=main,x=28,y=12,fg_bg=cpair(colors.lightBlue,colors.gray),height=6,width=2}
-- local hcool = VerticalBar{parent=main,x=31,y=12,fg_bg=cpair(colors.orange,colors.gray),height=6,width=2}
-- ---@fixme test code
-- fuel.update(1)
-- ccool.update(0.85)
-- hcool.update(0.08)
-- waste.update(0.32)
-- annunciator --
local annunciator = Div{parent=main,x=34,y=3}
@ -99,20 +101,35 @@ local function init(monitor, id)
local plc_online = IndicatorLight{parent=annunciator,label="PLC Online",colors=cpair(colors.green,colors.red)}
local plc_hbeat = IndicatorLight{parent=annunciator,label="PLC Heartbeat",colors=cpair(colors.white,colors.gray)}
local r_active = IndicatorLight{parent=annunciator,label="Active",colors=cpair(colors.green,colors.gray)}
--@todo auto control as info sent here
local r_auto = IndicatorLight{parent=annunciator,label="Auto Control",colors=cpair(colors.blue,colors.gray)}
r_ps.subscribe("PLCOnline", plc_online.update)
r_ps.subscribe("PLCHeartbeat", plc_hbeat.update)
r_ps.subscribe("status", r_active.update)
annunciator.line_break()
-- annunciator fields
local r_trip = IndicatorLight{parent=annunciator,label="Reactor SCRAM",colors=cpair(colors.red,colors.gray)}
local r_mtrp = IndicatorLight{parent=annunciator,label="Manual Reactor SCRAM",colors=cpair(colors.red,colors.gray)}
local r_rtrp = IndicatorLight{parent=annunciator,label="RCP Trip",colors=cpair(colors.red,colors.gray)}
local r_cflo = IndicatorLight{parent=annunciator,label="RCS Flow Low",colors=cpair(colors.yellow,colors.gray)}
local r_temp = IndicatorLight{parent=annunciator,label="Reactor Temp. High",colors=cpair(colors.red,colors.gray)}
local r_rhdt = IndicatorLight{parent=annunciator,label="Reactor High Delta T",colors=cpair(colors.yellow,colors.gray)}
local r_firl = IndicatorLight{parent=annunciator,label="Fuel Input Rate Low",colors=cpair(colors.yellow,colors.gray)}
local r_wloc = IndicatorLight{parent=annunciator,label="Waste Line Occlusion",colors=cpair(colors.yellow,colors.gray)}
local r_hsrt = IndicatorLight{parent=annunciator,label="High Startup Rate",colors=cpair(colors.yellow,colors.gray)}
local r_scram = IndicatorLight{parent=annunciator,label="Reactor SCRAM",colors=cpair(colors.red,colors.gray)}
local r_mscrm = IndicatorLight{parent=annunciator,label="Manual Reactor SCRAM",colors=cpair(colors.red,colors.gray)}
local r_rtrip = IndicatorLight{parent=annunciator,label="RCP Trip",colors=cpair(colors.red,colors.gray)}
local r_cflow = IndicatorLight{parent=annunciator,label="RCS Flow Low",colors=cpair(colors.yellow,colors.gray)}
local r_temp = IndicatorLight{parent=annunciator,label="Reactor Temp. High",colors=cpair(colors.red,colors.gray)}
local r_rhdt = IndicatorLight{parent=annunciator,label="Reactor High Delta T",colors=cpair(colors.yellow,colors.gray)}
local r_firl = IndicatorLight{parent=annunciator,label="Fuel Input Rate Low",colors=cpair(colors.yellow,colors.gray)}
local r_wloc = IndicatorLight{parent=annunciator,label="Waste Line Occlusion",colors=cpair(colors.yellow,colors.gray)}
local r_hsrt = IndicatorLight{parent=annunciator,label="High Startup Rate",colors=cpair(colors.yellow,colors.gray)}
r_ps.subscribe("ReactorSCRAM", r_scram.update)
r_ps.subscribe("ManualReactorSCRAM", r_mscrm.update)
r_ps.subscribe("RCPTrip", r_rtrip.update)
r_ps.subscribe("RCSFlowLow", r_cflow.update)
r_ps.subscribe("ReactorTempHigh", r_temp.update)
r_ps.subscribe("ReactorHighDeltaT", r_rhdt.update)
r_ps.subscribe("FuelInputRateLow", r_firl.update)
r_ps.subscribe("WasteLineOcclusion", r_wloc.update)
r_ps.subscribe("HighStartupRate", r_hsrt.update)
annunciator.line_break()
@ -120,13 +137,23 @@ local function init(monitor, id)
local rps_trp = IndicatorLight{parent=annunciator,label="RPS Trip",colors=cpair(colors.red,colors.gray)}
local rps_dmg = IndicatorLight{parent=annunciator,label="Damage Critical",colors=cpair(colors.yellow,colors.gray)}
local rps_exh = IndicatorLight{parent=annunciator,label="Excess Heated Coolant",colors=cpair(colors.yellow,colors.gray)}
local rps_exc = IndicatorLight{parent=annunciator,label="Excess Waste",colors=cpair(colors.yellow,colors.gray)}
local rps_exw = IndicatorLight{parent=annunciator,label="Excess Waste",colors=cpair(colors.yellow,colors.gray)}
local rps_tmp = IndicatorLight{parent=annunciator,label="High Core Temp",colors=cpair(colors.yellow,colors.gray)}
local rps_nof = IndicatorLight{parent=annunciator,label="No Fuel",colors=cpair(colors.yellow,colors.gray)}
local rps_noc = IndicatorLight{parent=annunciator,label="No Coolant",colors=cpair(colors.yellow,colors.gray)}
local rps_flt = IndicatorLight{parent=annunciator,label="PPM Fault",colors=cpair(colors.yellow,colors.gray)}
local rps_tmo = IndicatorLight{parent=annunciator,label="Timeout",colors=cpair(colors.yellow,colors.gray)}
r_ps.subscribe("rps_tripped", rps_trp.update)
r_ps.subscribe("dmg_crit", rps_dmg.update)
r_ps.subscribe("ex_hcool", rps_exh.update)
r_ps.subscribe("ex_waste", rps_exw.update)
r_ps.subscribe("high_temp", rps_tmp.update)
r_ps.subscribe("no_fuel", rps_nof.update)
r_ps.subscribe("no_cool", rps_noc.update)
r_ps.subscribe("fault", rps_flt.update)
r_ps.subscribe("timeout", rps_tmo.update)
annunciator.line_break()
-- cooling
@ -136,6 +163,12 @@ local function init(monitor, id)
local c_mwrf = IndicatorLight{parent=annunciator,label="Max Water Return Feed",colors=cpair(colors.yellow,colors.gray)}
local c_tbnt = IndicatorLight{parent=annunciator,label="Turbine Trip",colors=cpair(colors.red,colors.gray)}
r_ps.subscribe("BoilRateMismatch", c_brm.update)
r_ps.subscribe("CoolantFeedMismatch", c_cfm.update)
r_ps.subscribe("SteamFeedMismatch", c_sfm.update)
r_ps.subscribe("MaxWaterReturnFeed", c_mwrf.update)
r_ps.subscribe("TurbineTrip", c_tbnt.update)
annunciator.line_break()
-- machine-specific indicators
@ -174,19 +207,17 @@ local function init(monitor, id)
DataIndicator{parent=main,x=34,y=51,label="",format="%10.1f",value=0,unit="mSv/h",lu_colors=lu_cpair,width=18,fg_bg=stat_fg_bg}
---@fixme debug code
local f = function () print("unit " .. id .. " scram!") end
local fs = function () print("unit " .. id .. " start!") end
-- reactor controls --
local start = StartButton{parent=main,x=12,y=44,callback=fs,fg_bg=scram_fg_bg}
local scram = SCRAMButton{parent=main,x=22,y=44,callback=f,fg_bg=scram_fg_bg}
StartButton{parent=main,x=12,y=44,callback=unit.start,fg_bg=scram_fg_bg}
SCRAMButton{parent=main,x=22,y=44,callback=unit.scram,fg_bg=scram_fg_bg}
local burn_control = Div{parent=main,x=12,y=40,width=19,height=3,fg_bg=cpair(colors.gray,colors.white)}
local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=cpair(colors.black,colors.white)}
local set_burn = function () print("unit " .. id .. " set burn to " .. burn_rate.get_value()) end
TextBox{parent=burn_control,x=9,y=2,text="mB/t"}
local set_burn = function () unit.set_burn(burn_rate.get_value()) end
PushButton{parent=burn_control,x=14,y=2,text="SET",min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=set_burn}
local opts = {
@ -224,33 +255,16 @@ local function init(monitor, id)
main.line_break()
ColorMap{parent=main,x=2,y=51}
---@fixme test code
plc_hbeat.update(true)
r_auto.update(true)
r_trip.update(true)
r_mtrp.update(true)
rps_trp.update(true)
rps_nof.update(true)
---@fixme test code
local heartbeat = true
local function _test_toggle()
plc_hbeat.update(heartbeat)
heartbeat = not heartbeat
tcallbackdsp.dispatch(1, _test_toggle)
end
---@fixme test code
local rps = true
local function _test_toggle1()
local function _test_toggle()
rps_nof.update(rps)
rps = not rps
tcallbackdsp.dispatch(0.25, _test_toggle1)
tcallbackdsp.dispatch(0.25, _test_toggle)
end
---@fixme test code
tcallbackdsp.dispatch(1, _test_toggle)
tcallbackdsp.dispatch(0.25, _test_toggle1)
tcallbackdsp.dispatch(0.25, _test_toggle)
return main
end