#200 pocket alarm/status informational display, ECAM style

This commit is contained in:
Mikayla 2024-05-22 21:55:59 +00:00
parent 76e85da9d5
commit a268a770f2
7 changed files with 400 additions and 163 deletions

View File

@ -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])

View File

@ -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)

View File

@ -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,10 +589,11 @@ 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
unit.connected = data[2]
@ -721,6 +726,8 @@ function iocontrol.record_unit_data(data)
--#endregion
--#region RTU Devices
unit.boiler_data_tbl = data[7]
for id = 1, #unit.boiler_data_tbl do
@ -764,7 +771,202 @@ function iocontrol.record_unit_data(data)
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

View File

@ -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

View File

@ -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

View File

@ -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