Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -r549119eae64732f124d22df66de4fc88c56193c0 -r9a45dcbdfae33fc06479d99b8d52bada8f682194 --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 549119eae64732f124d22df66de4fc88c56193c0) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 9a45dcbdfae33fc06479d99b8d52bada8f682194) @@ -2,55 +2,73 @@ // TODO the copyright will be added automatically +#include + #include "DGInterface.h" #include "Dialysis.h" #include "DialInFlow.h" #include "MessageSupport.h" +#include "ModeTreatment.h" #include "OperationModes.h" #include "Reservoirs.h" #include "TaskGeneral.h" #include "Timers.h" // ********** private definitions ********** -#define RESERVOIR_SETTLE_TIME_MS 5000 ///< Allocated time to settle the filled reservoir in milliseconds. +#define RESERVOIR_SETTLE_TIME_MS 5000 ///< Allocated time to settle the filled reservoir in milliseconds. -#define MAX_RESERVOIR_VOLUME_ML 1950.0 ///< Maximum allowed fluid in a reservoir in milliliters. -#define MAX_RESERVOIR_DILUTION 0.15 ///< Maximum reservoir dilution limit. -#define MAX_RESERVOIR_RECIRCULATION 0.1 ///< Maximum reservoir recirculation limit. +#define MAX_RESERVOIR_VOLUME_ML 1950.0 ///< Maximum allowed fluid in a reservoir in milliliters. +#define MAX_RESERVOIR_DILUTION 0.15 ///< Maximum reservoir dilution limit. +#define MAX_RESERVOIR_RECIRCULATION 0.1 ///< Maximum reservoir recirculation limit. -#define MAX_RESERVOIR_DEPLETION_TIME_MS ( 30 * SEC_PER_MIN * MS_PER_SECOND ) ///< Maximum allowed depletion time in milliseconds. -#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_RESERVOIR_DEPLETION_TIME_MS ( 30 * SEC_PER_MIN * MS_PER_SECOND ) ///< Maximum allowed depletion time in milliseconds. +#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 DIALYSATE_FLOW_RATE_100_ML_PER_MIN 0.1 ///< Dialysate flow rate 100 mL/min. +#define DIALYSATE_FLOW_RATE_350_ML_PER_MIN 0.35 ///< Dialysate flow rate 350 mL/min. +#define DIALYSATE_FLOW_RATE_400_ML_PER_MIN 0.4 ///< Dialysate flow rate 400 mL/min. +#define DIALYSATE_FLOW_RATE_500_ML_PER_MIN 0.5 ///< Dialysate flow rate 500 mL/min. +#define DIALYSATE_FLOW_RATE_550_ML_PER_MIN 0.55 ///< Dialysate flow rate 550 mL/min. +#define DIALYSATE_FLOW_RATE_600_ML_PER_MIN 0.6 ///< Dialysate flow rate 600 mL/min. +#define TGT_FILL_FLOW_FOR_DIA_FLOW_100_TO_350_ML_PER_MIN 0.5 ///< Target fill flow rate for dialysate flow rates in between 100 to 350 mL/min. +#define TGT_FILL_FLOW_FOR_DIA_FLOW_550_TO_600_ML_PER_MIN 0.8 ///< Target fill flow rate for dialysate flow rates in between 500 to 600 mL/min. + +#define DIA_FLOW_TO_FILL_FLOW_SECOND_ORDER_COEFF 10.0 ///< Dialysate flow rate to fill flow rate second order coefficient. +#define DIA_FLOW_TO_FILL_FLOW_FIRST_ORDER_COEFF 7.5 ///< Dialysate flow rate to fill flow rate first order coefficient. +#define DIA_FLOW_TO_FILL_FLOW_CONSTANT 2.0 ///< Dialysate flow rate to fill flow rate constant. + // ********** private data ********** /// States of the treatment reservoir management state machine. typedef enum TreatmentReservoirMgmt_States { - TREATMENT_RESERVOIR_MGMT_START_STATE = 0, ///< Treatment reservoir management start state. - TREATMENT_RESERVOIR_MGMT_DRAIN_RESERVOIR_STATE, ///< Treatment reservoir management drain reservoir state. - TREATMENT_RESERVOIR_MGMT_WAIT_TO_FILL_STATE, ///< Treatment reservoir management wait to fill state. - TREATMENT_RESERVOIR_MGMT_FILL_RESERVOIR_STATE, ///< Treatment reservoir management fill state. - TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE, ///< Treatment reservoir management wait for fill to settle state. - TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE, ///< Treatment reservoir management wait for switch to settles state. - NUM_OF_TREATMENT_RESERVOIR_MGMT_STATES ///< Number of treatment reservoir management states. + TREATMENT_RESERVOIR_MGMT_START_STATE = 0, ///< Treatment reservoir management start state. + TREATMENT_RESERVOIR_MGMT_DRAIN_RESERVOIR_STATE, ///< Treatment reservoir management drain reservoir state. + TREATMENT_RESERVOIR_MGMT_WAIT_TO_FILL_STATE, ///< Treatment reservoir management wait to fill state. + TREATMENT_RESERVOIR_MGMT_FILL_RESERVOIR_STATE, ///< Treatment reservoir management fill state. + TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE, ///< Treatment reservoir management wait for fill to settle state. + TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE, ///< Treatment reservoir management wait for switch to settles state. + NUM_OF_TREATMENT_RESERVOIR_MGMT_STATES ///< Number of treatment reservoir management states. } TREATMENT_RESERVOIR_MGMT_STATE_T; // TODO update the init function with all the newest variables -static TREATMENT_RESERVOIR_MGMT_STATE_T reservoirsState; ///< Treatment mode's reservoirs state. -static U32 timeStartMS = 0; ///< Active reservoir start time in milliseconds. -static U32 timeDepletionMS = 0; ///< Active reservoir depletion time in milliseconds. -static F32 volTotalML = 0.0; ///< Active reservoir total volume in milliliters. -static F32 volSpentML = 0.0; ///< Active reservoir spent volume in milliliters. -static U32 reservoirPublicationCounter = 0; ///< Reservoirs data publication timer counter. +static TREATMENT_RESERVOIR_MGMT_STATE_T reservoirsState; ///< Treatment mode's reservoirs state. +static U32 timeStartMS = 0; ///< Active reservoir start time in milliseconds. +static U32 timeDepleteMS = 0; ///< Active reservoir depletion time in milliseconds. +static F32 volTotalML = 0.0; ///< Active reservoir total volume in milliliters. +static F32 volSpentML = 0.0; ///< Active reservoir spent volume in milliliters. +static U32 reservoirPublicationCounter = 0; ///< Reservoirs data publication timer counter. static F32 dilutionLevel = 0.0; static DG_OP_MODE_T dgOpMode = DG_MODE_INIT; static U32 dgSubMode = 0; static U32 timeReservoirInUseMS = 0; static F32 volSpentUFML = 0.0; static DG_RESERVOIR_ID_T activeReservoir; static F32 recirculationLevelPct = 0.0; -static U32 reservoirSwitchTimer = 0; +static U32 reservoirSwitchStartTime = 0; +static U32 timeFillReservoirMS = 0; +static U32 timeWaitToFillMS = 0; // TODO set the reservoirs alarms properties in the alarmsDefs.h so the alarms will be recoverable @@ -59,6 +77,8 @@ // Reservoir management functions static void checkReservoirDepletionTime( void ); static void checkReservoirMaxVolume( void ); +static F32 getTargetFillFlowRateLPM( void ); +static U32 getFillTimeMS( void ); static void publishReservoirData( void ); static TREATMENT_RESERVOIR_MGMT_STATE_T handleReservoirMgmtStartState( void ); @@ -79,9 +99,10 @@ *************************************************************************/ void initReservoirs( void ) { + // TODO don't forget to update the variables and the doxygen reservoirsState = TREATMENT_RESERVOIR_MGMT_START_STATE; timeStartMS = 0; - timeDepletionMS = 0; + timeDepleteMS = 0; volTotalML = 0.0; volSpentML = 0.0; reservoirPublicationCounter = 0; @@ -144,7 +165,7 @@ break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_TREATMENT_RESERVOIR_MANAGEMENT_STATE, (U32)reservoirsState ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_TREATMENT_RESERVOIR_MANAGEMENT_STATE, (U32)reservoirsState ) reservoirsState = TREATMENT_RESERVOIR_MGMT_START_STATE; } @@ -191,6 +212,62 @@ /*********************************************************************//** * @brief + * The getTargetFillFlowRateLPM function sets the target fill flow rate for DG + * based on the target dialysate flow rate. + * @details Inputs: none + * @details Outputs: none + * @return target fill flow rate + *************************************************************************/ +static F32 getTargetFillFlowRateLPM( void ) +{ + F32 fillFlowRate = 0.0; + + F32 dialysateFlow = getDGDialysateFlowRateLMin(); + + if ( ( dialysateFlow >= DIALYSATE_FLOW_RATE_100_ML_PER_MIN ) && ( dialysateFlow <= DIALYSATE_FLOW_RATE_350_ML_PER_MIN ) ) + { + fillFlowRate = TGT_FILL_FLOW_FOR_DIA_FLOW_100_TO_350_ML_PER_MIN; + } + else if ( ( dialysateFlow >= DIALYSATE_FLOW_RATE_400_ML_PER_MIN ) && ( dialysateFlow <= DIALYSATE_FLOW_RATE_500_ML_PER_MIN ) ) + { + fillFlowRate = pow( dialysateFlow, 2 ) * DIA_FLOW_TO_FILL_FLOW_SECOND_ORDER_COEFF - + dialysateFlow * DIA_FLOW_TO_FILL_FLOW_FIRST_ORDER_COEFF + DIA_FLOW_TO_FILL_FLOW_CONSTANT; + } + else if ( ( dialysateFlow >= DIALYSATE_FLOW_RATE_550_ML_PER_MIN ) && ( dialysateFlow <= DIALYSATE_FLOW_RATE_600_ML_PER_MIN ) ) + { + fillFlowRate = TGT_FILL_FLOW_FOR_DIA_FLOW_550_TO_600_ML_PER_MIN; + } + else + { + // TODO fault if the flow is not in range? + } + + return fillFlowRate; +} + +/*********************************************************************//** + * @brief + * The getFillTimeMS function calculates the fill time in milliseconds. + * @details Inputs: none + * @details Outputs: none + * @return target fill flow rate + *************************************************************************/ +static U32 getFillTimeMS( void ) +{ + DG_MIXING_RATIOS_T ratios = getDGMixingRatios(); + F32 targetFillFlowRate = getTargetFillFlowRateLPM(); + F32 totalTargetFillFlow = targetFillFlowRate + ( targetFillFlowRate * ratios.acidMixingRatio ) + + ( targetFillFlowRate * ratios.bicarbMixingRatio ); + + U32 timeFillMS = ( FILL_RESERVOIR_TO_VOLUME_ML / ( ML_PER_LITER * totalTargetFillFlow ) ) * SEC_PER_MIN * MS_PER_SECOND; + + U32 timeTotalFillMS = timeFillMS + ratios.fillPrepTimeMS; + + return timeTotalFillMS; +} + +/*********************************************************************//** + * @brief * The publishReservoirData function publishes reservoirs data during treatment. * @details Inputs: reservoirsPublicationCounter * @details Outputs: reservoirsPublicationCounter @@ -278,26 +355,27 @@ { if ( DG_GEN_IDLE_MODE_STATE_FLUSH_WATER == dgSubMode ) { - cmdStartDGFill( FILL_RESERVOIR_TO_VOLUME_ML ); + cmdStartDGFill( FILL_RESERVOIR_TO_VOLUME_ML, getTargetFillFlowRateLPM() ); } state = TREATMENT_RESERVOIR_MGMT_FILL_RESERVOIR_STATE; } else { - F32 volFreshML = FILL_RESERVOIR_TO_VOLUME_ML - volSpentML; - F32 timeFreshRemaining = volFreshML / ( getDGDialysateFlowRateLMin() * 1000 ); - F32 volMaxUFML = FILL_RESERVOIR_TO_VOLUME_ML * MAX_RESERVOIR_DILUTION; - volSpentUFML = getReservoirUltrafiltrationVol( activeReservoir ); - F32 volRemainingUF = volMaxUFML - volSpentUFML; - F32 timeDepleteRemaining = volRemainingUF / ( volSpentUFML / SEC_PER_MIN ); + F32 volFreshML = FILL_RESERVOIR_TO_VOLUME_ML - volSpentML; + F32 timeFreshRemainingMS = volFreshML / ( getDGDialysateFlowRateLMin() * 1000.0 ); + F32 volMaxUFML = FILL_RESERVOIR_TO_VOLUME_ML * MAX_RESERVOIR_DILUTION; + volSpentUFML = getReservoirUltrafiltrationVol( activeReservoir ); + F32 volRemainingUFML = volMaxUFML - volSpentUFML; + F32 timeDepleteRemainingMS = ( volRemainingUFML / getCurrentUFSetRate() ) * SEC_PER_MIN * MS_PER_SECOND; + timeDepleteMS = MIN( timeFreshRemainingMS, timeDepleteRemainingMS ); + timeWaitToFillMS = timeFillReservoirMS - reservoirSwitchStartTime; - timeDepletionMS = MIN( timeFreshRemaining, timeDepleteRemaining ); + if ( timeDepleteMS >= timeWaitToFillMS ) + { + cmdStartDGFill( FILL_RESERVOIR_TO_VOLUME_ML, getTargetFillFlowRateLPM() ); + } - // TODO what is twait? - - // TODO add the if condition to whether fill should be triggered or not - cmdStartDGFill( FILL_RESERVOIR_TO_VOLUME_ML ); state = TREATMENT_RESERVOIR_MGMT_FILL_RESERVOIR_STATE; } @@ -325,17 +403,14 @@ { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_HD_ACTIVE_RESERVOIR_RECIRCULATION_OUT_OF_RANGE, recirculationLevelPct ) } - // TODO check maximum vfill here shouldn't we do it in the mode fill in DG? - // TODO check fill timeout? - if ( ( DG_MODE_GENE == dgOpMode ) && ( DG_GEN_IDLE_MODE_STATE_FLUSH_WATER == dgSubMode ) ) { // Clear any of the recoverable conditions in case they were raised during the fill or wait to fill clearAlarmCondition( ALARM_ID_HD_ACTIVE_RESERVOIR_RECIRCULATION_OUT_OF_RANGE ); clearAlarmCondition( ALARM_ID_HD_ACTIVE_RESERVOIR_DEPLETION_TIME_OUT ); clearAlarmCondition( ALARM_ID_HD_ACTIVE_RESERVOIR_WEIGHT_OUT_OF_RANGE ); - reservoirSwitchTimer = getMSTimerCount(); + reservoirSwitchStartTime = getMSTimerCount(); state = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE; } @@ -355,7 +430,7 @@ TREATMENT_RESERVOIR_MGMT_STATE_T state = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE; // Wait for the reservoir to settle and then send the commands to switch the active reservoir - if ( TRUE == didTimeout( reservoirSwitchTimer, RESERVOIR_SETTLE_TIME_MS ) ) + if ( TRUE == didTimeout( reservoirSwitchStartTime, RESERVOIR_SETTLE_TIME_MS ) ) { DG_RESERVOIR_ID_T inactiveRes = getDGInactiveReservoir(); @@ -371,8 +446,10 @@ // Get ready for the next delivery volSpentML = 0.0; + timeFillReservoirMS = getFillTimeMS(); + // Wait for used reservoir to settle - reservoirSwitchTimer = getMSTimerCount(); + reservoirSwitchStartTime = getMSTimerCount(); state = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE; } @@ -392,7 +469,7 @@ { TREATMENT_RESERVOIR_MGMT_STATE_T state = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE; - if ( TRUE == didTimeout( reservoirSwitchTimer, RESERVOIR_SETTLE_TIME_MS ) ) + if ( TRUE == didTimeout( reservoirSwitchStartTime, RESERVOIR_SETTLE_TIME_MS ) ) { // Signal dialysis sub-mode to capture final volume of prior reservoir after settling. setFinalReservoirVolume();