/************************************************************************** * * Copyright (c) 2019-2021 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 Bubble.c * * @author (last) Peman Montazemi * @date (last) 30-Apr-2021 * * @author (original) Peman Montazemi * @date (original) 30-Apr-2021 * ***************************************************************************/ #include "AlarmMgmt.h" #include "Bubble.h" #include "FPGA.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskPriority.h" #include "Timers.h" /** * @addtogroup Bubble * @{ */ // ********** private definitions ********** #define BUBBLE_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval (ms/task time) at which the air bubble detector data is published on the CAN bus. #define BUBBLE_TIMEOUT_MS 500 ///< Air bubble detector timeout for self-test (15 ms extended edge detection) /// Defined states for the air bubble detectors state machine. typedef enum BubbleStates { BUBBLE_NORMAL_STATE = 0, ///< Normal state BUBBLE_SELF_TEST_STATE, ///< Self-test state NUM_OF_BUBBLE_STATES ///< Number of bubble detector states } BUBBLE_STATES_T; // ********** private data ********** static BUBBLE_STATES_T bubblesState[ NUM_OF_BUBBLES ]; ///< Current state of air bubble detectors state machines. static OVERRIDE_U32_T bubblesStatus[ NUM_OF_BUBBLES ]; ///< Detected air bubble status for air bubble detectors. static SELF_TEST_STATUS_T bubblesSelfTestStatus[ NUM_OF_BUBBLES ]; ///< Current status of air bubble detectors self-tests. static U32 bubblesSelfTestStartTime[ NUM_OF_BUBBLES ]; ///< Air bubble detectors self-test start times. static BOOL bubblesSelfTestRequested[ NUM_OF_BUBBLES ]; ///< Air bubble detectors self-test requested flags. /// Interval (in ms) at which to publish air bubble detectors data on CAN bus. static OVERRIDE_U32_T bubblesDataPublishInterval = { BUBBLE_PUB_INTERVAL, BUBBLE_PUB_INTERVAL, 0, 0 }; static U32 bubblesDataPublicationTimerCounter = 0; ///< Timer counter used to schedule air bubble detectors data publication to CAN bus. // ********** private function prototypes ********** static BUBBLE_STATES_T handleBubbleNormalState( BUBBLES_T ); static BUBBLE_STATES_T handleBubbleSelfTestState( BUBBLES_T ); static void publishBubblesData( void ); /*********************************************************************//** * @brief * The initBubbles function initializes the air bubble detectors module. * @details Inputs: none * @details Outputs: Air bubble detectors module initialized. * @return none *************************************************************************/ void initBubbles( void ) { BUBBLES_T bubble; for (bubble = ADA; bubble < NUM_OF_BUBBLES; bubble++) { bubblesState[ bubble ] = BUBBLE_NORMAL_STATE; bubblesStatus[ bubble ].data = BUBBLE_NOT_DETECTED; bubblesStatus[ bubble ].ovInitData = BUBBLE_NOT_DETECTED; bubblesStatus[ bubble ].ovData = BUBBLE_NOT_DETECTED; bubblesStatus[ bubble ].override = OVERRIDE_RESET; bubblesSelfTestStatus[ bubble ] = SELF_TEST_STATUS_IN_PROGRESS; bubblesSelfTestRequested[ bubble ] = FALSE; bubblesSelfTestStartTime[ bubble ] = 0; } } /*********************************************************************//** * @brief * The execBubbles function executes the air bubble detectors driver. * @details Inputs: FPGA air bubble detectors status GPIO pin state * @details Outputs: bubblesStatus * @return none *************************************************************************/ void execBubbles( void ) { BUBBLES_T bubble; if ( getCurrentOperationMode() != MODE_INIT ) { // Loop through all of the air bubble detectors for ( bubble = ADA; bubble < NUM_OF_BUBBLES; bubble++ ) { // Execute the air bubble detector state machine switch( bubblesState[ bubble ] ) { case BUBBLE_NORMAL_STATE: bubblesState[ bubble ] = handleBubbleNormalState( bubble ); break; case BUBBLE_SELF_TEST_STATE: bubblesState[ bubble ] = handleBubbleSelfTestState( bubble ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_BUBBLE_STATE, bubblesState[ bubble ] ) break; } } } // Publish air bubble detectors data if due publishBubblesData(); } /*********************************************************************//** * @brief * The selfTestBubble function requests that a specified air bubble detector * be self-tested. * @details Inputs: none * @details Outputs: Air bubble detector module self-testing * @return Boolean as success or failure *************************************************************************/ void selfTestBubble( BUBBLES_T bubble ) { bubblesSelfTestRequested[ bubble ] = TRUE; } /*********************************************************************//** * @brief * The getBubbleStatus function gets the current reading for a given air bubble * detector. * @details Inputs: bubblesStatus * @details Outputs: none * @return the current air bubble status. *************************************************************************/ BUBBLE_STATUS_T getBubbleStatus( BUBBLES_T bubble ) { BUBBLE_STATUS_T result = (BUBBLE_STATUS_T)bubblesStatus[ bubble ].data; if ( OVERRIDE_KEY == bubblesStatus[ bubble ].override ) { result = (BUBBLE_STATUS_T)bubblesStatus[ bubble ].ovData; } return result; } /*********************************************************************//** * @brief * The getBubbleSelfTestStatus function gets the status for a given air bubble * detector self-test. * @details Inputs: bubblesSelfTestStatus * @details Outputs: none * @return status of air bubble detector self-test. *************************************************************************/ SELF_TEST_STATUS_T getBubbleSelfTestStatus( BUBBLES_T bubble ) { return bubblesSelfTestStatus[ bubble ]; } /*********************************************************************//** * @brief * The handleBubbleNormalState function handles a given air bubble module * in normal state. * @details Inputs: none * @details Outputs: Bubble module normal. * @return next state *************************************************************************/ static BUBBLE_STATES_T handleBubbleNormalState( BUBBLES_T bubble ) { BUBBLE_STATES_T state = BUBBLE_NORMAL_STATE; TREATMENT_STATE_T treatmentState = getTreatmentState(); if ( ( bubble == ADA ) || ( bubble == ADV) ) { if ( TRUE == noFPGABubbleDetected( bubble ) ) { bubblesStatus[ bubble ].data = BUBBLE_NOT_DETECTED; } else { bubblesStatus[ bubble ].data = BUBBLE_DETECTED; } #ifndef DISABLE_BUBBLE_ALARMS // Check status reading and act upon if ( BUBBLE_DETECTED == getBubbleStatus( bubble ) ) { if ( getCurrentOperationMode() == MODE_TREA ) { switch ( treatmentState ) { case TREATMENT_BLOOD_PRIME_STATE: case TREATMENT_DIALYSIS_STATE: case TREATMENT_END_STATE: // TODO: Enable if ADA is ever used to detect air bubble on arterial line during treatment //if ( bubble == ADA ) //{ // activateAlarmNoData( ALARM_ID_HD_ARTERIAL_BUBBLE_DETECTED ); //} if ( bubble == ADV ) { activateAlarmNoData( ALARM_ID_HD_VENOUS_BUBBLE_DETECTED ); } break; case TREATMENT_RINSEBACK_STATE: // TODO: Enable if ADA is ever used to detect air bubble on arterial line during rinseback //if ( bubble == ADA ) //{ // activateAlarmNoData( ALARM_ID_HD_ARTERIAL_BUBBLE_DETECTED_RINSEBACK ); //} if ( bubble == ADV ) { activateAlarmNoData( ALARM_ID_HD_VENOUS_BUBBLE_DETECTED_RINSEBACK ); } break; default: // Ignore other treatment states break; } } } else // Air bubble not detected { if ( bubble == ADA ) { clearAlarmCondition( ALARM_ID_HD_ARTERIAL_BUBBLE_DETECTED ); } if ( bubble == ADV ) { clearAlarmCondition( ALARM_ID_HD_VENOUS_BUBBLE_DETECTED ); clearAlarmCondition( ALARM_ID_HD_VENOUS_BUBBLE_DETECTED_RINSEBACK ); } } #endif if ( TRUE == bubblesSelfTestRequested[ bubble ] ) { state = BUBBLE_SELF_TEST_STATE; bubblesSelfTestRequested[ bubble ] = FALSE; bubblesSelfTestStatus[ bubble ] = SELF_TEST_STATUS_IN_PROGRESS; setFPGABubbleSelfTest( bubble ); bubblesSelfTestStartTime[ bubble ] = getMSTimerCount(); } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_BUBBLE_ID, bubble ) } return state; } /*********************************************************************//** * @brief * The handleBubbleSelfTestState function handles a given air bubble detector * in self-test state. * @details Inputs: none * @details Outputs: Air bubble detector module self-test. * @return next state *************************************************************************/ static BUBBLE_STATES_T handleBubbleSelfTestState( BUBBLES_T bubble ) { BUBBLE_STATES_T state = BUBBLE_SELF_TEST_STATE; if ( ( bubble == ADA ) || ( bubble == ADV) ) { if ( TRUE == noFPGABubbleDetected( bubble ) ) { bubblesStatus[ bubble ].data = BUBBLE_NOT_DETECTED; } else { bubblesStatus[ bubble ].data = BUBBLE_DETECTED; } if ( SELF_TEST_STATUS_IN_PROGRESS == bubblesSelfTestStatus[ bubble ] ) { if ( BUBBLE_DETECTED == getBubbleStatus( bubble ) ) // Faked air bubble caused { bubblesSelfTestStatus[ bubble ] = SELF_TEST_STATUS_PASSED; clearFPGABubbleSelfTest( bubble ); } else if ( TRUE == didTimeout( bubblesSelfTestStartTime[ bubble ], BUBBLE_TIMEOUT_MS ) ) { bubblesSelfTestStatus[ bubble ] = SELF_TEST_STATUS_FAILED; if ( bubble == ADA ) { activateAlarmNoData( ALARM_ID_HD_ARTERIAL_BUBBLE_SELF_TEST_FAILURE ); } else // ADV { activateAlarmNoData( ALARM_ID_HD_VENOUS_BUBBLE_SELF_TEST_FAILURE ); } } } else { // Air bubble self-test finished, wait for self-test faked air bubble detector clear if ( BUBBLE_NOT_DETECTED == getBubbleStatus( bubble ) ) { state = BUBBLE_NORMAL_STATE; } } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_BUBBLE_ID, bubble ) } return state; } /*********************************************************************//** * @brief * The publishBubblesData function publishes air bubble detectors data at * the set interval. * @details Inputs: status, bubblesState * @details Outputs: if broadcast is due, send air bubble detectors data * @return none *************************************************************************/ static void publishBubblesData( void ) { // Publish air bubble detectors data on interval if ( ++bubblesDataPublicationTimerCounter >= getU32OverrideValue( &bubblesDataPublishInterval ) ) { BUBBLE_STATUS_T statusADA = getBubbleStatus( ADA ); BUBBLE_STATUS_T statusADV = getBubbleStatus( ADV ); broadcastBubblesData( (U32)statusADA, (U32)bubblesState[ ADA ], (U32)statusADV, (U32)bubblesState[ ADV ] ); bubblesDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetBubblesDataPublishIntervalOverride function overrides the * air bubbles data publish interval. * @details Inputs: none * @details Outputs: bubblesDataPublishInterval * @param value override air bubbles data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBubblesDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; result = TRUE; bubblesDataPublishInterval.ovData = intvl; bubblesDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetBubblesDataPublishIntervalOverride function resets the override * of the air bubbles data publish interval. * @details Inputs: none * @details Outputs: bubblesDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetBubblesDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; bubblesDataPublishInterval.override = OVERRIDE_RESET; bubblesDataPublishInterval.ovData = bubblesDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetBubbleStatusOverride function overrides the status * of a given air bubble detector. * @details Inputs: none * @details Outputs: bubblesStatus * @param none * @param status override air bubble detector with this * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBubbleStatusOverride( BUBBLE_STATUS_T status, BUBBLES_T bubble ) { BOOL result = FALSE; if ( status < NUM_OF_BUBBLE_STATUS ) { if ( TRUE == isTestingActivated() ) { result = TRUE; bubblesStatus[ bubble ].ovData = (U32)status; bubblesStatus[ bubble ].override = OVERRIDE_KEY; } } return result; } /*********************************************************************//** * @brief * The testResetBubbleStatusOverride function resets the override of a * given air bubble detector status. * @details Inputs: none * @details Outputs: bubblesStatus * @param none * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetBubbleStatusOverride( BUBBLES_T bubble ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; bubblesStatus[ bubble ].override = OVERRIDE_RESET; bubblesStatus[ bubble ].ovData = bubblesStatus[ bubble ].ovInitData; } return result; } /**@}*/