Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -rf1cb4be5fb8201cec188589e683c8bf20d895ec0 -rd5f2ac4d84453feb1b782f35f9d69432977a95f6 --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision f1cb4be5fb8201cec188589e683c8bf20d895ec0) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision d5f2ac4d84453feb1b782f35f9d69432977a95f6) @@ -7,8 +7,8 @@ * * @file Reservoirs.c * -* @author (last) Dara Navaei -* @date (last) 23-May-2022 +* @author (last) Michael Garthwaite +* @date (last) 08-Aug-2022 * * @author (original) Sean * @date (original) 18-Mar-2020 @@ -51,7 +51,7 @@ #define MAX_REDUNDANT_LOAD_CELL_DIFF 50.0F ///< Maximum difference in redundant load cells when determining if fill completed. #define MAX_DRAIN_RPM_MLP 2400.0F ///< Maximum drain RPM in mL/min. #define DATA_PUBLISH_COUNTER_START_COUNT 5 ///< Data publish counter start count. -#define ENVIRONMENT_TEMPERATURE_C 23.5F ///< Device's environment temperature in C. // TODO add this to the cal records +#define NUM_OF_ACID_AND_BICARB_NV_DATA_TO_CHECK 1 ///< Number of acid and bicarb non-volatile data to check. // ********** private data ********** @@ -98,9 +98,14 @@ static BOOL tareLoadCellRequest; ///< Flag indicates if load cell tare has been requested by HD. static DG_RESERVOIR_VOLUME_RECORD_T reservoirsCalRecord; ///< DG reservoirs non-volatile record. static DG_HEATING_CAL_RECORD_T heatingConstsCalRecord; ///< DG heating calibration record. +static DG_ACID_CONCENTRATES_RECORD_T acidConcentrateCalRecord; ///< Acid concentrate calibration record. +static DG_BICARB_CONCENTRATES_RECORD_T bicarbConcentrateCalRecord; ///< Bicarb concentrate calibration record. static F32 targetFillFlowRateLPM; ///< Target fill flow rate in L/min. static BOOL isThisTheFirstCycle; ///< Boolean flag to indicate whether this is the first cycle. static RESERVOIRS_PREVIOUS_STATUS reservoirPreviousStatus[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoirs previous status. +static HD_MODE_SUB_MODE_T hdModes; ///< HD operations mode. +static BOOL hasROVolBeenWrittenToNV; ///< Boolean flag to indicate whether the RO volume has been written to NV RAM or not. +static BOOL hasDisinfectStatusBeenSet; ///< Boolean flag to indicate whether disinfect has been voided after starting treatment. /*********************************************************************//** * @brief @@ -113,15 +118,17 @@ *************************************************************************/ void initReservoirs( void ) { - activeReservoir.data = (U32)DG_RESERVOIR_1; - fillVolumeTargetMl.data = DEFAULT_FILL_VOLUME_ML; - drainVolumeTargetMl.data = DEFAULT_DRAIN_VOLUME_ML; - targetFillFlowRateLPM = 0.0; - isThisTheFirstCycle = TRUE; - dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; + activeReservoir.data = (U32)DG_RESERVOIR_1; + fillVolumeTargetMl.data = DEFAULT_FILL_VOLUME_ML; + drainVolumeTargetMl.data = DEFAULT_DRAIN_VOLUME_ML; + targetFillFlowRateLPM = 0.0; + isThisTheFirstCycle = TRUE; + dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; + hasROVolBeenWrittenToNV = FALSE; + hasDisinfectStatusBeenSet = FALSE; memset( &reservoirPreviousStatus, 0.0, sizeof( RESERVOIRS_PREVIOUS_STATUS ) * NUM_OF_DG_RESERVOIRS ); -} +} /*********************************************************************//** * @brief @@ -143,19 +150,55 @@ getNVRecord2Driver( GET_CAL_HEATING_RECORD, (U08*)&heatingConstsCalRecord, sizeof( heatingConstsCalRecord ), NUM_OF_CAL_DATA_RSRVRS, ALARM_ID_DG_HEATING_INVALID_CAL_RECORD ); } - + + if ( ( ( DG_MODE_FAUL == getCurrentOperationMode() ) || ( DG_MODE_STAN == getCurrentOperationMode() ) ) && ( FALSE == hasROVolBeenWrittenToNV ) ) + { + BOOL status; + DG_OP_MODE_T prevMode = getPreviousOperationMode(); + + switch( prevMode ) + { + case DG_MODE_GENE: + case DG_MODE_FILL: + case DG_MODE_DRAI: + case DG_MODE_FLUS: + case DG_MODE_HEAT: + case DG_MODE_CHEM: + // Mode is changing, write the RO generated volume to the RTC RAM and set the service flag to FALSE + // since this is not a from a service change + status = setROWaterGeneratedL( getROGeneratedVolumeL() ); + break; + } + + if ( TRUE == status ) + { + hasROVolBeenWrittenToNV = TRUE; + } + } + + if ( ( FALSE == hasDisinfectStatusBeenSet ) && ( MODE_TREA == hdModes.hdMode ) ) + { + BOOL status = setDisinfectStatus( FALSE ); + + if ( TRUE == status ) + { + hasDisinfectStatusBeenSet = TRUE; + } + } + // publish active reservoir, fill/drain volume targets at 1 Hz. if ( ++dataPublishCounter >= RESERVOIR_DATA_PUB_INTERVAL ) { - RESERVOIR_DATA_T data; + RESERVOIR_DATA_T data; + data.activeReservoir = getU32OverrideValue( &activeReservoir ); data.fillToVolumeMl = getU32OverrideValue( &fillVolumeTargetMl ); data.drainToVolumeMl = getU32OverrideValue( &drainVolumeTargetMl ); data.timeReservoirCycleMS = heatersTempCalc.timeReservoirCycleMS; data.timeReservoirFill2SwitchMS = heatersTempCalc.timeReservoirFill2SwitchMS; data.timeUFDecayMS = heatersTempCalc.timeUFDecayMS; data.tempUFFill = heatersTempCalc.tempUFFill; - data.tempReservoirUseActual = getReservoirActualTemperature(); + data.tempReservoirUseActual = getReservoirCurrentTemperature(); data.tempReservoirEndFill = heatersTempCalc.tempReservoirEndFill; data.tempAvgFill = getAvgFillTemperature(); data.tempLastFill = getLastFillTemperature(); @@ -180,10 +223,15 @@ calStatus |= getNVRecord2Driver( GET_CAL_RSRVRS_VOL_RECORD, (U08*)&reservoirsCalRecord, sizeof( reservoirsCalRecord ), NUM_OF_CAL_DATA_RSRVRS, ALARM_ID_DG_RESERVOIRS_INVALID_CAL_RECORD ); - calStatus |= getNVRecord2Driver( GET_CAL_HEATING_RECORD, (U08*)&heatingConstsCalRecord, sizeof( heatingConstsCalRecord ), NUM_OF_CAL_DATA_RSRVRS, ALARM_ID_DG_HEATING_INVALID_CAL_RECORD ); + // Get the calibration values of acid and bicarb + calStatus |= getNVRecord2Driver( GET_CAL_ACID_CONCENTREATES, (U08*)&acidConcentrateCalRecord, sizeof( acidConcentrateCalRecord ), + NUM_OF_ACID_AND_BICARB_NV_DATA_TO_CHECK, ALARM_ID_DG_ACID_CONCENTRATE_INVALID_CAL_RECORD ); + calStatus |= getNVRecord2Driver( GET_CAL_BICARB_CONCENTRATES, (U08*)&bicarbConcentrateCalRecord, sizeof( bicarbConcentrateCalRecord ), + NUM_OF_ACID_AND_BICARB_NV_DATA_TO_CHECK, ALARM_ID_DG_BICARB_CONCENTRATE_INVALID_CAL_RECORD ); + if ( TRUE == calStatus ) { result = SELF_TEST_STATUS_PASSED; @@ -194,8 +242,8 @@ } return result; -} - +} + /*********************************************************************//** * @brief * The setActiveReservoirCmd function sets the given reservoir as active @@ -359,6 +407,9 @@ cmdResponse.rejected = TRUE; cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; + // regardless of mode, clear bad fill flag if HD is asking us to stop filling + setBadAvgConductivityDetectedFlag( FALSE ); + // stop fill command only valid in fill mode if ( DG_MODE_FILL == getCurrentOperationMode() ) { @@ -446,6 +497,35 @@ /*********************************************************************//** * @brief + * The setHDOperationMode function sets HD operation mode value. + * @details Inputs: none + * @details Outputs: hdMode + * @param mode which is HD mode + * @param subMode which is HD submode + * @return none + *************************************************************************/ +void setHDOperationMode( U32 mode, U32 subMode ) +{ + hdModes.hdMode = (HD_OP_MODE_T)mode; + hdModes.hdSubMode = subMode; +} + +/*********************************************************************//** + * @brief + * The getHDOperationMode function copies the provided buffer with the HD + * mode and submode. + * @details Inputs: none + * @details Outputs: hdMode + * @param mode* pointer to the buffer of type HD_MODE_SUB_MODE_T + * @return none + *************************************************************************/ +void getHDOperationMode( HD_MODE_SUB_MODE_T* mode ) +{ + memcpy( mode, &hdModes, sizeof( HD_MODE_SUB_MODE_T ) ); +} + +/*********************************************************************//** + * @brief * The getInactiveReservoir function gets the inactive reservoir. * @details Inputs: activeReservoir * @details Outputs: none @@ -478,6 +558,19 @@ /*********************************************************************//** * @brief + * The getTrimmerHeaterTargetTemperature function returns the trimmer heater + * target temperature in C. + * @details Inputs: none + * @details Outputs: heatersTempCalc + * @return trimmer target temperature in C + *************************************************************************/ +F32 getTrimmerHeaterTargetTemperature( void ) +{ + return heatersTempCalc.tempTargetTrimmer; +} + +/*********************************************************************//** + * @brief * The getReservoirWeight function returns the small filtered weight * of the reservoir's associated load cell. * @details Inputs: none @@ -548,45 +641,6 @@ /*********************************************************************//** * @brief - * The getReservoirActualTemperature function calculates the reservoir's - * actual temperature. - * @details Inputs: none - * @details Outputs: heatersTempCalc - * @return reservoir actual temperature - *************************************************************************/ -F32 getReservoirActualTemperature( void ) -{ - F32 UFTimeConstant = 0.0; - F32 targetFillVolML = getTargetFillVolumeML(); - F32 tempLastFill = getLastFillTemperature(); - F32 tempAvgFill = getAvgFillTemperature(); - F32 UFTauCPerMS = heatingConstsCalRecord.ultrafilterTempTauCPerMin / ( SEC_PER_MIN * MS_PER_SECOND ); - F32 RsrvrTauCPerMS = heatingConstsCalRecord.reservoirTempTauCPerMin / ( SEC_PER_MIN * MS_PER_SECOND ); - - // Only do the calculations if the fill volume is not 0.0 so the final value will not be a nan. - if ( targetFillVolML > NEARLY_ZERO ) - { - heatersTempCalc.timeUFDecayMS = (F32)heatersTempCalc.timeReservoirCycleMS - heatersTempCalc.timeReservoirFillMS; - UFTimeConstant = heatersTempCalc.timeUFDecayMS * UFTauCPerMS; - heatersTempCalc.tempUFFill = tempLastFill + UFTimeConstant; - - F32 ultrafilterPart = ( heatingConstsCalRecord.ultrafilterVolmL / targetFillVolML ) * heatersTempCalc.tempUFFill; - F32 fillPart = ( ( targetFillVolML - heatingConstsCalRecord.ultrafilterVolmL ) / targetFillVolML ) * tempAvgFill; - F32 tempReservoir0Actual = ultrafilterPart + fillPart; - - F32 tempReservoirEndfillActual = tempReservoir0Actual + ( ( heatersTempCalc.timeReservoirFillMS * HALF ) * RsrvrTauCPerMS ); - heatersTempCalc.tempReservoirUseActual = tempReservoirEndfillActual + ( heatersTempCalc.timeReservoirFill2SwitchMS * RsrvrTauCPerMS ); - } - else - { - heatersTempCalc.tempReservoirUseActual = 0.0; - } - - return heatersTempCalc.tempReservoirUseActual; -} - -/*********************************************************************//** - * @brief * The getPrimaryHeaterTargetTemperature function calculates the primary * heater target temperature and returns target temperature value. * @details Inputs: heatingConstsCalRecord @@ -633,22 +687,25 @@ if ( targetROFlowLPM > 0 ) { - priTargetTemp = ( tempTarget * ( tgtTotalFlowLPM / targetROFlowLPM ) ) - ( ENVIRONMENT_TEMPERATURE_C * ( tgtAicdFlowLPM / targetROFlowLPM ) ) - - ( ENVIRONMENT_TEMPERATURE_C * ( tgtBicarbFlowLPM / targetROFlowLPM ) ); + F32 acidTemperature = acidConcentrateCalRecord.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidBottleTemperature; + F32 bicarbTemperature = bicarbConcentrateCalRecord.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbBottleTemperature; + + priTargetTemp = ( tempTarget * ( tgtTotalFlowLPM / targetROFlowLPM ) ) - ( acidTemperature * ( tgtAicdFlowLPM / targetROFlowLPM ) ) - + ( bicarbTemperature * ( tgtBicarbFlowLPM / targetROFlowLPM ) ); } return priTargetTemp; } /*********************************************************************//** * @brief - * The getTrimmerHeaterTargetTemperature function calculates the trimmer - * heater target temperature and returns target temperature value. + * The getReservoirCurrentTemperature function calculates the reservoir's + * current temperature and returns target temperature value. * @details Inputs: heatingConstsCalRecord * @details Outputs: heatersTempCalc * @return primary heater target temperature *************************************************************************/ -F32 getTrimmerHeaterTargetTemperature( void ) +F32 getReservoirCurrentTemperature( void ) { F32 tempRsrvrActual = 0.0; F32 fillROAvgActual = getAvgFillTemperature(); @@ -664,10 +721,13 @@ if ( tgtTotalFlowLPM > 0 ) { - heatersTempCalc.tempFillMixAvgTrimmer = ( fillROAvgActual * ( tgtTotalFlowLPM / targetROFlowLPM ) ) + - ( ENVIRONMENT_TEMPERATURE_C * ( tgtAicdFlowLPM / targetROFlowLPM ) ) + - ( ENVIRONMENT_TEMPERATURE_C * ( tgtBicarbFlowLPM / targetROFlowLPM ) ); + F32 acidTemperature = acidConcentrateCalRecord.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidBottleTemperature; + F32 bicarbTemperature = bicarbConcentrateCalRecord.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbBottleTemperature; + heatersTempCalc.tempFillMixAvgTrimmer = ( fillROAvgActual * ( tgtTotalFlowLPM / targetROFlowLPM ) ) + + ( acidTemperature * ( tgtAicdFlowLPM / targetROFlowLPM ) ) + + ( bicarbTemperature * ( tgtBicarbFlowLPM / targetROFlowLPM ) ); + heatersTempCalc.tempRsrvr0ActualTrimmer = ( ( heatingConstsCalRecord.ultrafilterVolmL / targetFillVolML ) * tempUFFill ) + ( ( ( targetFillVolML - heatingConstsCalRecord.ultrafilterVolmL ) / targetFillVolML ) * heatersTempCalc.tempFillMixAvgTrimmer ); @@ -695,6 +755,34 @@ /*********************************************************************//** * @brief + * The getAcidConcentrateCalRecord function fills the provided buffer with + * the acid concentrate record. + * @details Inputs: acidConcentrateCalRecord + * @details Outputs: none + * @param acidRecord which is the pointer to the provided buffer + * @return none + *************************************************************************/ +void getAcidConcentrateCalRecord( DG_ACID_CONCENTRATES_RECORD_T* acidRecord ) +{ + memcpy( acidRecord, &acidConcentrateCalRecord, sizeof( DG_ACID_CONCENTRATES_RECORD_T ) ); +} + +/*********************************************************************//** + * @brief + * The getBicarbConcentrateCalRecord function fills the provided buffer with + * the bicarb concentrate record. + * @details Inputs: bicarbConcentrateCalRecord + * @details Outputs: none + * @param bicarbRecord which is the pointer to the provided buffer + * @return none + *************************************************************************/ +void getBicarbConcentrateCalRecord( DG_BICARB_CONCENTRATES_RECORD_T* bicarbRecord ) +{ + memcpy( bicarbRecord, &bicarbConcentrateCalRecord, sizeof( DG_BICARB_CONCENTRATES_RECORD_T ) ); +} + +/*********************************************************************//** + * @brief * The hasTargetFillVolumeReached function checks if the target fill volume * for specific reservoir has been reached. * @details Inputs: fillVolumeTargetMl @@ -975,4 +1063,32 @@ return result; } +/*********************************************************************//** + * @brief + * The testTareReservoir function tares a given reservoir. It is assumed + * that the given reservoir has already been drained. + * @details Inputs: drainVolumeTargetMl + * @details Outputs: drainVolumeTargetMl + * @param value ID of reservoir to tare + * @return TRUE if tare successful, FALSE if not + *************************************************************************/ +BOOL testTareReservoir( U32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + if ( value < NUM_OF_DG_RESERVOIRS ) + { + result = TRUE; + tareLoadCellRequest = TRUE; + testSetReservoirDrainVolumeMlOverride( 0 ); + tareLoadCellsAtEmpty( (DG_RESERVOIR_ID_T)value ); + testResetReservoirDrainVolumeMlOverride(); + } + } + + return result; +} + /**@}*/