/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file Pressures.c * * @author (last) Quang Nguyen * @date (last) 03-Aug-2020 * * @author (original) Sean * @date (original) 04-Apr-2020 * ***************************************************************************/ #include #include "Pressures.h" #include "AlarmMgmt.h" #include "FPGA.h" #include "InternalADC.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TaskPriority.h" #include "Timers.h" /** * @addtogroup DGPressures * @{ */ // ********** private definitions ********** #define PUMP_PRESSURE_ZERO 777 ///< ADC counts equivalent to 0 PSI for pump in/out pressure sensors. #define PUMP_PRESSURE_PSIA_PER_COUNT 0.06434 ///< PSIA per ADC count conversion factor for pump in/out pressure sensors. #define PUMP_PRESSURE_PSIA_TO_PSI_OFFSET 14.7 ///< Subtract this offset to convert PSIA to PSI. #define PRESSURE_SAMPLES_TO_AVERAGE (200 / TASK_PRIORITY_INTERVAL) ///< Averaging pressure data over the reporting interval. #define PRESSURE_AVERAGE_MULTIPLIER (1.0 / (F32)PRESSURE_SAMPLES_TO_AVERAGE) ///< Optimization - multiplying is faster than dividing. #define PRESSURE_SENSORS_ADC_BITS 12U ///< Pressure sensors ADC bits #define PRESSURE_SENSORS_ADC_MAX_COUNT ( pow(2, PRESSURE_SENSORS_ADC_BITS) - 1 ) ///< Pressure sensors max ADC count #define MIN_INLET_WATER_PRESSURE 25 ///< Minimum water input pressure #define INLET_WATER_PRESSURE_PERSISTENCE_COUNT ( 5 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Persistence count for pressure out of range error #define PRESSURES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< interval (ms/task time) at which the pressures data is published on the CAN bus. /// Defined states for the pressures monitor state machine. typedef enum PresOccl_States { PRESSURE_INIT_STATE = 0, ///< Initialization state. PRESSURE_CONTINUOUS_READ_STATE, ///< Continuous read sensors state. NUM_OF_PRESSURE_STATES ///< Number of pressure monitor states. } PRESSURE_STATE_T; /// Defined states for the pressures self test state machine. typedef enum Pressures_Self_Test_States { PRESSURE_SELF_TEST_STATE_START = 0, ///< Self test start state. PRESSURE_TEST_STATE_IN_PROGRESS, ///< Self test in progress state. PRESSURE_TEST_STATE_COMPLETE, ///< Self test completed state. NUM_OF_PRESSURE_SELF_TEST_STATES ///< Number of pressure self test states. } PRESSURE_SELF_TEST_STATE_T; // ********** private data ********** static PRESSURE_STATE_T pressuresState; ///< current state of pressure monitor state machine. static U32 pressuresDataPublicationTimerCounter = 0; ///< used to schedule pressure data publication to CAN bus. /// interval (in ms/task interval) at which to publish pressures data to CAN bus. static OVERRIDE_U32_T pressuresDataPublishInterval = { PRESSURES_DATA_PUB_INTERVAL, PRESSURES_DATA_PUB_INTERVAL, 0, 0 }; static OVERRIDE_F32_T pressures[ NUM_OF_PRESSURE_SENSORS ]; ///< Measured pressure from sensors. static S32 measuredPressureReadingsSum[ NUM_OF_PRESSURE_SENSORS ]; ///< Raw pressure sensor sums for averaging. static U32 pressureFilterCounter = 0; ///< Used to schedule pressure sensor filtering. static U32 inletWaterLowPressureCounter = 0; ///< Persistence counter for inlet water low pressure static U32 inletWaterPressureInRangeCounter = 0; ///< Persistence counter for inlet water in range pressure static PRESSURE_SELF_TEST_STATE_T pressuresSelfTestState; ///< Current pressure self test state. static SELF_TEST_STATUS_T pressuresSelfTestResult; ///< Self test result of the Pressures module // ********** private function prototypes ********** static PRESSURE_STATE_T handlePressuresInitState( void ); static PRESSURE_STATE_T handlePressuresContReadState( void ); static void publishPressuresData( void ); static DATA_GET_PROTOTYPE( U32, getPublishPressuresDataInterval ); static SELF_TEST_STATUS_T handleSelfTestADCCheck( void ); /*********************************************************************//** * @brief * The initPressures function initializes the Pressures module. * @details * Inputs : none * Outputs : Pressures module initialized. * @return none *************************************************************************/ void initPressures( void ) { U32 i; for ( i = 0; i < NUM_OF_PRESSURE_SENSORS; i++ ) { pressures[ i ].data = 0.0; pressures[ i ].ovData = 0.0; pressures[ i ].ovInitData = 0.0; pressures[ i ].override = OVERRIDE_RESET; measuredPressureReadingsSum[ i ] = 0; } pressuresState = PRESSURE_INIT_STATE; pressuresSelfTestState = PRESSURE_SELF_TEST_STATE_START; inletWaterLowPressureCounter = 0; inletWaterPressureInRangeCounter = 0; pressuresDataPublicationTimerCounter = 0; } /*********************************************************************//** * @brief * The execPressures function executes the pressure monitor. * @details * Inputs : pressuresState * Outputs : pressuresState * @return none *************************************************************************/ void execPressures( void ) { // state machine switch ( pressuresState ) { case PRESSURE_INIT_STATE: pressuresState = handlePressuresInitState(); break; case PRESSURE_CONTINUOUS_READ_STATE: pressuresState = handlePressuresContReadState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, 0, pressuresState ) // TODO - replace 1st param with s/w fault enum pressuresState = PRESSURE_INIT_STATE; break; } // publish pressure/occlusion data on interval publishPressuresData(); } /*********************************************************************//** * @brief * The handlePressuresInitState function handles the pressures initialize state \n * of the pressures monitor state machine. * @details * Inputs : TBD * Outputs : TBD * @return next state *************************************************************************/ static PRESSURE_STATE_T handlePressuresInitState( void ) { PRESSURE_STATE_T result = PRESSURE_CONTINUOUS_READ_STATE; return result; } /*********************************************************************//** * @brief * The handlePressuresContReadState function handles the continuous read state \n * of the pressures monitor state machine. * @details * Inputs : TBD * Outputs : pressure sensor values updated * @return next state *************************************************************************/ static PRESSURE_STATE_T handlePressuresContReadState( void ) { PRESSURE_STATE_T result = PRESSURE_CONTINUOUS_READ_STATE; S32 measuredPressureReadingsRaw[ NUM_OF_PRESSURE_SENSORS ]; // get latest raw pressure readings measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_INLET ] = (S32)getIntADCReading( INT_ADC_RO_PUMP_INLET_PRESSURE ) - PUMP_PRESSURE_ZERO; measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] = (S32)getIntADCReading( INT_ADC_RO_PUMP_OUTLET_PRESSURE ) - PUMP_PRESSURE_ZERO; measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = (S32)getIntADCReading( INT_ADC_DRAIN_PUMP_INLET_PRESSURE ) - PUMP_PRESSURE_ZERO; measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = (S32)getIntADCReading( INT_ADC_DRAIN_PUMP_OUTLET_PRESSURE ) - PUMP_PRESSURE_ZERO; // update sums for pressure average calculations measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_INLET ]; measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_RO_PUMP_OUTLET ]; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ]; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] += measuredPressureReadingsRaw[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ]; // filter every 200ms if ( ++pressureFilterCounter >= PRESSURE_SAMPLES_TO_AVERAGE ) { // calculate average pressures F32 avgRoIn = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] * PRESSURE_AVERAGE_MULTIPLIER; F32 avgRoOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; F32 avgDrnIn = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] * PRESSURE_AVERAGE_MULTIPLIER; F32 avgDrnOut = (F32)measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] * PRESSURE_AVERAGE_MULTIPLIER; // reset average counter pressureFilterCounter = 0; // convert average pressure readings to PSI pressures[ PRESSURE_SENSOR_RO_PUMP_INLET ].data = avgRoIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; pressures[ PRESSURE_SENSOR_RO_PUMP_OUTLET ].data = avgRoOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; pressures[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ].data = avgDrnIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; pressures[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ].data = avgDrnOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; // reset sums for next averaging measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_INLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = 0; measuredPressureReadingsSum[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = 0; } // TODO - any other checks return result; } /*********************************************************************//** * @brief * The getPublishPresOcclDataInterval function gets the pressure/occlusion data \n * publication interval. * @details * Inputs : pressuresDataPublishInterval * Outputs : none * @return the current pressures data publication interval (in task intervals). *************************************************************************/ static U32 getPublishPressuresDataInterval( void ) { U32 result = pressuresDataPublishInterval.data; if ( OVERRIDE_KEY == pressuresDataPublishInterval.override ) { result = pressuresDataPublishInterval.ovData; } return result; } /*********************************************************************//** * @brief * The checkInletPressures function checks inlet water pressure value * and triggers an alarm when pressure value is out of allowed range. * @details * Inputs : pressures[] * Outputs : none * @return none *************************************************************************/ void checkInletPressure( void ) { F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); if ( pressure < MIN_INLET_WATER_PRESSURE ) { ++inletWaterLowPressureCounter; inletWaterPressureInRangeCounter = 0; if ( inletWaterLowPressureCounter > INLET_WATER_PRESSURE_PERSISTENCE_COUNT ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_INLET_WATER_LOW_PRESSURE, pressure ); } } else { ++inletWaterPressureInRangeCounter; inletWaterLowPressureCounter = 0; if ( inletWaterPressureInRangeCounter > INLET_WATER_PRESSURE_PERSISTENCE_COUNT ) { clearAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE ); } } } /*********************************************************************//** * @brief * The getMeasuredArterialPressure function gets the current arterial pressure. * @details * Inputs : arterialPressure * Outputs : none * @return the current arterial pressure (in mmHg). *************************************************************************/ F32 getMeasuredDGPressure( U32 pressureID ) { F32 result = 0.0; if ( pressureID < NUM_OF_PRESSURE_SENSORS ) { if ( OVERRIDE_KEY == pressures[ pressureID ].override ) { result = pressures[ pressureID ].ovData; } else { result = pressures[ pressureID ].data; } } else { activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); } return result; } /*********************************************************************//** * @brief * The publishPressuresData function publishes DG pressures data at the \n * set interval. * @details * Inputs : pressuresDataPublicationTimerCounter * Outputs : DG Pressures data are published to CAN bus. * @return none *************************************************************************/ static void publishPressuresData( void ) { // publish pressure/occlusion data on interval if ( ++pressuresDataPublicationTimerCounter >= getPublishPressuresDataInterval() ) { F32 roIn = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); F32 roOut = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); F32 drainIn = getMeasuredDGPressure( PRESSURE_SENSOR_DRAIN_PUMP_INLET ); F32 drainOut = getMeasuredDGPressure( PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ); broadcastPressureSensorsData( roIn, roOut, drainIn, drainOut ); pressuresDataPublicationTimerCounter = 0; #ifdef DEBUG_ENABLED #ifdef PRESSURES_DEBUG { // TODO - temporary debug code - remove later F32 lc1 = getLoadCellFilteredWeight(LOAD_CELL_A1); F32 lc2 = getLoadCellFilteredWeight(LOAD_CELL_B1); char debugPresStr[ 256 ]; sprintf( debugPresStr, "RO: %5d, %5d, Drain: %5d, %5d, Loads: %6d, %6d\n", (S32)roIn, (S32)roOut, (S32)drainIn, (S32)drainOut, (S32)lc1, (S32)lc2 ); sendDebugData( (U08*)debugPresStr, strlen(debugPresStr) ); } #endif #endif } } /*********************************************************************//** * @brief * The execPressureSelfTest function executes the state machine for the \n * Pressures self test. * @details * Inputs : pressuresSelfTestState * Outputs : pressuresSelfTestState * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) *************************************************************************/ SELF_TEST_STATUS_T execPressureSelfTest( void ) { switch ( pressuresSelfTestState ) { case PRESSURE_SELF_TEST_STATE_START: pressuresSelfTestState = PRESSURE_TEST_STATE_IN_PROGRESS; pressuresSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; break; case PRESSURE_TEST_STATE_IN_PROGRESS: pressuresSelfTestResult = handleSelfTestADCCheck(); pressuresSelfTestState = PRESSURE_TEST_STATE_COMPLETE; break; case PRESSURE_TEST_STATE_COMPLETE: // Done with self test break; default: SET_ALARM_WITH_2_U32_DATA ( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_INVALID_SELF_TEST_STATE, pressuresSelfTestState ); break; } return pressuresSelfTestResult; } /*********************************************************************//** * @brief * The handleSelfTestADCCheck function checks whether the ADC reads and * report status back. If the reads are above the maximum 12bit ADC count * or below 0, it will throw an alarm. * @details * Inputs : none * Outputs : none * @param none * @return result (SELF_TEST_STATUS_T) *************************************************************************/ static SELF_TEST_STATUS_T handleSelfTestADCCheck( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; U16 const inletPressureADCReading = getIntADCReading( INT_ADC_RO_PUMP_INLET_PRESSURE ); if ( ( inletPressureADCReading == 0 ) || ( inletPressureADCReading >= PRESSURE_SENSORS_ADC_MAX_COUNT ) ) { result = SELF_TEST_STATUS_FAILED; SET_ALARM_WITH_1_U32_DATA ( ALARM_ID_PRESSURE_SENSOR_FAULT, inletPressureADCReading ); } return result; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetPressuresDataPublishIntervalOverride function overrides the \n * pressure and occlusion data publish interval. * @details * Inputs : none * Outputs : pressuresDataPublishInterval * @param value override pressure and occlusion data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetPressuresDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; result = TRUE; pressuresDataPublishInterval.ovData = intvl; pressuresDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetPressuresDataPublishIntervalOverride function resets the override \n * of the pressure and occlusion data publish interval. * @details * Inputs : none * Outputs : pressuresDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetPressuresDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; pressuresDataPublishInterval.override = OVERRIDE_RESET; pressuresDataPublishInterval.ovData = pressuresDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetDGPressureSensorOverride function overrides the value of the \n * specified pressure sensor with a given value. * Inputs : none * Outputs : pressures[] * @param sensor ID of pressure sensor to override for * @param value override value for the given pressure sensor ID * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetDGPressureSensorOverride( U32 sensor, F32 value ) { BOOL result = FALSE; if ( sensor < NUM_OF_PRESSURE_SENSORS ) { if ( TRUE == isTestingActivated() ) { result = TRUE; pressures[ sensor ].ovData = value; pressures[ sensor ].override = OVERRIDE_KEY; } } return result; } /*********************************************************************//** * @brief * The testResetDGPressureSensorOverride function resets the override of the \n * specified DG pressure sensor. * @details * Inputs : none * Outputs : pressures[] * @param value ID of sensor to reset override pressure for * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetDGPressureSensorOverride( U32 sensor ) { BOOL result = FALSE; if ( sensor < NUM_OF_PRESSURE_SENSORS ) { if ( TRUE == isTestingActivated() ) { result = TRUE; pressures[ sensor ].override = OVERRIDE_RESET; pressures[ sensor ].ovData = pressures[ sensor ].ovInitData; } } return result; } /**@}*/