Index: firmware/App/Controllers/DGInterface.c =================================================================== diff -u -r1a685471524555a374854c0c9ec8e208e71fe2df -raeb1dea9ea10fcc70ae66023a87b565f29924c07 --- firmware/App/Controllers/DGInterface.c (.../DGInterface.c) (revision 1a685471524555a374854c0c9ec8e208e71fe2df) +++ firmware/App/Controllers/DGInterface.c (.../DGInterface.c) (revision aeb1dea9ea10fcc70ae66023a87b565f29924c07) @@ -32,7 +32,10 @@ #define START_DG_CMD TRUE ///< Parameter for DG start/stop command function. True = start. #define STOP_DG_CMD FALSE ///< Parameter for DG start/stop command function. False = stop. -#define RESERVOIR_SETTLE_TIME_MS 5000 ///< Time (in ms) allotted for reservoir to settle (after fill, before drain). +#define RESERVOIR_SETTLE_TIME_MS 5000 ///< Time (in ms) allotted for reservoir to settle (after fill, before drain). + +#define SIZE_OF_SMALL_LOAD_CELL_AVG 8 ///< Small load cell moving average has 8 samples. +#define SIZE_OF_LARGE_LOAD_CELL_AVG 32 ///< Large load cell moving average has 32 samples. /// States of the treatment reservoir management state machine. typedef enum TreatmentReservoirMgmt_States @@ -51,39 +54,56 @@ // ********** private data ********** // DG status -static DG_OP_MODE_T dgCurrentOpMode = DG_MODE_INIT; ///< Current DG operation mode. -static U32 dgSubMode = 0; ///< Current state (sub-mode) of current DG operation mode. -static BOOL dgStarted = FALSE; ///< Flag indicates whether we've commanded the DG to start or stop. -static BOOL dgTrimmerHeaterOn = FALSE; ///< Flag indicates whether we've commanded the DG to start or stop the trimmer heater. -static BOOL dgWaterSampled = FALSE; ///< Flag indicates whether we've commanded the DG to sample water. +static DG_OP_MODE_T dgCurrentOpMode = DG_MODE_INIT; ///< Current DG operation mode. +static U32 dgSubMode = 0; ///< Current state (sub-mode) of current DG operation mode. +static BOOL dgStarted = FALSE; ///< Flag indicates whether we've commanded the DG to start or stop. +static BOOL dgTrimmerHeaterOn = FALSE; ///< Flag indicates whether we've commanded the DG to start or stop the trimmer heater. +static BOOL dgWaterSampled = FALSE; ///< Flag indicates whether we've commanded the DG to sample water. -// state machine states -static TREATMENT_RESERVOIR_MGMT_STATE_T currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_START_STATE; ///< Current state of treatment mode reservoir management. -static U32 resMgmtTimer = 0; ///< used for keeping state time. +// state machine states +/// Current state of treatment mode reservoir management. +static TREATMENT_RESERVOIR_MGMT_STATE_T currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_START_STATE; +static U32 resMgmtTimer = 0; ///< used for keeping state time. -// DG sensor data -static F32 dgPressures[ NUM_OF_DG_PRESSURE_SENSORS ]; ///< Latest pressures reported by the DG. -static F32 dgDialysateTemp = 0.0; ///< Dialysate temperature reported by the DG. -static F32 dgRedundantDialysateTemp = 0.0; ///< Redundant dialysate temperature reported by the DG. -static F32 dgPrimaryTempSet = 0.0; ///< Primary heater target temperature commanded. -static F32 dgPrimaryTemp = 0.0; ///< Latest RO water temperature reported by the DG. -static F32 dgTrimmerTempSet = 0.0; ///< Trimmer heater target temperature commanded. -static F32 dgTrimmerTemp = 0.0; ///< Latest dialysate temperature reported by the DG. +// DG sensor data +static F32 dgPressures[ NUM_OF_DG_PRESSURE_SENSORS ]; ///< Latest pressures reported by the DG. +static F32 dgDialysateTemp = 0.0; ///< Dialysate temperature reported by the DG. +static F32 dgRedundantDialysateTemp = 0.0; ///< Redundant dialysate temperature reported by the DG. +static F32 dgPrimaryTempSet = 0.0; ///< Primary heater target temperature commanded. +static F32 dgPrimaryTemp = 0.0; ///< Latest RO water temperature reported by the DG. +static F32 dgTrimmerTempSet = 0.0; ///< Trimmer heater target temperature commanded. +static F32 dgTrimmerTemp = 0.0; ///< Latest dialysate temperature reported by the DG. +/// Measured weight from load cells. +static OVERRIDE_F32_T loadCellWeightInGrams[ NUM_OF_LOAD_CELLS ]; +/// Filtered (8 sample) weight of reservoirs. +static F32 smFilteredReservoirWeightInGrams[ NUM_OF_DG_RESERVOIRS ]; +/// Filtered (32 sample) weight of reservoirs. +static F32 lgFilteredReservoirWeightInGrams[ NUM_OF_DG_RESERVOIRS ]; +// load cell filtering data +/// holds load cell samples for small load cell moving average +static F32 smLoadCellReadings[ NUM_OF_DG_RESERVOIRS ][ SIZE_OF_SMALL_LOAD_CELL_AVG ]; +static U32 smLoadCellReadingsIdx = 0; ///< index for next sample in small load cell rolling average sample array +static F32 smLoadCellReadingsTotal[ NUM_OF_DG_RESERVOIRS ]; ///< rolling total - used to calc small load cell moving average +/// holds load cell samples for large load cell moving average +static F32 lgLoadCellReadings[ NUM_OF_DG_RESERVOIRS ][ SIZE_OF_LARGE_LOAD_CELL_AVG ]; +static U32 lgLoadCellReadingsIdx = 0; ///< index for next sample in large load cell rolling average sample array +static F32 lgLoadCellReadingsTotal[ NUM_OF_DG_RESERVOIRS ]; ///< rolling total - used to calc large load cell moving average + // DG pumps data -static F32 dgROPumpFlowRateMlMin = 0.0; ///< Latest RO water flow rate reported by the DG. -static U32 dgROPumpPressureSetPtPSI = 0; ///< Latest RO pump target pressure reported by the DG. -static U32 dgDrainPumpSpeedSetPtRPM = 0; ///< Latest Drain pump target speed reported by the DG. +static F32 dgROPumpFlowRateMlMin = 0.0; ///< Latest RO water flow rate reported by the DG. +static U32 dgROPumpPressureSetPtPSI = 0; ///< Latest RO pump target pressure reported by the DG. +static U32 dgDrainPumpSpeedSetPtRPM = 0; ///< Latest Drain pump target speed reported by the DG. // reservoir data -static DG_RESERVOIR_ID_T dgActiveReservoir = DG_RESERVOIR_2; ///< Latest active reservoir reported by the DG. -static DG_RESERVOIR_ID_T dgActiveReservoirSet = DG_RESERVOIR_2; ///< Active reservoir commanded. -static U32 dgReservoirFillVolumeTarget = 0; ///< Latest fill-to volume reported by the DG. -static U32 dgReservoirFillVolumeTargetSet = 0; ///< Fill-to volume commanded. -static U32 dgReservoirDrainVolumeTarget = 0; ///< Latest drain-to volume reported by the DG. -static U32 dgReservoirDrainVolumeTargetSet = 0; ///< Drain-to volume commanded. -static U32 resUseTimer = 0; ///< used to track time pumping from active reservoir (for volume used calculation). -static F32 resUseVolumeMl = 0.0; ///< Accumulated volume used from active reservoir. +static DG_RESERVOIR_ID_T dgActiveReservoir = DG_RESERVOIR_2; ///< Latest active reservoir reported by the DG. +static DG_RESERVOIR_ID_T dgActiveReservoirSet = DG_RESERVOIR_2; ///< Active reservoir commanded. +static U32 dgReservoirFillVolumeTarget = 0; ///< Latest fill-to volume reported by the DG. +static U32 dgReservoirFillVolumeTargetSet = 0; ///< Fill-to volume commanded. +static U32 dgReservoirDrainVolumeTarget = 0; ///< Latest drain-to volume reported by the DG. +static U32 dgReservoirDrainVolumeTargetSet = 0; ///< Drain-to volume commanded. +static U32 resUseTimer = 0; ///< used to track time pumping from active reservoir (for volume used calculation). +static F32 resUseVolumeMl = 0.0; ///< Accumulated volume used from active reservoir. // ********** private function prototypes ********** @@ -97,6 +117,8 @@ *************************************************************************/ void initDGInterface( void ) { + U32 i, j; + dgStarted = FALSE; dgTrimmerHeaterOn = FALSE; dgWaterSampled = FALSE; @@ -105,8 +127,50 @@ dgActiveReservoirSet = DG_RESERVOIR_2; dgReservoirFillVolumeTargetSet = 0; dgReservoirDrainVolumeTargetSet = 0; -} + + // initialize load cell weights + for ( i = 0; i < NUM_OF_LOAD_CELLS; i++ ) + { + loadCellWeightInGrams[ i ].data = 0.0; + loadCellWeightInGrams[ i ].ovInitData = 0.0; + loadCellWeightInGrams[ i ].ovData = 0.0; + loadCellWeightInGrams[ i ].override = 0; + } + // initialize reservoirs weights + for ( i = 0; i < NUM_OF_DG_RESERVOIRS; i++ ) + { + smFilteredReservoirWeightInGrams[ i ] = 0.0; + lgFilteredReservoirWeightInGrams[ i ] = 0.0; + for ( j = 0; j < SIZE_OF_SMALL_LOAD_CELL_AVG; j++ ) + { + smLoadCellReadings[ i ][ j ] = 0.0; + } + for ( j = 0; j < SIZE_OF_LARGE_LOAD_CELL_AVG; j++ ) + { + lgLoadCellReadings[ i ][ j ] = 0.0; + } + } + smLoadCellReadingsIdx = 0; + smLoadCellReadingsTotal[ DG_RESERVOIR_1 ] = 0.0; + smLoadCellReadingsTotal[ DG_RESERVOIR_2 ] = 0.0; + lgLoadCellReadingsIdx = 0; + lgLoadCellReadingsTotal[ DG_RESERVOIR_1 ] = 0.0; + lgLoadCellReadingsTotal[ DG_RESERVOIR_2 ] = 0.0; +} +/*********************************************************************//** + * @brief + * The execDGInterfaceMonitor function executes the DG Interface monitoring + * function. Ensures DG is sending fresh data in a timely manner. + * @details Inputs: TBD + * @details Outputs: TBD + * @return none + *************************************************************************/ +void execDGInterfaceMonitor( void ) +{ + // TODO - make sure DG sensor/state data is coming in timely manner (e.g. load cells s/b every 100 ms) +} + /*********************************************************************//** * @brief * The initTreatmentReservoirMgmt function initializes the treatment reservoir @@ -287,6 +351,20 @@ DG_RESERVOIR_ID_T getDGActiveReservoir( void ) { return dgActiveReservoirSet; +} + +/*********************************************************************//** + * @brief + * The getDGInactiveReservoir function gets the currently inactive reservoir. + * @details Inputs: dgActiveReservoirSet + * @details Outputs: none + * @return Currently commanded inactive reservoir. + *************************************************************************/ +DG_RESERVOIR_ID_T getDGInactiveReservoir( void ) +{ + DG_RESERVOIR_ID_T inactiveRes = ( DG_RESERVOIR_2 == dgActiveReservoirSet ? DG_RESERVOIR_1 : DG_RESERVOIR_2 ); + + return inactiveRes; } /*********************************************************************//** @@ -359,6 +437,79 @@ return result; } +/*********************************************************************//** + * @brief + * The getLoadCellWeightInGrams function gets the load cell weight. + * @details Inputs: loadCellWeightInGrams + * @details Outputs: none + * @param loadCellID ID of load cell to get + * @return the current load cell weight in grams + *************************************************************************/ +F32 getLoadCellWeightInGrams( LOAD_CELL_T loadCellID ) +{ + F32 result = 0.0; + + if ( loadCellID < NUM_OF_LOAD_CELLS ) + { + if ( OVERRIDE_KEY == loadCellWeightInGrams[ loadCellID ].override ) + { + result = loadCellWeightInGrams[ loadCellID ].ovData; + } + else + { + result = loadCellWeightInGrams[ loadCellID ].data; + } + } + else + { + activateAlarmNoData( ALARM_ID_HD_SOFTWARE_FAULT ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getReservoirWeightSmallFilter function gets the load cell weight + * of the given reservoir after large (8 sample) filter applied. + * @details Inputs: lgFilteredReservoirWeightInGrams[] + * @details Outputs: none + * @param resID ID of reservoir to get filtered weight for + * @return the current filtered weight of the given reservoir in grams + *************************************************************************/ +F32 getReservoirWeightSmallFilter( DG_RESERVOIR_ID_T resID ) +{ + F32 result = 0.0; + + if ( resID < NUM_OF_DG_RESERVOIRS ) + { + result = smFilteredReservoirWeightInGrams[ resID ]; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getReservoirWeightLargeFilter function gets the load cell weight + * of the given reservoir after large (32 sample) filter applied. + * @details Inputs: lgFilteredReservoirWeightInGrams[] + * @details Outputs: none + * @param resID ID of reservoir to get filtered weight for + * @return the current filtered weight of the given reservoir in grams + *************************************************************************/ +F32 getReservoirWeightLargeFilter( DG_RESERVOIR_ID_T resID ) +{ + F32 result = 0.0; + + if ( resID < NUM_OF_DG_RESERVOIRS ) + { + result = lgFilteredReservoirWeightInGrams[ resID ]; + } + + return result; +} + /*********************************************************************//** * @brief * The setDGOpMode function sets the latest DG operating mode reported by @@ -486,6 +637,48 @@ dgDrainPumpSpeedSetPtRPM = rpmSetPt; } +/*********************************************************************//** + * @brief + * The setNewLoadCellReadings function sets the latest DG reservoir load cell + * readings sent by the DG (in g or mL). New readings are expected once + * every 100 ms. + * @details Inputs: none + * @details Outputs: loadCellWeightInGrams[], smFilteredReservoirWeightInGrams[], + * lgFilteredReservoirWeightInGrams[] + * @param res1Primary New weight from primary load cell of reservoir 1 + * @param res1Backup New weight from backup load cell of reservoir 1 + * @param res2Primary New weight from primary load cell of reservoir 2 + * @param res2Backup New weight from backup load cell of reservoir 2 + * @return none + *************************************************************************/ +void setNewLoadCellReadings( F32 res1Primary, F32 res1Backup, F32 res2Primary, F32 res2Backup ) +{ + loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_1_PRIMARY ].data = res1Primary; + loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_1_BACKUP ].data = res1Backup; + loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_2_PRIMARY ].data = res2Primary; + loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_2_BACKUP ].data = res2Backup; + + // feed new weight samples into filters and update moving averages + smLoadCellReadingsTotal[ DG_RESERVOIR_1 ] -= smLoadCellReadings[ DG_RESERVOIR_1 ][ smLoadCellReadingsIdx ]; + lgLoadCellReadingsTotal[ DG_RESERVOIR_1 ] -= lgLoadCellReadings[ DG_RESERVOIR_1 ][ lgLoadCellReadingsIdx ]; + smLoadCellReadingsTotal[ DG_RESERVOIR_2 ] -= smLoadCellReadings[ DG_RESERVOIR_2 ][ smLoadCellReadingsIdx ]; + lgLoadCellReadingsTotal[ DG_RESERVOIR_2 ] -= lgLoadCellReadings[ DG_RESERVOIR_2 ][ lgLoadCellReadingsIdx ]; + smLoadCellReadings[ DG_RESERVOIR_1 ][ smLoadCellReadingsIdx ] = res1Primary; + lgLoadCellReadings[ DG_RESERVOIR_1 ][ lgLoadCellReadingsIdx ] = res1Primary; + smLoadCellReadings[ DG_RESERVOIR_2 ][ smLoadCellReadingsIdx ] = res2Primary; + lgLoadCellReadings[ DG_RESERVOIR_2 ][ lgLoadCellReadingsIdx ] = res2Primary; + smLoadCellReadingsTotal[ DG_RESERVOIR_1 ] += res1Primary; + lgLoadCellReadingsTotal[ DG_RESERVOIR_1 ] += res1Primary; + smLoadCellReadingsTotal[ DG_RESERVOIR_2 ] += res2Primary; + lgLoadCellReadingsTotal[ DG_RESERVOIR_2 ] += res2Primary; + smFilteredReservoirWeightInGrams[ DG_RESERVOIR_1 ] = smLoadCellReadingsTotal[ DG_RESERVOIR_1 ] / (F32)SIZE_OF_SMALL_LOAD_CELL_AVG; + lgFilteredReservoirWeightInGrams[ DG_RESERVOIR_1 ] = lgLoadCellReadingsTotal[ DG_RESERVOIR_1 ] / (F32)SIZE_OF_LARGE_LOAD_CELL_AVG; + smFilteredReservoirWeightInGrams[ DG_RESERVOIR_2 ] = smLoadCellReadingsTotal[ DG_RESERVOIR_2 ] / (F32)SIZE_OF_SMALL_LOAD_CELL_AVG; + lgFilteredReservoirWeightInGrams[ DG_RESERVOIR_2 ] = lgLoadCellReadingsTotal[ DG_RESERVOIR_2 ] / (F32)SIZE_OF_LARGE_LOAD_CELL_AVG; + smLoadCellReadingsIdx = INC_WRAP( smLoadCellReadingsIdx, 0, SIZE_OF_SMALL_LOAD_CELL_AVG - 1 ); + lgLoadCellReadingsIdx = INC_WRAP( lgLoadCellReadingsIdx, 0, SIZE_OF_LARGE_LOAD_CELL_AVG - 1 ); +} + /*********************************************************************//** * @brief * The cmdSetDGDialysateTargetTemps function sends a target dialysate @@ -621,5 +814,64 @@ dgWaterSampled = TRUE; sendDGSampleWaterCommand(); } - + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testSetDialOutLoadCellWeightOverride function overrides the value of the + * load cell sensor with a given weight (in grams). + * @details Inputs: loadCellWeightInGrams[] + * @details Outputs: loadCellWeightInGrams[] + * @param sensor ID of load cell sensor to override weight for + * @param value override weight (in grams) for the given sensor + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetDialOutLoadCellWeightOverride( U32 sensor, F32 value ) +{ + BOOL result = FALSE; + + if ( sensor < NUM_OF_LOAD_CELLS ) + { + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + loadCellWeightInGrams[ sensor ].ovData = value; + loadCellWeightInGrams[ sensor ].override = OVERRIDE_KEY; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetDialOutLoadCellWeightOverride function resets the override of the + * load cell sensor. + * @details Inputs: loadCellWeightInGrams[] + * @details Outputs: loadCellWeightInGrams[] + * @param sensor ID of load cell sensor to override weight for + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetDialOutLoadCellWeightOverride( U32 sensor ) +{ + BOOL result = FALSE; + + if ( sensor < NUM_OF_LOAD_CELLS ) + { + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + loadCellWeightInGrams[ sensor ].override = OVERRIDE_RESET; + loadCellWeightInGrams[ sensor ].ovData = loadCellWeightInGrams[ sensor ].ovInitData; + } + } + + return result; +} + /**@}*/