/************************************************************************** * * Copyright (c) 2020-2024 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) Darren Cox * @date (last) 15-Feb-2024 * * @author (original) Sean * @date (original) 18-Mar-2020 * ***************************************************************************/ #include // for memcpy() #include "ConcentratePumps.h" #include "DrainPump.h" #include "FPGA.h" #include "Heaters.h" #include "LoadCell.h" #include "MessageSupport.h" #include "ModeDrain.h" #include "ModeFill.h" #include "ModeGenIdle.h" #include "OperationModes.h" #include "Pressures.h" #include "Reservoirs.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "Utilities.h" #include "Valves.h" /** * @addtogroup Reservoirs * @{ */ // ********** private definitions ********** #define MIN_RESERVOIR_VOLUME_ML 0 ///< Minimum reservoir volume in mL. #define DEFAULT_FILL_VOLUME_ML 1500 ///< Default fill volume for treatment in mL. #define MAX_FILL_VOLUME_ML MAX_RESERVOIR_VOLUME_ML ///< Maximum fill volume in mL. #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 -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. #define DATA_PUBLISH_COUNTER_START_COUNT 5 ///< Data publish counter start count. #define NUM_OF_ACID_AND_BICARB_NV_DATA_TO_CHECK 1 ///< Number of acid and bicarb non-volatile data to check. // The gain and offset are for the equation to account for the loss of temperature during the transition from DG to HD and back /* * Qd = 100 mL/min Tloss = 1.5C * Qd = 500 mL/min Tloss = 0.5C */ #define TRIMMER_HEATER_TARGET_TEMP_GAIN -0.002F ///< Trimmer heater target temperature gain. #define TRIMMER_HEATER_TARGET_TEMP_OFFSET 1.7F ///< Trimmer heater target temperature offset. // ********** private data ********** /// Heaters temperature calculation data structure typedef struct { U32 timeReservoirCycleMS; ///< Time reservoir cycle in milliseconds. U32 timeReservoirFill2SwitchMS; ///< Time reservoir fill to switch in milliseconds. F32 timeUFDecayMS; ///< Time ultrafilter decay in milliseconds. F32 timeReservoirFillMS; ///< Time reservoir fill in milliseconds. F32 tempUFFill; ///< Temperature ultrafilter fill in C. F32 tempReservoirUseActual; ///< Temperature actual reservoir in C. F32 tempReservoir0; ///< Temperature reservoir at the beginning of fill in C. F32 tempReservoirEndFill; ///< Temperature reservoir at the end of the fill in C. F32 tempTargetTrimmer; ///< Temperature target trimmer heater in C. F32 flowTargetDialysateLPM; ///< Dialysate target flow rate in L/min. 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 typedef struct { F32 previousReservoirWeightG; ///< Previous reservoir weight in grams. F32 previousDrainFlowML; ///< Previous reservoir drain flow in milliliters. } RESERVOIRS_PREVIOUS_STATUS; /// Non-volatile write structure typedef struct { BOOL hasDisStatusBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect status been written to NV or not. BOOL hasROGenVolBeenWrittenToNV; ///< Boolean flag to indicate whether the generated RO volume has been written to NV or not. } NV_OPS_T; static HEATERS_TEMPERATURE_CALC_DATA_T heatersTempCalc; ///< Heaters temperature calculations data structure. static U32 dataPublishCounter; ///< used to schedule reservoir data publication to CAN bus. static OVERRIDE_U32_T activeReservoir = { 0, 0, 0, 0 }; ///< The active reservoir that serves the HD dialysate need. static OVERRIDE_U32_T fillVolumeTargetMl = { 0, 0, 0, 0 }; ///< The target reservoir fill volume (in mL). static OVERRIDE_U32_T drainVolumeTargetMl = { 0, 0, 0, 0 }; ///< The target reservoir drain volume (in mL). static OVERRIDE_U32_T reservoirDataPublishInterval = { RESERVOIR_DATA_PUB_INTERVAL, RESERVOIR_DATA_PUB_INTERVAL, 0, 0 }; ///< Interval (in ms) at which to publish reservoir data to CAN bus. /// The reservoirs' associate load cell. static LOAD_CELL_ID_T associatedLoadCell[ NUM_OF_DG_RESERVOIRS ] = { LOAD_CELL_RESERVOIR_1_PRIMARY, LOAD_CELL_RESERVOIR_2_PRIMARY }; /// The reservoirs' associate redundant load cell. static LOAD_CELL_ID_T redundantLoadCell[ NUM_OF_DG_RESERVOIRS ] = { LOAD_CELL_RESERVOIR_1_BACKUP, LOAD_CELL_RESERVOIR_2_BACKUP }; /// The reservoirs' lowest weight during draining. static U32 reservoirWeightUnchangeStartTime[ NUM_OF_DG_RESERVOIRS ] = { 0, 0 }; ///< The reservoirs' weight start time when weight stop decreasing. 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 DG_CHEMICALS_FILL_COND_CAL_RECORD_T fillCondCalRecord; ///< Fill conductivities 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 NV_OPS_T nvOps; ///< Non-volatile memory operations. static BOOL transferInProgress; ///< Flag indicates if transfer is in progress between reservoirs. // ********** private function prototypes ********** static void publishReservoirData( void ); /*********************************************************************//** * @brief * The initReservoirs function initializes the Reservoirs module. * @details Inputs: none * @details Outputs: activeReservoir.data, fillVolumeTargetMl.data, * drainVolumeTargetMl.data, targetFillFlowRateLPM, isThisTheFirstCycle, * previousReservoiWeight, dataPublishCounter, nvOps * @return none *************************************************************************/ 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; nvOps.hasDisStatusBeenWrittenToNV = FALSE; nvOps.hasROGenVolBeenWrittenToNV = FALSE; transferInProgress = FALSE; memset( &reservoirPreviousStatus, 0.0, sizeof( RESERVOIRS_PREVIOUS_STATUS ) * NUM_OF_DG_RESERVOIRS ); } /*********************************************************************//** * @brief * The execReservoirs function manages periodic tasks for the Reservoirs module. * @details Inputs: dataPublishCounter, nvOps * @details Outputs: dataPublishCounter, heatingConstsCalRecord, * reservoirsCalRecord, nvOps * @return none *************************************************************************/ void execReservoirs( void ) { // Check if a new calibration is available if ( TRUE == isNewCalibrationRecordAvailable() ) { // Get the new calibration data and check its validity getNVRecord2Driver( GET_CAL_RSRVRS_VOL_RECORD, (U08*)&reservoirsCalRecord, sizeof( reservoirsCalRecord ), NUM_OF_CAL_DATA_RSRVRS, ALARM_ID_DG_RESERVOIRS_INVALID_CAL_RECORD ); getNVRecord2Driver( GET_CAL_HEATING_RECORD, (U08*)&heatingConstsCalRecord, sizeof( heatingConstsCalRecord ), NUM_OF_CAL_DATA_RSRVRS, ALARM_ID_DG_HEATING_INVALID_CAL_RECORD ); getNVRecord2Driver( GET_CAL_FILL_CONDUCTIVITIES_RECORD, (U08*)&fillCondCalRecord, sizeof( fillCondCalRecord ), NUM_OF_ACID_TYPE, ALARM_ID_DG_FILL_CONDUCTIVITIES_INVALID_CAL_RECORD ); } // If the mode is fault or it is standby and the RO volume has not been written already, write it if ( ( ( DG_MODE_FAUL == getCurrentOperationMode() ) || ( DG_MODE_STAN == getCurrentOperationMode() ) ) && ( FALSE == nvOps.hasROGenVolBeenWrittenToNV ) ) { 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. If the previous modes were any of the above modes, it means // it has been generating water. nvOps.hasROGenVolBeenWrittenToNV = setROWaterGeneratedL( getROGeneratedVolumeL() ); break; } } publishReservoirData(); } /*********************************************************************//** * @brief * The execDrainPumpSelfTest function executes the drain pump's self-test. * @details Inputs: none * @details Outputs: none * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) *************************************************************************/ SELF_TEST_STATUS_T execReservoirsSelfTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; BOOL calStatus = FALSE; 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 ); calStatus |= getNVRecord2Driver( GET_CAL_FILL_CONDUCTIVITIES_RECORD, (U08*)&fillCondCalRecord, sizeof( fillCondCalRecord ), NUM_OF_ACID_TYPE, ALARM_ID_DG_FILL_CONDUCTIVITIES_INVALID_CAL_RECORD ); if ( TRUE == calStatus ) { result = SELF_TEST_STATUS_PASSED; } else { result = SELF_TEST_STATUS_FAILED; } return result; } /*********************************************************************//** * @brief * The setActiveReservoirCmd function sets the given reservoir as active * (meaning HD will be drawing from this reservoir). * @details Inputs: none * @details Outputs: Specified reservoir is set as active. * @param resID ID of reservoir to set as active * @return none *************************************************************************/ void setActiveReservoirCmd( DG_RESERVOIR_ID_T resID ) { DG_CMD_RESPONSE_T cmdResponse; cmdResponse.commandID = DG_CMD_SWITCH_RESERVOIR; cmdResponse.rejected = TRUE; cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; // switch reservoir command only valid in generation idle mode if ( DG_MODE_GENE == getCurrentOperationMode() ) { switch ( resID ) { case DG_RESERVOIR_1: activeReservoir.data = (U32)resID; cmdResponse.rejected = FALSE; setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); break; case DG_RESERVOIR_2: activeReservoir.data = (U32)resID; cmdResponse.rejected = FALSE; setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); break; default: // invalid reservoir given - cmd will be NAK'd w/ false result. cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_PARAMETER; break; } } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_MODE; } if ( FALSE == cmdResponse.rejected ) { transferInProgress = FALSE; } sendCommandResponseMsg( &cmdResponse ); } /*********************************************************************//** * @brief * The changeValveSettingCmd function changes valve settings according to * the given setting ID. * @details Inputs: none * @details Outputs: Specified valve settings has been setup. * @param valveSettingID ID of valve setting to change valves to * @return none *************************************************************************/ void changeValveSettingCmd( DG_VALVE_SETTING_ID_T valveSettingID ) { DG_CMD_RESPONSE_T cmdResponse; cmdResponse.commandID = DG_CMD_VALVE_SETTING; cmdResponse.rejected = TRUE; cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; // valve setting command only valid in generation idle mode if ( DG_MODE_GENE == getCurrentOperationMode() ) { switch ( valveSettingID ) { case DG_VALVE_SETTING_R1_TO_R2: cmdResponse.rejected = FALSE; setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); break; case DG_VALVE_SETTING_R2_TO_R1: cmdResponse.rejected = FALSE; setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); break; default: // invalid reservoir given - cmd will be NAK'd w/ false result. cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_PARAMETER; break; } } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_MODE; } if ( FALSE == cmdResponse.rejected ) { transferInProgress = TRUE; } sendCommandResponseMsg( &cmdResponse ); } /*********************************************************************//** * @brief * The startFillCmd function handles a fill command from the HD. * @details Inputs: none * @details Outputs: move to fill mode * @param fillToVolMl target volume (in mL) to fill reservoir to * @param fillTargeteFlowLPM target fill flow rate in L/min * @return none *************************************************************************/ void startFillCmd( U32 fillToVolMl, F32 fillTargetLPM ) { DG_CMD_RESPONSE_T cmdResponse; cmdResponse.commandID = DG_CMD_START_FILL; cmdResponse.rejected = TRUE; cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; // fill command only valid in generation idle mode if ( ( DG_MODE_GENE == getCurrentOperationMode() ) && ( ( DG_GEN_IDLE_MODE_STATE_FLUSH_WATER == getCurrentGenIdleState() ) || ( DG_GEN_IDLE_MODE_STATE_HANDLE_BAD_FILL == getCurrentGenIdleState() ) ) ) { // validate parameters if ( fillToVolMl < MAX_FILL_VOLUME_ML ) { fillVolumeTargetMl.data = fillToVolMl; cmdResponse.rejected = FALSE; if ( ( FALSE == isAlarmActive( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ) ) && // reject moving to fill mode if ( FALSE == isAlarmActive( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME ) ) ) // alarm is active { requestNewOperationMode( DG_MODE_FILL ); } } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_PARAMETER; } } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_MODE; } // Set the fill flow rate targetFillFlowRateLPM = fillTargetLPM; sendCommandResponseMsg( &cmdResponse ); } /*********************************************************************//** * @brief * The stopFillCmd function handles a stop fill command from the HD. * @details Inputs: none * @details Outputs: move to generation idle mode * @return none *************************************************************************/ void stopFillCmd( void ) { DG_CMD_RESPONSE_T cmdResponse; cmdResponse.commandID = DG_CMD_STOP_FILL; 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() ) { requestNewOperationMode( DG_MODE_GENE ); cmdResponse.rejected = FALSE; } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_MODE; } sendCommandResponseMsg( &cmdResponse ); } /*********************************************************************//** * @brief * The startDrainCmd function handles a drain command from the HD. * @details Inputs: none * @details Outputs: Start draining in generation idle mode * @param drainCmd drain command data record * @return none *************************************************************************/ void startDrainCmd( DRAIN_CMD_T drainCmd ) { DG_CMD_RESPONSE_T cmdResponse; cmdResponse.commandID = DG_CMD_START_DRAIN; cmdResponse.rejected = TRUE; cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; // drain command only valid in generation idle mode if ( DG_MODE_GENE == getCurrentOperationMode() ) { // validate parameters if ( drainCmd.targetVolume <= MAX_DRAIN_VOLUME_ML ) { drainVolumeTargetMl.data = drainCmd.targetVolume; tareLoadCellRequest = drainCmd.tareLoadCell; signalDrainModeRinseConcentrateLines( drainCmd.rinseConcentrateLines ); requestNewOperationMode( DG_MODE_DRAI ); cmdResponse.rejected = FALSE; } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_PARAMETER; } } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_MODE; } sendCommandResponseMsg( &cmdResponse ); } /*********************************************************************//** * @brief * The stopDrainCmd function handles a stop drain command from the HD. * @details Inputs: none * @details Outputs: move to generation idle mode * @return none *************************************************************************/ void stopDrainCmd( void ) { DG_CMD_RESPONSE_T cmdResponse; cmdResponse.commandID = DG_CMD_STOP_DRAIN; cmdResponse.rejected = TRUE; cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; // stop drain command only valid in drain mode if ( DG_MODE_DRAI == getCurrentOperationMode() ) { drainVolumeTargetMl.data = 0; requestNewOperationMode( DG_MODE_GENE ); cmdResponse.rejected = FALSE; } else { cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_INVALID_MODE; } sendCommandResponseMsg( &cmdResponse ); } /*********************************************************************//** * @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 ) { // thread protection for queue operations _disable_IRQ(); hdModes.hdMode = (HD_OP_MODE_T)mode; hdModes.hdSubMode = subMode; // release thread protection _enable_IRQ(); } /*********************************************************************//** * @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 ) { mode->hdMode = hdModes.hdMode; mode->hdSubMode = hdModes.hdSubMode; } /*********************************************************************//** * @brief * The getInactiveReservoir function gets the inactive reservoir. * @details Inputs: activeReservoir * @details Outputs: none * @return the currently inactive reservoir. *************************************************************************/ DG_RESERVOIR_ID_T getInactiveReservoir( void ) { DG_RESERVOIR_ID_T inactiveReservoir = DG_RESERVOIR_1; if ( DG_RESERVOIR_1 == getU32OverrideValue( &activeReservoir ) ) { inactiveReservoir = DG_RESERVOIR_2; } return inactiveReservoir; } /*********************************************************************//** * @brief * The isReservoirTransferInProgress function gets the transferInProgress flag. * @details Inputs: transferInProgress * @details Outputs: none * @return reservoir in progress flag. *************************************************************************/ BOOL isReservoirTransferInProgress( void ) { return transferInProgress; } /*********************************************************************//** * @brief * The getTargetDialysateFlowLPM function returns the target dialysate flow * rate in L/min. * @details Inputs: none * @details Outputs: heatersTempCalc * @return target dialysate flow rate in L/min *************************************************************************/ F32 getTargetDialysateFlowLPM( void ) { return heatersTempCalc.flowTargetDialysateLPM; } /*********************************************************************//** * @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 + ( getTargetDialysateFlowLPM() * TRIMMER_HEATER_TARGET_TEMP_GAIN * ML_PER_LITER ) + TRIMMER_HEATER_TARGET_TEMP_OFFSET; } /*********************************************************************//** * @brief * The getReservoirWeight function returns the small filtered weight * of the reservoir's associated load cell. * @details Inputs: none * @details Outputs: none * @param reservoirId id of reservoir to get weight from * @return small filtered weight *************************************************************************/ F32 getReservoirWeight( DG_RESERVOIR_ID_T reservoirId ) { return getLoadCellSmallFilteredWeight( associatedLoadCell[ reservoirId ] ); } /*********************************************************************//** * @brief * The getTargetFillVolumeML function returns the target fill volume in mL. * @details Inputs: none * @details Outputs: none * @return target fill volume in mL *************************************************************************/ U32 getTargetFillVolumeML( void ) { U32 targetFill = fillVolumeTargetMl.data; if ( OVERRIDE_KEY == fillVolumeTargetMl.override ) { targetFill = fillVolumeTargetMl.ovData; } return targetFill; } /*********************************************************************//** * @brief * The getTargetFillFlowRateLPM function returns the target fill flow rate * in L/min. * @details Inputs: none * @details Outputs: none * @return target fill flow rate in L/min *************************************************************************/ F32 getTargetFillFlowRateLPM( void ) { return targetFillFlowRateLPM; } /*********************************************************************//** * @brief * The getTargetDrainVolumeML function returns the target drain volume * in milliliters. * @details Inputs: drainVolumeTargetMl * @details Outputs: none * @return target drain volume in milliliters *************************************************************************/ U32 getTargetDrainVolumeML( void ) { U32 targetDrainVolML = getU32OverrideValue( &drainVolumeTargetMl ); return targetDrainVolML; } /*********************************************************************//** * @brief * The setDialysateHeatingParameters function sets the dialysate heating * parameters. * @details Inputs: none * @details Outputs: heatersTempCalc * @return none *************************************************************************/ void setDialysateHeatingParameters( DG_CMD_DIALYSATE_HEATING_PARAMS_T params ) { heatersTempCalc.timeReservoirCycleMS = params.timeReservoirCycleMS; heatersTempCalc.timeReservoirFill2SwitchMS = params.timeReservoirWait2SwitchMS; 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 ) { resetFillStatusParameters(); isThisTheFirstCycle = FALSE; } // Constantly update the trimmer heater target temperature setHeaterTargetTemperature( DG_TRIMMER_HEATER, getTrimmerHeaterTargetTemperature() ); } /*********************************************************************//** * @brief * The getPrimaryHeaterTargetTemperature function calculates the primary * heater target temperature and returns target temperature value. * @details Inputs: heatingConstsCalRecord * @details Outputs: heatersTempCalc * @return primary heater target temperature *************************************************************************/ F32 getPrimaryHeaterTargetTemperature( void ) { F32 tempTargetC = getTrimmerHeaterTargetTemperature(); F32 priTargetTempC = 0.0F; F32 targetFillVolML = getTargetFillVolumeML(); 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 tgtAcidFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP1_ACID ) / ML_PER_LITER; F32 tgtBicarbFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP2_BICARB ) / ML_PER_LITER; F32 tgtTotalFlowLPM = targetROFlowLPM + tgtAcidFlowLPM + tgtBicarbFlowLPM; if ( TRUE == heatersTempCalc.usePriTargetTempEquation ) { if ( FALSE == isThisTheFirstFill() ) { F32 tempTargetNumerator; F32 tempTargetDenominator; F32 tempReservoirUse; /* * 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 */ tempReservoirUse = getTrimmerHeaterTargetTemperature(); heatersTempCalc.tempReservoirEndFill = tempReservoirUse - ( heatersTempCalc.timeReservoirFill2SwitchMS * RsrvrTauCPerMS ); heatersTempCalc.tempReservoir0 = heatersTempCalc.tempReservoirEndFill - ( ( heatersTempCalc.timeReservoirFillMS * HALF ) * RsrvrTauCPerMS ); heatersTempCalc.timeUFDecayMS = (F32)heatersTempCalc.timeReservoirCycleMS - heatersTempCalc.timeReservoirFillMS; UFTimeConstant = heatersTempCalc.timeUFDecayMS * UFTauCPerMS; heatersTempCalc.tempUFFill = tempLastFill + UFTimeConstant; // TODO If tempUFFill < 25 make it 25 C or apply exponential Tau tempTargetNumerator = heatersTempCalc.tempReservoir0 - ( ( heatingConstsCalRecord.ultrafilterVolmL / targetFillVolML ) * heatersTempCalc.tempUFFill ); tempTargetDenominator = ( ( targetFillVolML - heatingConstsCalRecord.ultrafilterVolmL ) / targetFillVolML ); tempTargetC = tempTargetNumerator / tempTargetDenominator; } else { tempTargetC = getTrimmerHeaterTargetTemperature() + RESERVOIR_EXTRA_TEMPERATURE; } } if ( targetROFlowLPM > 0 ) { #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 priTargetTempC; } /*********************************************************************//** * @brief * 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 getReservoirCurrentTemperature( void ) { F32 tempRsrvrActual = 0.0; F32 fillROAvgActual = getAvgFillTemperature(); F32 targetFillVolML = getTargetFillVolumeML(); F32 UFTauCPerMS = heatingConstsCalRecord.ultrafilterTempTauCPerMin / ( SEC_PER_MIN * MS_PER_SECOND ); F32 tempLastFill = getLastFillTemperature(); F32 tempUFFill = tempLastFill + ( heatersTempCalc.timeUFDecayMS * UFTauCPerMS ); F32 rsrvrTauCPerMS = heatingConstsCalRecord.reservoirTempTauCPerMin / ( SEC_PER_MIN * MS_PER_SECOND ); F32 targetROFlowLPM = getTargetROPumpFlowRateLPM(); F32 tgtAicdFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP1_ACID ) / ML_PER_LITER; F32 tgtBicarbFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP2_BICARB ) / ML_PER_LITER; F32 tgtTotalFlowLPM = targetROFlowLPM + tgtAicdFlowLPM + tgtBicarbFlowLPM; if ( tgtTotalFlowLPM > 0 ) { 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 ); heatersTempCalc.tempRsrvrEndFillTrimmer = heatersTempCalc.tempRsrvr0ActualTrimmer + ( ( heatersTempCalc.timeReservoirFillMS * HALF ) * rsrvrTauCPerMS ); tempRsrvrActual = heatersTempCalc.tempRsrvrEndFillTrimmer + ( ( heatersTempCalc.timeReservoirFillMS * HALF ) * rsrvrTauCPerMS ); } return tempRsrvrActual; } /*********************************************************************//** * @brief * The getReservoirsCalRecord function returns the reservoirs' calibration * record. * @details Inputs: reservoirsCalRecord * @details Outputs: none * @return reservoirs' calibration record *************************************************************************/ DG_RESERVOIR_VOLUME_RECORD_T getReservoirsCalRecord( void ) { return reservoirsCalRecord; } /*********************************************************************//** * @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 getFillChemicalCondRecord function fills the provided buffer with * the fill conductivities record. * @details Inputs: fillCondCalRecord * @details Outputs: none * @param fillRecord which is the pointer to the provided buffer * @return none *************************************************************************/ void getFillChemicalCondRecord( DG_CHEMICALS_FILL_COND_CAL_RECORD_T* fillRecord ) { memcpy( fillRecord, &fillCondCalRecord, sizeof( DG_CHEMICALS_FILL_COND_CAL_RECORD_T ) ); } /*********************************************************************//** * @brief * The hasTargetFillVolumeReached function checks if the target fill volume * for specific reservoir has been reached. * @details Inputs: fillVolumeTargetMl * @details Outputs: none * @param reservoirId reservoir id * @return TRUE if target fill volume has been reached, FALSE if not. *************************************************************************/ BOOL hasTargetFillVolumeBeenReached( DG_RESERVOIR_ID_T reservoirId ) { F32 loadcellWeight1 = getLoadCellSmallFilteredWeight( associatedLoadCell[ reservoirId ] ); F32 loadcellWeight2 = getLoadCellSmallFilteredWeight( redundantLoadCell[ reservoirId ] ); U32 targetFillVolume = getU32OverrideValue( &fillVolumeTargetMl ); BOOL hasTargetReached = FALSE; if ( DG_HANDLE_BAD_FILL_STATE_FLUSH_FILL == getCurrentGenIdleBadFillState() ) { // In bad fill flush fill we fill to 1000 mL targetFillVolume = BAD_FLUSH_FILL_TARGET_VOLUME_ML; } hasTargetReached = ( ( loadcellWeight1 >= targetFillVolume || loadcellWeight2 > ( targetFillVolume + MAX_REDUNDANT_LOAD_CELL_DIFF ) ) ? TRUE : FALSE ); // if redundant load cells too far apart at end of fill, alarm if ( loadcellWeight1 < targetFillVolume ) { // TODO - alarm } return hasTargetReached; } /*********************************************************************//** * @brief * The hasTargetDrainToZeroBeenReached function checks if the specific * reservoir has been drained to zero or it has exceeded the time limit. * @details Inputs: reservoirWeightUnchangeStartTime, associatedLoadCell * @details Outputs: reservoirWeightUnchangeStartTime, tareLoadCellRequest * @param reservoirId reservoir id * @param timeout timeout period when weight remains the same * @return TRUE if target drain volume has been reached or exceeds time limit, FALSE if not. *************************************************************************/ BOOL hasTargetDrainToZeroBeenReached( DG_RESERVOIR_ID_T reservoirId, U32 timeout ) { BOOL result = FALSE; F32 loadcellWeightML = getLoadCellSmallFilteredWeight( associatedLoadCell[ reservoirId ] ); U32 drainPumpFeedbackRPM = getDrainPumpMeasuredRPM( DRAIN_PUMP_HALL_SNSR_FB ); // Check if the drain pump is running, the drain pump usually starts with delay so it might take several // milliseconds to see the RPM feedback is greater than 0. If the feedback is not greater than 0, checking // for drain is not needed if ( drainPumpFeedbackRPM > 0 ) { // Calculate the drain threshold that is needed for the target RPM. For instance, RPM = 500 / 2400 as reference = 0.20833 // so the difference in between the current and previous load cell values must be less than this value to be considered // as reaching towards the bottom of the reservoir. Otherwise, the drain pump must be easily able to drain more fluid // than the threshold every 50 ms. F32 drainThresholdML = ( (F32)getDrainPumpTargetRPM() ) / MAX_DRAIN_RPM_MLP; // Calculate the flow reservoirPreviousStatus[ reservoirId ].previousDrainFlowML = reservoirPreviousStatus[ reservoirId ].previousReservoirWeightG - loadcellWeightML; // If the previous load cell is greater than the current load cell, it means the reservoir is draining and // update the previous load cell value if ( reservoirPreviousStatus[ reservoirId ].previousReservoirWeightG > loadcellWeightML ) { reservoirPreviousStatus[ reservoirId ].previousReservoirWeightG = loadcellWeightML; } // If the flow is less than the threshold and the time has not been set, set the timer // If the flow is greater than the threshold and timer has been set, 0 it because the flow is out of range and we are not // ready to consider this the end of the reservoir flow // If the wait for drain to steady has elapsed and the drain pump inlet pressure sensor is indicating and increased vacuum, // signal the drain is complete if ( ( reservoirPreviousStatus[ reservoirId ].previousDrainFlowML <= drainThresholdML ) && ( 0 == reservoirWeightUnchangeStartTime[ reservoirId ] ) ) { reservoirWeightUnchangeStartTime[ reservoirId ] = getMSTimerCount(); } else if ( reservoirPreviousStatus[ reservoirId ].previousDrainFlowML > drainThresholdML ) { reservoirWeightUnchangeStartTime[ reservoirId ] = 0; } else if ( ( TRUE == didTimeout( reservoirWeightUnchangeStartTime[ reservoirId ], timeout ) && ( getMeasuredDGPressure( PRESSURE_SENSOR_DRAIN_PUMP_INLET ) > MIN_DRAIN_INLET_PSI_EMPTY ) ) ) { result = TRUE; } } return result; } /*********************************************************************//** * @brief * The hasTargetDrainVolumeReached function checks if the target drain volume * for specific reservoir has been reached or exceeded time limit. * @details Inputs: reservoirWeightUnchangeStartTime, associatedLoadCell * @details Outputs: reservoirWeightUnchangeStartTime * @param reservoirId reservoir id * @param targetVolumeML target volume to be drained to in milliliters * @param timeoutMS timeout period when weight remains the same in milliseconds * @return TRUE if target drain volume has been reached or exceeds time * limit, FALSE if not. *************************************************************************/ BOOL hasTargetDrainToVolumeBeenReached( DG_RESERVOIR_ID_T reservoirId, U32 targetVolumeML, U32 timeoutMS ) { // NOTE: this function is used for testing only. No timeout is implemented right now. BOOL result = FALSE; F32 loadcellWeightML = getLoadCellSmallFilteredWeight( associatedLoadCell[ reservoirId ] ); U32 drainPumpFeedbackRPM = getDrainPumpMeasuredRPM( DRAIN_PUMP_HALL_SNSR_FB ); if ( drainPumpFeedbackRPM > 0 ) { if ( loadcellWeightML < (F32)targetVolumeML ) { result = TRUE; } } return result; } /*********************************************************************//** * @brief * The tareLoadCellsAtEmpty function tares the load cells for the given * reservoir when empty and tare request is pending. * @details Inputs: tareLoadCellRequest * @details Outputs: tareLoadCellRequest * @param reservoirId ID of reservoir to tare * @return none *************************************************************************/ void tareLoadCellsAtEmpty( DG_RESERVOIR_ID_T reservoirId ) { U32 targetDrainVolume = getU32OverrideValue( &drainVolumeTargetMl ); if ( TRUE == tareLoadCellRequest ) { tareLoadCellRequest = FALSE; if ( MIN_RESERVOIR_VOLUME_ML == targetDrainVolume ) { tareLoadCell( associatedLoadCell[ reservoirId ] ); tareLoadCell( redundantLoadCell[ reservoirId ] ); } } } /*********************************************************************//** * @brief * The isReservoirTarePending function determines whether a reservoir tare * request is currently pending. * @details Inputs: tareLoadCellRequest * @details Outputs: none * @return tareLoadCellRequest *************************************************************************/ BOOL isReservoirTarePending( void ) { return tareLoadCellRequest; } /*********************************************************************//** * @brief * The initDrainParameters function initializes the drain parameters. * @details Inputs: none * @details Outputs: reservoirWeightUnchangeStartTime, previousReservoirWeight * associatedLoadCell * @param reservoirId the ID of the reservoir that it drain parameters have * to be initialized * @return none *************************************************************************/ void initDrainParameters( DG_RESERVOIR_ID_T reservoirId ) { // 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.0F; } /*********************************************************************//** * @brief * The publishReservoirData function publishes reservoir data * at the set interval. * @details Inputs: dataPublishCounter, reservoirDataPublishInterval * @details Outputs: data * @return none *************************************************************************/ static void publishReservoirData( void ) { // publish active reservoir, fill/drain volume targets at 1 Hz. if ( ++dataPublishCounter >= getU32OverrideValue( &reservoirDataPublishInterval ) ) { 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 = getReservoirCurrentTemperature(); data.tempReservoirEndFill = heatersTempCalc.tempReservoirEndFill; data.tempAvgFill = getAvgFillTemperature(); data.tempLastFill = getLastFillTemperature(); data.timereservoirFill = heatersTempCalc.timeReservoirFillMS; broadcastData( MSG_ID_DG_RESERVOIRS_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( RESERVOIR_DATA_T ) ); dataPublishCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetDGActiveReservoirOverride function overrides the active reservoir. * @details Inputs: activeReservoir * @details Outputs: activeReservoir * @param value override active reservoir ID * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetDGActiveReservoirOverride( DG_RESERVOIR_ID_T value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; activeReservoir.ovData = value; activeReservoir.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The activeReservoir function resets the override of the active reservoir. * @details Inputs: activeReservoir * @details Outputs: activeReservoir * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetDGActiveReservoirOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; activeReservoir.override = OVERRIDE_RESET; activeReservoir.ovData = activeReservoir.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetReservoirFillVolumeMlOverride function overrides the target * reservoir fill volume (in mL). * @details Inputs: fillVolumeTargetMl * @details Outputs: fillVolumeTargetMl * @param value override target reservoir fill volume (in mL) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetReservoirFillVolumeMlOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; fillVolumeTargetMl.ovData = value; fillVolumeTargetMl.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetReservoirFillVolumeMlOverride function resets the override of * the target reservoir fill volume. * @details Inputs: fillVolumeTargetMl * @details Outputs: fillVolumeTargetMl * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetReservoirFillVolumeMlOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; fillVolumeTargetMl.override = OVERRIDE_RESET; fillVolumeTargetMl.ovData = fillVolumeTargetMl.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetReservoirDrainVolumeMlOverride function overrides the target * reservoir drain volume (in mL). * @details Inputs: drainVolumeTargetMl * @details Outputs: drainVolumeTargetMl * @param value override target reservoir drain volume (in mL) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetReservoirDrainVolumeMlOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; drainVolumeTargetMl.ovData = value; drainVolumeTargetMl.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetReservoirDrainVolumeMlOverride function resets the override of * the target reservoir drain volume. * @details Inputs: drainVolumeTargetMl * @details Outputs: drainVolumeTargetMl * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetReservoirDrainVolumeMlOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; drainVolumeTargetMl.override = OVERRIDE_RESET; drainVolumeTargetMl.ovData = drainVolumeTargetMl.ovInitData; } 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; } /*********************************************************************//** * @brief * The testSetDialysateMixingRatios function sets the acid and bicarb * dialysate mixing ratios. * @details Inputs: none * @details Outputs: acidConcentrateCalRecord, bicarbConcentrateCalRecord * @param acid which is the acid dialysate mixing ratio * @param bicarb which is the bicarb dialysate mixing ratio * @return TRUE if set successful, FALSE if not *************************************************************************/ BOOL testSetDialysateMixingRatios( F32 acid, F32 bicarb ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; acidConcentrateCalRecord.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio = acid; bicarbConcentrateCalRecord.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio = bicarb; } return result; } /*********************************************************************//** * @brief * The testSetReservoirDataPublishIntervalOverride function overrides the * reservoir data publish interval. * @details Inputs: reservoirDataPublishInterval * @details Outputs: reservoirDataPublishInterval * @param value override reservoir data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetReservoirDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_GENERAL_INTERVAL; reservoirDataPublishInterval.ovData = intvl; reservoirDataPublishInterval.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetReservoirDataPublishIntervalOverride function resets the * override of the reservoir publish interval. * @details Inputs: reservoirDataPublishInterval * @details Outputs: reservoirDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetReservoirDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { reservoirDataPublishInterval.override = OVERRIDE_RESET; reservoirDataPublishInterval.ovData = reservoirDataPublishInterval.ovInitData; result = TRUE; } return result; } /**@}*/