/************************************************************************** * * 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 AirTrap.c * * @author (last) Sean Nash * @date (last) 12-Nov-2021 * * @author (original) Sean Nash * @date (original) 16-Sep-2020 * ***************************************************************************/ #include "AirTrap.h" #include "AlarmMgmt.h" #include "FPGA.h" #include "ModeTreatmentParams.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TaskPriority.h" #include "Timers.h" /** * @addtogroup AirTrap * @{ */ // ********** private definitions ********** /// Interval (ms/task time) at which the air trap data is published on the CAN bus. #define AIR_TRAP_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) /// Persistence period for illegal level sensors fault. static const U32 AIR_TRAP_ILLEGAL_LEVELS_PERSISTENCE = ( MS_PER_SECOND * 2 / TASK_PRIORITY_INTERVAL ); /// Volume (in mL) of venous portion of blood circuit line. TODO - get actual volume from Systems. #define VENOUS_LINE_VOLUME_ML ( 200.0 ) /// Defined states for the air trap controller state machine. typedef enum AirTrap_States { AIR_TRAP_INIT_STATE = 0, ///< Initialization state AIR_TRAP_MANUAL_CONTROL_STATE, ///< Manually control air trap valve state AIR_TRAP_VALVE_CLOSED_STATE, ///< Valve closed state - until air detected at lower level AIR_TRAP_VALVE_OPEN_STATE, ///< Valve open state - until fluid detected at upper level NUM_OF_AIR_TRAP_STATES ///< Number of air trap controller states } AIR_TRAP_STATE_T; // ********** private data ********** static AIR_TRAP_STATE_T airTrapControllerState; ///< Current state of air trap controller state machine. static U32 airTrapDataPublicationTimerCounter = 0; ///< Used to schedule air trap data publication to CAN bus. /// Interval (in ms) at which to publish air trap data to CAN bus. static OVERRIDE_U32_T airTrapDataPublishInterval = { AIR_TRAP_DATA_PUB_INTERVAL, AIR_TRAP_DATA_PUB_INTERVAL, 0, 0 }; static OVERRIDE_U32_T airTrapLevels[ NUM_OF_AIR_TRAP_LEVEL_SENSORS ]; ///< Detected air trap level for each level sensor. static BOOL pendingStartAirTrapController = FALSE; ///< Flag indicates an air trap controller start request is pending. static BOOL pendingStopAirTrapController = FALSE; ///< Flag indicates an air trap controller stop request is pending. static U32 fillStartTime = 0; ///< Time stamp for start of air trap fill. static U32 airTrapIllegalLevelSensorsCtr = 0; ///< Timer counter for illegal level sensor fault. // ********** private function prototypes ********** static AIR_TRAP_STATE_T handleAirTrapManualControlState( void ); static AIR_TRAP_STATE_T handleAirTrapValveClosedState( void ); static AIR_TRAP_STATE_T handleAirTrapValveOpenState( void ); static void publishAirTrapData( void ); /*********************************************************************//** * @brief * The initAirTrap function initializes the Air Trap module. * @details Inputs: none * @details Outputs: Air Trap module initialized. * @return none *************************************************************************/ void initAirTrap( void ) { U32 i; resetAirTrap(); airTrapIllegalLevelSensorsCtr = 0; for ( i = 0; i < NUM_OF_AIR_TRAP_LEVEL_SENSORS; i++ ) { airTrapLevels[i].data = 0; airTrapLevels[i].ovData = 0; airTrapLevels[i].ovInitData = 0; airTrapLevels[i].override = OVERRIDE_RESET; } } /*********************************************************************//** * @brief * The resetAirTrap function resets certain parts of the air trap module * between treatments. * @details Inputs: none * @details Outputs: Air Trap module reset. * @return none *************************************************************************/ void resetAirTrap( void ) { airTrapControllerState = AIR_TRAP_INIT_STATE; pendingStartAirTrapController = FALSE; pendingStopAirTrapController = FALSE; } /*********************************************************************//** * @brief * The startAirTrapControl function requests a start to air trap control. * @details Inputs: airTrapControllerState * @details Outputs: pendingStartAirTrapController * @return none *************************************************************************/ void startAirTrapControl( void ) { if ( FALSE == isAirTrapControlling() ) { pendingStartAirTrapController = TRUE; } } /*********************************************************************//** * @brief * The endAirTrapControl function requests a stop to air trap control. * @details Inputs: airTrapControllerState * @details Outputs: pendingStopAirTrapController * @return none *************************************************************************/ void endAirTrapControl( void ) { if ( TRUE == isAirTrapControlling() ) { pendingStopAirTrapController = TRUE; setValveAirTrap( STATE_CLOSED ); // Always exit air trap valve control w/ valve closed. } } /*********************************************************************//** * @brief * The isAirTrapControlling function determines whether the air trap is * currently controlling. * @details Inputs: airTrapControllerState * @details Outputs: none * @return TRUE if air trap is currently controlling, FALSE if not. *************************************************************************/ BOOL isAirTrapControlling( void ) { BOOL result = FALSE; if ( airTrapControllerState >= AIR_TRAP_VALVE_CLOSED_STATE ) { result = TRUE; } return result; } /*********************************************************************//** * @brief * The execAirTrapMonitor function executes the air trap monitor. * @details Inputs: FPGA air trap levels GPIO pin levels, airTrapIllegalLevelSensorsCtr * @details Outputs: airTrapLevels[], airTrapIllegalLevelSensorsCtr * @return none *************************************************************************/ void execAirTrapMonitor( void ) { BOOL lower, upper; // Get latest level readings getFPGAAirTrapLevels( &lower, &upper ); airTrapLevels[ AIR_TRAP_LEVEL_SENSOR_LOWER ].data = (U32)( TRUE == lower ? AIR_TRAP_LEVEL_AIR : AIR_TRAP_LEVEL_FLUID ); airTrapLevels[ AIR_TRAP_LEVEL_SENSOR_UPPER ].data = (U32)( TRUE == upper ? AIR_TRAP_LEVEL_AIR : AIR_TRAP_LEVEL_FLUID ); // Check level readings are valid if ( ( AIR_TRAP_LEVEL_AIR == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ) ) && ( AIR_TRAP_LEVEL_FLUID == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_UPPER ) ) ) { if ( ++airTrapIllegalLevelSensorsCtr >= AIR_TRAP_ILLEGAL_LEVELS_PERSISTENCE ) { #ifndef DISABLE_ILLEGAL_AIR_TRAP_ALARM activateAlarmNoData( ALARM_ID_AIR_TRAP_ILLEGAL_LEVELS ); #endif } } else { if ( airTrapIllegalLevelSensorsCtr > 0 ) { airTrapIllegalLevelSensorsCtr--; } } } /*********************************************************************//** * @brief * The execAirTrapMonitorTreatment function executes the air trap monitor * for treatment mode. * @details Inputs: airTrapLevels[], airTrapFillAlarmCtr * @details Outputs: airTrapFillAlarmCtr * @return none *************************************************************************/ void execAirTrapMonitorTreatment( void ) { // Check air trap fill timeout during treatment if ( AIR_TRAP_VALVE_OPEN_STATE == airTrapControllerState ) { F32 bldFlowRate = (F32)getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ); // Function will never return zero F32 fillTimeoutMS = ( VENOUS_LINE_VOLUME_ML / bldFlowRate ) * (F32)SEC_PER_MIN * (F32)MS_PER_SECOND; if ( TRUE == didTimeout( fillStartTime, fillTimeoutMS ) ) { #ifndef DISABLE_AIR_TRAP_LEVELING_ALARM activateAlarmNoData( ALARM_ID_AIR_TRAP_FILL_DURING_TREATMENT ); #endif } } } /*********************************************************************//** * @brief * The execAirTrapController function executes the air trap control state machine. * @details Inputs: airTrapControllerState * @details Outputs: airTrapControllerState * @return none *************************************************************************/ void execAirTrapController( void ) { // If we have faulted, close valve and go to manual control if ( MODE_FAUL == getCurrentOperationMode() ) { airTrapControllerState = AIR_TRAP_MANUAL_CONTROL_STATE; setValveAirTrap( STATE_CLOSED ); pendingStartAirTrapController = FALSE; } // Execute air trap state machine switch( airTrapControllerState ) { case AIR_TRAP_INIT_STATE: airTrapControllerState = AIR_TRAP_MANUAL_CONTROL_STATE; break; case AIR_TRAP_MANUAL_CONTROL_STATE: airTrapControllerState = handleAirTrapManualControlState(); break; case AIR_TRAP_VALVE_CLOSED_STATE: airTrapControllerState = handleAirTrapValveClosedState(); break; case AIR_TRAP_VALVE_OPEN_STATE: airTrapControllerState = handleAirTrapValveOpenState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, (U32)SW_FAULT_ID_AIR_TRAP_INVALID_STATE, (U32)airTrapControllerState ) airTrapControllerState = AIR_TRAP_INIT_STATE; break; } // Publish air trap data if due publishAirTrapData(); } /*********************************************************************//** * @brief * The handleAirTrapManualControlState function handles the manual control * state of the air trap. * @details Inputs: pendingStartAirTrapController * @details Outputs: none * @return next state *************************************************************************/ static AIR_TRAP_STATE_T handleAirTrapManualControlState( void ) { AIR_TRAP_STATE_T result = AIR_TRAP_MANUAL_CONTROL_STATE; // Transition to valve control states when requested if ( TRUE == pendingStartAirTrapController ) { pendingStartAirTrapController = FALSE; setValveAirTrap( STATE_CLOSED ); result = AIR_TRAP_VALVE_CLOSED_STATE; } return result; } /*********************************************************************//** * @brief * The handleAirTrapValveClosedState function handles the valve closed state * of the air trap. * @details Inputs: pendingStopAirTrapController, airTrapLevels[] * @details Outputs: none * @return next state *************************************************************************/ static AIR_TRAP_STATE_T handleAirTrapValveClosedState( void ) { AIR_TRAP_STATE_T result = AIR_TRAP_VALVE_CLOSED_STATE; // Transition to manual valve control state when requested if ( TRUE == pendingStopAirTrapController ) { pendingStopAirTrapController = FALSE; result = AIR_TRAP_MANUAL_CONTROL_STATE; } // Transition to open valve state when air detected at lower level else if ( AIR_TRAP_LEVEL_AIR == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ) ) { setValveAirTrap( STATE_OPEN ); fillStartTime = getMSTimerCount(); result = AIR_TRAP_VALVE_OPEN_STATE; } return result; } /*********************************************************************//** * @brief * The handleAirTrapValveOpenState function handles the valve open state of * the air trap. * @details Inputs: pendingStopAirTrapController, airTrapLevels[] * @details Outputs: none * @return next state *************************************************************************/ static AIR_TRAP_STATE_T handleAirTrapValveOpenState( void ) { AIR_TRAP_STATE_T result = AIR_TRAP_VALVE_OPEN_STATE; // Transition to manual valve control state when requested if ( TRUE == pendingStopAirTrapController ) { pendingStopAirTrapController = FALSE; result = AIR_TRAP_MANUAL_CONTROL_STATE; } // Transition to closed valve state when fluid detected at upper level else if ( AIR_TRAP_LEVEL_FLUID == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_UPPER ) ) { setValveAirTrap( STATE_CLOSED ); result = AIR_TRAP_VALVE_CLOSED_STATE; } return result; } /*********************************************************************//** * @brief * The getAirTrapLevel function gets the current reading for the given * level sensor. * @details Inputs: airTrapLevels[] * @details Outputs: none * @param sensor ID of level sensor to get reading for * @return the current level sensor reading for the given sensor (air or fluid). *************************************************************************/ AIR_TRAP_LEVELS_T getAirTrapLevel( AIR_TRAP_LEVEL_SENSORS_T sensor ) { AIR_TRAP_LEVELS_T result; if ( sensor < NUM_OF_AIR_TRAP_LEVEL_SENSORS ) { result = (AIR_TRAP_LEVELS_T)getU32OverrideValue( &airTrapLevels[ sensor ] ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, (U32)SW_FAULT_ID_AIR_TRAP_INVALID_LEVEL_SENSOR, (U32)sensor ) result = AIR_TRAP_LEVEL_AIR; } return result; } /*********************************************************************//** * @brief * The publishAirTrapData function publishes air trap data at the set interval. * @details Inputs: airTrapLevels[] * @details Outputs: if broadcast is due, send air trap data * @return none *************************************************************************/ static void publishAirTrapData( void ) { // Publish air trap data on interval if ( ++airTrapDataPublicationTimerCounter >= getU32OverrideValue( &airTrapDataPublishInterval ) ) { AIR_TRAP_PAYLOAD_T data; data.lowerLevel = getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ); data.upperLevel = getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_UPPER ); broadcastData( MSG_ID_HD_AIR_TRAP_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( AIR_TRAP_PAYLOAD_T ) ); airTrapDataPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief * The execAirTrapTest function executes the state machine for the air trap * self-test. * @details Inputs: none * @details Outputs: none * @return the current state of the PresOccl self-test. *************************************************************************/ SELF_TEST_STATUS_T execAirTrapTest( void ) { SELF_TEST_STATUS_T status = SELF_TEST_STATUS_PASSED; // TODO - Implement POST return status; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetAirTrapDataPublishIntervalOverride function overrides the * air trap data publish interval. * @details Inputs: none * @details Outputs: airTrapDataPublishInterval * @param value override air trap data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetAirTrapDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_GENERAL_INTERVAL; result = TRUE; airTrapDataPublishInterval.ovData = intvl; airTrapDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetAirTrapDataPublishIntervalOverride function resets the override * of the air trap data publish interval. * @details Inputs: none * @details Outputs: airTrapDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetAirTrapDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; airTrapDataPublishInterval.override = OVERRIDE_RESET; airTrapDataPublishInterval.ovData = airTrapDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetAirTrapLevelOverride function overrides the measured level * for a given level sensor. * @details Inputs: none * @details Outputs: airTrapLevels[] * @param sensor ID of level sensor to override * @param level override level sensor with this * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetAirTrapLevelOverride( AIR_TRAP_LEVEL_SENSORS_T sensor, AIR_TRAP_LEVELS_T level ) { BOOL result = FALSE; if ( ( sensor < NUM_OF_AIR_TRAP_LEVEL_SENSORS ) && ( level < NUM_OF_AIR_TRAP_LEVELS ) ) { if ( TRUE == isTestingActivated() ) { result = TRUE; airTrapLevels[ sensor ].ovData = (U32)level; airTrapLevels[ sensor ].override = OVERRIDE_KEY; } } return result; } /*********************************************************************//** * @brief * The testResetAirTrapLevelOverride function resets the override of the * level sensor. * @details Inputs: none * @details Outputs: airTrapLevels[] * @param sensor ID of level sensor to reset override * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetAirTrapLevelOverride( AIR_TRAP_LEVEL_SENSORS_T sensor ) { BOOL result = FALSE; if ( sensor < NUM_OF_AIR_TRAP_LEVEL_SENSORS ) { if ( TRUE == isTestingActivated() ) { result = TRUE; airTrapLevels[ sensor ].override = OVERRIDE_RESET; airTrapLevels[ sensor ].ovData = airTrapLevels[ sensor ].ovInitData; } } return result; } /**@}*/