Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -r5a61bccd959265c00e5276ba23391198ca82b6dd -rddc20ee738e1c688d5a16e2f3f94613d3663597d --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 5a61bccd959265c00e5276ba23391198ca82b6dd) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision ddc20ee738e1c688d5a16e2f3f94613d3663597d) @@ -1,386 +1,994 @@ -/**********************************************************************//** - * - * Copyright (c) 2020 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 - * - * @date 18-Mar-2020 - * @author S. Nash - * - * @brief Reservoirs service module. Maintains reservoir set points and \n - * handles reservoir related commands from the HD. - * - **************************************************************************/ +/************************************************************************** +* +* Copyright (c) 2020-2022 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) 23-May-2022 +* +* @author (original) Sean +* @date (original) 18-Mar-2020 +* +***************************************************************************/ + +#include // for memcpy() + +#include "ConcentratePumps.h" +#include "DrainPump.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 "Valves.h" -#include // for memcpy() - -#include "OperationModes.h" -#include "SystemCommMessages.h" -#include "Reservoirs.h" - /** * @addtogroup Reservoirs * @{ */ // ********** private definitions ********** -#define MIN_RESERVOIR_VOLUME_ML 0 ///< Minimum reservoir volume in mL. -#define MAX_RESERVOIR_VOLUME_ML 3000 ///< Maximum reservoir volume in mL. -#define DEFAULT_FILL_VOLUME_ML 1500 ///< Default fill volume for treatment in mL. -#define DISINFECT_FILL_VOLUME_ML 2900 ///> Fill volume for disinfection in mL. -#define MAX_FILL_VOLUME_ML MAX_RESERVOIR_VOLUME_ML ///> Maximum fill volume in mL. -#define DEFAULT_DRAIN_VOLUME_ML 100 ///> Default drain volume in mL. -#define MAX_DRAIN_VOLUME_ML MAX_RESERVOIR_VOLUME_ML ///> Maximum drain volume in mL. -#define MIN_DRAIN_VOLUME_ML 20 ///> Minimum drain volume in mL. +#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 -3.0F ///< 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 ENVIRONMENT_TEMPERATURE_C 23.5F ///< Device's environment temperature in C. // TODo add this to the cal records + +// ********** 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. +} 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; + +static HEATERS_TEMPERATURE_CALC_DATA_T heatersTempCalc; ///< Heaters temperature calculations data structure. +static U32 dataPublishCounter; ///< used to schedule reservoir data publication to CAN bus. -// ********** private data ********** - -static OVERRIDE_U32_T activeReservoir = { 0, 0, 0, 0 }; ///< The active reservoir that the DG is filling/draining/etc. -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). - -// ********** private function prototypes ********** - +static OVERRIDE_U32_T activeReservoir = { 0, 0, 0, 0 }; ///< The active reservoir that the DG is filling/draining/etc. +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). + +/// 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 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. + /*********************************************************************//** * @brief * The initReservoirs function initializes the Reservoirs module. - * @details - * Inputs : none - * Outputs : Reservoirs module initialized. - * @param none + * @details Inputs: none + * @details Outputs: activeReservoir.data, fillVolumeTargetMl.data, + * drainVolumeTargetMl.data, targetFillFlowRateLPM, isThisTheFirstCycle, + * previousReservoiWeight, dataPublishCounter * @return none *************************************************************************/ void initReservoirs( void ) -{ - activeReservoir.data = RESERVOIR_1; - fillVolumeTargetMl.data = DEFAULT_FILL_VOLUME_ML; - drainVolumeTargetMl.data = DEFAULT_DRAIN_VOLUME_ML; +{ + 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; + + memset( &reservoirPreviousStatus, 0.0, sizeof( RESERVOIRS_PREVIOUS_STATUS ) * NUM_OF_DG_RESERVOIRS ); } -BOOL setActiveReservoir( RESERVOIR_ID_T resID ) -{ - // switch reservoir command only valid in re-circulate mode - if ( MODE_CIRC == getCurrentOperationMode() ) - { - // validate parameters - if ( resID < NUM_OF_RESERVOIRS ) - { - activeReservoir.data = (U32)resID; - } - } -} - /*********************************************************************//** * @brief - * The startFill function handles a fill command from the HD. - * @details - * Inputs : none - * Outputs : move to fill mode - * @param fillToVolMl : Target volume (in mL) to fill reservoir to. - * @return TRUE if fill command successful, FALSE if not. + * The execReservoirs function manages periodic tasks for the Reservoirs module. + * @details Inputs: reservoirDataPublicationTimerCounter + * @details Outputs: reservoirDataPublicationTimerCounter, heatingCalRecord, + * reservoirsCalRecord + * @return none *************************************************************************/ -BOOL startFill( U32 fillToVolMl ) -{ - BOOL result = FALSE; +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 ); + } - // fill command only valid in re-circulate mode - if ( MODE_CIRC == getCurrentOperationMode() ) - { - // validate parameters - if ( fillToVolMl < MAX_FILL_VOLUME_ML ) - { - fillVolumeTargetMl.data = fillToVolMl; - requestNewOperationMode( MODE_FILL ); - } + // publish active reservoir, fill/drain volume targets at 1 Hz. + if ( ++dataPublishCounter >= RESERVOIR_DATA_PUB_INTERVAL ) + { + 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; } - - return result; +} + +/*********************************************************************//** + * @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 ); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + + return result; } /*********************************************************************//** * @brief - * The stopFill function handles a stop fill command from the HD. - * @details - * Inputs : none - * Outputs : move to standby mode - * @return TRUE if stop fill command successful, FALSE if not. + * 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 *************************************************************************/ -BOOL stopFill( void ) -{ - BOOL result = FALSE; +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; - // stop fill command only valid in fill mode - if ( MODE_FILL == getCurrentOperationMode() ) - { - requestNewOperationMode( MODE_CIRC ); - } + 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; - return result; -} - -/*********************************************************************//** - * @brief - * The startDrain function handles a fill command from the HD - * @details - * Inputs : none - * Outputs : - * @param drainToVolMl : Target volume (in mL) to drain reservoir to. - * @return TRUE if drain command successful, FALSE if not. - *************************************************************************/ -BOOL startDrain( U32 drainToVolMl ) -{ - BOOL result = FALSE; - - // drain command only valid in re-circulate mode - if ( MODE_CIRC == getCurrentOperationMode() ) - { - // validate parameters - if ( ( drainToVolMl > MIN_DRAIN_VOLUME_ML ) && ( drainToVolMl < MAX_DRAIN_VOLUME_ML ) ) - { - drainVolumeTargetMl.data = drainToVolMl; - requestNewOperationMode( MODE_DRAI ); - } - } - - return result; -} - -/*********************************************************************//** - * @brief - * The stopDrain function handles a stop drain command from the HD. - * @details - * Inputs : none - * Outputs : move to standby mode - * @return TRUE if stop drain command successful, FALSE if not. - *************************************************************************/ -BOOL stopDrain( void ) -{ - BOOL result = FALSE; - - // stop drain command only valid in drain mode - if ( MODE_DRAI == getCurrentOperationMode() ) - { - requestNewOperationMode( MODE_CIRC ); - } - - return result; -} - -/************************************************************************* - * GET SUPPORT FUNCTIONS - *************************************************************************/ - -/*********************************************************************//** - * @brief - * The getActiveReservoir function gets the active reservoir. - * @details - * Inputs : activeReservoir - * Outputs : none - * @return the currently active reservoir. - *************************************************************************/ -RESERVOIR_ID_T getActiveReservoir( void ) -{ - RESERVOIR_ID_T result = (RESERVOIR_ID_T)activeReservoir.data; - - if ( OVERRIDE_KEY == activeReservoir.override ) - { - result = (RESERVOIR_ID_T)activeReservoir.ovData; - } - - return result; -} - -/*********************************************************************//** - * @brief - * The getReservoirFillVolumeTargetMl function gets the reservoir fill \n - * volume (in mL). - * @details - * Inputs : fillVolumeTargetMl - * Outputs : none - * @return the current target reservoir fill volume (in mL). - *************************************************************************/ -U32 getReservoirFillVolumeTargetMl( void ) -{ - U32 result = (RESERVOIR_ID_T)fillVolumeTargetMl.data; - - if ( OVERRIDE_KEY == fillVolumeTargetMl.override ) - { - result = (RESERVOIR_ID_T)fillVolumeTargetMl.ovData; - } - - return result; -} - -/*********************************************************************//** - * @brief - * The getReservoirDrainVolumeTargetMl function gets the reservoir drain \n - * volume (in mL). - * @details - * Inputs : drainVolumeTargetMl - * Outputs : none - * @return the current target reservoir drain volume (in mL). - *************************************************************************/ -U32 getReservoirDrainVolumeTargetMl( void ) -{ - U32 result = (RESERVOIR_ID_T)drainVolumeTargetMl.data; - - if ( OVERRIDE_KEY == drainVolumeTargetMl.override ) - { - result = (RESERVOIR_ID_T)drainVolumeTargetMl.ovData; - } - - return result; -} - - -/************************************************************************* - * TEST SUPPORT FUNCTIONS - *************************************************************************/ - - -/*********************************************************************//** - * @brief - * The testSetDGActiveReservoirOverride function overrides the active \n - * reservoir. - * @details - * Inputs : activeReservoir - * Outputs : activeReservoir - * @param value : override active reservoir ID. - * @return TRUE if override successful, FALSE if not - *************************************************************************/ -BOOL testSetDGActiveReservoirOverride( 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 \n - * reservoir. - * @details - * Inputs : activeReservoir - * 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 \n - * reservoir fill volume (in mL). - * @details - * Inputs : fillVolumeTargetMl - * 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 \n - * target reservoir fill volume. - * @details - * Inputs : fillVolumeTargetMl - * 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 \n - * reservoir drain volume (in mL). - * @details - * Inputs : drainVolumeTargetMl - * 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 \n - * target reservoir drain volume. - * @details - * Inputs : drainVolumeTargetMl - * 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; -} - -/**@}*/ + 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; + } + + 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; + } + + 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 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 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; +} + +/*********************************************************************//** + * @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 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; + + // Check if this is the first time that the dialysate heating parameters are set in DG + if ( TRUE == isThisTheFirstCycle ) + { + resetFillStatusParameters(); + isThisTheFirstCycle = FALSE; + } +} + +/*********************************************************************//** + * @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 * 0.5 ) * 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 + * @details Outputs: heatersTempCalc + * @return primary heater target temperature + *************************************************************************/ +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 targetFillVolML = getTargetFillVolumeML(); + F32 UFTimeConstant = 0.0; + 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 tgtBicarbFlowLPM = getConcentratePumpTargetFlowMLPM( CONCENTRATEPUMPS_CP2_BICARB ) / ML_PER_LITER; + F32 tgtTotalFlowLPM = targetROFlowLPM + tgtAicdFlowLPM + tgtBicarbFlowLPM; + + if ( FALSE == isThisTheFirstFill() ) + { + F32 tempTargetNumerator; + F32 targetTempDenominator; + F32 tempReservoirUse; + + tempReservoirUse = heatersTempCalc.tempTargetTrimmer + RESERVOIR_EXTRA_TEMPERATURE; + heatersTempCalc.tempReservoirEndFill = tempReservoirUse - ( heatersTempCalc.timeReservoirFill2SwitchMS * RsrvrTauCPerMS ); + heatersTempCalc.tempReservoir0 = heatersTempCalc.tempReservoirEndFill - ( ( heatersTempCalc.timeReservoirFillMS * 0.5 ) * RsrvrTauCPerMS ); + + heatersTempCalc.timeUFDecayMS = (F32)heatersTempCalc.timeReservoirCycleMS - heatersTempCalc.timeReservoirFillMS; + UFTimeConstant = heatersTempCalc.timeUFDecayMS * UFTauCPerMS; + heatersTempCalc.tempUFFill = tempLastFill + UFTimeConstant; + + tempTargetNumerator = heatersTempCalc.tempReservoir0 - ( ( heatingConstsCalRecord.ultrafilterVolmL / targetFillVolML ) * heatersTempCalc.tempUFFill ); + targetTempDenominator = ( ( targetFillVolML - heatingConstsCalRecord.ultrafilterVolmL ) / targetFillVolML ); + tempTarget = tempTargetNumerator / targetTempDenominator; + } + else + { + tempTarget = heatersTempCalc.tempTargetTrimmer + RESERVOIR_EXTRA_TEMPERATURE; + } + + if ( targetROFlowLPM > 0 ) + { + priTargetTemp = ( tempTarget * ( tgtTotalFlowLPM / targetROFlowLPM ) ) - ( ENVIRONMENT_TEMPERATURE_C * ( tgtAicdFlowLPM / targetROFlowLPM ) ) - + ( ENVIRONMENT_TEMPERATURE_C * ( tgtBicarbFlowLPM / targetROFlowLPM ) ); + } + + return priTargetTemp; +} + +/*********************************************************************//** + * @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 ) + { + heatersTempCalc.tempFillMixAvgTrimmer = ( fillROAvgActual * ( tgtTotalFlowLPM / targetROFlowLPM ) ) + + ( ENVIRONMENT_TEMPERATURE_C * ( tgtAicdFlowLPM / targetROFlowLPM ) ) + + ( ENVIRONMENT_TEMPERATURE_C * ( tgtBicarbFlowLPM / targetROFlowLPM ) ); + + heatersTempCalc.tempRsrvr0ActualTrimmer = ( ( heatingConstsCalRecord.ultrafilterVolmL / targetFillVolML ) * tempUFFill ) + + ( ( ( targetFillVolML - heatingConstsCalRecord.ultrafilterVolmL ) / targetFillVolML ) * + heatersTempCalc.tempFillMixAvgTrimmer ); + + heatersTempCalc.tempRsrvrEndFillTrimmer = heatersTempCalc.tempRsrvr0ActualTrimmer + ( ( heatersTempCalc.timeReservoirFillMS * 0.5 ) * rsrvrTauCPerMS ); + + tempRsrvrActual = heatersTempCalc.tempRsrvrEndFillTrimmer + ( ( heatersTempCalc.timeReservoirFillMS * 0.5 ) * 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 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 = ( ( 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 hasTargetDrainVolumeReached function checks if the target drain volume + * for specific reservoir has been reached or exceed time limit. + * @details Inputs: reservoirWeightUnchangeStartTime, reservoirWeightUnchangeStartTime + * @details Outputs: reservoirWeightUnchangeStartTime, tareLoadCellRequest, + * reservoirWeightUnchangeStartTime + * @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 hasTargetDrainVolumeBeenReached( DG_RESERVOIR_ID_T reservoirId, U32 timeout ) +{ + BOOL result = FALSE; + + F32 loadcellWeightML = getLoadCellSmallFilteredWeight( associatedLoadCell[ reservoirId ] ); + U32 drainPumpFeedbackRPM = getDrainPumpMeasuredRPM(); + + // 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 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 const 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.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; +} + +/**@}*/