/************************************************************************** * * Copyright (c) 2024-2024 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 AlarmMgmtTD.c * * @author (last) Sean * @date (last) 30-Jul-2024 * * @author (original) Sean * @date (original) 30-Jul-2024 * ***************************************************************************/ #define __ALARM_MGMT_TD_C__ #include "mibspi.h" #include "AlarmMgmtTD.h" #include "CpldInterface.h" #include "Messaging.h" #include "OperationModes.h" #include "TaskGeneral.h" #include "Timers.h" /** * @addtogroup AlarmManagementTD * @{ */ // ********** 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 ) /// Interval (ms/task time) at which the alarm information is published on the CAN bus. #define ALARM_INFO_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) #define SUPERVISOR_ALARM_KEY 0xD2C3B4A5 ///< 32-bit key required for clear all alarms request. /// Interval (ms/task time) Alarms are blocked after the return of AC power. #define ALARM_BLOCKED_COUNT_AFTER_AC_RETURN ( 10*MS_PER_SECOND / TASK_GENERAL_INTERVAL ) #define ALARM_SILENCE_EXPIRES_IN_SECS (60) ///< Alarm silence expiration time in seconds. #define LOWEST_ALARM_SUB_RANK 999 ///< Lowest alarm sub-rank that can be set. #define ALARM_NOT_BLOCKED 0 ///< Alarm blocked timer value that indicates no alarm block #define MIN_TIME_BETWEEN_ALARM_ACTIONS_MS MS_PER_SECOND ///< Minimum time between user alarm actions (in ms). /// 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 }; /// 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 S32 timeSinceTriggeredMS; ///< Time (in ms) since this alarm was triggered } ALARM_PRIORITY_RANKS_T; // ********** private data ********** static OVERRIDE_U32_T alarmStartedAt[ NUM_OF_ALARM_IDS ]; ///< Table - when alarm became active for each alarm (if active) or zero (if inactive) static U32 alarmStatusPublicationTimerCounter = 0; ///< Used to schedule alarm status publication to CAN bus. static U32 alarmInfoPublicationTimerCounter = 0; ///< Used to schedule alarm information publication to CAN bus. static U32 alarmsBlockedTimer = 0; ///< Countdown timer used to temporarily block new alarms from being initiated static U32 lastUserAlarmActionReceivedTime = 0; ///< Time of last alarm action by user received from the UI (ms timestamp). /// Interval (in task intervals) at which to publish alarm status to CAN bus. static OVERRIDE_U32_T alarmStatusPublishInterval = { ALARM_STATUS_PUBLISH_INTERVAL, ALARM_STATUS_PUBLISH_INTERVAL, ALARM_STATUS_PUBLISH_INTERVAL, 0 }; /// Interval (in task intervals) at which to publish alarm information to CAN bus. static OVERRIDE_U32_T alarmInfoPublishInterval = { ALARM_INFO_PUB_INTERVAL, ALARM_INFO_PUB_INTERVAL, ALARM_INFO_PUB_INTERVAL, 0 }; static COMP_ALARM_STATUS_T alarmStatus; ///< Record for the current composite alarm status. static ALARM_PRIORITY_RANKS_T alarmPriorityFIFO[ NUM_OF_ALARM_PRIORITIES ]; ///< FIFO - first activated or highest sub-rank alarm in each alarm priority category. static BOOL alarmUserRecoveryActionEnabled[ NUMBER_OF_ALARM_USER_ACTIONS ]; ///< Alarm user recovery actions enabled flags. static BOOL alarmButtonBlockers[ NUM_OF_ALARM_BUTTON_BLOCKERS ]; ///< Flags indicating whether alarm table or state properties are blocking alarm buttons for UI. static BOOL resumeBlockedByAlarmProperty; ///< Flag indicates whether treatment resumption is currently blocked by alarm property. // ********** private function prototypes ********** static void activateAlarmTD( ALARM_ID_T alarm ); static void monitorAlarms( void ); static void updateAlarmsState( void ); static void setAlarmLamp( void ); static void updateAlarmsSilenceStatus( void ); static void updateAlarmsFlags( void ); static BOOL clearAllRecoverableAlarms( ALARM_USER_ACTION_T action ); static void resetAlarmPriorityFIFO( ALARM_PRIORITY_T priority ); static U32 getAlarmStartTime( ALARM_ID_T alarmID ); static void publishAlarmInfo( void ); static BOOL broadcastAlarmStatus( COMP_ALARM_STATUS_T almStatus ); /*********************************************************************//** * @brief * The initAlarmMgmtTD function initializes the TD AlarmMgmt unit. * @details \b Inputs: none * @details \b Outputs: TD AlarmMgmt unit initialized. * @return none *************************************************************************/ void initAlarmMgmtTD( void ) { ALARM_PRIORITY_T p; ALARM_ID_T a; ALARM_BUTTON_BLOCKER_T b; // Initialize common alarm mgmt unit initAlarmMgmt(); // Initialize alarm states and start time stamps for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { 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 alarm button blocker flags for ( b = (ALARM_BUTTON_BLOCKER_T)0; b < NUM_OF_ALARM_BUTTON_BLOCKERS; b++ ) { alarmButtonBlockers[ b ] = FALSE; } // Initialize composite alarm state alarmStatus.alarmsState = ALARM_PRIORITY_NONE; alarmStatus.alarmsSilenced = FALSE; alarmStatus.alarmsSilenceStart = 0; alarmStatus.alarmsSilenceExpiresIn = 0; alarmStatus.alarmTop = ALARM_ID_NO_ALARM; alarmStatus.topAlarmConditionDetected = FALSE; alarmStatus.systemFault = FALSE; alarmStatus.stop = FALSE; alarmStatus.lampOn = FALSE; alarmStatus.noClear = FALSE; alarmStatus.noResume = FALSE; alarmStatus.noRinseback = FALSE; alarmStatus.noEndTreatment = FALSE; alarmStatus.noBloodRecirc = FALSE; alarmStatus.noDialRecirc = FALSE; alarmStatus.ok = FALSE; alarmsBlockedTimer = 0; lastUserAlarmActionReceivedTime = 0; resumeBlockedByAlarmProperty = FALSE; // Initialize alarm audio and lamp initAlarmLamp(); initAlarmAudio(); } /*********************************************************************//** * @brief * The execAlarmMgmt function executes the TD alarm management functions to be * done periodically. The system alarm state is updated, alarm lamp and * audio patterns are updated, and the state of the alarm system is sent out * to the rest of the system. * @details \b Inputs: alarmsBlockedTimer * @details \b Outputs: alarmsBlockedTimer * @return none *************************************************************************/ void execAlarmMgmt( void ) { monitorAlarms(); updateAlarmsState(); updateAlarmsFlags(); updateAlarmsSilenceStatus(); // Publish alarm status and information at interval publishAlarmInfo(); // Block new machine alarms during power fail recovery if ( alarmsBlockedTimer > 0 ) { alarmsBlockedTimer--; } } /*********************************************************************//** * @brief * The activateAlarmTD function activates a given alarm. If alarm system * is silenced, it will no longer be. The "top" alarm is set to given * alarm if it is highest in priority/rank. If the alarm is a fault, we * will transition to fault mode. If the alarm is meant to stop activity, * a stop signal is sent to the current operating mode. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given alarm ID is invalid. * @details \b Inputs: none * @details \b Outputs: alarmIsActive[], alarmStartedAt[], alarmStatus is updated * @param alarm ID of alarm to activate * @return none *************************************************************************/ static void activateAlarmTD( ALARM_ID_T alarm ) { // Verify valid alarm index if ( ( alarm > ALARM_ID_NO_ALARM ) && ( alarm < NUM_OF_ALARM_IDS ) ) { ALARM_T props = getAlarmProperties( alarm ); ALARM_T props_top = getAlarmProperties( alarmStatus.alarmTop ); // No need to do anything if alarm is already active, but if condition was cleared then re-trigger alarm if ( ( FALSE == isAlarmActive( alarm ) ) || ( ( FALSE == isAlarmConditionDetected( alarm ) ) && ( FALSE == props.alarmConditionClearImmed ) ) ) { activateAlarm( alarm ); alarmStartedAt[ alarm ].data = getMSTimerCount(); // If alarms are silenced and this new alarm is of higher or same priority, end silence due to new alarm if ( ( props.alarmPriority > props_top.alarmPriority ) || ( ( props.alarmPriority == props_top.alarmPriority ) && ( props.alarmSubRank < props_top.alarmSubRank ) ) ) { alarmStatus.alarmsSilenced = FALSE; } // If alarm status was that no alarms currently active, set this alarm as top alarm until status formally updated later if ( ALARM_ID_NO_ALARM == alarmStatus.alarmTop ) { alarmStatus.alarmTop = alarm; } // If alarm stops, set that status immediately (don't wait for status update function) if ( TRUE == props.alarmStops ) { alarmStatus.stop = TRUE; } // If alarm is a fault (and not in service mode), request transition to fault mode if ( ( TRUE == props.alarmIsFault ) && ( getCurrentOperationMode() != MODE_SERV ) ) { requestNewOperationMode( MODE_FAUL ); } // If alarm has stop property, signal stop now if ( TRUE == props.alarmStops ) { initiateAlarmAction( ALARM_ACTION_STOP ); } } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_ALARM_TO_ACTIVATE1, alarm ) } } /*********************************************************************//** * @brief * The clearAlarmTD function clears a given alarm if it is recoverable. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given alarm ID is invalid. * @details \b Inputs: none * @details \b Outputs: alarmStartedAt[], * @param alarm ID of alarm to clear * @return none *************************************************************************/ void clearAlarmTD( ALARM_ID_T alarm ) { // Verify given alarm if ( ( alarm > ALARM_ID_NO_ALARM ) && ( alarm < NUM_OF_ALARM_IDS ) ) { ALARM_T props = getAlarmProperties( alarm ); // Verify alarm can be cleared if ( FALSE == props.alarmNoClear ) { // Clear alarm and broadcast alarm clear if not already cleared if ( TRUE == isAlarmActive( alarm ) ) { clearAlarm( alarm ); alarmStartedAt[ alarm ].data = 0; } } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_ALARM_TO_CLEAR1, alarm ) } } /*********************************************************************//** * @brief * The activateAlarmNoData function activates a given alarm. The alarm * data that gets logged with this alarm will be blank. * @details \b Inputs: none * @details \b Outputs: Alarm is activated * @param alarm ID of alarm to activate * @return none *************************************************************************/ void activateAlarmNoData( ALARM_ID_T alarm ) { activateAlarm2Data( alarm, BLANK_ALARM_DATA, BLANK_ALARM_DATA, FALSE ); } /*********************************************************************//** * @brief * The activateAlarm1Data function activates a given alarm. The one given * alarm data will be logged with this alarm as well as a second blank data. * @details \b Inputs: none * @details \b Outputs: Alarm is activated * @param alarm ID of alarm to activate * @param alarmData First supporting data to include in alarm message * @return none *************************************************************************/ void activateAlarm1Data( ALARM_ID_T alarm, ALARM_DATA_T alarmData ) { activateAlarm2Data( alarm, alarmData, BLANK_ALARM_DATA, FALSE ); } /*********************************************************************//** * @brief * The activateAlarm2Data function activates a given alarm. The two given * alarm data will be logged with this alarm. * @details \b Message \b Sent: MSG_ID_ALARM_TRIGGERED * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given alarm ID is invalid. * @details \b Inputs: alarmsBlockedTimer, determines blocked alarm conditions * @details \b Outputs: Alarm is activated * @param alarm ID of alarm to activate * @param alarmData1 First supporting data to include in alarm message * @param alarmData2 Second supporting data to include in alarm message * @param outside flag indicates whether alarm is originating from outside TD f/w * @return none *************************************************************************/ void activateAlarm2Data( ALARM_ID_T alarm, ALARM_DATA_T alarmData1, ALARM_DATA_T alarmData2, BOOL outside ) { // Block if new alarms are occur during loss of AC power // if ( ( TRUE == getCPLDACPowerLossDetected() ) ) // { // alarmsBlockedTimer = ALARM_BLOCKED_COUNT_AFTER_AC_RETURN; // } // Sanity check, verify valid alarm index if ( ( alarm > ALARM_ID_NO_ALARM ) && ( alarm < NUM_OF_ALARM_IDS ) ) { // if the block timer is 0 OR we have an unblockable alarm // if ( ( ALARM_NOT_BLOCKED == alarmsBlockedTimer ) // || ( ALARM_ID_TD_AC_POWER_LOST == alarm ) // || ( ALARM_ID_TD_AC_POWER_LOST_IN_TREATMENT == alarm ) // || ( ALARM_ID_TD_DD_RESTARTED_FAULT == alarm ) ) { TD_OP_MODE_T opMode = getCurrentOperationMode(); // TREATMENT_STATE_T ts = getTreatmentState(); ALARM_T props = getAlarmProperties( alarm ); // do not trigger alarm if blocked by current mode/state // if ( ( ( props.alarmBlockRinseback != TRUE ) || ( opMode != MODE_TREA ) || ( ts != TREATMENT_RINSEBACK_STATE ) || ( TRUE == outside ) ) && // ( ( props.alarmBlockEndTx != TRUE ) || ( opMode != MODE_POST ) || ( TRUE == outside )) ) { // Broadcast alarm and data if alarm not already active if ( FALSE == isAlarmActive( alarm ) ) { ALARM_TRIGGERED_PAYLOAD_T data; data.alarm = (U32)alarm; data.almDataType1 = (U32)alarmData1.dataType; data.almData1 = alarmData1.data.uInt.data; data.almDataType2 = (U32)alarmData2.dataType; data.almData2 = alarmData2.data.uInt.data; data.almPriority = props.alarmPriority; data.almRank = props.alarmSubRank; data.almClrTopOnly = props.alarmClearOnly; broadcastData( MSG_ID_ALARM_TRIGGERED, COMM_BUFFER_OUT_CAN_TD_ALARM, (U08*)&data, sizeof( ALARM_TRIGGERED_PAYLOAD_T ) ); // Send information for UI to log to treatment log if ( ( TRUE == props.alarmTreatmentLog ) && ( MODE_TREA == getCurrentOperationMode() ) ) { F32 data1; F32 data2; memcpy( &data1, &alarmData1.data, sizeof( F32 ) ); memcpy( &data2, &alarmData2.data, sizeof( F32 ) ); // sendTreatmentLogAlarmEventData( alarm, data1, data2 ); } } activateAlarmTD( alarm ); } } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_ALARM_TO_ACTIVATE2, alarm ) } } /*********************************************************************//** * @brief * The setAlarmUserActionEnabled function enables/disables specific alarm * recovery user actions while in specific modes. * @note The Ack option is always potentially enabled - automatically enabled * as appropriate by updateAlarmsFlags(). * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given alarm action is invalid. * @details \b Inputs: none * @details \b Outputs: alarmUserRecoveryActionEnabled[] * @param action ID of user alarm recovery action to enable/disable * @param enabled Set to TRUE to enable action, FALSE to disable it * @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_TD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_USER_ACTION, (U32)action ) } } /*********************************************************************//** * @brief * The signalAlarmSilence function executes an alarm silence request from * the user. * @details \b Inputs: alarmStatus * @details \b Outputs: alarmStatus * @param cmd ID of alarm silence request type (1=silence, 0=cancel silence) * @return none *************************************************************************/ void signalAlarmSilence( ALARM_SILENCE_CMD_T cmd ) { if ( ALARM_SILENCE_CMD_START == 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 * (or just the top alarm if it is recoverable and clear-only) and then executes * the given user action. * @note User actions are debounced (must come at least 1 second after last * received user action). And user actions must be appropriate for current * alarm state. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given user action is invalid. * @details \b Inputs: ALARM_TABLE[], alarmStatus, lastUserAlarmActionReceivedTime * @details \b Outputs: alarmIsActive[], lastUserAlarmActionReceivedTime * @param action ID of given user action to execute * @return none *************************************************************************/ void signalAlarmUserActionInitiated( ALARM_USER_ACTION_T action ) { BOOL alarmActionIsValid = FALSE; // Validate user selected action is appropriate per current alarm system status switch ( action ) { case ALARM_USER_ACTION_RESUME: if ( alarmStatus.noResume != TRUE ) { alarmActionIsValid = TRUE; } break; case ALARM_USER_ACTION_RINSEBACK: if ( alarmStatus.noRinseback != TRUE ) { alarmActionIsValid = TRUE; } break; case ALARM_USER_ACTION_END_TREATMENT: if ( alarmStatus.noEndTreatment != TRUE ) { alarmActionIsValid = TRUE; } break; case ALARM_USER_ACTION_ACK: if ( TRUE == alarmStatus.ok ) { alarmActionIsValid = TRUE; } break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_ALARM_USER_ACTION1, action ); break; } // Ignore alarm action if invalid or too soon after last one (essentially a debounce in case user double tapped a button) if ( ( TRUE == alarmActionIsValid ) && ( calcTimeSince( lastUserAlarmActionReceivedTime ) >= MIN_TIME_BETWEEN_ALARM_ACTIONS_MS ) ) { BOOL allRecAlarmsCleared = TRUE; // Clear recoverable alarms on user action if ( action != ALARM_USER_ACTION_END_TREATMENT ) // end tx action must be confirmed first { ALARM_ID_T a = alarmStatus.alarmTop; ALARM_T props = getAlarmProperties( a ); if ( ALARM_USER_ACTION_ACK == action ) { // If user acknowledged alarm w/ clear only property, just clear that alarm if ( TRUE == props.alarmClearOnly ) { clearAlarmTD( a ); } // Otherwise we must be in mode/state where ack was only option - so clear all like other options else { allRecAlarmsCleared = clearAllRecoverableAlarms( action ); } } else { allRecAlarmsCleared = clearAllRecoverableAlarms( action ); } } // Initiate user selected action switch ( action ) { case ALARM_USER_ACTION_RESUME: if ( TRUE == allRecAlarmsCleared ) { // only resume if we've cleared all recoverable alarms initiateAlarmAction( ALARM_ACTION_RESUME ); } break; case ALARM_USER_ACTION_RINSEBACK: initiateAlarmAction( ALARM_ACTION_RINSEBACK ); break; case ALARM_USER_ACTION_END_TREATMENT: // Send message to UI to get user confirmation to end treatment - action initiated only upon receipt of user confirmation from UI addConfirmationRequest( GENERIC_CONFIRM_ID_TREATMENT_END, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0 ); break; case ALARM_USER_ACTION_ACK: initiateAlarmAction( ALARM_ACTION_ACK ); break; // This default cannot be reached in VectorCAST due to check above for alarmActionIsValid #ifndef _VECTORCAST_ default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_ALARM_USER_ACTION2, action ); break; #endif } } // Remember last time user selected an alarm action lastUserAlarmActionReceivedTime = getMSTimerCount(); } /*********************************************************************//** * @brief * The isAnyAlarmActive function determines whether any alarm is currently * active. * @details \b Inputs: alarmStatus * @details \b 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 areAlarmsSilenced function determines whether alarms are currently * silenced. * @details \b Inputs: alarmStatus * @details \b Outputs: none * @return TRUE if alarms are currently silenced, FALSE if not *************************************************************************/ BOOL areAlarmsSilenced( void ) { return alarmStatus.alarmsSilenced; } /*********************************************************************//** * @brief * The isBloodRecircBlocked function determines whether any currently * active alarm is blocking blood re-circulation. * @details \b Inputs: alarmStatus * @details \b Outputs: none * @return TRUE if any active alarm prevents blood re-circulation, FALSE if not *************************************************************************/ BOOL isBloodRecircBlocked( void ) { return alarmStatus.noBloodRecirc; } /*********************************************************************//** * @brief * The isDialysateRecircBlocked function determines whether any currently * active alarm is blocking dialysate re-circulation. * @details \b Inputs: alarmStatus * @details \b Outputs: none * @return TRUE if any active alarm prevents dialysate re-circulation, FALSE if not *************************************************************************/ BOOL isDialysateRecircBlocked( void ) { return alarmStatus.noDialRecirc; } /*********************************************************************//** * @brief * The doesAlarmStatusIndicateEndTxOnly function determines whether the * alarm system will only allow end treatment option from full stop. * @details \b Inputs: alarmStatus * @details \b Outputs: none * @return TRUE if end treatment will be only option, FALSE if not *************************************************************************/ BOOL doesAlarmStatusIndicateEndTxOnly( void ) { return ( ( ( TRUE == alarmStatus.noResume ) && ( TRUE == alarmStatus.noRinseback ) && ( TRUE == alarmStatus.stop ) && ( TRUE == alarmStatus.noBloodRecirc ) && ( FALSE == alarmStatus.noEndTreatment ) ) ? TRUE : FALSE ); } /*********************************************************************//** * @brief * The doesAlarmStatusIndicateStop function determines whether alarm system * is currently indicating we should stop. * @details \b Inputs: alarmStatus * @details \b Outputs: none * @return TRUE if alarm system indicates stop, FALSE if not *************************************************************************/ BOOL doesAlarmStatusIndicateStop( void ) { return alarmStatus.stop; } /*********************************************************************//** * @brief * The doesAlarmIndicateNoResume function determines whether the alarm system * currently indicates resume option is not allowed. * @details \b Inputs: resumeBlockedByAlarmProperty * @details \b Outputs: none * @return TRUE if alarm system indicates resume option is blocked, FALSE if not *************************************************************************/ BOOL doesAlarmIndicateNoResume( void ) { return resumeBlockedByAlarmProperty; } /*********************************************************************//** * @brief * The getCurrentAlarmStatePriority function determines the current alarm * state (NONE, LOW, MEDIUM, or HIGH). * @details \b Inputs: alarmStatus.alarmsState * @details \b Outputs: none * @return current alarm state (priority of highest ranking active alarm) *************************************************************************/ ALARM_PRIORITY_T getCurrentAlarmStatePriority( void ) { return alarmStatus.alarmsState; } /*********************************************************************//** * @brief * The handleActiveAlarmListRequest function handles the active alarms list * request from UI. * @note Active alarm list is sorted by rank and is limited to maximum 10 alarm * entries. * @details \b Message \b Sent: MSG_ID_TD_ACTIVE_ALARMS_LIST_REQUEST_RESPONSE * @details \b Inputs: alarmStatus, alarmIsActive[], ALARM_RANK_TABLE[] * @details \b Outputs: sent active alarms list to UI * @return none *************************************************************************/ void handleActiveAlarmListRequest( void ) { ACTIVE_ALARM_LIST_RESPONSE_PAYLOAD_T activeAlarmPayload; U32 index; U32 activeAlarmListIndex = 0; activeAlarmPayload.accepted = TRUE; activeAlarmPayload.rejectionReason = (U32)REQUEST_REJECT_REASON_NONE; // Blank alarm list initially for ( index = 0; index < MAX_ALARM_LIST_SIZE; index++ ) { activeAlarmPayload.activeAlarmList[ index ] = ALARM_ID_NO_ALARM; } // Fill alarm list from (up to) 10 highest priority active alarms if ( TRUE == isAnyAlarmActive() ) { for ( index = 0; index < NUM_OF_ALARM_IDS; index++ ) { ALARM_RANK_T ranks = getAlarmRank( index ); if ( ( TRUE == isAlarmActive( ranks.alarmID ) ) && ( activeAlarmListIndex < MAX_ALARM_LIST_SIZE ) ) { activeAlarmPayload.activeAlarmList[ activeAlarmListIndex ] = ranks.alarmID; activeAlarmListIndex++; } } } // TODO sendActiveAlarmsList( activeAlarmPayload ); } /*********************************************************************//** * @brief * The handleResendActiveAlarmsRequest function executes the request to re-send * all currently active alarms. * @details \b Message \b Sent: MSG_ID_ALARM_TRIGGERED for each active alarm. * @details \b Inputs: alarmIsActive[], ALARM_TABLE[] * @details \b Outputs: none * @return none *************************************************************************/ void handleResendActiveAlarmsRequest( void ) { U32 index; if ( TRUE == isAnyAlarmActive() ) { for ( index = 0; index < NUM_OF_ALARM_IDS; index++ ) { if ( TRUE == isAlarmActive( (ALARM_ID_T)index ) ) { ALARM_TRIGGERED_PAYLOAD_T data; ALARM_T props = getAlarmProperties( (ALARM_ID_T)index ); data.alarm = index; data.almDataType1 = BLANK_ALARM_DATA.dataType; data.almData1 = BLANK_ALARM_DATA.data.uInt.data; data.almDataType2 = BLANK_ALARM_DATA.dataType; data.almData2 = BLANK_ALARM_DATA.data.uInt.data; data.almPriority = props.alarmPriority; data.almRank = props.alarmSubRank; data.almClrTopOnly = props.alarmClearOnly; broadcastData( MSG_ID_ALARM_TRIGGERED, COMM_BUFFER_OUT_CAN_TD_ALARM, (U08*)&data, sizeof( ALARM_TRIGGERED_PAYLOAD_T ) ); } } } } /*********************************************************************//** * @brief * The getAlarmStartTime function gets the start time of a given alarm. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given alarm ID is invalid. * @details \b Inputs: alarmStartedAt[] * @details \b Outputs: none * @param alarmID ID of alarm to get start time for * @return The start time stamp (seconds since power up) 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 { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_ALARM_FOR_START_TIME, alarmID ) } return result; } /*********************************************************************//** * @brief * The monitorAlarms function monitors alarm audio current and also looks * for and executes a user confirmation of user request to end treatment. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ static void monitorAlarms( void ) { execAlarmAudio(); // Check for user confirmation of end treatment alarm response if ( CONFIRMATION_REQUEST_STATUS_ACCEPTED == getConfirmationRequestStatus( GENERIC_CONFIRM_ID_TREATMENT_END ) ) { // To avoid raising repeated alarm before reaching end treatment // setVenousBubbleDetectionEnabled( FALSE ); clearAllRecoverableAlarms( ALARM_USER_ACTION_END_TREATMENT ); initiateAlarmAction( ALARM_ACTION_END_TREATMENT ); } // TODO - Check current vs. expected audio output } /*********************************************************************//** * @brief * The updateAlarmsState function re-evaluates the current alarm system state * and the "top" alarm to display (highest ranking active alarm). Some of * the properties of alarm system status are re-evaluated as well. * @details \b Inputs: alarmStatusTable[] * @details \b Outputs: alarmStatus, alarmPriorityFIFO[] * @return none *************************************************************************/ static void updateAlarmsState( void ) { ALARM_PRIORITY_T highestPriority = ALARM_PRIORITY_NONE, p; ALARM_ID_T a; BOOL faultsActive = FALSE; BOOL dialysateRecircBlocked = FALSE; BOOL bloodRecircBlocked = FALSE; // Reset priority FIFOs so we can re-determine them below for ( p = ALARM_PRIORITY_NONE; p < NUM_OF_ALARM_PRIORITIES; p++ ) { resetAlarmPriorityFIFO( p ); } // 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 == isAlarmActive( a ) ) { ALARM_T props = getAlarmProperties( a ); ALARM_PRIORITY_T almPriority = props.alarmPriority; U32 subRank = props.alarmSubRank; U32 msSinceTriggered = calcTimeSince( getAlarmStartTime( a ) ); // See if this alarm is higher rank than highest active alarm in this priority category so far (lower rank # = higher rank) if ( subRank <= alarmPriorityFIFO[ almPriority ].subRank ) { // If sub-rank is a tie, see which alarm was triggered first if ( subRank == alarmPriorityFIFO[ almPriority ].subRank ) { if ( (S32)msSinceTriggered > alarmPriorityFIFO[ almPriority ].timeSinceTriggeredMS ) { alarmPriorityFIFO[ almPriority ].alarmID = a; alarmPriorityFIFO[ almPriority ].subRank = subRank; alarmPriorityFIFO[ almPriority ].timeSinceTriggeredMS = (S32)msSinceTriggered; } } // Otherwise, this alarm simply outranks current candidate and wins outright else { alarmPriorityFIFO[ almPriority ].alarmID = a; alarmPriorityFIFO[ almPriority ].subRank = subRank; alarmPriorityFIFO[ almPriority ].timeSinceTriggeredMS = (S32)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 == props.alarmIsFault ) { faultsActive = TRUE; } // Track whether any active alarms prevent dialysate re-circulation so far if ( TRUE == props.alarmNoDialysateRecirc ) { dialysateRecircBlocked = TRUE; } // Track whether any active alarms prevent blood re-circulation so far if ( TRUE == props.alarmNoBloodRecirc ) { bloodRecircBlocked = TRUE; } } } // Update alarm to display per highest priority FIFO alarmStatus.alarmsState = highestPriority; alarmStatus.alarmTop = alarmPriorityFIFO[ highestPriority ].alarmID; alarmStatus.topAlarmConditionDetected = isAlarmConditionDetected( alarmStatus.alarmTop ); alarmStatus.systemFault = faultsActive; alarmStatus.noBloodRecirc = bloodRecircBlocked; alarmStatus.noDialRecirc = dialysateRecircBlocked; } /*********************************************************************//** * @brief * The setAlarmLamp function sets the alarm lamp pattern according to the * current state of alarms. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if reported alarm state is invalid. * @details \b Inputs: alarmStatus, ALARM_TABLE[] * @details \b Outputs: alarmStatus, alarm lamp pattern 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: { ALARM_T propsTop = getAlarmProperties( alarmStatus.alarmTop ); if ( TRUE == propsTop.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_TD_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 updateAlarmsSilenceStatus function updates the alarms silence state. * @details \b Inputs: alarmStatus * @details \b 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 updateAlarmsFlags function updates the alarms status flags of the * alarms status record. * @details \b Inputs: alarmStatus, alarmIsActive, ALARM_TABLE[], * alarmButtonBlockers[]. alarmUserRecoveryActionEnabled[] * @details \b Outputs: alarmStatus, alarmButtonBlockers[] * @return none *************************************************************************/ static void updateAlarmsFlags( void ) { BOOL systemFault = FALSE; BOOL stop = FALSE; BOOL noClear = FALSE; BOOL noResume = FALSE; BOOL noResumePerAlarmPropertyOnly = FALSE; BOOL noRinseback = FALSE; BOOL noEndTreatment = FALSE; BOOL endTxOnlyAlarmActive = FALSE; BOOL usrAckReq = FALSE; BOOL noMinimize = TRUE; TD_OP_MODE_T currentMode = getCurrentOperationMode(); ALARM_T propsTop = getAlarmProperties( alarmStatus.alarmTop ); ALARM_ID_T a; // Set user alarm recovery actions allowed by state flags alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_RESUME ] = ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RESUME ] ? FALSE : TRUE ); alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_RINSEBACK ] = ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RINSEBACK ] ? FALSE : TRUE ); alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_END_TREATMENT ] = ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_END_TREATMENT ] ? FALSE : TRUE ); // Reset user alarm recovery actions allowed by active alarms flags alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RESUME ] = FALSE; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RINSEBACK ] = FALSE; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_END_TREATMENT ] = FALSE; // Determine alarm flags for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { if ( TRUE == isAlarmActive( a ) ) { ALARM_T props = getAlarmProperties( a ); systemFault = ( TRUE == props.alarmIsFault ? TRUE : systemFault ); stop = ( TRUE == props.alarmStops ? TRUE : stop ); noClear = ( TRUE == props.alarmNoClear ? TRUE : noClear ); noResumePerAlarmPropertyOnly = ( props.alarmNoResume ? TRUE : noResumePerAlarmPropertyOnly ); // Set user alarm recovery actions allowed flags alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RESUME ] |= props.alarmNoResume; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RINSEBACK ] |= props.alarmNoRinseback; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_END_TREATMENT ] |= props.alarmNoEndTreatment; if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RESUME ] ) { noResume = ( TRUE == props.alarmNoResume ? TRUE : noResume ); } else { noResume = TRUE; } if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RINSEBACK ] ) { noRinseback = ( TRUE == props.alarmNoRinseback ? TRUE : noRinseback ); } else { noRinseback = TRUE; } if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_END_TREATMENT ] ) { noEndTreatment = ( TRUE == props.alarmNoEndTreatment ? TRUE : noEndTreatment ); } else { noEndTreatment = TRUE; } // if there are any active alarms that only allow end treatment, set flag so we can ensure we do not allow OK option. if ( ( TRUE == props.alarmNoResume ) && ( TRUE == props.alarmNoRinseback ) && ( FALSE == props.alarmNoEndTreatment ) ) { endTxOnlyAlarmActive = TRUE; } } // If alarm active } // Alarm table loop // If top alarm condition not cleared, block resume if ( TRUE == alarmStatus.topAlarmConditionDetected ) { noResume = TRUE; } // If top alarm has clear only property or no other user options enabled for recoverable alarm and condition cleared, set user ack flag and block other flags if ( ( TRUE == propsTop.alarmClearOnly ) || ( ( FALSE == alarmStatus.noClear ) && ( TRUE == noResume ) && ( TRUE == noRinseback ) && ( TRUE == noEndTreatment ) && ( FALSE == alarmStatus.topAlarmConditionDetected ) ) ) { usrAckReq = TRUE; noResume = TRUE; noRinseback = TRUE; noEndTreatment = TRUE; } // if OK option enabled and there are any active alarms that only allow end treatment, block OK option and allow end treatment option (DEN-16594). if ( ( TRUE == usrAckReq ) && ( TRUE == endTxOnlyAlarmActive ) ) { usrAckReq = FALSE; noEndTreatment = FALSE; } // If AC power is out, block all user options if ( TRUE == getCPLDACPowerLossDetected() ) { usrAckReq = FALSE; noResume = TRUE; noRinseback = TRUE; noEndTreatment = TRUE; } // If in Treatment-Stop state or Fault/Service/Standby Mode, allow user to minimize the alarm window // if ( ( MODE_FAUL == currentMode ) || ( MODE_SERV == currentMode ) || ( MODE_STAN == currentMode ) || // ( ( MODE_TREA == currentMode ) && ( TREATMENT_STOP_STATE == getTreatmentState() ) ) ) { noMinimize = FALSE; } // Set updated alarm flags alarmStatus.systemFault = systemFault; alarmStatus.stop = stop; alarmStatus.noClear = noClear; alarmStatus.noResume = noResume; alarmStatus.noRinseback = noRinseback; alarmStatus.noEndTreatment = noEndTreatment; alarmStatus.ok = usrAckReq; alarmStatus.noMinimize = noMinimize; resumeBlockedByAlarmProperty = noResumePerAlarmPropertyOnly; } /*********************************************************************//** * @brief * The clearAllRecoverableAlarms function clears all currently active * recoverable alarms. * @details \b Inputs: ALARM_TABLE[], isAlarmActive[], alarmIsDetected[] * @details \b Outputs: alarmIsActive[], alarmIsDetected[], alarmStartedAt[] * @param action User action that prompted clearing of recoverable alarms * @return TRUE if all recoverable alarms cleared, FALSE if any left active *************************************************************************/ static BOOL clearAllRecoverableAlarms( ALARM_USER_ACTION_T action ) { BOOL result = TRUE; ALARM_ID_T a; // assigning to 1 in order to prevent ALARM_ID_NO_ALARM being cleared // which will cause a fault for ( a = ( ( ALARM_ID_T ) 1 ); a < NUM_OF_ALARM_IDS; a++ ) { ALARM_T props = getAlarmProperties( a ); // Clear alarm if alarm allowed to be cleared and not clear only (those are cleared individually) if ( ( FALSE == props.alarmNoClear ) && ( FALSE == props.alarmClearOnly ) ) { // Clear alarm if active and condition not active if ( ( TRUE == isAlarmActive( a ) ) && ( ( TRUE == props.alarmConditionClearImmed ) || ( isAlarmConditionDetected( a ) != TRUE ) || ( action != ALARM_USER_ACTION_RESUME ) ) ) { // clear this alarm clearAlarmTD( a ); } else if ( TRUE == isAlarmActive( a ) ) { result = FALSE; // we didn't clear this alarm because condition still active } } } return result; } /*********************************************************************//** * @brief * The resetAlarmPriorityFIFO function resets a FIFO for a given alarm * priority. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given priority is invalid. * @details \b Inputs: none * @details \b Outputs: alarmPriorityFIFO[] * @param priority Alarm priority associated with 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 = -1; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_ALARM_MGMT_INVALID_FIFO_TO_RESET, priority ) } } /*********************************************************************//** * @brief * The publishAlarmInfo function publishes alarm information and status * at the set time intervals. * @details \b Message \b Sent: MSG_ID_ALARM_STATUS_DATA * @details \b Message \b Sent: MSG_ID_TD_ALARM_INFORMATION_DATA * @details \b Inputs: alarmStatusPublicationTimerCounter, alarmInfoPublicationTimerCounter, * alarmButtonBlockers[] * @details \b Outputs: alarmStatusPublicationTimerCounter, alarmInfoPublicationTimerCounter * @return none *************************************************************************/ static void publishAlarmInfo( void ) { // Publish alarm status at interval if ( ++alarmStatusPublicationTimerCounter >= getU32OverrideValue( &alarmStatusPublishInterval ) ) { // Lamp and audio timing sync'd with alarm status broadcast so UI/lamp/audio can stay in sync setAlarmLamp(); setAlarmAudio(); broadcastAlarmStatus( alarmStatus ); alarmStatusPublicationTimerCounter = 0; } // Publish voltages monitor data on interval if ( ++alarmInfoPublicationTimerCounter >= getU32OverrideValue( &alarmInfoPublishInterval ) ) { ALARM_INFO_PAYLOAD_T data; data.audioVolume = MAX_ALARM_VOLUME_LEVEL - getAlarmAudioVolume(); // convert back to 1..5 volume level for publication data.audioCurrHG = getAlarmAudioPrimaryHighGainCurrent(); data.audioCurrLG = getAlarmAudioPrimaryLowGainCurrent(); data.backupAudioCurr = getAlarmAudioBackupCurrent(); data.safetyShutdown = isSafetyShutdownActivated(); data.acPowerLost = getCPLDACPowerLossDetected(); data.uiAlarmButtonBlocks[ ALARM_BUTTON_TABLE_BLOCK_RESUME ] = (U08)alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RESUME ]; data.uiAlarmButtonBlocks[ ALARM_BUTTON_TABLE_BLOCK_RINSEBACK ] = (U08)alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RINSEBACK ]; data.uiAlarmButtonBlocks[ ALARM_BUTTON_TABLE_BLOCK_END_TREATMENT ] = (U08)alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_END_TREATMENT ]; data.uiAlarmButtonBlocks[ ALARM_BUTTON_STATE_BLOCK_RESUME ] = (U08)alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_RESUME ]; data.uiAlarmButtonBlocks[ ALARM_BUTTON_STATE_BLOCK_RINSEBACK ] = (U08)alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_RINSEBACK ]; data.uiAlarmButtonBlocks[ ALARM_BUTTON_STATE_BLOCK_END_TREATMENT ] = (U08)alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_END_TREATMENT ]; broadcastData( MSG_ID_TD_ALARM_INFORMATION_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( ALARM_INFO_PAYLOAD_T ) ); alarmInfoPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief * The broadcastAlarmStatus function constructs an alarm status msg to * be broadcast and queues the msg for transmit on the appropriate CAN channel. * @details \b Inputs: none * @details \b Outputs: alarm status msg constructed and queued. * @param almStatus alarm status record * @return TRUE if msg successfully queued for transmit, FALSE if not *************************************************************************/ static BOOL broadcastAlarmStatus( COMP_ALARM_STATUS_T almStatus ) { BOOL result; ALARM_COMP_STATUS_PAYLOAD_T payload; payload.alarmState = (U32)almStatus.alarmsState; payload.alarmTop = (U32)almStatus.alarmTop; payload.silenceExpiresIn = almStatus.alarmsSilenceExpiresIn; payload.alarmsFlags = ( almStatus.systemFault ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_SYSTEM_FAULT) : 0 ); payload.alarmsFlags |= ( almStatus.stop ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_STOP) : 0 ); payload.alarmsFlags |= ( almStatus.noClear ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_CLEAR) : 0 ); payload.alarmsFlags |= ( almStatus.noResume ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_RESUME) : 0 ); payload.alarmsFlags |= ( almStatus.noRinseback ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_RINSEBACK) : 0 ); payload.alarmsFlags |= ( almStatus.noEndTreatment ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_END_TREATMENT) : 0 ); payload.alarmsFlags |= ( almStatus.ok ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_OK_BUTTON_ONLY) : 0 ); payload.alarmsFlags |= ( almStatus.alarmsSilenced ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_ALARMS_SILENCED) : 0 ); payload.alarmsFlags |= ( almStatus.lampOn ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_LAMP_ON) : 0 ); payload.alarmsFlags |= ( almStatus.noBloodRecirc ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_BLOOD_RECIRC) : 0 ); payload.alarmsFlags |= ( almStatus.noDialRecirc ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_DIALYSATE_RECIRC) : 0 ); payload.alarmsFlags |= ( almStatus.noMinimize ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_MINIMIZE) : 0 ); payload.alarmsFlags |= ( almStatus.topAlarmConditionDetected ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_TOP_CONDITION) : 0 ); result = broadcastData( MSG_ID_ALARM_STATUS_DATA, COMM_BUFFER_OUT_CAN_TD_ALARM, (U08*)&payload, sizeof( ALARM_COMP_STATUS_PAYLOAD_T ) ); return result; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetAlarmStartTimeOverride function overrides the start time for a * given alarm with a given start time. * @details \b Inputs: msTimerCount * @details \b Outputs: alarmStartedAt[] * @param message Override message from Dialin which includes an ID of * the alarm to override start time for and a number of milliseconds the * override is seeking to make the elapsed time since the given alarm was * triggered. * @return TRUE if override is successful, FALSE if not *************************************************************************/ BOOL testSetAlarmStartTimeOverride( MESSAGE_T *message ) { BOOL result = FALSE; TEST_OVERRIDE_ARRAY_PAYLOAD_T override; OVERRIDE_TYPE_T ovType = getOverrideArrayPayloadFromMessage( message, &override ); // Verify tester has logged in with TD and override type is valid if ( ( TRUE == isTestingActivated() ) && ( ovType != OVERRIDE_INVALID ) && ( ovType < NUM_OF_OVERRIDE_TYPES ) ) { U32 alarmID = override.index; // Verify alarm index of override if ( alarmID < NUM_OF_ALARM_IDS ) { if ( OVERRIDE_OVERRIDE == ovType ) { U32 value = override.state.u32; U32 tim = getMSTimerCount(); if ( tim > value ) { result = TRUE; alarmStartedAt[ alarmID ].ovData = ( tim - value ); alarmStartedAt[ alarmID ].override = OVERRIDE_KEY; } } else { 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 TD. * @details \b Message \b Sent: MSG_ID_ALARM_CLEARED for each active alarm. * @details \b Inputs: none * @details \b Outputs: alarmIsActive[], alarmStartedAt[] * @param message Pointer to a clear all alarms message which contains a * 32-bit supervisor alarm key required to perform this function * @return TRUE if command was successful, FALSE if rejected *************************************************************************/ BOOL testClearAllAlarms( MESSAGE_T *message ) { BOOL result = FALSE; U32 key; // Verify tester has logged in with TD if ( TRUE == isTestingActivated() ) { // Verify payload length if ( sizeof(U32) == message->hdr.payloadLen ) { memcpy( &key, message->payload, sizeof(U32) ); // Verify key if ( SUPERVISOR_ALARM_KEY == key ) { ALARM_ID_T a; // clear the flags when Dialin clears alarms resumeBlockedByAlarmProperty = FALSE; // Clear all active alarms for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { if ( TRUE == isAlarmActive( a ) ) { ALARM_T props = getAlarmProperties( a ); ALARM_ID_DATA_PUBLISH_T data; data.alarmID = (U32)a; broadcastData( MSG_ID_ALARM_CLEARED, COMM_BUFFER_OUT_CAN_TD_ALARM, (U08*)&data, sizeof( ALARM_ID_DATA_PUBLISH_T ) ); setAlarmActive( a , FALSE ); alarmStartedAt[ a ].data = 0; // Clear FIFO if this alarm was in it if ( alarmPriorityFIFO[ props.alarmPriority ].alarmID == a ) { resetAlarmPriorityFIFO( props.alarmPriority ); } } } result = TRUE; } } } return result; } /*********************************************************************//** * @brief * The testAlarmStatusPublishIntervalOverride function overrides the interval * at which the TD alarm status data is published. * @details \b Inputs: none * @details \b Outputs: alarmStatusPublishInterval * @param message Override message from Dialin which includes the interval * (in ms) to override the alarm status broadcast interval to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testAlarmStatusPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &alarmStatusPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testAlarmInfoPublishIntervalOverride function overrides the interval * at which the TD alarm information is published. * @details \b Inputs: none * @details \b Outputs: alarmInfoPublishInterval * @param message Override message from Dialin which includes the interval * (in ms) to override the alarm information broadcast interval to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testAlarmInfoPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &alarmInfoPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /**@}*/