mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#131 start of unit status text, added updating coordinator waste processing option on reconnect
This commit is contained in:
parent
5224dcbd25
commit
6bdde02268
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -174,7 +174,7 @@ local function hazard_button(args)
|
||||
end
|
||||
end
|
||||
|
||||
-- set the value
|
||||
-- set the value (true simulates pressing the button)
|
||||
---@param val boolean new value
|
||||
function e.set_value(val)
|
||||
if val then e.handle_touch(core.events.touch("", 1, 1)) end
|
||||
|
@ -109,7 +109,6 @@ local function multi_button(args)
|
||||
function e.set_value(val)
|
||||
e.value = val
|
||||
draw()
|
||||
args.callback(e.value)
|
||||
end
|
||||
|
||||
-- initial draw
|
||||
|
@ -72,7 +72,7 @@ local function push_button(args)
|
||||
end
|
||||
end
|
||||
|
||||
-- set the value
|
||||
-- set the value (true simulates pressing the button)
|
||||
---@param val boolean new value
|
||||
function e.set_value(val)
|
||||
if val then e.handle_touch(core.events.touch("", 1, 1)) end
|
||||
|
@ -82,9 +82,6 @@ local function switch_button(args)
|
||||
-- set state
|
||||
e.value = val
|
||||
draw_state()
|
||||
|
||||
-- call the touch callback with state
|
||||
args.callback(e.value)
|
||||
end
|
||||
|
||||
return e.get()
|
||||
|
@ -129,7 +129,13 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
|
||||
|
||||
for i = 1, #self.units do
|
||||
local unit = self.units[i] ---@type reactor_unit
|
||||
status[unit.get_id()] = { unit.get_reactor_status(), unit.get_annunciator(), unit.get_alarms(), unit.get_rtu_statuses() }
|
||||
status[unit.get_id()] = {
|
||||
unit.get_reactor_status(),
|
||||
unit.get_rtu_statuses(),
|
||||
unit.get_annunciator(),
|
||||
unit.get_alarms(),
|
||||
unit.get_state()
|
||||
}
|
||||
end
|
||||
|
||||
_send(SCADA_CRDN_TYPES.UNIT_STATUSES, status)
|
||||
|
@ -54,6 +54,13 @@ local aistate_string = {
|
||||
"RING_BACK_TRIPPING"
|
||||
}
|
||||
|
||||
-- check if an alarm is active (tripped or ack'd)
|
||||
---@param alarm table alarm entry
|
||||
---@return boolean active
|
||||
local function is_active(alarm)
|
||||
return alarm.state == AISTATE.TRIPPED or alarm.state == AISTATE.ACKED
|
||||
end
|
||||
|
||||
---@class alarm_def
|
||||
---@field state ALARM_INT_STATE internal alarm state
|
||||
---@field trip_time integer time (ms) when first tripped
|
||||
@ -73,11 +80,16 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
turbines = {},
|
||||
boilers = {},
|
||||
redstone = {},
|
||||
-- state tracking
|
||||
deltas = {},
|
||||
last_heartbeat = 0,
|
||||
damage_initial = 0,
|
||||
damage_start = 0,
|
||||
waste_mode = WASTE_MODE.AUTO,
|
||||
status_text = { "Unknown", "Awaiting Connection..." },
|
||||
-- logic for alarms
|
||||
had_reactor = false,
|
||||
start_time = 0,
|
||||
start_ms = 0,
|
||||
plc_cache = {
|
||||
ok = false,
|
||||
rps_trip = false,
|
||||
@ -110,7 +122,7 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
RPSTransient = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.RPSTransient, tier = PRIO.URGENT },
|
||||
-- BoilRateMismatch, CoolantFeedMismatch, SteamFeedMismatch, MaxWaterReturnFeed
|
||||
RCSTransient = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 5, id = ALARM.RCSTransient, tier = PRIO.TIMELY },
|
||||
-- "It's just a routine turbin' trip!" -Bill Gibson
|
||||
-- "It's just a routine turbin' trip!" -Bill Gibson, "The China Syndrome"
|
||||
TurbineTrip = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.TurbineTrip, tier = PRIO.URGENT }
|
||||
},
|
||||
---@class unit_db
|
||||
@ -244,10 +256,10 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
end
|
||||
|
||||
-- waste valves
|
||||
local waste_pu = { open = function () __rs_w(IO.WASTE_PU, true) end, close = function () __rs_w(IO.WASTE_PU, false) end }
|
||||
local waste_sna = { open = function () __rs_w(IO.WASTE_PO, true) end, close = function () __rs_w(IO.WASTE_PO, false) end }
|
||||
local waste_pu = { open = function () __rs_w(IO.WASTE_PU, true) end, close = function () __rs_w(IO.WASTE_PU, false) end }
|
||||
local waste_sna = { open = function () __rs_w(IO.WASTE_PO, true) end, close = function () __rs_w(IO.WASTE_PO, false) end }
|
||||
local waste_po = { open = function () __rs_w(IO.WASTE_POPL, true) end, close = function () __rs_w(IO.WASTE_POPL, false) end }
|
||||
local waste_sps = { open = function () __rs_w(IO.WASTE_AM, true) end, close = function () __rs_w(IO.WASTE_AM, false) end }
|
||||
local waste_sps = { open = function () __rs_w(IO.WASTE_AM, true) end, close = function () __rs_w(IO.WASTE_AM, false) end }
|
||||
|
||||
--#endregion
|
||||
|
||||
@ -396,14 +408,14 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
-- check PLC status
|
||||
self.db.annunciator.PLCOnline = (self.plc_s ~= nil) and (self.plc_s.open)
|
||||
|
||||
if self.plc_s ~= nil then
|
||||
if self.plc_i ~= nil then
|
||||
local plc_db = self.plc_i.get_db()
|
||||
|
||||
-- record reactor start time (some alarms are delayed during reactor heatup)
|
||||
if self.start_time == 0 and plc_db.mek_status.status then
|
||||
self.start_time = util.time_ms()
|
||||
if self.start_ms == 0 and plc_db.mek_status.status then
|
||||
self.start_ms = util.time_ms()
|
||||
elseif not plc_db.mek_status.status then
|
||||
self.start_time = 0
|
||||
self.start_ms = 0
|
||||
end
|
||||
|
||||
-- record reactor stats
|
||||
@ -414,6 +426,15 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
self.plc_cache.temp = plc_db.mek_status.temp
|
||||
self.plc_cache.waste = plc_db.mek_status.waste_fill
|
||||
|
||||
-- track damage
|
||||
if (self.damage_initial == 0) and (plc_db.mek_status.damage > 0) then
|
||||
self.damage_start = util.time_s()
|
||||
self.damage_initial = plc_db.mek_status.damage
|
||||
else
|
||||
self.damage_initial = 0
|
||||
self.damage_start = 0
|
||||
end
|
||||
|
||||
-- heartbeat blink about every second
|
||||
if self.last_heartbeat + 1000 < plc_db.last_status_update then
|
||||
self.db.annunciator.PLCHeartbeat = not self.db.annunciator.PLCHeartbeat
|
||||
@ -633,9 +654,7 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
if plc_cache.rps_status.manual ~= nil then
|
||||
if plc_cache.rps_trip then
|
||||
for key, val in pairs(plc_cache.rps_status) do
|
||||
if key ~= "manual" and key ~= "timeout" then
|
||||
rps_alarm = rps_alarm or val
|
||||
end
|
||||
if key ~= "manual" and key ~= "timeout" then rps_alarm = rps_alarm or val end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -651,7 +670,7 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
local rcs_trans = any_low or any_over or annunc.RCPTrip or annunc.RCSFlowLow or annunc.MaxWaterReturnFeed
|
||||
|
||||
-- flow is ramping up right after reactor start, annunciator indicators for these states may not indicate a real issue
|
||||
if util.time_ms() - self.start_time > FLOW_STABILITY_DELAY_MS then
|
||||
if util.time_ms() - self.start_ms > FLOW_STABILITY_DELAY_MS then
|
||||
rcs_trans = rcs_trans or annunc.BoilRateMismatch or annunc.CoolantFeedMismatch or annunc.SteamFeedMismatch
|
||||
end
|
||||
|
||||
@ -758,6 +777,42 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
|
||||
-- update alarm status
|
||||
_update_alarms()
|
||||
|
||||
-- update status text (what the reactor doin?)
|
||||
if is_active(self.alarms.ContainmentBreach) then
|
||||
-- boom? or was boom disabled
|
||||
if self.plc_i ~= nil and self.plc_i.get_rps().force_dis then
|
||||
self.status_text = { "REACTOR FORCE DISABLED", "meltdown would have occured" }
|
||||
else
|
||||
self.status_text = { "CORE MELTDOWN", "reactor destroyed" }
|
||||
end
|
||||
elseif is_active(self.alarms.CriticalDamage) then
|
||||
-- so much for it being a "routine turbin' trip"...
|
||||
self.status_text = { "MELTDOWN IMMINENT", "evacuate facility immediately" }
|
||||
elseif is_active(self.alarms.ReactorDamage) then
|
||||
-- attempt to determine when a chance of a meltdown will occur
|
||||
self.status_text[1] = "Containment Taking Damage"
|
||||
if self.plc_cache.damage >= 100 then
|
||||
self.status_text[2] = "damage critical"
|
||||
elseif (self.plc_cache.damage - self.damage_initial) > 0 then
|
||||
local rate = (self.plc_cache.damage - self.damage_initial) / (util.time_s() - self.damage_start)
|
||||
local remaining_s = (100 - self.plc_cache.damage) * rate
|
||||
|
||||
self.status_text[2] = util.c("damage critical in ", remaining_s, "s")
|
||||
else
|
||||
self.status_text[2] = "estimating time to critical..."
|
||||
end
|
||||
-- connection dependent states
|
||||
elseif self.plc_i ~= nil then
|
||||
local plc_db = self.plc_i.get_db()
|
||||
if plc_db.mek_status.status then
|
||||
self.status_text = { "Active", "reactor nominal" }
|
||||
else
|
||||
self.status_text = { "Idle", "" }
|
||||
end
|
||||
else
|
||||
self.status_text = { "Reactor Off-line", "awaiting connection..." }
|
||||
end
|
||||
end
|
||||
|
||||
-- OPERATIONS --
|
||||
@ -792,24 +847,30 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
function public.set_waste(mode)
|
||||
if mode == WASTE_MODE.AUTO then
|
||||
---@todo automatic waste routing
|
||||
self.waste_mode = mode
|
||||
elseif mode == WASTE_MODE.PLUTONIUM then
|
||||
-- route through plutonium generation
|
||||
self.waste_mode = mode
|
||||
waste_pu.open()
|
||||
waste_sna.close()
|
||||
waste_po.close()
|
||||
waste_sps.close()
|
||||
elseif mode == WASTE_MODE.POLONIUM then
|
||||
-- route through polonium generation into pellets
|
||||
self.waste_mode = mode
|
||||
waste_pu.close()
|
||||
waste_sna.open()
|
||||
waste_po.open()
|
||||
waste_sps.close()
|
||||
elseif mode == WASTE_MODE.ANTI_MATTER then
|
||||
-- route through polonium generation into SPS
|
||||
self.waste_mode = mode
|
||||
waste_pu.close()
|
||||
waste_sna.open()
|
||||
waste_po.close()
|
||||
waste_sps.open()
|
||||
else
|
||||
log.debug(util.c("invalid waste mode setting ", mode))
|
||||
end
|
||||
end
|
||||
|
||||
@ -889,6 +950,11 @@ function unit.new(for_reactor, num_boilers, num_turbines)
|
||||
-- get the alarm states
|
||||
function public.get_alarms() return self.db.alarm_states end
|
||||
|
||||
-- get unit state (currently only waste mode)
|
||||
function public.get_state()
|
||||
return { self.status_text[1], self.status_text[2], self.waste_mode }
|
||||
end
|
||||
|
||||
-- get the reactor ID
|
||||
function public.get_id() return self.r_id end
|
||||
|
||||
|
@ -14,7 +14,7 @@ local svsessions = require("supervisor.session.svsessions")
|
||||
local config = require("supervisor.config")
|
||||
local supervisor = require("supervisor.supervisor")
|
||||
|
||||
local SUPERVISOR_VERSION = "beta-v0.8.2"
|
||||
local SUPERVISOR_VERSION = "beta-v0.8.3"
|
||||
|
||||
local print = util.print
|
||||
local println = util.println
|
||||
|
Loading…
Reference in New Issue
Block a user