#include #include "ConcentratePumps.h" #include "FlowSensors.h" #include "FPGA.h" #include "NVDataMgmt.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "TaskPriority.h" #include "Utilities.h" /** * @addtogroup FlowSensors * @{ */ // ********** private definitions ********** #define FLOW_SENSORS_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval (ms/task time) at which the Dialysate flow data is published on the CAN bus. #define FLOW_SENSORS_EDGES_BUFFER_LENGTH 100 #define FLOW_SENSORS_PULSES_PER_LITER 110000 // TODO calibration record #define FLOW_OUT_OF_RANGE_PERSISTENT_INTERVAL ( 12 * MS_PER_SECOND ) // Why is this so long? ///< Flow out of range time out in counts. #define DATA_PUBLISH_COUNTER_START_COUNT 20 ///< Data publish counter start count. static const F32 FLOW_SENSORS_LITERS_PER_PULSES = ( 1.0 / (F32)FLOW_SENSORS_PULSES_PER_LITER ); // ********** private data ********** typedef struct { U08 edgeCountsIndex; U16 edgeCountsBuffer[ FLOW_SENSORS_EDGES_BUFFER_LENGTH ]; OVERRIDE_F32_T measuredFlowLPM; OVERRIDE_F32_T measuredROFlowWithCPsLPM; } FLOW_SENSOR_STATUS_T; static U32 dataPublicationCounter; ///< Used to schedule Dialysate flow data publication to CAN bus. static OVERRIDE_U32_T flowSensorsDataPublishInterval = { FLOW_SENSORS_DATA_PUB_INTERVAL, FLOW_SENSORS_DATA_PUB_INTERVAL, 0, 0 }; ///< Interval (in ms) at which to publish Dialysate flow data to CAN bus. static FLOW_SENSOR_STATUS_T flowSensorStatus[ NUM_OF_FLOW_SENSORS ]; static DG_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< Flow sensors calibration record. // ********** private function prototypes ********** static F32 getCalibrationAppliedFlowLPM( U32 sensorId, F32 flow ); static void publishFlowSensorsData( void ); /*********************************************************************//** * @brief * The initFlowSensors function initializes the flow sensors module. * @details Inputs: none * @details Outputs: flowSensorStatus, dataPublicationCounter * @return none *************************************************************************/ void initFlowSensors( void ) { U08 i; for ( i = 0; i < NUM_OF_FLOW_SENSORS; i++ ) { memset( &flowSensorStatus[ i ], 0x0, sizeof( FLOW_SENSOR_STATUS_T ) ); } // Initialize the persistent alarm for flow out of upper and lower range initPersistentAlarm( ALARM_ID_DIALYSATE_FLOW_RATE_OUT_OF_RANGE, FLOW_OUT_OF_RANGE_PERSISTENT_INTERVAL, FLOW_OUT_OF_RANGE_PERSISTENT_INTERVAL ); dataPublicationCounter = DATA_PUBLISH_COUNTER_START_COUNT; } /*********************************************************************//** * @brief * The execFlowSensorsSelfTest function executes the flow sensor's self-test. * @details Inputs: none * @details Outputs: none * @return flow sensor self test results (SELF_TEST_STATUS_T) *************************************************************************/ SELF_TEST_STATUS_T execFlowSensorsSelfTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; BOOL calStatus = getNVRecord2Driver( GET_CAL_FLOW_SENSORS, (U08*)&flowSensorsCalRecord, sizeof( DG_FLOW_SENSORS_CAL_RECORD_T ), NUM_OF_CAL_DATA_FLOW_SENSORS, ALARM_ID_DG_DIALYSATE_FLOW_SENSOR_INVALID_CAL_RECORD ); result = ( TRUE == calStatus ? SELF_TEST_STATUS_PASSED : SELF_TEST_STATUS_FAILED ); return result; } /*********************************************************************//** * @brief * The execFlowSesnorsMonitor function executes the flow sensors monitor. * @details Inputs: none * @details Outputs: flowSensorStatus, flowSensorsCalRecord * @return none *************************************************************************/ void execFlowSesnorsMonitor( void ) { U08 i; U08 countsIndex; U16 oldestEdgeCount; U16 oldest2CurrentEdgeDiff; F32 flowBeforeCalibrationLPM; // Check if a new calibration is available if ( TRUE == isNewCalibrationRecordAvailable() ) { getNVRecord2Driver( GET_CAL_FLOW_SENSORS, (U08*)&flowSensorsCalRecord, sizeof( DG_FLOW_SENSORS_CAL_RECORD_T ), NUM_OF_CAL_DATA_FLOW_SENSORS, ALARM_ID_DG_DIALYSATE_FLOW_SENSOR_INVALID_CAL_RECORD ); } for ( i = 0; i < NUM_OF_FLOW_SENSORS; i++ ) { countsIndex = flowSensorStatus[ i ].edgeCountsIndex; oldestEdgeCount = flowSensorStatus[ i ].edgeCountsBuffer[ countsIndex ]; switch ( i ) { case RO_FLOW_SENSOR: flowSensorStatus[ i ].edgeCountsBuffer[ countsIndex ] = getFPGAROFlowSensorEdgeCount(); oldest2CurrentEdgeDiff = u16DiffWithWrap( oldestEdgeCount, flowSensorStatus[ i ].edgeCountsBuffer[ countsIndex ] ); flowBeforeCalibrationLPM = oldest2CurrentEdgeDiff * FLOW_SENSORS_LITERS_PER_PULSES * SEC_PER_MIN; flowSensorStatus[ i ].measuredROFlowWithCPsLPM.data = getCalibrationAppliedFlowLPM( i, flowBeforeCalibrationLPM ); flowSensorStatus[ i ].measuredFlowLPM.data = flowSensorStatus[ i ].measuredROFlowWithCPsLPM.data - ( getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) / ML_PER_LITER ) - ( getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) / ML_PER_LITER ); flowSensorStatus[ i ].measuredFlowLPM.data = MAX( flowSensorStatus[ i ].measuredFlowLPM.data, 0.0 ); break; case DIALYSATE_FLOW_SENSOR: flowSensorStatus[ i ].edgeCountsBuffer[ countsIndex ] = getFPGADialysateFlowSensorEdgeCount(); oldest2CurrentEdgeDiff = u16DiffWithWrap( oldestEdgeCount, flowSensorStatus[ i ].edgeCountsBuffer[ countsIndex ] ); flowBeforeCalibrationLPM = oldest2CurrentEdgeDiff * FLOW_SENSORS_LITERS_PER_PULSES * SEC_PER_MIN; flowSensorStatus[ i ].measuredFlowLPM.data = getCalibrationAppliedFlowLPM( i, flowBeforeCalibrationLPM ); flowSensorStatus[ i ].measuredFlowLPM.data = MAX( flowSensorStatus[ i ].measuredFlowLPM.data, 0.0 ); break; #ifndef _VECTORCAST_ default: // TODO software fault break; #endif } flowSensorStatus[ i ].edgeCountsIndex = INC_WRAP( countsIndex, 0, FLOW_SENSORS_EDGES_BUFFER_LENGTH ); } // TODO dialysate flow alarm. Is it needed? //currentFlow = getMeasuredDialysateFlowRate(); //isFlowOutOfUpperRange = ( currentFlow > MAX_DIALYSATE_FLOWRATE_LPM ? TRUE : FALSE ); //checkPersistentAlarm( ALARM_ID_DIALYSATE_FLOW_RATE_OUT_OF_RANGE, isFlowOutOfUpperRange, currentFlow, MAX_DIALYSATE_FLOWRATE_LPM ); publishFlowSensorsData(); } /*********************************************************************//** * @brief * The getMeasuredFlowRateLPM function gets the measured flow rate of a flow sensor * in L/min. * @details Inputs: flowSensorStatus * @details Outputs: flowSensorStatus * @return the current flow rate (in L/min). *************************************************************************/ F32 getMeasuredFlowRateLPM( FLOW_SENSORS_T sensorId ) { return getF32OverrideValue( &flowSensorStatus[ sensorId ].measuredFlowLPM ); } /*********************************************************************//** * @brief * The publishFlowSensorsData function publishes flow sensors data at * the set interval. * @details Inputs: flowSensorsDataPublicationCounter, * flowSensorsDataPublishInterval * @details Outputs: flowSensorsDataPublicationCounter * @return none *************************************************************************/ static void publishFlowSensorsData( void ) { // publish dialysate flow meter data on interval if ( ++dataPublicationCounter >= getU32OverrideValue( &flowSensorsDataPublishInterval ) ) { FLOW_SENSORS_DATA_T data; data.ROFlowRateLPM = getMeasuredFlowRateLPM( RO_FLOW_SENSOR ); data.dialysateFlowRateLPM = getMeasuredFlowRateLPM( DIALYSATE_FLOW_SENSOR ); data.ROFlowRateWithCPsLPM = getF32OverrideValue( &flowSensorStatus[ RO_FLOW_SENSOR ].measuredROFlowWithCPsLPM ); dataPublicationCounter = 0; broadcastData( MSG_ID_DG_FLOW_SENSORS_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( FLOW_SENSORS_DATA_T ) ); } } /*********************************************************************//** * @brief * The getCalibrationAppliedFlowLPM function applies the calibration record * values to the flow. * @details Inputs: flowSensorsCalRecord * @details Outputs: none * @param sensorId which is the flow sensor ID * @param flow which is the flow in L/min to apply calibration to it * @return the calibration applied flow value *************************************************************************/ static F32 getCalibrationAppliedFlowLPM( U32 sensorId, F32 flow ) { F32 flowAfterCal = pow( flow, 4 ) * flowSensorsCalRecord.flowSensors[ (CAL_DATA_DG_FLOW_SENSORS_T)sensorId ].fourthOrderCoeff + pow( flow, 3 ) * flowSensorsCalRecord.flowSensors[ (CAL_DATA_DG_FLOW_SENSORS_T)sensorId ].thirdOrderCoeff + pow( flow, 2 ) * flowSensorsCalRecord.flowSensors[ (CAL_DATA_DG_FLOW_SENSORS_T)sensorId ].secondOrderCoeff + flow * flowSensorsCalRecord.flowSensors[ (CAL_DATA_DG_FLOW_SENSORS_T)sensorId ].gain + flowSensorsCalRecord.flowSensors[ (CAL_DATA_DG_FLOW_SENSORS_T)sensorId ].offset; return flowAfterCal; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetFlowDataPublishIntervalOverride function overrides the * flow sensors data publish interval. * @details Inputs: none * @details Outputs: flowSensorsDataPublishInterval * @param: value override flow data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetFlowDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; flowSensorsDataPublishInterval.ovData = intvl; flowSensorsDataPublishInterval.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetFlowDataPublishIntervalOverride function resets the * override of the flow sensors data publish interval. * @details Inputs: flowSensorsDataPublishInterval * @details Outputs: flowSensorsDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetFlowDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { flowSensorsDataPublishInterval.override = OVERRIDE_RESET; flowSensorsDataPublishInterval.ovData = flowSensorsDataPublishInterval.ovInitData; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetMeasuredFlowRateOverride function overrides the measured * flow rate. * @details Inputs: none * @details Outputs: flowSensorStatus * @param: sensorId the Id of the sensor which its value is overridden. * @param: value override measured flow rate in LPM * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredFlowRateOverride( U32 sensorId, F32 flowLPM ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { flowSensorStatus[ sensorId ].measuredFlowLPM.ovInitData = flowSensorStatus[ sensorId ].measuredFlowLPM.data; flowSensorStatus[ sensorId ].measuredFlowLPM.ovData = flowLPM; flowSensorStatus[ sensorId ].measuredFlowLPM.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredFlowRateOverride function resets the override * of the measured flow rate. * @details Inputs: none * @details Outputs: flowSensorStatus * @param: sensorId the Id of the sensor which its value is reset. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredFlowRateOverride( U32 sensorId ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { flowSensorStatus[ sensorId ].measuredFlowLPM.data = flowSensorStatus[ sensorId ].measuredFlowLPM.ovInitData; flowSensorStatus[ sensorId ].measuredFlowLPM.override = OVERRIDE_RESET; flowSensorStatus[ sensorId ].measuredFlowLPM.ovInitData = 0.0; flowSensorStatus[ sensorId ].measuredFlowLPM.ovData = 0.0; result = TRUE; } return result; } /**@}*/