Index: firmware/App/Controllers/PresOccl.c =================================================================== diff -u -rf7b149d8b8c9ea6ac58e4739101693d251d7a355 -rf5f00981805e265ce63058f650d784f06db4d188 --- firmware/App/Controllers/PresOccl.c (.../PresOccl.c) (revision f7b149d8b8c9ea6ac58e4739101693d251d7a355) +++ firmware/App/Controllers/PresOccl.c (.../PresOccl.c) (revision f5f00981805e265ce63058f650d784f06db4d188) @@ -22,7 +22,8 @@ #include "OperationModes.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" -#include "TaskGeneral.h" +#include "TaskGeneral.h" +#include "Temperatures.h" #include "Timers.h" /** @@ -50,18 +51,21 @@ #define VENOUS_PRESSURE_SELF_TEST_MIN ( -100.0 ) ///< Minimum self-test value for venous pressure sensor reading (in mmHg). #define VENOUS_PRESSURE_SELF_TEST_MAX ( 600.0 ) ///< Maximum self-test value for venous pressure sensor reading (in mmHg). +#define VENOUS_PRESSURE_MIN_TEMP ( 0.0 ) ///< Minimum venous pressure sensor temperature. TODO - get from Systems +#define VENOUS_PRESSURE_MAX_TEMP ( 50.0 ) ///< Maximum venous pressure sensor temperature. TODO - get from Systems + #define PSI_TO_MMHG ( 51.7149 ) ///< Conversion factor for converting PSI to mmHg. #define VENOUS_PRESSURE_NORMAL_OP 0 ///< Venous pressure status bits indicate normal operation. #define VENOUS_PRESSURE_CMD_MODE 1 ///< Venous pressure status bits indicate sensor in command mode. #define VENOUS_PRESSURE_STALE_DATA 2 ///< Venous pressure status bits indicate data is stale (no new data since last fpga read). #define VENOUS_PRESSURE_DIAG_CONDITION 3 ///< Venous pressure status bits diagnostic condition (alarm). -#define MAX_TIME_BETWEEN_VENOUS_READINGS ( 500 / TASK_GENERAL_INTERVAL ) ///< Maximum time without fresh inline venous pressure reading. - #define OCCLUSION_THRESHOLD 25000 ///< Threshold above which an occlusion is detected. #define CARTRIDGE_LOADED_THRESHOLD 5000 ///< Threshold above which a cartridge is considered loaded. +#define EMPTY_SALINE_BAG_THRESHOLD_MMHG -300.0 ///< Threshold below which the saline bag is considered empty (in mmHg). TODO - get real threshold from Systems +static const U32 EMPTY_SALINE_BAG_PERSISTENCE = ( 250 / TASK_GENERAL_INTERVAL ); ///< Time that saline bag looks empty before saying it is empty. /// Occlusion sensors minimum pressure reading limit when no cartridge is loaded. #define OCCLUSION_NO_CARTRIDGE_PRESSURE_READING_MIN 2000 /// Occlusion sensors maximum pressure reading limit when cartridge is considered loaded. @@ -99,11 +103,18 @@ static OVERRIDE_U32_T dialInPumpOcclusion = {0, 0, 0, 0 }; ///< Measured dialysate inlet pump occlusion pressure. static OVERRIDE_U32_T dialOutPumpOcclusion = {0, 0, 0, 0 }; ///< Measured dialysate outlet pump occlusion pressure. -static U32 staleVenousPressureCtr = 0; ///< Timer counter for stale venous pressure reading. +static U32 emptySalineBagCtr = 0; ///< Timer counter for empty bag detection. + +static U08 lastVenousPressureReadCtr; ///< Previous venous pressure read count. +static U08 lastBPOcclReadCtr; ///< Previous BP occlusion read count. +static U08 lastDPiOcclReadCtr; ///< Previous DPi occlusion read count. +static U08 lastDPoOcclReadCtr; ///< Previous DPo occlusion read count. +static U08 lastBPErrorCtr; ///< Previous BP error count. +static U08 lastDPIErrorCtr; ///< Previous DPi error count. +static U08 lastDPOErrorCtr; ///< Previous DPo error count. // ********** private function prototypes ********** -static PRESSURE_STATE_T handlePresOcclInitState( void ); static PRESSURE_STATE_T handlePresOcclContReadState( void ); static void convertInlinePressures( void ); static void convertOcclusionPressures( void ); @@ -115,9 +126,9 @@ /*********************************************************************//** * @brief - * The initPresOccl function initializes the initPresOccl module. + * The initPresOccl function initializes the PresOccl module. * @details Inputs: none - * @details Outputs: initPresOccl module initialized. + * @details Outputs: PresOccl module initialized. * @return none *************************************************************************/ void initPresOccl( void ) @@ -127,6 +138,22 @@ initPersistentAlarm( ALARM_ID_ARTERIAL_PRESSURE_HIGH, PRES_ALARM_PERSISTENCE, PRES_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_VENOUS_PRESSURE_LOW, PRES_ALARM_PERSISTENCE, PRES_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_VENOUS_PRESSURE_HIGH, PRES_ALARM_PERSISTENCE, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_VENOUS_PRESSURE_READ_TIMEOUT_ERROR, 0, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_VENOUS_PRESSURE_SENSOR_TEMP_OUT_OF_RANGE, 0, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_BP_OCCLUSION_READ_TIMEOUT_ERROR, 0, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_DPI_OCCLUSON_READ_TIMEOUT_ERROR, 0, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_DPO_OCCLUSION_READ_TIMEOUT_ERROR, 0, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_BP_OCCLUSION_SENSOR_ERROR, 0, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_DPI_OCCLUSION_SENSOR_ERROR, 0, PRES_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_HD_DPO_OCCLUSION_SENSOR_ERROR, 0, PRES_ALARM_PERSISTENCE ); + + lastVenousPressureReadCtr = 0; + lastBPOcclReadCtr = 0; + lastDPiOcclReadCtr = 0; + lastDPoOcclReadCtr = 0; + lastBPErrorCtr = 0; + lastDPIErrorCtr = 0; + lastDPOErrorCtr = 0; } /*********************************************************************//** @@ -144,8 +171,7 @@ U32 diOccl = getMeasuredDialInPumpOcclusion(); U32 doOccl = getMeasuredDialOutPumpOcclusion(); - if ( ( bpOccl >= CARTRIDGE_LOADED_THRESHOLD ) && - ( diOccl >= CARTRIDGE_LOADED_THRESHOLD ) && + if ( ( bpOccl >= CARTRIDGE_LOADED_THRESHOLD ) && ( diOccl >= CARTRIDGE_LOADED_THRESHOLD ) && ( doOccl >= CARTRIDGE_LOADED_THRESHOLD ) ) { result = TRUE; @@ -154,6 +180,52 @@ return result; } +/*********************************************************************//** + * @brief + * The isCartridgeUnloaded function determines if a cartridge has been + * unloaded by looking at the 3 occlusion pressure sensors. + * @details Inputs: occlusion pressures for the pumps + * @details Outputs: none + * @return TRUE if all 3 occlusion sensors read below loaded threshold, FALSE if not. + *************************************************************************/ +BOOL isCartridgeUnloaded( void ) +{ + BOOL const bpOcclBelowLoadedThreshold = getMeasuredBloodPumpOcclusion() <= CARTRIDGE_LOADED_THRESHOLD; + BOOL const diOcclBelowLoadedThreshold = getMeasuredDialInPumpOcclusion() <= CARTRIDGE_LOADED_THRESHOLD; + BOOL const doOcclBelowLoadedThreshold = getMeasuredDialOutPumpOcclusion() <= CARTRIDGE_LOADED_THRESHOLD; + + return ( bpOcclBelowLoadedThreshold && diOcclBelowLoadedThreshold && doOcclBelowLoadedThreshold ); +} + +/*********************************************************************//** + * @brief + * The isSalineBagEmpty function determines whether the saline bag is empty. + * It is assumed that this function will only be called from mode handling + * (General Task) when pumping (BP) from the saline bag. + * Determination is based on pressure going below a negative threshold. + * @details Inputs: arterial line pressure + * @details Outputs: none + * @return TRUE if arterial line pressure is below threshold, FALSE if not. + *************************************************************************/ +BOOL isSalineBagEmpty( void ) +{ + BOOL result = FALSE; + + if ( getMeasuredArterialPressure() < EMPTY_SALINE_BAG_THRESHOLD_MMHG ) + { + if ( ++emptySalineBagCtr >= EMPTY_SALINE_BAG_PERSISTENCE ) + { + result = TRUE; + } + } + else + { + emptySalineBagCtr = 0; + } + + return result; +} + /*********************************************************************//** * @brief * The execPresOccl function executes the pressure and occlusion monitor. @@ -167,7 +239,7 @@ switch ( presOcclState ) { case PRESSURE_INIT_STATE: - presOcclState = handlePresOcclInitState(); + presOcclState = PRESSURE_CONTINUOUS_READ_STATE; break; case PRESSURE_CONTINUOUS_READ_STATE: @@ -185,21 +257,6 @@ /*********************************************************************//** * @brief - * The handlePresOcclInitState function handles the pres/occl initialize state - * of the pressure/occlusion monitor state machine. - * @details Inputs: TBD - * @details Outputs: TBD - * @return next state - *************************************************************************/ -static PRESSURE_STATE_T handlePresOcclInitState( void ) -{ - PRESSURE_STATE_T result = PRESSURE_CONTINUOUS_READ_STATE; - - return result; -} - -/*********************************************************************//** - * @brief * The handlePresOcclContReadState function handles the continuous read state * of the pressure/occlusion monitor state machine. * @details Inputs: FPGA pressure/occlusion readings @@ -238,15 +295,17 @@ { U32 fpgaArtPres = getFPGAArterialPressure(); S32 artPres = (S32)( fpgaArtPres & MASK_OFF_U32_MSB ) - 0x800000; // Subtract 2^23 from low 24 bits to get signed reading - U08 artPresAlarm = (U08)( fpgaArtPres >> 24 ); // High byte is alarm code for arterial pressure + U08 artPresAlarm = (U08)( fpgaArtPres >> SHIFT_24_BITS ); // High byte is alarm code for arterial pressure U16 fpgaVenPres = getFPGAVenousPressure(); U16 venPres = fpgaVenPres & 0x3FFF; // 14-bit data U08 venPresStatus = (U08)( fpgaVenPres >> 14 ); // High 2 bits is status code for venous pressure F32 venPresPSI; + F32 venTemp = getTemperatureValue( TEMPSENSOR_VENOUS_PRESSURE_SENSOR ); + U08 venReadCtr = getFPGAVenousPressureReadCounter(); // TODO - any filtering required??? - // Convert arterial pressure to mmHg if no alarm + // Convert arterial pressure to mmHg if no fault if ( 0 == artPresAlarm ) { arterialPressure.data = ARTERIAL_PRESSURE_V_PER_BIT * ( (F32)(artPres) / ( ARTERIAL_PRESSURE_SENSITIVITY * ARTERIAL_PRESSURE_V_BIAS ) ); @@ -258,31 +317,33 @@ #endif } - // Convert venous pressure to PSI + // Check for stale venous pressure reading + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_VENOUS_PRESSURE_READ_TIMEOUT_ERROR, ( lastVenousPressureReadCtr == venReadCtr ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_VENOUS_PRESSURE_READ_TIMEOUT_ERROR ); + } + // Record venous read counter for next time around + lastVenousPressureReadCtr = venReadCtr; + // Convert venous pressure to PSI and then mmHg venPresPSI = ( (F32)(venPres - VENOUS_PRESSURE_OFFSET) * (VENOUS_PRESSURE_MAX - VENOUS_PRESSURE_MIN) / (F32)VENOUS_PRESSURE_SCALE ) + VENOUS_PRESSURE_MIN; // Convert venous pressure from PSI to mmHg if sensor status is normal if ( VENOUS_PRESSURE_NORMAL_OP == venPresStatus ) { venousPressure.data = venPresPSI * PSI_TO_MMHG; - staleVenousPressureCtr = 0; } - // If venous pressure sensor status is not normal or reading is stale for too long, fault + // If venous pressure sensor status is not normal, fault else { - if ( ++staleVenousPressureCtr > MAX_TIME_BETWEEN_VENOUS_READINGS ) - { #ifndef DISABLE_PRESSURE_CHECKS - SET_ALARM_WITH_1_U32_DATA( ALARM_ID_VENOUS_PRESSURE_SENSOR_FAULT, VENOUS_PRESSURE_STALE_DATA ) +// SET_ALARM_WITH_1_U32_DATA( ALARM_ID_VENOUS_PRESSURE_SENSOR_FAULT, (U32)venPresStatus ) // TODO - persistence? YES, need persistence - getting a stale data status. OR maybe speed up ADC in FPGA. #endif - } } - // Check for venous pressure sensor alarm or in wrong (cmd) mode - if ( ( VENOUS_PRESSURE_DIAG_CONDITION == venPresStatus ) || ( VENOUS_PRESSURE_CMD_MODE == venPresStatus ) ) + // Check venous pressure sensor temperature + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_VENOUS_PRESSURE_SENSOR_TEMP_OUT_OF_RANGE, + ( venTemp > VENOUS_PRESSURE_MAX_TEMP || venTemp < VENOUS_PRESSURE_MIN_TEMP ) ) ) { -#ifndef DISABLE_PRESSURE_CHECKS - SET_ALARM_WITH_1_U32_DATA( ALARM_ID_VENOUS_PRESSURE_SENSOR_FAULT, (U32)venPresStatus ) -#endif + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_VENOUS_PRESSURE_SENSOR_FAULT, venTemp ) } } @@ -295,12 +356,55 @@ *************************************************************************/ static void convertOcclusionPressures( void ) { - // TODO - any filtering required??? + U08 bpReadCtr = getFPGABloodPumpOcclusionReadCounter(); + U08 dpiReadCtr = getFPGADialInPumpOcclusionReadCounter(); + U08 dpoReadCtr = getFPGADialOutPumpOcclusionReadCounter(); + U08 bpErrorCtr = getFPGABloodPumpOcclusionErrorCounter(); + U08 dpiErrorCtr = getFPGADialInPumpOcclusionErrorCounter(); + U08 dpoErrorCtr = getFPGADialOutPumpOcclusionErrorCounter(); - // Occlusion sensor values have no unit - take as is +#ifndef DISABLE_PRESSURE_CHECKS + // Check for sensor errors + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_BP_OCCLUSION_SENSOR_ERROR, ( bpErrorCtr != lastBPErrorCtr ) ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_BP_OCCLUSION_SENSOR_ERROR, (U32)bpErrorCtr ) + } + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_DPI_OCCLUSION_SENSOR_ERROR, ( dpiErrorCtr != lastDPIErrorCtr ) ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DPI_OCCLUSION_SENSOR_ERROR, (U32)dpiErrorCtr ) + } + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_DPO_OCCLUSION_SENSOR_ERROR, ( dpoErrorCtr != lastDPOErrorCtr ) ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DPO_OCCLUSION_SENSOR_ERROR, (U32)dpoErrorCtr ) + } + + // Check for stale occlusion reads + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_BP_OCCLUSION_READ_TIMEOUT_ERROR, ( bpReadCtr == lastBPOcclReadCtr ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_BP_OCCLUSION_READ_TIMEOUT_ERROR ); + } + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_BP_OCCLUSION_READ_TIMEOUT_ERROR, ( dpiReadCtr == lastDPiOcclReadCtr ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_DPI_OCCLUSON_READ_TIMEOUT_ERROR ); + } + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_BP_OCCLUSION_READ_TIMEOUT_ERROR, ( dpoReadCtr == lastDPoOcclReadCtr ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_DPO_OCCLUSION_READ_TIMEOUT_ERROR ); + } +#endif + + // Record occlusion sensor readings bloodPumpOcclusion.data = (U32)getFPGABloodPumpOcclusion(); dialInPumpOcclusion.data = (U32)getFPGADialInPumpOcclusion(); dialOutPumpOcclusion.data = (U32)getFPGADialOutPumpOcclusion(); + + // Record occlusion read and error counters for next time around + lastBPOcclReadCtr = bpReadCtr; + lastDPiOcclReadCtr = dpiReadCtr; + lastDPoOcclReadCtr = dpoReadCtr; + lastBPErrorCtr = bpErrorCtr; + lastDPIErrorCtr = dpiErrorCtr; + lastDPOErrorCtr = dpoErrorCtr; } /*********************************************************************//** @@ -401,7 +505,6 @@ U32 diOccl = getMeasuredDialInPumpOcclusion(); U32 doOccl = getMeasuredDialOutPumpOcclusion(); - // TODO - add persistence #ifndef DISABLE_PRESSURE_CHECKS if ( bpOccl > OCCLUSION_THRESHOLD ) { @@ -429,7 +532,7 @@ * @details Outputs: none * @return the current pressure/occlusion data publication interval (in task intervals). *************************************************************************/ -U32 getPublishPresOcclDataInterval( void ) +static U32 getPublishPresOcclDataInterval( void ) { U32 result = presOcclDataPublishInterval.data; @@ -569,13 +672,12 @@ * @brief * The execPresOcclTest function executes the PresOccl self-test. * @details Inputs: none - * @details Outputs: none - * @return the result of the PresOccl self-test. + * @details Outputs: Triggers fault when test case fails + * @return none *************************************************************************/ -SELF_TEST_STATUS_T execPresOcclTest( void ) -{ - SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; - +void execPresOcclTest( void ) +{ +#ifndef DISABLE_PRESSURE_CHECKS U32 const bpPressure = getMeasuredBloodPumpOcclusion(); U32 const dialysateInPressure = getMeasuredDialInPumpOcclusion(); U32 const dialysateOutPressure = getMeasuredDialOutPumpOcclusion(); @@ -584,30 +686,29 @@ if ( ( bpPressure <= OCCLUSION_NO_CARTRIDGE_PRESSURE_READING_MIN ) || ( bpPressure >= OCCLUSION_THRESHOLD ) ) { - result = SELF_TEST_STATUS_FAILED; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_BP_OCCLUSION_SELF_TEST_FAILURE, bpPressure ); } if ( ( dialysateInPressure <= OCCLUSION_NO_CARTRIDGE_PRESSURE_READING_MIN ) || ( dialysateInPressure >= OCCLUSION_THRESHOLD ) ) { - result = SELF_TEST_STATUS_FAILED; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DIP_OCCLUSION_SELF_TEST_FAILURE, dialysateInPressure ); } if ( ( dialysateOutPressure <= OCCLUSION_NO_CARTRIDGE_PRESSURE_READING_MIN ) || ( dialysateOutPressure >= OCCLUSION_THRESHOLD ) ) { - result = SELF_TEST_STATUS_FAILED; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DOP_OCCLUSION_SELF_TEST_FAILURE, dialysateOutPressure ); } if ( ( arterialPressure <= ARTERIAL_PRESSURE_SELF_TEST_MIN ) || ( arterialPressure >= ARTERIAL_PRESSURE_SELF_TEST_MAX ) ) { - result = SELF_TEST_STATUS_FAILED; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_ARTERIAL_PRESSURE_SELF_TEST_FAILURE, arterialPressure ); } if ( ( venousPressure <= VENOUS_PRESSURE_SELF_TEST_MIN ) || ( venousPressure >= VENOUS_PRESSURE_SELF_TEST_MAX ) ) { - result = SELF_TEST_STATUS_FAILED; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_VENOUS_PRESSURE_SELF_TEST_FAILURE, venousPressure ); } - - return result; +#endif } /*********************************************************************//** @@ -621,6 +722,7 @@ { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; +#ifndef DISABLE_OCCLUSION_SELF_TEST U32 const bpPressure = getMeasuredBloodPumpOcclusion(); U32 const dialysateInPressure = getMeasuredDialInPumpOcclusion(); U32 const dialysateOutPressure = getMeasuredDialOutPumpOcclusion(); @@ -652,6 +754,7 @@ activateAlarmNoData( ALARM_ID_CARTRIDGE_INSTALLED_IMPROPERLY ); } } +#endif return result; }