/************************************************************************** * * 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) 14-Sep-2020 * * @author (original) Sean * @date (original) 04-Apr-2020 * ***************************************************************************/ #include #include "AlarmMgmt.h" #include "FPGA.h" #include "InternalADC.h" #include "MessageSupport.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "Pressures.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. TODO - use barometric sensor when available. #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 MIN_VALID_PRESSURE_RANGE 0.0 ///< Minimum valid range on pressure reading. #define MAX_VALID_PRESSURE_RANGE 200.0 ///< Maximum valid range on pressure reading. #define MIN_INLET_WATER_PRESSURE 25 ///< Minimum water input pressure. #define INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ( 5 * MS_PER_SECOND ) ///< Persistence period 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. static OVERRIDE_U32_T pressuresDataPublishInterval = { PRESSURES_DATA_PUB_INTERVAL, PRESSURES_DATA_PUB_INTERVAL, 0, 0 }; /// Pressure data publish interval. 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 PRESSURE_SELF_TEST_STATE_T pressuresSelfTestState; ///< Current pressure self-test state. static SELF_TEST_STATUS_T pressuresSelfTestResult; ///< Self-test result of the Pressures module. static DG_PRES_SENSORS_CAL_RECORD_T pressuresCalRecord; ///< Pressures calibration record. // ********** private function prototypes ********** static PRESSURE_STATE_T handlePressuresInitState( void ); static PRESSURE_STATE_T handlePressuresContReadState( void ); static void publishPressuresData( void ); static BOOL processCalibrationData( void ); static SELF_TEST_STATUS_T handleSelfTestADCCheck( void ); /*********************************************************************//** * @brief * The initPressures function initializes the Pressures module. * @details Inputs: none * @details 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; pressuresDataPublicationTimerCounter = 0; initPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); } /*********************************************************************//** * @brief * The checkInletPressure function checks inlet water pressure value * and triggers an alarm when pressure value is out of allowed range. * @details Inputs: RO pump inlet pressure sensor value * @details Outputs: Triggers low pressure persistent alarm * @return none *************************************************************************/ void checkInletPressure( void ) { #ifndef DISABLE_WATER_QUALITY_CHECK F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); #endif } /*********************************************************************//** * @brief * The checkInletPressureFault function checks inlet water pressure value * and triggers a machine fault when pressure value is out of allowed range. * @details Inputs: RO pump inlet pressure sensor value * @details Outputs: Triggers pressure fault persistent alarm * @return none *************************************************************************/ void checkInletPressureFault( void ) { F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); } /*********************************************************************//** * @brief * The getMeasuredArterialPressure function gets the current arterial pressure. * @details Inputs: arterialPressure * @details Outputs: none * @param pressureID pressure sensor ID * @return the current arterial pressure (in mmHg). *************************************************************************/ F32 getMeasuredDGPressure( U32 pressureID ) { F32 result = 0.0; if ( pressureID < NUM_OF_PRESSURE_SENSORS ) { result = getF32OverrideValue( &pressures[ pressureID ] ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_PRESSURE_SENSOR_ID, pressureID ); } return result; } /*********************************************************************//** * @brief * The execPressures function executes the pressures' monitor state machine * and publish pressures' data. * @details Inputs: pressuresState * @details 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, SW_FAULT_ID_PRESSURE_INVALID_EXEC_STATE, pressuresState ) pressuresState = PRESSURE_INIT_STATE; break; } // publish pressure/occlusion data on interval publishPressuresData(); } /*********************************************************************//** * @brief * The execPressureSelfTest function executes the pressures self-test's * state machine. * @details Inputs: pressuresSelfTestState * @details Outputs: pressuresSelfTestState * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) *************************************************************************/ SELF_TEST_STATUS_T execPressureSelfTest( void ) { switch ( pressuresSelfTestState ) { case PRESSURE_SELF_TEST_STATE_START: processCalibrationData(); 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_DG_PRESSURES_INVALID_SELF_TEST_STATE, pressuresSelfTestState ); break; } return pressuresSelfTestResult; } /*********************************************************************//** * @brief * The handlePressuresInitState function handles the pressures initialize state * of the pressures monitor state machine. * @details Inputs: none * @details Outputs: none * @return next state *************************************************************************/ static PRESSURE_STATE_T handlePressuresInitState( void ) { return PRESSURE_CONTINUOUS_READ_STATE; } /*********************************************************************//** * @brief * The handlePressuresContReadState function handles the continuous read state * of the pressures monitor state machine. * @details Inputs: pressureFilterCounter * @details 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 ]; U32 sensorID; F32 pressureReading; // 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 ]; // Check if a new calibration is available if ( TRUE == isNewCalibrationRecordAvailable() ) { // Get the new calibration data and check its validity processCalibrationData(); } // filter every 200ms if ( ++pressureFilterCounter >= PRESSURE_SAMPLES_TO_AVERAGE ) { U32 sensor; F32 pressuresBeforeCal[ NUM_OF_PRESSURE_SENSORS ]; // 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; // Convert average pressure readings to PSI pressuresBeforeCal[ PRESSURE_SENSOR_RO_PUMP_INLET ] = avgRoIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; pressuresBeforeCal[ PRESSURE_SENSOR_RO_PUMP_OUTLET ] = avgRoOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; pressuresBeforeCal[ PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = avgDrnIn * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; pressuresBeforeCal[ PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = avgDrnOut * PUMP_PRESSURE_PSIA_PER_COUNT - PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; // Reset average counter pressureFilterCounter = 0; // Apply calibration to the pressure values prior to storing them for ( sensor = 0; sensor < NUM_OF_PRESSURE_SENSORS; sensor++ ) { pressures[ sensor ].data = pow(pressuresBeforeCal[ sensor ], 4) * pressuresCalRecord.pressureSensors[ sensor ].fourthOrderCoeff + pow(pressuresBeforeCal[ sensor ], 3) * pressuresCalRecord.pressureSensors[ sensor ].thirdOrderCoeff + pow(pressuresBeforeCal[ sensor ], 2) * pressuresCalRecord.pressureSensors[ sensor ].secondOrderCoeff + pressuresBeforeCal[ sensor ] * pressuresCalRecord.pressureSensors[ sensor ].gain + pressuresCalRecord.pressureSensors[ sensor ].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; } for ( sensorID = 0; sensorID < NUM_OF_PRESSURE_SENSORS; sensorID++ ) { pressureReading = getMeasuredDGPressure( sensorID ) + PUMP_PRESSURE_PSIA_TO_PSI_OFFSET; if ( ( pressureReading < MIN_VALID_PRESSURE_RANGE ) || ( pressureReading > MAX_VALID_PRESSURE_RANGE ) ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_PRESSURE_SENSOR_FAULT, sensorID, pressureReading ); } } return result; } /*********************************************************************//** * @brief * The processCalibrationData function gets the calibration data and makes * sure it is valid by checking the calibration date. The calibration date * should not be 0. * @details Inputs: none * @details Outputs: pressuresCalRecord * @return TRUE if the calibration record is valid, otherwise FALSE *************************************************************************/ static BOOL processCalibrationData( void ) { BOOL status = TRUE; U32 sensor; // Get the calibration record from NVDataMgmt DG_PRES_SENSORS_CAL_RECORD_T calData = getDGPressureSensorsCalibrationRecord(); for ( sensor = 0; sensor < NUM_OF_CAL_DATA_PRES_SENSORS; sensor++ ) { #ifndef SKIP_CAL_CHECK // Check if the calibration data that was received from NVDataMgmt is legitimate // The calibration date item should not be zero. If the calibration date is 0, // then the data is not stored in the NV memory or it was corrupted. if ( 0 == calData.pressureSensors[ sensor ].calibrationTime ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_PRESSURE_SENSORS_INVALID_CAL_RECORD, (U32)sensor ); status = FALSE; } #endif // The calibration data was valid, update the local copy pressuresCalRecord.pressureSensors[ sensor ].fourthOrderCoeff = calData.pressureSensors[ sensor ].fourthOrderCoeff; pressuresCalRecord.pressureSensors[ sensor ].thirdOrderCoeff = calData.pressureSensors[ sensor ].thirdOrderCoeff; pressuresCalRecord.pressureSensors[ sensor ].secondOrderCoeff = calData.pressureSensors[ sensor ].secondOrderCoeff; pressuresCalRecord.pressureSensors[ sensor ].gain = calData.pressureSensors[ sensor ].gain; pressuresCalRecord.pressureSensors[ sensor ].offset = calData.pressureSensors[ sensor ].offset; } return status; } /*********************************************************************//** * @brief * The publishPressuresData function publishes DG pressures data at a set interval. * @details Inputs: pressuresDataPublicationTimerCounter * @details Outputs: pressuresDataPublicationTimerCounter * @return none *************************************************************************/ static void publishPressuresData( void ) { // publish pressure/occlusion data on interval if ( ++pressuresDataPublicationTimerCounter >= getU32OverrideValue( &pressuresDataPublishInterval ) ) { PRESSURES_DATA_T data; data.roPumpInletPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); data.roPumpOutletPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); data.drainPumpInletPressure = getMeasuredDGPressure( PRESSURE_SENSOR_DRAIN_PUMP_INLET ); data.drainPumpOutletPressure = getMeasuredDGPressure( PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ); broadcastData( MSG_ID_DG_PRESSURES_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( PRESSURES_DATA_T ) ); pressuresDataPublicationTimerCounter = 0; } } /*********************************************************************//** * @brief * The handleSelfTestADCCheck function checks whether the ADC reads and * report status back. If the reads are above the maximum 12bit ADC count * or equals zero, it will throw an alarm. * @details Inputs: ADC reading for RO pump inlet pressure sensor * @details Outputs: Performed ADC check self-test. * @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 ( ( 0 == inletPressureADCReading ) || ( inletPressureADCReading >= INT_ADC_FULL_SCALE_BITS ) ) { 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 * pressure data publish interval. * @details Inputs: none * @details Outputs: pressuresDataPublishInterval * @param value value to override pressure 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 * of the pressure and occlusion data publish interval. * @details Inputs: none * @details 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 * specified pressure sensor with a given value. * @details Inputs: none * @details Outputs: pressures[] * @param sensor pressure sensor ID * @param value override value for pressure data * @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 * @details Outputs: pressures[] * @param sensor pressure sensor ID * @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; } /**@}*/