/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file AlarmMgmt.c * * @author (last) Sean Nash * @date (last) 14-Oct-2020 * * @author (original) Sean Nash * @date (original) 07-Nov-2019 * ***************************************************************************/ #include "mibspi.h" #define __ALARM_MGMT_C__ #include "AlarmLamp.h" #include "FPGA.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" /** * @addtogroup AlarmManagement * @{ */ // ********** private definitions ********** /// Interval to control lamp and audio and to publish alarm status data. #define ALARM_STATUS_PUBLISH_INTERVAL ( ALARM_LAMP_AND_AUDIO_CONTROL_INTERVAL_MS / TASK_GENERAL_INTERVAL ) #define ALARM_SILENCE_EXPIRES_IN_SECS (60) ///< Alarm silence expiration time in seconds. #define ALARM_SILENCE_REQUESTED 1 ///< User cmd to request alarm silence (1=silence, 0=cancel silence). #define SUPERVISOR_ALARM_KEY 0xD2C3B4A5 ///< 32-bit key required for clear all alarms request. #define LOWEST_ALARM_SUB_RANK 999 ///< Lowest alarm sub-rank that can be set. // *** This declaration will cause a compiler error if ALARM_TABLE does not have same # of alarms as the Alarm_List enumeration. U08 alarmTableSizeAssertion[ ( ( sizeof( ALARM_TABLE ) / sizeof( ALARM_T ) ) == NUM_OF_ALARM_IDS ? 1 : -1 ) ]; /// A blank alarm data record for alarms that do not include alarm data when triggered. const ALARM_DATA_T BLANK_ALARM_DATA = { ALARM_DATA_TYPE_NONE, 0 }; // Pin assignment for backup alarm audio enable #define BACKUP_AUDIO_ENABLE_SPI3_PORT_MASK 0x00000001 ///< Pin SPI3-CS0 - re-purposed as output GPIO for back audio enable. // Backup alarm audio enable/disable macros #define SET_BACKUP_AUDIO_ENABLE() {mibspiREG3->PC3 |= BACKUP_AUDIO_ENABLE_SPI3_PORT_MASK;} ///< Macro to enable backup alarm audio. #define CLR_BACKUP_AUDIO_ENABLE() {mibspiREG3->PC3 &= ~BACKUP_AUDIO_ENABLE_SPI3_PORT_MASK;} ///< Macro to disable backup alarm audio. /// Alarm priority ranking record. typedef struct { ALARM_ID_T alarmID; ///< ID of highest priority alarm in this priority category U32 subRank; ///< Sub-rank of this alarm U32 timeSinceTriggeredMS; ///< Time (in ms) since this alarm was triggered } ALARM_PRIORITY_RANKS_T; // ********** private data ********** static U32 alarmStatusPublicationTimerCounter = 0; ///< Used to schedule alarm status publication to CAN bus. /// Table - current state of each alarm static BOOL alarmIsActive[ NUM_OF_ALARM_IDS ]; /// Table - current state of each alarm condition (detected or cleared) static BOOL alarmIsDetected[ NUM_OF_ALARM_IDS ]; /// Table - when alarm became active for each alarm (if active) or zero (if inactive) static OVERRIDE_U32_T alarmStartedAt[ NUM_OF_ALARM_IDS ]; /// Record for the current composite alarm status. static COMP_ALARM_STATUS_T alarmStatus; /// FIFO - first activated or highest sub-rank alarm in each alarm priority category. static ALARM_PRIORITY_RANKS_T alarmPriorityFIFO[ NUM_OF_ALARM_PRIORITIES ]; /// Alarm user recovery actions enabled flags. static BOOL alarmUserRecoveryActionEnabled[ NUMBER_OF_ALARM_USER_ACTIONS ]; static U32 alarmAudioVolumeLevel = 3; //MIN_ALARM_VOLUME_ATTENUATION; ///< Set alarm audio volume attenuation level (0..4 - lower level = higher gain). // ********** private function prototypes ********** static void activateAlarm( ALARM_ID_T alarm ); static void updateAlarmsState( void ); static void setAlarmLamp( void ); static void setAlarmAudio( void ); static void updateAlarmsSilenceStatus( void ); static void handleAlarmEscalations( void ); static void updateAlarmsFlags( void ); static void clearAllRecoverableAlarms( void ); static void resetAlarmPriorityFIFO( ALARM_PRIORITY_T priority ); static U32 getAlarmStartTime( ALARM_ID_T alarmID ); /*********************************************************************//** * @brief * The initAlarmMgmt function initializes the AlarmMgmt module. * @details Inputs: none * @details Outputs: AlarmMgmt module initialized. * @return none *************************************************************************/ void initAlarmMgmt( void ) { ALARM_PRIORITY_T p; ALARM_ID_T a; // Disable backup audio CLR_BACKUP_AUDIO_ENABLE(); // Initialize alarm states and start time stamps for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { alarmIsActive[ a ] = FALSE; alarmIsDetected[ a ] = FALSE;; alarmStartedAt[ a ].data = 0; alarmStartedAt[ a ].ovData = 0; alarmStartedAt[ a ].ovInitData = 0; alarmStartedAt[ a ].override = OVERRIDE_RESET; } // Initialize alarm FIFOs for ( p = ALARM_PRIORITY_NONE; p < NUM_OF_ALARM_PRIORITIES; p++ ) { alarmPriorityFIFO[ p ].alarmID = ALARM_ID_NO_ALARM; alarmPriorityFIFO[ p ].subRank = LOWEST_ALARM_SUB_RANK; alarmPriorityFIFO[ p ].timeSinceTriggeredMS = 0; } // Initialize composite alarm state alarmStatus.alarmsState = ALARM_PRIORITY_NONE; alarmStatus.alarmsSilenced = FALSE; alarmStatus.alarmsSilenceStart = 0; alarmStatus.alarmsSilenceExpiresIn = 0; alarmStatus.alarmsEscalatesIn = 0; alarmStatus.alarmTop = ALARM_ID_NO_ALARM; alarmStatus.topAlarmConditionnDetected = FALSE; alarmStatus.systemFault = FALSE; alarmStatus.stop = FALSE; alarmStatus.lampOn = FALSE; alarmStatus.noClear = FALSE; alarmStatus.noResume = FALSE; alarmStatus.noRinseback = FALSE; alarmStatus.noEndTreatment = FALSE; alarmStatus.noNewTreatment = FALSE; alarmStatus.usrACKRequired = FALSE; } /*********************************************************************//** * @brief * The execAlarmMgmt function executes the alarm management functions to be * done periodically. The composite alarm state is updated, alarm lamp and * audio patterns are updated, and status is sent out to the rest of the system. * @details Inputs: alarmStatusTable[], ALARM_TABLE[] * @details Outputs: alarmStatus * @return none *************************************************************************/ void execAlarmMgmt( void ) { handleAlarmEscalations(); updateAlarmsState(); updateAlarmsFlags(); updateAlarmsSilenceStatus(); // Publish alarm status at interval if ( ++alarmStatusPublicationTimerCounter >= ALARM_STATUS_PUBLISH_INTERVAL ) { // Lamp and audio timing sync'd with broadcast so UI can stay in sync with lamp rhythm setAlarmLamp(); setAlarmAudio(); broadcastAlarmStatus( alarmStatus ); alarmStatusPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief * The activateAlarm function activates a given alarm. * @details Inputs: none * @details Outputs: alarmIsActive[], alarmStartedAt[] * @param alarm ID of alarm to activate * @return none *************************************************************************/ static void activateAlarm( ALARM_ID_T alarm ) { // Verify given alarm if ( ( alarm > ALARM_ID_NO_ALARM ) && ( alarm < NUM_OF_ALARM_IDS ) ) { // No need to do anything if alarm is already active if ( FALSE == alarmIsActive[ alarm ] ) { // If alarms silenced, end silence due to new alarm alarmStatus.alarmsSilenced = FALSE; // If alarm is a fault, request transition to fault mode if ( TRUE == ALARM_TABLE[ alarm ].alarmIsFault ) { requestNewOperationMode( MODE_FAUL ); } // Activate alarm alarmIsActive[ alarm ] = TRUE; alarmStartedAt[ alarm ].data = getMSTimerCount(); alarmIsDetected[ alarm ] = TRUE; // If alarm has clear condition immediately property, clear condition now if ( TRUE == ALARM_TABLE[ alarm ].alarmConditionClearImmed ) { clearAlarmCondition( alarm ); } // If alarm has stop property, signal stop now if ( TRUE == ALARM_TABLE[ alarm ].alarmStops ) { initiateAlarmAction( ALARM_ACTION_STOP ); } } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_ALARM_TO_ACTIVATE, alarm ) } } /*********************************************************************//** * @brief * The activateAlarmNoData function activates a given alarm. Also, an alarm * message is broadcast to the rest of the system. This function will * include given data in the broadcast message for logging. * @details Inputs: none * @details Outputs: alarm triggered message sent, alarm activated * @param alarm ID of alarm to activate * @return none *************************************************************************/ void activateAlarmNoData( ALARM_ID_T alarm ) { // Broadcast alarm and data if alarm not already active if ( FALSE == alarmIsActive[ alarm ] ) { broadcastAlarmTriggered( (U16)alarm, BLANK_ALARM_DATA, BLANK_ALARM_DATA ); } activateAlarm( alarm ); #ifdef DEBUG_ENABLED #ifdef ALARMS_DEBUG if ( FALSE == alarmIsActive[ alarm ] ) { // TODO - temporary debug code - remove later char debugStr[ 256 ]; sprintf( debugStr, "ALARM trig:%5d \n", alarm ); sendDebugData( (U08*)debugStr, strlen(debugStr) ); sendDebugDataToUI( (U08*)debugStr ); } #endif #endif } /*********************************************************************//** * @brief * The activateAlarm1Data function activates a given alarm. Also, an alarm * message is broadcast to the rest of the system. This function will * include given data in the broadcast message for logging. * @details Inputs: none * @details Outputs: alarm triggered message sent, alarm activated * @param alarm ID of alarm to activate * @param alarmData supporting data to include in alarm msg * @return none *************************************************************************/ void activateAlarm1Data( ALARM_ID_T alarm, ALARM_DATA_T alarmData ) { // Broadcast alarm and data if alarm not already active if ( FALSE == alarmIsActive[ alarm ] ) { broadcastAlarmTriggered( (U16)alarm, alarmData, BLANK_ALARM_DATA ); } activateAlarm( alarm ); #ifdef DEBUG_ENABLED #ifdef ALARMS_DEBUG if ( FALSE == alarmIsActive[ alarm ] ) { // TODO - temporary debug code - remove later char debugStr[ 256 ]; sprintf( debugStr, "ALARM trig:%5d %8X \n", alarm, alarmData.data.uInt.data ); sendDebugData( (U08*)debugStr, strlen(debugStr) ); sendDebugDataToUI( (U08*)debugStr ); } #endif #endif } /*********************************************************************//** * @brief * The activateAlarm2Data function activates a given alarm. Also, an alarm * message is broadcast to the rest of the system. This function will * include two given data in the broadcast message for logging. * @details Inputs: none * @details Outputs: alarm triggered message sent, alarm activated * @param alarm ID of alarm to activate * @param alarmData1 supporting data to include in alarm msg * @param alarmData2 supporting data to include in alarm msg * @return none *************************************************************************/ void activateAlarm2Data( ALARM_ID_T alarm, ALARM_DATA_T alarmData1, ALARM_DATA_T alarmData2 ) { // Broadcast alarm and data if alarm not already active if ( FALSE == alarmIsActive[ alarm ] ) { broadcastAlarmTriggered( (U16)alarm, alarmData1, alarmData2 ); } activateAlarm( alarm ); #ifdef DEBUG_ENABLED #ifdef ALARMS_DEBUG if ( FALSE == alarmIsActive[ alarm ] ) { // TODO - temporary debug code - remove later char debugStr[ 256 ]; sprintf( debugStr, "ALARM trig:%5d %8X %8X \n", (S32)alarm, alarmData1.data.uInt.data, alarmData2.data.uInt.data ); sendDebugData( (U08*)debugStr, strlen(debugStr) ); sendDebugDataToUI( (U08*)debugStr ); } #endif #endif } /*********************************************************************//** * @brief * The clearAlarmCondition function clears a given alarm's condition detected * flag. Also an alarm message is broadcast to the rest of the system. * @details Inputs: none * @details Outputs: alarmIsDetected[] * @param alarm ID of alarm to clear condition for * @return none *************************************************************************/ void clearAlarmCondition( ALARM_ID_T alarm ) { // Verify given alarm if ( ( alarm > ALARM_ID_NO_ALARM ) && ( alarm < NUM_OF_ALARM_IDS ) ) { // Clear alarm condition and broadcast alarm condition clear if not already cleared if ( TRUE == alarmIsDetected[ alarm ] ) { alarmIsDetected[ alarm ] = FALSE; broadcastAlarmConditionCleared( alarm ); } } } /*********************************************************************//** * @brief * The clearAlarm function clears a given alarm if it is recoverable. Also * an alarm message is broadcast to the rest of the system. * @details Inputs: none * @details Outputs: AlarmStatusTable[], alarmIsActive[], alarmStartedAt[], * alarmIsDetected[] * @param alarm ID of alarm to clear * @return none *************************************************************************/ void clearAlarm( ALARM_ID_T alarm ) { // Verify given alarm if ( ( alarm > ALARM_ID_NO_ALARM ) && ( alarm < NUM_OF_ALARM_IDS ) ) { // Verify alarm can be cleared if ( FALSE == ALARM_TABLE[ alarm ].alarmNoClear ) { // Clear alarm and broadcast alarm clear if not already cleared if ( TRUE == alarmIsActive[ alarm ] ) { broadcastAlarmCleared( alarm ); alarmIsActive[ alarm ] = FALSE; alarmIsDetected[ alarm ] = FALSE; alarmStartedAt[ alarm ].data = 0; // Clear FIFO if this alarm was in it if ( alarmPriorityFIFO[ ALARM_TABLE[ alarm ].alarmPriority ].alarmID == alarm ) { resetAlarmPriorityFIFO( ALARM_TABLE[ alarm ].alarmPriority ); } #ifdef DEBUG_ENABLED #ifdef ALARMS_DEBUG { // TODO - temporary debug code - remove later char debugStr[ 256 ]; sprintf( debugStr, "ALARM cleared:%5d \n", alarm ); sendDebugData( (U08*)debugStr, strlen(debugStr) ); sendDebugDataToUI( (U08*)debugStr ); } #endif #endif } } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_ALARM_TO_CLEAR, alarm ) } } /*********************************************************************//** * @brief * The setAlarmUserActionEnabled function enables/disables specific alarm * recovery user actions while in specific modes. Ack option is always * potentially enabled - automatically enabled as appropriate by updateAlarmsFlags(). * @details Inputs: none * @details Outputs: * @param action ID of user alarm recovery action to enable/disable * @param enabled set to TRUE to enable action, FALSE to disable * @return none *************************************************************************/ void setAlarmUserActionEnabled( ALARM_USER_ACTION_T action, BOOL enabled ) { if ( action < NUMBER_OF_ALARM_USER_ACTIONS ) { alarmUserRecoveryActionEnabled[ action ] = enabled; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_USER_ACTION, (U32)action ) } } /*********************************************************************//** * @brief * The signalAlarmSilence function handles an alarm silence request from * the user. * @details Inputs: none * @details Outputs: alarm silence status updated * @param cmd ID of user command (1=silence, 0=cancel silence) * @return none *************************************************************************/ void signalAlarmSilence( U32 cmd ) { if ( ALARM_SILENCE_REQUESTED == cmd ) { if ( FALSE == alarmStatus.alarmsSilenced ) { alarmStatus.alarmsSilenced = TRUE; alarmStatus.alarmsSilenceStart = getMSTimerCount(); alarmStatus.alarmsSilenceExpiresIn = ALARM_SILENCE_EXPIRES_IN_SECS; } } else { if ( TRUE == alarmStatus.alarmsSilenced ) { alarmStatus.alarmsSilenced = FALSE; alarmStatus.alarmsSilenceStart = 0; alarmStatus.alarmsSilenceExpiresIn = 0; } } } /*********************************************************************//** * @brief * The signalAlarmUserActionInitiated function clears all non-recoverable alarms * and initiates selected user action. * @details Inputs: none * @details Outputs: * @param action ID of user's selected action to initiate * @return none *************************************************************************/ void signalAlarmUserActionInitiated( ALARM_USER_ACTION_T action ) { // Validate given action if ( action < NUMBER_OF_ALARM_USER_ACTIONS ) { ALARM_ID_T a = alarmStatus.alarmTop; if ( ALARM_USER_ACTION_ACK == action ) { // If user acknowledged top alarm, just clear that alarm if ( TRUE == ALARM_TABLE[ a ].alarmUserAckRequired ) { clearAlarm( a ); } // Otherwise we must be in mode/state where ack was only option - so clear all like other options else { clearAllRecoverableAlarms(); } } else { clearAllRecoverableAlarms(); } } // Initiate user selected action switch ( action ) { case ALARM_USER_ACTION_RESUME: initiateAlarmAction( ALARM_ACTION_RESUME ); break; case ALARM_USER_ACTION_RINSEBACK: initiateAlarmAction( ALARM_ACTION_RINSEBACK ); break; case ALARM_USER_ACTION_END_TREATMENT: initiateAlarmAction( ALARM_ACTION_END_TREATMENT ); break; case ALARM_USER_ACTION_ACK: initiateAlarmAction( ALARM_ACTION_ACK ); break; default: // TODO - s/w fault? break; } } /*********************************************************************//** * @brief * The isAlarmActive function determines whether a given alarm is currently * active. * @details Inputs: alarmIsActive[] * @details Outputs: none * @param alarm ID of alarm to check * @return TRUE if given alarm is active, FALSE if not *************************************************************************/ BOOL isAlarmActive( ALARM_ID_T alarm ) { BOOL result = alarmIsActive[ alarm ]; return result; } /*********************************************************************//** * @brief * The isAnyAlarmActive function determines whether any alarm is currently * active. * @details Inputs: alarmStatus * @details Outputs: none * @return TRUE if any alarm is active, FALSE if not *************************************************************************/ BOOL isAnyAlarmActive( void ) { BOOL result = ( alarmStatus.alarmTop != ALARM_ID_NO_ALARM ? TRUE : FALSE ); return result; } /*********************************************************************//** * @brief * The doesAlarmStatusIndicateStop function determines whether any currently * active alarm has stop property. * @details Inputs: alarmStatus * @details Outputs: none * @return TRUE if any active alarm has stop property, FALSE if not *************************************************************************/ BOOL doesAlarmStatusIndicateStop( void ) { return alarmStatus.stop; } /*********************************************************************//** * @brief * The getCurrentAlarmStatePriority function determines the current alarm * state priority (NONE, LOW, MEDIUM, or HIGH). * @details Inputs: alarmStatus * @details Outputs: none * @return current alarm state priority *************************************************************************/ ALARM_PRIORITY_T getCurrentAlarmStatePriority( void ) { return alarmStatus.alarmsState; } /*********************************************************************//** * @brief * The isAlarmRecoverable function determines whether a given alarm is * recoverable. * @details Inputs: ALARM_TABLE[] * @details Outputs: none * @param alarm ID of alarm to check * @return TRUE if given alarm is recoverable, FALSE if not *************************************************************************/ BOOL isAlarmRecoverable( ALARM_ID_T alarm ) { BOOL result = ( TRUE == ALARM_TABLE[ alarm ].alarmNoClear ? FALSE : TRUE ); return result; } /*********************************************************************//** * @brief * The setAlarmAudioVolume function sets the current alarm audio volume level. * @details Inputs: none * @details Outputs: alarmAudioVolumeLevel * @param volumeLevel level of volume requested (1..5) * @return none *************************************************************************/ void setAlarmAudioVolume( U32 volumeLevel ) { if ( ( volumeLevel > 0 ) && ( volumeLevel <= MAX_ALARM_VOLUME_LEVEL ) ) { // Convert volume level to attenuation level alarmAudioVolumeLevel = MAX_ALARM_VOLUME_LEVEL - volumeLevel; } } /*********************************************************************//** * @brief * The getAlarmStartTime function gets the active state of a given alarm. * @details Inputs: alarmStartedAt[] * @details Outputs: none * @param alarmID ID of alarm to check * @return The start time stamp of given alarm ID *************************************************************************/ static U32 getAlarmStartTime( ALARM_ID_T alarmID ) { U32 result = 0; if ( alarmID < NUM_OF_ALARM_IDS ) { if ( OVERRIDE_KEY == alarmStartedAt[ alarmID ].override ) { result = alarmStartedAt[ alarmID ].ovData; } else { result = alarmStartedAt[ alarmID ].data; } } else { activateAlarmNoData( ALARM_ID_HD_SOFTWARE_FAULT ); } return result; } /*********************************************************************//** * @brief * The updateAlarmsState function updates the alarms state and alarm to * display. * @details Inputs: alarmStatusTable[] * @details Outputs: alarmPriorityFIFO[], alarmStatus * @return none *************************************************************************/ static void updateAlarmsState( void ) { ALARM_PRIORITY_T highestPriority = ALARM_PRIORITY_NONE; ALARM_ID_T a; BOOL faultsActive = FALSE; // Update FIFOs and sub-ranks per active alarms table - for alarm ranking purposes to determine "top" alarm for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { if ( TRUE == alarmIsActive[a] ) { ALARM_PRIORITY_T almPriority = ALARM_TABLE[ a ].alarmPriority; U32 subRank = ALARM_TABLE[ a ].alarmSubRank; U32 msSinceTriggered = calcTimeSince( getAlarmStartTime( a ) ); // See if this alarm is higher rank than highest active alarm in this priority category so far if ( subRank <= alarmPriorityFIFO[ almPriority ].subRank ) { // If sub-rank is a tie, see which alarm was triggered first if ( subRank == alarmPriorityFIFO[ almPriority ].subRank ) { if ( msSinceTriggered > alarmPriorityFIFO[ almPriority ].timeSinceTriggeredMS ) { alarmPriorityFIFO[ almPriority ].alarmID = a; alarmPriorityFIFO[ almPriority ].subRank = subRank; alarmPriorityFIFO[ almPriority ].timeSinceTriggeredMS = msSinceTriggered; } } // Otherwise, this alarm simply outranks current candidate and wins outright else { alarmPriorityFIFO[ almPriority ].alarmID = a; alarmPriorityFIFO[ almPriority ].subRank = subRank; alarmPriorityFIFO[ almPriority ].timeSinceTriggeredMS = msSinceTriggered; } } // Track highest priority alarm found so far of all priority categories highestPriority = MAX( almPriority, highestPriority ); // Track whether any active faults have been found so far if ( TRUE == ALARM_TABLE[ a ].alarmIsFault ) { faultsActive = TRUE; } } } // Update alarm to display per highest priority FIFO alarmStatus.alarmsState = highestPriority; alarmStatus.alarmTop = alarmPriorityFIFO[ highestPriority ].alarmID; alarmStatus.systemFault = faultsActive; } /*********************************************************************//** * @brief * The setAlarmLamp function sets the alarm lamp pattern according to the * current state of alarms. * @details Inputs: none * @details Outputs: Alarm lamp patter set according to current alarms status. * @return none *************************************************************************/ static void setAlarmLamp( void ) { // Set alarm lamp pattern to appropriate pattern for current alarm state if ( getCurrentAlarmLampPattern() != LAMP_PATTERN_MANUAL ) { switch ( alarmStatus.alarmsState ) { case ALARM_PRIORITY_NONE: requestAlarmLampPattern( LAMP_PATTERN_OK ); break; case ALARM_PRIORITY_LOW: requestAlarmLampPattern( LAMP_PATTERN_LOW_ALARM ); break; case ALARM_PRIORITY_MEDIUM: requestAlarmLampPattern( LAMP_PATTERN_MED_ALARM ); break; case ALARM_PRIORITY_HIGH: if ( TRUE == ALARM_TABLE[ alarmStatus.alarmTop ].alarmIsFault ) { requestAlarmLampPattern( LAMP_PATTERN_FAULT ); } else { requestAlarmLampPattern( LAMP_PATTERN_HIGH_ALARM ); } break; default: requestAlarmLampPattern( LAMP_PATTERN_FAULT ); SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_LAMP_INVALID_ALARM_STATE, alarmStatus.alarmsState ) break; } } // Execute alarm lamp controller execAlarmLamp(); // Set lamp on flag to match current state of alarm lamp if ( getCurrentAlarmLampPattern() != LAMP_PATTERN_MANUAL ) { alarmStatus.lampOn = getAlarmLampOn(); } else { alarmStatus.lampOn = FALSE; } } /*********************************************************************//** * @brief * The setAlarmAudio function sets the alarm audio pattern according to * the current state of alarms. * @details Inputs: none * @details Outputs: Alarm audio patter set according to current alarms status. * @return none *************************************************************************/ static void setAlarmAudio( void ) { if ( TRUE == alarmStatus.alarmsSilenced ) { setAlarmAudioState( ALARM_PRIORITY_NONE, alarmAudioVolumeLevel ); } else // Alarms not silenced { if ( alarmStatus.alarmsState < NUM_OF_ALARM_PRIORITIES ) { #ifndef DISABLE_ALARM_AUDIO setAlarmAudioState( alarmStatus.alarmsState, alarmAudioVolumeLevel ); #endif } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_AUDIO_INVALID_ALARM_STATE, alarmStatus.alarmsState ) setAlarmAudioState( ALARM_PRIORITY_HIGH, alarmAudioVolumeLevel ); } } } /*********************************************************************//** * @brief * The updateAlarmsSilenceStatus function updates the alarms silence state. * @details Inputs: alarmStatus * @details Outputs: alarmStatus * @return none *************************************************************************/ static void updateAlarmsSilenceStatus( void ) { // If alarms not silenced, reset alarms silence related properties if ( TRUE != alarmStatus.alarmsSilenced ) { alarmStatus.alarmsSilenceExpiresIn = 0; alarmStatus.alarmsSilenceStart = 0; } else { U32 timeSinceAlarmSilenceStart = calcTimeSince( alarmStatus.alarmsSilenceStart ) / MS_PER_SECOND; if ( timeSinceAlarmSilenceStart >= ALARM_SILENCE_EXPIRES_IN_SECS ) { alarmStatus.alarmsSilenceExpiresIn = 0; } else { alarmStatus.alarmsSilenceExpiresIn = ALARM_SILENCE_EXPIRES_IN_SECS - timeSinceAlarmSilenceStart; } // If alarms silence expires, end it if ( 0 == alarmStatus.alarmsSilenceExpiresIn ) { alarmStatus.alarmsSilenced = FALSE; } } } /*********************************************************************//** * @brief * The handleAlarmEscalations function handles alarm escalation. * @details Inputs: alarmIsActive[], ALARM_TABLE[] * @details Outputs: alarm(s) escalated when appropriate * @return none *************************************************************************/ static void handleAlarmEscalations( void ) { U32 secsToEscalate = 0; ALARM_ID_T nextAlarmToEscalate = ALARM_ID_NO_ALARM; ALARM_ID_T a; // Update escalations for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { if ( TRUE == alarmIsActive[ a ] ) { // Does active alarm escalate? if ( ALARM_ID_NO_ALARM != ALARM_TABLE[ a ].alarmEscalatesTo ) { S32 msRemaining = (S32)ALARM_TABLE[ a ].alarmEscalatesAfter - (S32)calcTimeSince( getAlarmStartTime( a ) ); S32 secsRemaining = ( msRemaining / MS_PER_SECOND ) + 1; // Time to escalate? if ( msRemaining <= 0 ) { activateAlarmNoData( ALARM_TABLE[ a ].alarmEscalatesTo ); clearAlarm( a ); } else { // No candidates for alarm escalation yet? if ( ALARM_ID_NO_ALARM == nextAlarmToEscalate ) { secsToEscalate = secsRemaining; nextAlarmToEscalate = a; } // Sooner candidate for alarm escalation? else if ( secsRemaining < secsToEscalate ) { secsToEscalate = secsRemaining; nextAlarmToEscalate = a; } } } // If alarm escalates } // If alarm active } // Alarm table loop // Update alarm escalation properties if ( TRUE == alarmStatus.systemFault || ALARM_ID_NO_ALARM == nextAlarmToEscalate ) { alarmStatus.alarmsToEscalate = FALSE; alarmStatus.alarmsEscalatesIn = 0; } else { alarmStatus.alarmsToEscalate = TRUE; alarmStatus.alarmsEscalatesIn = secsToEscalate; } } /*********************************************************************//** * @brief * The updateAlarmsFlags function updates the alarms flags of the alarms * status record. * @details Inputs: none * @details Outputs: alarmStatus * @return none *************************************************************************/ static void updateAlarmsFlags( void ) { BOOL systemFault = FALSE; BOOL stop = FALSE; BOOL noClear = FALSE; BOOL noResume = FALSE; BOOL noRinseback = FALSE; BOOL noEndTreatment = FALSE; BOOL noNewTreatment = FALSE; BOOL usrAckReq = FALSE; ALARM_ID_T a; // Determine alarm flags for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { if ( TRUE == alarmIsActive[ a ] ) { systemFault = ( TRUE == ALARM_TABLE[ a ].alarmIsFault ? TRUE : systemFault ); stop = ( TRUE == ALARM_TABLE[ a ].alarmStops ? TRUE : stop ); noClear = ( TRUE == ALARM_TABLE[ a ].alarmNoClear ? TRUE : noClear ); noNewTreatment = ( TRUE == ALARM_TABLE[ a ].alarmNoNewTreatment ? TRUE : noNewTreatment ); // Set user alarm recovery actions allowed flags if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RESUME ] ) { noResume = ( TRUE == ALARM_TABLE[ a ].alarmNoResume ? TRUE : noResume ); } else { noResume = TRUE; } if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RINSEBACK ] ) { noRinseback = ( TRUE == ALARM_TABLE[ a ].alarmNoRinseback ? TRUE : noRinseback ); } else { noRinseback = TRUE; } if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_END_TREATMENT ] ) { noEndTreatment = ( TRUE == ALARM_TABLE[ a ].alarmNoEndTreatment ? TRUE : noEndTreatment ); } else { noEndTreatment = TRUE; } } // If alarm active } // Alarm table loop // If top alarm condition not cleared, block resume and rinseback flags if ( TRUE == alarmStatus.topAlarmConditionnDetected ) { noResume = TRUE; noRinseback = TRUE; } // If top alarm requires user ack or no other user options enabled for recoverable alarm and condition cleared, set user ack flag and block other flags if ( ( TRUE == ALARM_TABLE[ alarmStatus.alarmTop ].alarmUserAckRequired ) || ( ( FALSE == alarmStatus.noClear ) && ( noResume ) && ( noRinseback ) && ( noEndTreatment ) && ( FALSE == alarmStatus.topAlarmConditionnDetected ) ) ) { usrAckReq = TRUE; noResume = TRUE; noRinseback = TRUE; noEndTreatment = TRUE; } // Set updated alarm flags alarmStatus.systemFault = systemFault; alarmStatus.stop = stop; alarmStatus.noClear = noClear; alarmStatus.noResume = noResume; alarmStatus.noRinseback = noRinseback; alarmStatus.noEndTreatment = noEndTreatment; alarmStatus.noNewTreatment = noNewTreatment; alarmStatus.usrACKRequired = usrAckReq; } /*********************************************************************//** * @brief * The clearAllRecoverableAlarms function clears all currently active * recoverable alarms. * @details Inputs: ALARM_TABLE[] * @details Outputs: All currently active recoverable alarms are cleared * @return none *************************************************************************/ static void clearAllRecoverableAlarms( void ) { ALARM_ID_T a = alarmStatus.alarmTop; for ( a = ALARM_ID_HD_SOFTWARE_FAULT; a < NUM_OF_ALARM_IDS; a++ ) { // Is alarm recoverable? if ( FALSE == ALARM_TABLE[ a ].alarmNoClear ) { clearAlarm( a ); } } } /*********************************************************************//** * @brief * The resetAlarmPriorityFIFO function resets a FIFO for a given alarm * priority. * @details Inputs: none * @details Outputs: alarmPriorityFIFO[] * @param priority priority of FIFO to reset * @return none *************************************************************************/ static void resetAlarmPriorityFIFO( ALARM_PRIORITY_T priority ) { // Verify priority if ( priority < NUM_OF_ALARM_PRIORITIES ) { alarmPriorityFIFO[ priority ].alarmID = ALARM_ID_NO_ALARM; alarmPriorityFIFO[ priority ].subRank = LOWEST_ALARM_SUB_RANK; alarmPriorityFIFO[ priority ].timeSinceTriggeredMS = 0; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_FIFO_TO_RESET, priority ) } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetAlarmStateOverride function overrides the state of the * alarm active state for a given alarm with the alarm management with * a given active state. * @details Inputs: none * @details Outputs: alarm activated or cleared * @param alarmID ID of alarm to activate or clear * @param value override state for the given alarm ID (1=activate, 0=clear) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetAlarmStateOverride( U32 alarmID, U32 state ) { BOOL result = FALSE; if ( alarmID < NUM_OF_ALARM_IDS ) { // Verify tester has logged in with HD if ( TRUE == isTestingActivated() ) { if ( TRUE == state ) { activateAlarmNoData( (ALARM_ID_T)alarmID ); } else { clearAlarm( (ALARM_ID_T)alarmID ); } result = TRUE; } } return result; } /*********************************************************************//** * @brief * The testResetAlarmStateOverride function resets the override of the * state of the active state for a given alarm with the alarm management. * @details Inputs: none * @details Outputs: alarm cleared * @param alarmID ID of alarm to clear * @return TRUE if alarm clear successful, FALSE if not *************************************************************************/ BOOL testResetAlarmStateOverride( U32 alarmID ) { BOOL result = FALSE; if ( alarmID < NUM_OF_ALARM_IDS ) { // Verify tester has logged in with HD if ( TRUE == isTestingActivated() ) { result = TRUE; clearAlarm( (ALARM_ID_T)alarmID ); } } return result; } /*********************************************************************//** * @brief * The testSetAlarmStartOverride function overrides the start time * for a given alarm with the alarm management with a given start time. * @details Inputs: none * @details Outputs: alarmStartedAt[] * @param alarmID ID of alarm to override start time for * @param value override time since start (in ms) for the given alarm ID * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetAlarmStartOverride( U32 alarmID, U32 value ) { BOOL result = FALSE; if ( alarmID < NUM_OF_ALARM_IDS ) { // Verify tester has logged in with HD if ( TRUE == isTestingActivated() ) { U32 tim = getMSTimerCount(); if ( tim > value ) { result = TRUE; alarmStartedAt[ alarmID ].ovData = ( tim - value ); alarmStartedAt[ alarmID ].override = OVERRIDE_KEY; } } } return result; } /*********************************************************************//** * @brief * The testResetAlarmStartOverride function resets the override of the * start time for a given alarm with the alarm management. * @details Inputs: none * @details Outputs: alarmStartedAt[] * @param alarmID ID of alarm to reset override of start time for * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetAlarmStartOverride( U32 alarmID ) { BOOL result = FALSE; if ( alarmID < NUM_OF_ALARM_IDS ) { // Verify tester has logged in with HD if ( TRUE == isTestingActivated() ) { result = TRUE; alarmStartedAt[ alarmID ].override = OVERRIDE_RESET; alarmStartedAt[ alarmID ].ovData = alarmStartedAt[ alarmID ].ovInitData; } } return result; } /*********************************************************************//** * @brief * The testClearAllAlarms function clears all active alarms, even if they * are non-recoverable or faults. The caller of this function must provide * the correct 32-bit key. A Dialin user must also be logged into HD. * @details Inputs: none * @details Outputs: alarmIsActive[], alarmStartedAt[] * @param key 32-bit supervior alarm key required to perform this function * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testClearAllAlarms( U32 key ) { BOOL result = FALSE; // Verify key if ( SUPERVISOR_ALARM_KEY == key ) { // Verify tester has logged in with HD if ( TRUE == isTestingActivated() ) { ALARM_ID_T a; // Clear all active alarms for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { if ( TRUE == alarmIsActive[ a ] ) { broadcastAlarmCleared( a ); alarmIsActive[ a ] = FALSE; alarmStartedAt[ a ].data = 0; // Clear FIFO if this alarm was in it if ( alarmPriorityFIFO[ ALARM_TABLE[ a ].alarmPriority ].alarmID == a ) { resetAlarmPriorityFIFO( ALARM_TABLE[ a ].alarmPriority ); } } } result = TRUE; } } return result; } /**@}*/