Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -r696e732c9742535a58b9c65f243df7cd797d1423 -rb4f8e2229eb0a08cdce5fdd2e0a14ca25d792946 --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 696e732c9742535a58b9c65f243df7cd797d1423) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision b4f8e2229eb0a08cdce5fdd2e0a14ca25d792946) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2020-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2023 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 Reservoirs.c * * @author (last) Dara Navaei -* @date (last) 21-Sep-2022 +* @date (last) 07-Mar-2023 * * @author (original) Sean * @date (original) 18-Mar-2020 @@ -19,6 +19,7 @@ #include "ConcentratePumps.h" #include "DrainPump.h" +#include "FPGA.h" #include "Heaters.h" #include "LoadCell.h" #include "MessageSupport.h" @@ -30,7 +31,8 @@ #include "Reservoirs.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" -#include "Timers.h" +#include "Timers.h" +#include "Utilities.h" #include "Valves.h" /** @@ -46,7 +48,7 @@ #define DEFAULT_DRAIN_VOLUME_ML 0 ///< Default drain volume in mL. #define MAX_DRAIN_VOLUME_ML MAX_RESERVOIR_VOLUME_ML ///< Maximum drain volume in mL. -#define MIN_DRAIN_INLET_PSI_EMPTY -3.0F ///< Minimum drain inlet pressure (in PSI) to indicate reservoir is empty while drain pump on. +#define MIN_DRAIN_INLET_PSI_EMPTY -4.5F ///< Minimum drain inlet pressure (in PSI) to indicate reservoir is empty while drain pump on. #define RESERVOIR_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< interval (ms/task time) at which the reservoir data is published on the CAN bus. #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. @@ -71,6 +73,7 @@ F32 tempRsrvr0ActualTrimmer; ///< Temperature actual reservoir in C. F32 tempFillMixAvgTrimmer; ///< Temperature fill mix average trimmer in C. F32 tempRsrvrEndFillTrimmer; ///< Temperature reservoir end fill trimmer in C. + BOOL usePriTargetTempEquation; ///< Use primary heater target temperature enquation flag. } HEATERS_TEMPERATURE_CALC_DATA_T; /// Reservoirs previous status @@ -178,12 +181,6 @@ } } - if ( ( FALSE == nvOps.hasDisStatusBeenWrittenToNV ) && ( MODE_TREA == hdModes.hdMode ) ) - { - // If the disinfect status has not been set and we are in treatment mode, void the disinfect (chemical or heat disinfects) - nvOps.hasDisStatusBeenWrittenToNV = setDisinfectStatus( FALSE ); - } - // publish active reservoir, fill/drain volume targets at 1 Hz. if ( ++dataPublishCounter >= RESERVOIR_DATA_PUB_INTERVAL ) { @@ -628,6 +625,7 @@ heatersTempCalc.timeReservoirFillMS = params.timeReservoirFillMS; heatersTempCalc.tempTargetTrimmer = params.trimmerTargetTemperature; heatersTempCalc.flowTargetDialysateLPM = params.dialysateFlowLPM; + heatersTempCalc.usePriTargetTempEquation = params.usePriTargetTempEquation; // Check if this is the first time that the dialysate heating parameters are set in DG if ( TRUE == isThisTheFirstCycle ) @@ -647,52 +645,76 @@ *************************************************************************/ F32 getPrimaryHeaterTargetTemperature( void ) { - // TODO once the equations are solidified, add the equations as comments to the lines - F32 tempTarget = 0.0; - F32 priTargetTemp = 0.0; + F32 tempTargetC = heatersTempCalc.tempTargetTrimmer; + F32 priTargetTempC = 0.0F; F32 targetFillVolML = getTargetFillVolumeML(); - F32 UFTimeConstant = 0.0; + F32 UFTimeConstant = 0.0F; F32 tempLastFill = getLastFillTemperature(); F32 UFTauCPerMS = heatingConstsCalRecord.ultrafilterTempTauCPerMin / ( SEC_PER_MIN * MS_PER_SECOND ); F32 RsrvrTauCPerMS = heatingConstsCalRecord.reservoirTempTauCPerMin / ( SEC_PER_MIN * MS_PER_SECOND ); F32 targetROFlowLPM = getTargetROPumpFlowRateLPM(); - F32 tgtAicdFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP1_ACID ) / ML_PER_LITER; + F32 tgtAcidFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP1_ACID ) / ML_PER_LITER; F32 tgtBicarbFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP2_BICARB ) / ML_PER_LITER; - F32 tgtTotalFlowLPM = targetROFlowLPM + tgtAicdFlowLPM + tgtBicarbFlowLPM; + F32 tgtTotalFlowLPM = targetROFlowLPM + tgtAcidFlowLPM + tgtBicarbFlowLPM; - if ( FALSE == isThisTheFirstFill() ) + if ( TRUE == heatersTempCalc.usePriTargetTempEquation ) { - F32 tempTargetNumerator; - F32 targetTempDenominator; - F32 tempReservoirUse; + if ( FALSE == isThisTheFirstFill() ) + { + F32 tempTargetNumerator; + F32 tempTargetDenominator; + F32 tempReservoirUse; - tempReservoirUse = heatersTempCalc.tempTargetTrimmer + RESERVOIR_EXTRA_TEMPERATURE; - heatersTempCalc.tempReservoirEndFill = tempReservoirUse - ( heatersTempCalc.timeReservoirFill2SwitchMS * RsrvrTauCPerMS ); - heatersTempCalc.tempReservoir0 = heatersTempCalc.tempReservoirEndFill - ( ( heatersTempCalc.timeReservoirFillMS * HALF ) * RsrvrTauCPerMS ); + /* + * 1. T_rsrvr_use = T_target + * 2. T_rsrvr_end_fill = T_rsrvr_use - (t_rsrvr_fill_2_switch * Tau_rsrvr) + * 3. T_rsrvr_0 = T_rsrvr_end_fill - (t_rsrvr_fill/2 * Tau_rsrvr) + * 4. T_UF_fill = T_last_fill + [(t_rsrvr_cycle - t_rsrvr_fill) * Tau_UF] + * 5. T_numerator = T_rsrvr_0 - [(V_UF / V_fill) * T_UF_fill] + * 6. T_denominator = (V_fill - V_UF) / V_fill + * 7. T_mix_target = T_numerator / T_denominator + * 8. T_primary_target = T_mix_target * (Q_total / Q_RO) - (Q_acid / Q_RO) * T_acid - (Q_bicarb / Q_RO) * T_bicarb + */ - heatersTempCalc.timeUFDecayMS = (F32)heatersTempCalc.timeReservoirCycleMS - heatersTempCalc.timeReservoirFillMS; - UFTimeConstant = heatersTempCalc.timeUFDecayMS * UFTauCPerMS; - heatersTempCalc.tempUFFill = tempLastFill + UFTimeConstant; + tempReservoirUse = heatersTempCalc.tempTargetTrimmer; + heatersTempCalc.tempReservoirEndFill = tempReservoirUse - ( heatersTempCalc.timeReservoirFill2SwitchMS * RsrvrTauCPerMS ); + heatersTempCalc.tempReservoir0 = heatersTempCalc.tempReservoirEndFill - ( ( heatersTempCalc.timeReservoirFillMS * HALF ) * RsrvrTauCPerMS ); - tempTargetNumerator = heatersTempCalc.tempReservoir0 - ( ( heatingConstsCalRecord.ultrafilterVolmL / targetFillVolML ) * heatersTempCalc.tempUFFill ); - targetTempDenominator = ( ( targetFillVolML - heatingConstsCalRecord.ultrafilterVolmL ) / targetFillVolML ); - tempTarget = tempTargetNumerator / targetTempDenominator; + heatersTempCalc.timeUFDecayMS = (F32)heatersTempCalc.timeReservoirCycleMS - heatersTempCalc.timeReservoirFillMS; + UFTimeConstant = heatersTempCalc.timeUFDecayMS * UFTauCPerMS; + heatersTempCalc.tempUFFill = tempLastFill + UFTimeConstant; + + tempTargetNumerator = heatersTempCalc.tempReservoir0 - ( ( heatingConstsCalRecord.ultrafilterVolmL / targetFillVolML ) * heatersTempCalc.tempUFFill ); + tempTargetDenominator = ( ( targetFillVolML - heatingConstsCalRecord.ultrafilterVolmL ) / targetFillVolML ); + tempTargetC = tempTargetNumerator / tempTargetDenominator; + } + else + { + tempTargetC = heatersTempCalc.tempTargetTrimmer + RESERVOIR_EXTRA_TEMPERATURE; + } } - else - { - tempTarget = heatersTempCalc.tempTargetTrimmer + RESERVOIR_EXTRA_TEMPERATURE; - } if ( targetROFlowLPM > 0 ) { - 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 ) ); +#ifndef _RELEASE_ + if ( ( HW_CONFIG_BETA == getHardwareConfigStatus() ) || + ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_USING_TPO_FOR_PRIMARY_HEATER_CONTROL ) ) ) + { + // In the later DVT units, TD2 is used for setting the primary heater target temperature. But in the older DVTs as well as the Beta units + // the target temperature is calculated by estimating the temperature of the dialysates and volume that is being mixed into the RO water + F32 acidTemperature = acidConcentrateCalRecord.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidBottleTemperature; + F32 bicarbTemperature = bicarbConcentrateCalRecord.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbBottleTemperature; + priTargetTempC = ( tempTargetC * ( tgtTotalFlowLPM / targetROFlowLPM ) ) - ( acidTemperature * ( tgtAcidFlowLPM / targetROFlowLPM ) ) - + ( bicarbTemperature * ( tgtBicarbFlowLPM / targetROFlowLPM ) ); + } + else +#endif + { + priTargetTempC = tempTargetC; + } } - return priTargetTemp; + return priTargetTempC; } /*********************************************************************//** @@ -817,8 +839,7 @@ *************************************************************************/ BOOL hasTargetDrainVolumeBeenReached( DG_RESERVOIR_ID_T reservoirId, U32 timeout ) { - BOOL result = FALSE; - + BOOL result = FALSE; F32 loadcellWeightML = getLoadCellSmallFilteredWeight( associatedLoadCell[ reservoirId ] ); U32 drainPumpFeedbackRPM = getDrainPumpMeasuredRPM(); @@ -919,7 +940,7 @@ // Set the start time to 0 for the next drain and update the current load cell reading to the previous value reservoirWeightUnchangeStartTime[ reservoirId ] = 0; reservoirPreviousStatus[ reservoirId ].previousReservoirWeightG = getLoadCellSmallFilteredWeight( associatedLoadCell[ reservoirId ] ); - reservoirPreviousStatus[ reservoirId ].previousDrainFlowML = 0.0; + reservoirPreviousStatus[ reservoirId ].previousDrainFlowML = 0.0F; }