#131 start of unit status text, added updating coordinator waste processing option on reconnect

This commit is contained in:
Mikayla Fischler
2022-12-05 16:17:09 -05:00
parent 5224dcbd25
commit 6bdde02268
12 changed files with 233 additions and 134 deletions

View File

@@ -39,7 +39,7 @@ function iocontrol.init(conf, comms)
---@class ioctl_entry
local entry = {
unit_id = i, ---@type integer
unit_id = i, ---@type integer
initialized = false,
num_boilers = 0,
@@ -53,15 +53,15 @@ function iocontrol.init(conf, comms)
scram = function () end,
reset_rps = function () end,
ack_alarms = function () end,
set_burn = function (rate) end, ---@param rate number
set_waste = function (mode) end, ---@param mode integer
set_burn = function (rate) end, ---@param rate number
set_waste = function (mode) end, ---@param mode integer
start_ack = function (success) end, ---@param success boolean
scram_ack = function (success) end, ---@param success boolean
reset_rps_ack = function (success) end, ---@param success boolean
ack_alarms_ack = function (success) end,---@param success boolean
set_burn_ack = function (success) end, ---@param success boolean
set_waste_ack = function (success) end, ---@param success boolean
start_ack = function (success) end, ---@param success boolean
scram_ack = function (success) end, ---@param success boolean
reset_rps_ack = function (success) end, ---@param success boolean
ack_alarms_ack = function (success) end, ---@param success boolean
set_burn_ack = function (success) end, ---@param success boolean
set_waste_ack = function (success) end, ---@param success boolean
alarm_callbacks = {
c_breach = { ack = function () ack(1) end, reset = function () reset(1) end },
@@ -95,7 +95,7 @@ function iocontrol.init(conf, comms)
},
reactor_ps = psil.create(),
reactor_data = {}, ---@type reactor_db
reactor_data = {}, ---@type reactor_db
boiler_ps_tbl = {},
boiler_data_tbl = {},
@@ -221,18 +221,19 @@ end
---@return boolean valid
function iocontrol.update_statuses(statuses)
if type(statuses) ~= "table" then
log.debug("unit statuses not a table")
log.debug("iocontrol.update_statuses: unit statuses not a table")
return false
elseif #statuses ~= #io.units then
log.debug("number of provided unit statuses does not match expected number of units")
log.debug("iocontrol.update_statuses: number of provided unit statuses does not match expected number of units")
return false
else
for i = 1, #statuses do
local log_header = util.c("iocontrol.update_statuses[unit ", i, "]: ")
local unit = io.units[i] ---@type ioctl_entry
local status = statuses[i]
if type(status) ~= "table" or #status ~= 4 then
log.debug("invalid status entry in unit statuses (not a table or invalid length)")
if type(status) ~= "table" or #status ~= 5 then
log.debug(log_header .. "invalid status entry in unit statuses (not a table or invalid length)")
return false
end
@@ -255,7 +256,7 @@ function iocontrol.update_statuses(statuses)
unit.reactor_data.no_reactor = gen_status[5]
unit.reactor_data.formed = gen_status[6]
else
log.debug("reactor general status length mismatch")
log.debug(log_header .. "reactor general status length mismatch")
end
unit.reactor_data.rps_status = rps_status ---@type rps_status
@@ -295,75 +296,16 @@ function iocontrol.update_statuses(statuses)
end
end
else
log.debug("reactor status length mismatch")
end
-- annunciator
local annunciator = status[2] ---@type annunciator
for key, val in pairs(annunciator) do
if key == "TurbineTrip" then
-- split up turbine trip table for all turbines and a general OR combination
local trips = val
local any = false
for id = 1, #trips do
any = any or trips[id]
unit.turbine_ps_tbl[id].publish(key, trips[id])
end
unit.reactor_ps.publish("TurbineTrip", any)
elseif key == "BoilerOnline" or key == "HeatingRateLow" or key == "WaterLevelLow" then
-- split up array for all boilers
for id = 1, #val do
unit.boiler_ps_tbl[id].publish(key, val[id])
end
elseif key == "TurbineOnline" or key == "SteamDumpOpen" or key == "TurbineOverSpeed" then
-- split up array for all turbines
for id = 1, #val do
unit.turbine_ps_tbl[id].publish(key, val[id])
end
elseif type(val) == "table" then
-- we missed one of the tables?
log.error("unrecognized table found in annunciator list, this is a bug", true)
else
-- non-table fields
unit.reactor_ps.publish(key, val)
end
end
-- alarms
local alarm_states = status[3]
if type(alarm_states) == "table" then
for id = 1, #alarm_states do
local state = alarm_states[id]
unit.alarms[id] = state
if state == types.ALARM_STATE.TRIPPED or state == types.ALARM_STATE.ACKED then
unit.reactor_ps.publish("ALM" .. id, 2)
elseif state == types.ALARM_STATE.RING_BACK then
unit.reactor_ps.publish("ALM" .. id, 3)
else
unit.reactor_ps.publish("ALM" .. id, 1)
end
end
else
log.debug("alarm states not a table")
return false
log.debug(log_header .. "reactor status length mismatch")
end
-- RTU statuses
local rtu_statuses = status[4]
local rtu_statuses = status[2]
if type(rtu_statuses) == "table" then
-- boiler statuses
if type(rtu_statuses.boilers) == "table" then
-- boiler statuses
for id = 1, #unit.boiler_data_tbl do
if rtu_statuses.boilers[i] == nil then
-- disconnected
@@ -403,12 +345,11 @@ function iocontrol.update_statuses(statuses)
end
end
else
log.debug("boiler list not a table")
log.debug(log_header .. "boiler list not a table")
end
-- turbine statuses
if type(rtu_statuses.turbines) == "table" then
-- turbine statuses
for id = 1, #unit.turbine_ps_tbl do
if rtu_statuses.turbines[i] == nil then
-- disconnected
@@ -450,11 +391,84 @@ function iocontrol.update_statuses(statuses)
end
end
else
log.debug("turbine list not a table")
log.debug(log_header .. "turbine list not a table")
return false
end
else
log.debug("rtu list not a table")
log.debug(log_header .. "rtu list not a table")
end
-- annunciator
local annunciator = status[3] ---@type annunciator
for key, val in pairs(annunciator) do
if key == "TurbineTrip" then
-- split up turbine trip table for all turbines and a general OR combination
local trips = val
local any = false
for id = 1, #trips do
any = any or trips[id]
unit.turbine_ps_tbl[id].publish(key, trips[id])
end
unit.reactor_ps.publish("TurbineTrip", any)
elseif key == "BoilerOnline" or key == "HeatingRateLow" or key == "WaterLevelLow" then
-- split up array for all boilers
for id = 1, #val do
unit.boiler_ps_tbl[id].publish(key, val[id])
end
elseif key == "TurbineOnline" or key == "SteamDumpOpen" or key == "TurbineOverSpeed" then
-- split up array for all turbines
for id = 1, #val do
unit.turbine_ps_tbl[id].publish(key, val[id])
end
elseif type(val) == "table" then
-- we missed one of the tables?
log.error(log_header .. "unrecognized table found in annunciator list, this is a bug", true)
else
-- non-table fields
unit.reactor_ps.publish(key, val)
end
end
-- alarms
local alarm_states = status[4]
if type(alarm_states) == "table" then
for id = 1, #alarm_states do
local state = alarm_states[id]
unit.alarms[id] = state
if state == types.ALARM_STATE.TRIPPED or state == types.ALARM_STATE.ACKED then
unit.reactor_ps.publish("Alarm_" .. id, 2)
elseif state == types.ALARM_STATE.RING_BACK then
unit.reactor_ps.publish("Alarm_" .. id, 3)
else
unit.reactor_ps.publish("Alarm_" .. id, 1)
end
end
else
log.debug(log_header .. "alarm states not a table")
end
-- unit state fields
local unit_state = status[5]
if type(unit_state) == "table" then
if #unit_state == 3 then
unit.reactor_ps.publish("U_StatusLine1", unit_state[1])
unit.reactor_ps.publish("U_StatusLine2", unit_state[2])
unit.reactor_ps.publish("U_WasteMode", unit_state[3])
else
log.debug(log_header .. "unit state length mismatch")
end
else
log.debug(log_header .. "unit state not a table")
end
end

View File

@@ -18,7 +18,7 @@ local coordinator = require("coordinator.coordinator")
local renderer = require("coordinator.renderer")
local sounder = require("coordinator.sounder")
local COORDINATOR_VERSION = "beta-v0.7.4"
local COORDINATOR_VERSION = "beta-v0.7.5"
local print = util.print
local println = util.println

View File

@@ -308,8 +308,8 @@ local function init(parent, id)
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}
r_ps.subscribe("burn_rate", function (v) burn_rate.set_value(v) end)
r_ps.subscribe("max_burn", function (v) burn_rate.set_max(v) end)
r_ps.subscribe("burn_rate", burn_rate.set_value)
r_ps.subscribe("max_burn", burn_rate.set_max)
local dis_colors = cpair(colors.white, colors.lightGray)
@@ -334,18 +334,24 @@ local function init(parent, id)
r_ps.subscribe("rps_tripped", start_button_en_check)
r_ps.subscribe("rps_tripped", function (active) if active then reset.enable() else reset.disable() end end)
TextBox{parent=main,x=2,y=30,text="Idle",width=29,height=1,alignment=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.gray, colors.white)}
local stat_line_1 = DataIndicator{parent=main,x=2,y=30,label="",format="%s",value="",width=29,height=1,alignment=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.gray, colors.white)}
local stat_line_2 = DataIndicator{parent=main,x=2,y=31,label="",format="%s",value="",width=29,height=1,alignment=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.gray, colors.white)}
r_ps.subscribe("U_StatusLine1", stat_line_1.update)
r_ps.subscribe("U_StatusLine2", stat_line_2.update)
local waste_sel = Div{parent=main,x=2,y=50,width=29,height=2,fg_bg=cpair(colors.black, colors.white)}
MultiButton{parent=waste_sel,x=1,y=1,options=waste_opts,callback=unit.set_waste,min_width=6,fg_bg=cpair(colors.black, colors.white)}
local waste_mode = MultiButton{parent=waste_sel,x=1,y=1,options=waste_opts,callback=unit.set_waste,min_width=6,fg_bg=cpair(colors.black, colors.white)}
TextBox{parent=waste_sel,text="Waste Processing",alignment=TEXT_ALIGN.CENTER,x=1,y=1,height=1}
r_ps.subscribe("U_WasteMode", waste_mode.set_value)
----------------------
-- alarm management --
----------------------
local alarm_panel = Div{parent=main,x=2,y=32,width=29,height=16,fg_bg=cpair(colors.black,colors.white)}
local alarm_panel = Div{parent=main,x=2,y=33,width=29,height=16,fg_bg=cpair(colors.black,colors.white)}
local a_brc = AlarmLight{parent=alarm_panel,x=6,y=2,label="Containment Breach",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
local a_rad = AlarmLight{parent=alarm_panel,x=6,label="Containment Radiation",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
@@ -362,20 +368,20 @@ local function init(parent, id)
local a_clt = AlarmLight{parent=alarm_panel,x=6,label="RCS Transient",c1=colors.gray,c2=colors.yellow,c3=colors.green,flash=true,period=period.BLINK_500_MS}
local a_tbt = AlarmLight{parent=alarm_panel,x=6,label="Turbine Trip",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
r_ps.subscribe("ALM1", a_brc.update)
r_ps.subscribe("ALM2", a_rad.update)
r_ps.subscribe("ALM4", a_dmg.update)
r_ps.subscribe("Alarm_1", a_brc.update)
r_ps.subscribe("Alarm_2", a_rad.update)
r_ps.subscribe("Alarm_4", a_dmg.update)
r_ps.subscribe("ALM3", a_rcl.update)
r_ps.subscribe("ALM5", a_rcd.update)
r_ps.subscribe("ALM6", a_rot.update)
r_ps.subscribe("ALM7", a_rht.update)
r_ps.subscribe("ALM8", a_rwl.update)
r_ps.subscribe("ALM9", a_rwh.update)
r_ps.subscribe("Alarm_3", a_rcl.update)
r_ps.subscribe("Alarm_5", a_rcd.update)
r_ps.subscribe("Alarm_6", a_rot.update)
r_ps.subscribe("Alarm_7", a_rht.update)
r_ps.subscribe("Alarm_8", a_rwl.update)
r_ps.subscribe("Alarm_9", a_rwh.update)
r_ps.subscribe("ALM10", a_rps.update)
r_ps.subscribe("ALM11", a_clt.update)
r_ps.subscribe("ALM12", a_tbt.update)
r_ps.subscribe("Alarm_10", a_rps.update)
r_ps.subscribe("Alarm_11", a_clt.update)
r_ps.subscribe("Alarm_12", a_tbt.update)
-- ack's and resets

View File

@@ -4,6 +4,7 @@
local iocontrol = require("coordinator.iocontrol")
local sounder = require("coordinator.sounder")
local util = require("scada-common.util")
local style = require("coordinator.ui.style")
@@ -28,32 +29,46 @@ 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 header = TextBox{parent=main,text="Nuclear Generation Facility SCADA Coordinator",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header}
local db = iocontrol.get_db()
local uo_1, uo_2, uo_3, uo_4 ---@type graphics_element
local cnc_y_start = 3
-- unit overviews
if db.facility.num_units >= 1 then uo_1 = unit_overview(main, 2, 3, db.units[1]) end
if db.facility.num_units >= 2 then uo_2 = unit_overview(main, 84, 3, db.units[2]) end
if db.facility.num_units >= 1 then
uo_1 = unit_overview(main, 2, 3, db.units[1])
cnc_y_start = cnc_y_start + uo_1.height() + 1
end
if db.facility.num_units >= 2 then
uo_2 = unit_overview(main, 84, 3, db.units[2])
end
if db.facility.num_units >= 3 then
-- base offset 3, spacing 1, max height of units 1 and 2
local row_2_offset = 3 + 1 + math.max(uo_1.height(), uo_2.height())
uo_3 = unit_overview(main, 2, row_2_offset, db.units[3])
if db.facility.num_units == 4 then uo_4 = unit_overview(main, 84, row_2_offset, db.units[4]) end
cnc_y_start = cnc_y_start + uo_3.height() + 1
if db.facility.num_units == 4 then
uo_4 = unit_overview(main, 84, row_2_offset, db.units[4])
end
end
-- command & control
TextBox{parent=main,y=cnc_y_start,text=util.strrep("\x8c", header.width()),alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header}
-- testing
---@fixme remove test code
ColorMap{parent=main,x=2,y=(main.height()-1)}
PushButton{parent=main,x=2,y=(main.height()-20),text="TEST 1",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_1}
PushButton{parent=main,x=2,y=(cnc_y_start+2),text="TEST 1",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_1}
PushButton{parent=main,x=2,text="TEST 2",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_2}
PushButton{parent=main,x=2,text="TEST 3",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_3}
PushButton{parent=main,x=2,text="TEST 4",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_4}
@@ -64,7 +79,7 @@ local function init(monitor)
PushButton{parent=main,x=2,text="STOP",min_width=8,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.stop}
PushButton{parent=main,x=2,text="PSCALE",min_width=8,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_power_scale}
SwitchButton{parent=main,x=12,y=(main.height()-20),text="CONTAINMENT BREACH",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_breach}
SwitchButton{parent=main,x=12,y=(cnc_y_start+2),text="CONTAINMENT BREACH",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_breach}
SwitchButton{parent=main,x=12,text="CONTAINMENT RADIATION",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_rad}
SwitchButton{parent=main,x=12,text="REACTOR LOST",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_lost}
SwitchButton{parent=main,x=12,text="CRITICAL DAMAGE",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_crit}

View File

@@ -2,15 +2,11 @@
-- Reactor Unit SCADA Coordinator GUI
--
local tcallbackdsp = require("scada-common.tcallbackdsp")
local style = require("coordinator.ui.style")
local iocontrol = require("coordinator.iocontrol")
local unit_detail = require("coordinator.ui.components.unit_detail")
local style = require("coordinator.ui.style")
local unit_detail = require("coordinator.ui.components.unit_detail")
local DisplayBox = require("graphics.elements.displaybox")
local DisplayBox = require("graphics.elements.displaybox")
-- create a unit view
---@param monitor table