Index: firmware/App/Controllers/DGInterface.c =================================================================== diff -u -ra2bc96881a5fc3d8f779246b2abebf15a8de9384 -r766708fceb0bdf1af8c7897df29d4f5036bfd3db --- firmware/App/Controllers/DGInterface.c (.../DGInterface.c) (revision a2bc96881a5fc3d8f779246b2abebf15a8de9384) +++ firmware/App/Controllers/DGInterface.c (.../DGInterface.c) (revision 766708fceb0bdf1af8c7897df29d4f5036bfd3db) @@ -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 @@ -57,8 +60,9 @@ static BOOL dgTrimmerHeaterOn = FALSE; ///< Flag indicates whether we have commanded the DG to start or stop the trimmer heater. static BOOL dgWaterSampled = FALSE; ///< Flag indicates whether we have 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. +// 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 @@ -70,10 +74,27 @@ 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. @@ -98,19 +119,62 @@ *************************************************************************/ void initDGInterface( void ) { + U32 i, j; + dgStarted = FALSE; dgTrimmerHeaterOn = FALSE; dgWaterSampled = FALSE; dgPrimaryTempSet = 0.0; dgTrimmerTempSet = 0.0; dgActiveReservoirSet = DG_RESERVOIR_2; dgReservoirFillVolumeTargetSet = 0; - dgReservoirDrainVolumeTargetSet = 0; - initPreTreatmentReservoirMgmt(); -} + 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 initPreTreatmentReservoirMgmt function initializes the pre-treatment * reservoir management state machine. * @details Inputs: none @@ -141,18 +205,6 @@ resUseVolumeMl = 0.0; } -/*********************************************************************//** - * @brief - * The execPreTreatmentReservoirMgmt function executes the state machine for the - * reservoir management during pre-treatment mode. - * @details Inputs: none - * @details Outputs: DG reservoirs (drains & fills) managed. - * @return none - *************************************************************************/ -void execPreTreatmentReservoirMgmt( void ) -{ -} - /*********************************************************************//** * @brief * The execTreatmentReservoirMgmt function executes the state machine for the @@ -187,7 +239,7 @@ { if ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == dgSubMode ) { - cmdStartDGDrain( DRAIN_RESERVOIR_TO_VOLUME_ML ); + cmdStartDGDrain( DRAIN_RESERVOIR_TO_VOLUME_ML, FALSE ); } } else if ( DG_MODE_DRAI == dgOpMode ) @@ -317,8 +369,8 @@ DG_RESERVOIR_ID_T getDGActiveReservoir( void ) { return dgActiveReservoirSet; -} - +} + /*********************************************************************//** * @brief * The getDGInactiveReservoir function gets the currently inactive reservoir. @@ -331,8 +383,8 @@ DG_RESERVOIR_ID_T inactiveRes = ( DG_RESERVOIR_2 == dgActiveReservoirSet ? DG_RESERVOIR_1 : DG_RESERVOIR_2 ); return inactiveRes; -} - +} + /*********************************************************************//** * @brief * The getDGPressure function gets the latest pressure reported by the DG @@ -403,6 +455,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 @@ -530,6 +655,47 @@ 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 ) +{ + DG_RESERVOIR_ID_T res; + + 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 + for ( res = DG_RESERVOIR_1; res < NUM_OF_DG_RESERVOIRS; res++ ) + { + F32 wt = ( res == DG_RESERVOIR_1 ? res1Primary : res2Primary ); + + smLoadCellReadingsTotal[ res ] -= smLoadCellReadings[ res ][ smLoadCellReadingsIdx ]; + lgLoadCellReadingsTotal[ res ] -= lgLoadCellReadings[ res ][ lgLoadCellReadingsIdx ]; + smLoadCellReadings[ res ][ smLoadCellReadingsIdx ] = wt; + lgLoadCellReadings[ res ][ lgLoadCellReadingsIdx ] = wt; + smLoadCellReadingsTotal[ res ] += wt; + lgLoadCellReadingsTotal[ res ] += wt; + smFilteredReservoirWeightInGrams[ res ] = smLoadCellReadingsTotal[ res ] / (F32)SIZE_OF_SMALL_LOAD_CELL_AVG; + lgFilteredReservoirWeightInGrams[ res ] = lgLoadCellReadingsTotal[ res ] / (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 @@ -643,18 +809,18 @@ * @brief * The cmdStartDGDrain function sends a drain command message to the DG. * @details Inputs: none - * @details Outputs: drain command sent to DG. + * @details Outputs: drain command sent to DG. * @param drainToVolMl volume (in mL) to drain inactive reservoir to + * @param tareLoadCell flag to tell DG tare load cell or not * @return none *************************************************************************/ -void cmdStartDGDrain( U32 drainToVolMl ) +void cmdStartDGDrain( U32 drainToVolMl, BOOL tareLoadCell ) { DRAIN_RESERVOIR_CMD_PAYLOAD_T payload; - DG_RESERVOIR_ID_T inactiveRes = getDGInactiveReservoir(); - - dgReservoirDrainVolumeTargetSet = drainToVolMl; + payload.drainToVolumeML = drainToVolMl; - payload.tareLoadCells = ( FALSE == resHasBeenTared[ inactiveRes ] ? TRUE : FALSE ); + payload.tareLoadCells = tareLoadCell; + dgReservoirDrainVolumeTargetSet = drainToVolMl; sendDGDrainCommand( &payload ); } @@ -670,5 +836,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; +} + /**@}*/