Merge pull request #618 from MikaylaFischler/572-audible-alarms-on-facility-radiation
572 audible alarms on facility radiation
This commit is contained in:
commit
f1014ce941
@ -465,7 +465,8 @@ types.ALARM = {
|
|||||||
ReactorHighWaste = 9,
|
ReactorHighWaste = 9,
|
||||||
RPSTransient = 10,
|
RPSTransient = 10,
|
||||||
RCSTransient = 11,
|
RCSTransient = 11,
|
||||||
TurbineTrip = 12
|
TurbineTrip = 12,
|
||||||
|
FacilityRadiation = 13
|
||||||
}
|
}
|
||||||
|
|
||||||
types.ALARM_NAMES = {
|
types.ALARM_NAMES = {
|
||||||
@ -480,7 +481,8 @@ types.ALARM_NAMES = {
|
|||||||
"ReactorHighWaste",
|
"ReactorHighWaste",
|
||||||
"RPSTransient",
|
"RPSTransient",
|
||||||
"RCSTransient",
|
"RCSTransient",
|
||||||
"TurbineTrip"
|
"TurbineTrip",
|
||||||
|
"FacilityRadiation"
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum ALARM_PRIORITY
|
---@enum ALARM_PRIORITY
|
||||||
|
|||||||
@ -24,7 +24,7 @@ local t_pack = table.pack
|
|||||||
local util = {}
|
local util = {}
|
||||||
|
|
||||||
-- scada-common version
|
-- scada-common version
|
||||||
util.version = "1.5.1"
|
util.version = "1.5.2"
|
||||||
|
|
||||||
util.TICK_TIME_S = 0.05
|
util.TICK_TIME_S = 0.05
|
||||||
util.TICK_TIME_MS = 50
|
util.TICK_TIME_MS = 50
|
||||||
|
|||||||
137
supervisor/alarm_ctl.lua
Normal file
137
supervisor/alarm_ctl.lua
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
local log = require("scada-common.log")
|
||||||
|
local types = require("scada-common.types")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local ALARM_STATE = types.ALARM_STATE
|
||||||
|
|
||||||
|
---@class alarm_def
|
||||||
|
---@field state ALARM_INT_STATE internal alarm state
|
||||||
|
---@field trip_time integer time (ms) when first tripped
|
||||||
|
---@field hold_time integer time (s) to hold before tripping
|
||||||
|
---@field id ALARM alarm ID
|
||||||
|
---@field tier integer alarm urgency tier (0 = highest)
|
||||||
|
|
||||||
|
local AISTATE_NAMES = {
|
||||||
|
"INACTIVE",
|
||||||
|
"TRIPPING",
|
||||||
|
"TRIPPED",
|
||||||
|
"ACKED",
|
||||||
|
"RING_BACK",
|
||||||
|
"RING_BACK_TRIPPING"
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum ALARM_INT_STATE
|
||||||
|
local AISTATE = {
|
||||||
|
INACTIVE = 1,
|
||||||
|
TRIPPING = 2,
|
||||||
|
TRIPPED = 3,
|
||||||
|
ACKED = 4,
|
||||||
|
RING_BACK = 5,
|
||||||
|
RING_BACK_TRIPPING = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
local alarm_ctl = {}
|
||||||
|
|
||||||
|
alarm_ctl.AISTATE = AISTATE
|
||||||
|
alarm_ctl.AISTATE_NAMES = AISTATE_NAMES
|
||||||
|
|
||||||
|
-- update an alarm state based on its current status and if it is tripped
|
||||||
|
---@param caller_tag string tag to use in log messages
|
||||||
|
---@param alarm_states { [ALARM]: ALARM_STATE } unit instance
|
||||||
|
---@param tripped boolean if the alarm condition is sti ll active
|
||||||
|
---@param alarm alarm_def alarm table
|
||||||
|
---@param no_ring_back boolean? true to skip the ring back state, returning to inactive instead
|
||||||
|
---@return boolean new_trip if the alarm just changed to being tripped
|
||||||
|
function alarm_ctl.update_alarm_state(caller_tag, alarm_states, tripped, alarm, no_ring_back)
|
||||||
|
local int_state = alarm.state
|
||||||
|
local ext_state = alarm_states[alarm.id]
|
||||||
|
|
||||||
|
-- alarm inactive
|
||||||
|
if int_state == AISTATE.INACTIVE then
|
||||||
|
if tripped then
|
||||||
|
alarm.trip_time = util.time_ms()
|
||||||
|
if alarm.hold_time > 0 then
|
||||||
|
alarm.state = AISTATE.TRIPPING
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
||||||
|
else
|
||||||
|
alarm.state = AISTATE.TRIPPED
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.TRIPPED
|
||||||
|
log.info(util.c(caller_tag, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): TRIPPED [PRIORITY ",
|
||||||
|
types.ALARM_PRIORITY_NAMES[alarm.tier],"]"))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
alarm.trip_time = util.time_ms()
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
||||||
|
end
|
||||||
|
-- alarm condition met, but not yet for required hold time
|
||||||
|
elseif (int_state == AISTATE.TRIPPING) or (int_state == AISTATE.RING_BACK_TRIPPING) then
|
||||||
|
if tripped then
|
||||||
|
local elapsed = util.time_ms() - alarm.trip_time
|
||||||
|
if elapsed > (alarm.hold_time * 1000) then
|
||||||
|
alarm.state = AISTATE.TRIPPED
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.TRIPPED
|
||||||
|
log.info(util.c(caller_tag, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): TRIPPED [PRIORITY ",
|
||||||
|
types.ALARM_PRIORITY_NAMES[alarm.tier],"]"))
|
||||||
|
end
|
||||||
|
elseif int_state == AISTATE.RING_BACK_TRIPPING then
|
||||||
|
alarm.trip_time = 0
|
||||||
|
alarm.state = AISTATE.RING_BACK
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.RING_BACK
|
||||||
|
else
|
||||||
|
alarm.trip_time = 0
|
||||||
|
alarm.state = AISTATE.INACTIVE
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
||||||
|
end
|
||||||
|
-- alarm tripped and alarming
|
||||||
|
elseif int_state == AISTATE.TRIPPED then
|
||||||
|
if tripped then
|
||||||
|
if ext_state == ALARM_STATE.ACKED then
|
||||||
|
-- was acked by coordinator
|
||||||
|
alarm.state = AISTATE.ACKED
|
||||||
|
end
|
||||||
|
elseif no_ring_back then
|
||||||
|
alarm.state = AISTATE.INACTIVE
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
||||||
|
else
|
||||||
|
alarm.state = AISTATE.RING_BACK
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.RING_BACK
|
||||||
|
end
|
||||||
|
-- alarm acknowledged but still tripped
|
||||||
|
elseif int_state == AISTATE.ACKED then
|
||||||
|
if not tripped then
|
||||||
|
if no_ring_back then
|
||||||
|
alarm.state = AISTATE.INACTIVE
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
||||||
|
else
|
||||||
|
alarm.state = AISTATE.RING_BACK
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.RING_BACK
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- alarm no longer tripped, operator must reset to clear
|
||||||
|
elseif int_state == AISTATE.RING_BACK then
|
||||||
|
if tripped then
|
||||||
|
alarm.trip_time = util.time_ms()
|
||||||
|
if alarm.hold_time > 0 then
|
||||||
|
alarm.state = AISTATE.RING_BACK_TRIPPING
|
||||||
|
else
|
||||||
|
alarm.state = AISTATE.TRIPPED
|
||||||
|
alarm_states[alarm.id] = ALARM_STATE.TRIPPED
|
||||||
|
end
|
||||||
|
elseif ext_state == ALARM_STATE.INACTIVE then
|
||||||
|
-- was reset by coordinator
|
||||||
|
alarm.state = AISTATE.INACTIVE
|
||||||
|
alarm.trip_time = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.error(util.c(caller_tag, " invalid alarm state for alarm ", alarm.id), true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for state change
|
||||||
|
if alarm.state ~= int_state then
|
||||||
|
local change_str = util.c(AISTATE_NAMES[int_state], " -> ", AISTATE_NAMES[alarm.state])
|
||||||
|
log.debug(util.c(caller_tag, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): ", change_str))
|
||||||
|
return alarm.state == AISTATE.TRIPPED
|
||||||
|
else return false end
|
||||||
|
end
|
||||||
|
|
||||||
|
return alarm_ctl
|
||||||
@ -2,13 +2,19 @@ local log = require("scada-common.log")
|
|||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local alarm_ctl = require("supervisor.alarm_ctl")
|
||||||
local unit = require("supervisor.unit")
|
local unit = require("supervisor.unit")
|
||||||
local fac_update = require("supervisor.facility_update")
|
local fac_update = require("supervisor.facility_update")
|
||||||
|
|
||||||
local rsctl = require("supervisor.session.rsctl")
|
local rsctl = require("supervisor.session.rsctl")
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
|
local AISTATE = alarm_ctl.AISTATE
|
||||||
|
|
||||||
|
local ALARM = types.ALARM
|
||||||
|
local ALARM_STATE = types.ALARM_STATE
|
||||||
local AUTO_GROUP = types.AUTO_GROUP
|
local AUTO_GROUP = types.AUTO_GROUP
|
||||||
|
local PRIO = types.ALARM_PRIORITY
|
||||||
local PROCESS = types.PROCESS
|
local PROCESS = types.PROCESS
|
||||||
local RTU_ID_FAIL = types.RTU_ID_FAIL
|
local RTU_ID_FAIL = types.RTU_ID_FAIL
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
@ -138,7 +144,17 @@ function facility.new(config)
|
|||||||
imtx_last_charge = 0,
|
imtx_last_charge = 0,
|
||||||
imtx_last_charge_t = 0,
|
imtx_last_charge_t = 0,
|
||||||
-- track faulted induction matrix update times to reject
|
-- track faulted induction matrix update times to reject
|
||||||
imtx_faulted_times = { 0, 0, 0 }
|
imtx_faulted_times = { 0, 0, 0 },
|
||||||
|
-- facility alarms
|
||||||
|
---@type { [string]: alarm_def }
|
||||||
|
alarms = {
|
||||||
|
-- radiation monitor alarm for the facility
|
||||||
|
FacilityRadiation = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.FacilityRadiation, tier = PRIO.CRITICAL },
|
||||||
|
},
|
||||||
|
---@type { [ALARM]: ALARM_STATE }
|
||||||
|
alarm_states = {
|
||||||
|
[ALARM.FacilityRadiation] = ALARM_STATE.INACTIVE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
--#region SETUP
|
--#region SETUP
|
||||||
@ -335,6 +351,9 @@ function facility.new(config)
|
|||||||
-- unit tasks
|
-- unit tasks
|
||||||
f_update.unit_mgmt()
|
f_update.unit_mgmt()
|
||||||
|
|
||||||
|
-- update alarm states right before updating the audio
|
||||||
|
f_update.update_alarms()
|
||||||
|
|
||||||
-- update alarm tones
|
-- update alarm tones
|
||||||
f_update.alarm_audio()
|
f_update.alarm_audio()
|
||||||
end
|
end
|
||||||
@ -404,10 +423,14 @@ function facility.new(config)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ack all alarms on all reactor units
|
-- ack all alarms on all reactor units and the facility
|
||||||
function public.ack_all()
|
function public.ack_all()
|
||||||
for i = 1, #self.units do
|
-- unit alarms
|
||||||
self.units[i].ack_all()
|
for i = 1, #self.units do self.units[i].ack_all() end
|
||||||
|
|
||||||
|
-- facility alarms
|
||||||
|
for id, state in pairs(self.alarm_states) do
|
||||||
|
if state == ALARM_STATE.TRIPPED then self.alarm_states[id] = ALARM_STATE.ACKED end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,8 @@ local rsio = require("scada-common.rsio")
|
|||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local alarm_ctl = require("supervisor.alarm_ctl")
|
||||||
|
|
||||||
local plc = require("supervisor.session.plc")
|
local plc = require("supervisor.session.plc")
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
@ -643,7 +645,7 @@ function update.auto_safety()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if (self.mode ~= PROCESS.INACTIVE) and (self.mode ~= PROCESS.SYSTEM_ALARM_IDLE) then
|
if (self.mode ~= PROCESS.INACTIVE) and (self.mode ~= PROCESS.SYSTEM_ALARM_IDLE) then
|
||||||
local scram = astatus.matrix_fault or astatus.matrix_fill or astatus.crit_alarm or astatus.gen_fault
|
local scram = astatus.matrix_fault or astatus.matrix_fill or astatus.crit_alarm or astatus.radiation or astatus.gen_fault
|
||||||
|
|
||||||
if scram and not self.ascram then
|
if scram and not self.ascram then
|
||||||
-- SCRAM all units
|
-- SCRAM all units
|
||||||
@ -714,11 +716,17 @@ function update.post_auto()
|
|||||||
self.mode = next_mode
|
self.mode = next_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- update facility alarm states
|
||||||
|
function update.update_alarms()
|
||||||
|
-- Facility Radiation
|
||||||
|
alarm_ctl.update_alarm_state("FAC", self.alarm_states, self.ascram_status.radiation, self.alarms.FacilityRadiation, true)
|
||||||
|
end
|
||||||
|
|
||||||
-- update alarm audio control
|
-- update alarm audio control
|
||||||
function update.alarm_audio()
|
function update.alarm_audio()
|
||||||
local allow_test = self.allow_testing and self.test_tone_set
|
local allow_test = self.allow_testing and self.test_tone_set
|
||||||
|
|
||||||
local alarms = { false, false, false, false, false, false, false, false, false, false, false, false }
|
local alarms = { false, false, false, false, false, false, false, false, false, false, false, false, false }
|
||||||
|
|
||||||
-- reset tone states before re-evaluting
|
-- reset tone states before re-evaluting
|
||||||
for i = 1, #self.tone_states do self.tone_states[i] = false end
|
for i = 1, #self.tone_states do self.tone_states[i] = false end
|
||||||
@ -734,8 +742,11 @@ function update.alarm_audio()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- record facility alarms
|
||||||
|
alarms[ALARM.FacilityRadiation] = self.alarm_states[ALARM.FacilityRadiation] == ALARM_STATE.TRIPPED
|
||||||
|
|
||||||
|
-- clear testing alarms if we aren't using them
|
||||||
if not self.test_tone_reset then
|
if not self.test_tone_reset then
|
||||||
-- clear testing alarms if we aren't using them
|
|
||||||
for i = 1, #self.test_alarm_states do self.test_alarm_states[i] = false end
|
for i = 1, #self.test_alarm_states do self.test_alarm_states[i] = false end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -774,7 +785,7 @@ function update.alarm_audio()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- radiation is a big concern, always play this CRITICAL level alarm if active
|
-- radiation is a big concern, always play this CRITICAL level alarm if active
|
||||||
if alarms[ALARM.ContainmentRadiation] then
|
if alarms[ALARM.ContainmentRadiation] or alarms[ALARM.FacilityRadiation] then
|
||||||
self.tone_states[TONE.T_800Hz_1000Hz_Alt] = true
|
self.tone_states[TONE.T_800Hz_1000Hz_Alt] = true
|
||||||
-- we are going to disable the RPS trip alarm audio due to conflict, and if it was enabled
|
-- we are going to disable the RPS trip alarm audio due to conflict, and if it was enabled
|
||||||
-- then we can re-enable the reactor lost alarm audio since it doesn't painfully combine with this one
|
-- then we can re-enable the reactor lost alarm audio since it doesn't painfully combine with this one
|
||||||
|
|||||||
@ -23,7 +23,7 @@ local supervisor = require("supervisor.supervisor")
|
|||||||
|
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "v1.6.8"
|
local SUPERVISOR_VERSION = "v1.6.9"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
|||||||
@ -3,20 +3,23 @@ local rsio = require("scada-common.rsio")
|
|||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local logic = require("supervisor.unitlogic")
|
local alarm_ctl = require("supervisor.alarm_ctl")
|
||||||
|
local unit_logic = require("supervisor.unit_logic")
|
||||||
|
|
||||||
local plc = require("supervisor.session.plc")
|
local plc = require("supervisor.session.plc")
|
||||||
local rsctl = require("supervisor.session.rsctl")
|
local rsctl = require("supervisor.session.rsctl")
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local WASTE_MODE = types.WASTE_MODE
|
local AISTATE = alarm_ctl.AISTATE
|
||||||
local WASTE = types.WASTE_PRODUCT
|
|
||||||
local ALARM = types.ALARM
|
local ALARM = types.ALARM
|
||||||
local PRIO = types.ALARM_PRIORITY
|
|
||||||
local ALARM_STATE = types.ALARM_STATE
|
local ALARM_STATE = types.ALARM_STATE
|
||||||
local TRI_FAIL = types.TRI_FAIL
|
local PRIO = types.ALARM_PRIORITY
|
||||||
local RTU_ID_FAIL = types.RTU_ID_FAIL
|
local RTU_ID_FAIL = types.RTU_ID_FAIL
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
|
local TRI_FAIL = types.TRI_FAIL
|
||||||
|
local WASTE_MODE = types.WASTE_MODE
|
||||||
|
local WASTE = types.WASTE_PRODUCT
|
||||||
|
|
||||||
local PLC_S_CMDS = plc.PLC_S_CMDS
|
local PLC_S_CMDS = plc.PLC_S_CMDS
|
||||||
|
|
||||||
@ -37,23 +40,6 @@ local DT_KEYS = {
|
|||||||
TurbinePower = "TPR"
|
TurbinePower = "TPR"
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum ALARM_INT_STATE
|
|
||||||
local AISTATE = {
|
|
||||||
INACTIVE = 1,
|
|
||||||
TRIPPING = 2,
|
|
||||||
TRIPPED = 3,
|
|
||||||
ACKED = 4,
|
|
||||||
RING_BACK = 5,
|
|
||||||
RING_BACK_TRIPPING = 6
|
|
||||||
}
|
|
||||||
|
|
||||||
---@class alarm_def
|
|
||||||
---@field state ALARM_INT_STATE internal alarm state
|
|
||||||
---@field trip_time integer time (ms) when first tripped
|
|
||||||
---@field hold_time integer time (s) to hold before tripping
|
|
||||||
---@field id ALARM alarm ID
|
|
||||||
---@field tier integer alarm urgency tier (0 = highest)
|
|
||||||
|
|
||||||
-- burn rate to idle at
|
-- burn rate to idle at
|
||||||
local IDLE_RATE = 0.01
|
local IDLE_RATE = 0.01
|
||||||
|
|
||||||
@ -81,7 +67,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle, aux_coolant)
|
|||||||
num_boilers = num_boilers,
|
num_boilers = num_boilers,
|
||||||
num_turbines = num_turbines,
|
num_turbines = num_turbines,
|
||||||
aux_coolant = aux_coolant,
|
aux_coolant = aux_coolant,
|
||||||
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
types = { DT_KEYS = DT_KEYS },
|
||||||
-- rtus
|
-- rtus
|
||||||
rtu_list = {}, ---@type unit_session[][]
|
rtu_list = {}, ---@type unit_session[][]
|
||||||
redstone = {}, ---@type redstone_session[]
|
redstone = {}, ---@type redstone_session[]
|
||||||
@ -597,20 +583,20 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle, aux_coolant)
|
|||||||
_dt__compute_all()
|
_dt__compute_all()
|
||||||
|
|
||||||
-- update annunciator logic
|
-- update annunciator logic
|
||||||
logic.update_annunciator(self)
|
unit_logic.update_annunciator(self)
|
||||||
|
|
||||||
-- update alarm status
|
-- update alarm status
|
||||||
logic.update_alarms(self)
|
unit_logic.update_alarms(self)
|
||||||
|
|
||||||
-- if in auto mode, SCRAM on certain alarms
|
-- if in auto mode, SCRAM on certain alarms
|
||||||
logic.update_auto_safety(public, self)
|
unit_logic.update_auto_safety(self, public)
|
||||||
|
|
||||||
-- update status text
|
-- update status text
|
||||||
logic.update_status_text(self)
|
unit_logic.update_status_text(self)
|
||||||
|
|
||||||
-- handle redstone I/O
|
-- handle redstone I/O
|
||||||
if #self.redstone > 0 then
|
if #self.redstone > 0 then
|
||||||
logic.handle_redstone(self)
|
unit_logic.handle_redstone(self)
|
||||||
elseif not self.plc_cache.rps_trip then
|
elseif not self.plc_cache.rps_trip then
|
||||||
self.em_cool_opened = false
|
self.em_cool_opened = false
|
||||||
end
|
end
|
||||||
@ -775,10 +761,8 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle, aux_coolant)
|
|||||||
|
|
||||||
-- acknowledge all alarms (if possible)
|
-- acknowledge all alarms (if possible)
|
||||||
function public.ack_all()
|
function public.ack_all()
|
||||||
for i = 1, #self.db.alarm_states do
|
for id, state in pairs(self.db.alarm_states) do
|
||||||
if self.db.alarm_states[i] == ALARM_STATE.TRIPPED then
|
if state == ALARM_STATE.TRIPPED then self.db.alarm_states[id] = ALARM_STATE.ACKED end
|
||||||
self.db.alarm_states[i] = ALARM_STATE.ACKED
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
local const = require("scada-common.constants")
|
local const = require("scada-common.constants")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local plc = require("supervisor.session.plc")
|
local alarm_ctl = require("supervisor.alarm_ctl")
|
||||||
|
|
||||||
local qtypes = require("supervisor.session.rtu.qtypes")
|
local plc = require("supervisor.session.plc")
|
||||||
|
|
||||||
|
local qtypes = require("supervisor.session.rtu.qtypes")
|
||||||
|
|
||||||
|
local AISTATE = alarm_ctl.AISTATE
|
||||||
|
|
||||||
local RPS_TRIP_CAUSE = types.RPS_TRIP_CAUSE
|
local RPS_TRIP_CAUSE = types.RPS_TRIP_CAUSE
|
||||||
local TRI_FAIL = types.TRI_FAIL
|
local TRI_FAIL = types.TRI_FAIL
|
||||||
@ -22,20 +26,10 @@ local IO = rsio.IO
|
|||||||
|
|
||||||
local PLC_S_CMDS = plc.PLC_S_CMDS
|
local PLC_S_CMDS = plc.PLC_S_CMDS
|
||||||
|
|
||||||
local AISTATE_NAMES = {
|
local ANNUNC_LIMS = const.ANNUNCIATOR_LIMITS
|
||||||
"INACTIVE",
|
local ALARM_LIMS = const.ALARM_LIMITS
|
||||||
"TRIPPING",
|
|
||||||
"TRIPPED",
|
|
||||||
"ACKED",
|
|
||||||
"RING_BACK",
|
|
||||||
"RING_BACK_TRIPPING"
|
|
||||||
}
|
|
||||||
|
|
||||||
local FLOW_STABILITY_DELAY_MS = const.FLOW_STABILITY_DELAY_MS
|
local FLOW_STABILITY_DELAY_MS = const.FLOW_STABILITY_DELAY_MS
|
||||||
|
local RS_THRESH = const.RS_THRESHOLDS
|
||||||
local ANNUNC_LIMS = const.ANNUNCIATOR_LIMITS
|
|
||||||
local ALARM_LIMS = const.ALARM_LIMITS
|
|
||||||
local RS_THRESH = const.RS_THRESHOLDS
|
|
||||||
|
|
||||||
---@class unit_logic_extension
|
---@class unit_logic_extension
|
||||||
local logic = {}
|
local logic = {}
|
||||||
@ -426,97 +420,16 @@ function logic.update_annunciator(self)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- update an alarm state given conditions
|
-- update an alarm state given conditions
|
||||||
---@param self _unit_self unit instance
|
---@param self _unit_self
|
||||||
---@param tripped boolean if the alarm condition is still active
|
---@param tripped boolean if the alarm condition is still active
|
||||||
---@param alarm alarm_def alarm table
|
---@param alarm alarm_def alarm table
|
||||||
---@return boolean new_trip if the alarm just changed to being tripped
|
---@return boolean new_trip if the alarm just changed to being tripped
|
||||||
local function _update_alarm_state(self, tripped, alarm)
|
local function _update_alarm_state(self, tripped, alarm)
|
||||||
local AISTATE = self.types.AISTATE
|
return alarm_ctl.update_alarm_state("UNIT " .. self.r_id, self.db.alarm_states, tripped, alarm)
|
||||||
local int_state = alarm.state
|
|
||||||
local ext_state = self.db.alarm_states[alarm.id]
|
|
||||||
|
|
||||||
-- alarm inactive
|
|
||||||
if int_state == AISTATE.INACTIVE then
|
|
||||||
if tripped then
|
|
||||||
alarm.trip_time = util.time_ms()
|
|
||||||
if alarm.hold_time > 0 then
|
|
||||||
alarm.state = AISTATE.TRIPPING
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
|
||||||
else
|
|
||||||
alarm.state = AISTATE.TRIPPED
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.TRIPPED
|
|
||||||
log.info(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): TRIPPED [PRIORITY ",
|
|
||||||
types.ALARM_PRIORITY_NAMES[alarm.tier],"]"))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
alarm.trip_time = util.time_ms()
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
|
||||||
end
|
|
||||||
-- alarm condition met, but not yet for required hold time
|
|
||||||
elseif (int_state == AISTATE.TRIPPING) or (int_state == AISTATE.RING_BACK_TRIPPING) then
|
|
||||||
if tripped then
|
|
||||||
local elapsed = util.time_ms() - alarm.trip_time
|
|
||||||
if elapsed > (alarm.hold_time * 1000) then
|
|
||||||
alarm.state = AISTATE.TRIPPED
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.TRIPPED
|
|
||||||
log.info(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): TRIPPED [PRIORITY ",
|
|
||||||
types.ALARM_PRIORITY_NAMES[alarm.tier],"]"))
|
|
||||||
end
|
|
||||||
elseif int_state == AISTATE.RING_BACK_TRIPPING then
|
|
||||||
alarm.trip_time = 0
|
|
||||||
alarm.state = AISTATE.RING_BACK
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.RING_BACK
|
|
||||||
else
|
|
||||||
alarm.trip_time = 0
|
|
||||||
alarm.state = AISTATE.INACTIVE
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.INACTIVE
|
|
||||||
end
|
|
||||||
-- alarm tripped and alarming
|
|
||||||
elseif int_state == AISTATE.TRIPPED then
|
|
||||||
if tripped then
|
|
||||||
if ext_state == ALARM_STATE.ACKED then
|
|
||||||
-- was acked by coordinator
|
|
||||||
alarm.state = AISTATE.ACKED
|
|
||||||
end
|
|
||||||
else
|
|
||||||
alarm.state = AISTATE.RING_BACK
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.RING_BACK
|
|
||||||
end
|
|
||||||
-- alarm acknowledged but still tripped
|
|
||||||
elseif int_state == AISTATE.ACKED then
|
|
||||||
if not tripped then
|
|
||||||
alarm.state = AISTATE.RING_BACK
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.RING_BACK
|
|
||||||
end
|
|
||||||
-- alarm no longer tripped, operator must reset to clear
|
|
||||||
elseif int_state == AISTATE.RING_BACK then
|
|
||||||
if tripped then
|
|
||||||
alarm.trip_time = util.time_ms()
|
|
||||||
if alarm.hold_time > 0 then
|
|
||||||
alarm.state = AISTATE.RING_BACK_TRIPPING
|
|
||||||
else
|
|
||||||
alarm.state = AISTATE.TRIPPED
|
|
||||||
self.db.alarm_states[alarm.id] = ALARM_STATE.TRIPPED
|
|
||||||
end
|
|
||||||
elseif ext_state == ALARM_STATE.INACTIVE then
|
|
||||||
-- was reset by coordinator
|
|
||||||
alarm.state = AISTATE.INACTIVE
|
|
||||||
alarm.trip_time = 0
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.error(util.c("invalid alarm state for unit ", self.r_id, " alarm ", alarm.id), true)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check for state change
|
|
||||||
if alarm.state ~= int_state then
|
|
||||||
local change_str = util.c(AISTATE_NAMES[int_state], " -> ", AISTATE_NAMES[alarm.state])
|
|
||||||
log.debug(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): ", change_str))
|
|
||||||
return alarm.state == AISTATE.TRIPPED
|
|
||||||
else return false end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- evaluate alarm conditions
|
-- evaluate alarm conditions
|
||||||
---@param self _unit_self unit instance
|
---@param self _unit_self
|
||||||
function logic.update_alarms(self)
|
function logic.update_alarms(self)
|
||||||
local annunc = self.db.annunciator
|
local annunc = self.db.annunciator
|
||||||
local plc_cache = self.plc_cache
|
local plc_cache = self.plc_cache
|
||||||
@ -629,11 +542,9 @@ function logic.update_alarms(self)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- update the internal automatic safety control performed while in auto control mode
|
-- update the internal automatic safety control performed while in auto control mode
|
||||||
|
---@param self _unit_self
|
||||||
---@param public reactor_unit reactor unit public functions
|
---@param public reactor_unit reactor unit public functions
|
||||||
---@param self _unit_self unit instance
|
function logic.update_auto_safety(self, public)
|
||||||
function logic.update_auto_safety(public, self)
|
|
||||||
local AISTATE = self.types.AISTATE
|
|
||||||
|
|
||||||
if self.auto_engaged then
|
if self.auto_engaged then
|
||||||
local alarmed = false
|
local alarmed = false
|
||||||
|
|
||||||
@ -660,9 +571,8 @@ function logic.update_auto_safety(public, self)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- update the two unit status text messages
|
-- update the two unit status text messages
|
||||||
---@param self _unit_self unit instance
|
---@param self _unit_self
|
||||||
function logic.update_status_text(self)
|
function logic.update_status_text(self)
|
||||||
local AISTATE = self.types.AISTATE
|
|
||||||
local annunc = self.db.annunciator
|
local annunc = self.db.annunciator
|
||||||
|
|
||||||
-- check if an alarm is active (tripped or ack'd)
|
-- check if an alarm is active (tripped or ack'd)
|
||||||
@ -824,9 +734,8 @@ function logic.update_status_text(self)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- handle unit redstone I/O
|
-- handle unit redstone I/O
|
||||||
---@param self _unit_self unit instance
|
---@param self _unit_self
|
||||||
function logic.handle_redstone(self)
|
function logic.handle_redstone(self)
|
||||||
local AISTATE = self.types.AISTATE
|
|
||||||
local annunc = self.db.annunciator
|
local annunc = self.db.annunciator
|
||||||
local cache = self.plc_cache
|
local cache = self.plc_cache
|
||||||
local rps = cache.rps_status
|
local rps = cache.rps_status
|
||||||
@ -906,7 +815,7 @@ function logic.handle_redstone(self)
|
|||||||
if enable_emer_cool and not self.em_cool_opened then
|
if enable_emer_cool and not self.em_cool_opened then
|
||||||
log.debug(util.c(">> Emergency Coolant Enable Detail Report (Unit ", self.r_id, ") <<"))
|
log.debug(util.c(">> Emergency Coolant Enable Detail Report (Unit ", self.r_id, ") <<"))
|
||||||
log.debug(util.c("| CoolantLevelLow[", annunc.CoolantLevelLow, "] CoolantLevelLowLow[", rps.low_cool, "] ExcessHeatedCoolant[", rps.ex_hcool, "]"))
|
log.debug(util.c("| CoolantLevelLow[", annunc.CoolantLevelLow, "] CoolantLevelLowLow[", rps.low_cool, "] ExcessHeatedCoolant[", rps.ex_hcool, "]"))
|
||||||
log.debug(util.c("| ReactorOverTemp[", AISTATE_NAMES[self.alarms.ReactorOverTemp.state], "]"))
|
log.debug(util.c("| ReactorOverTemp[", alarm_ctl.AISTATE_NAMES[self.alarms.ReactorOverTemp.state], "]"))
|
||||||
|
|
||||||
for i = 1, #annunc.WaterLevelLow do
|
for i = 1, #annunc.WaterLevelLow do
|
||||||
log.debug(util.c("| WaterLevelLow(", i, ")[", annunc.WaterLevelLow[i], "]"))
|
log.debug(util.c("| WaterLevelLow(", i, ")[", annunc.WaterLevelLow[i], "]"))
|
||||||
Loading…
x
Reference in New Issue
Block a user