/************************************************************************** * * Copyright (c) 2024-2026 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 SubstitutionPumps.c * * @author (last) Jashwant Gantyada * @date (last) 02-Apr-2026 * * @author (original) Vinayakam Mani * @date (original) 19-Sep-2024 * ***************************************************************************/ #include "SubstitutionPump.h" #include "FpgaDD.h" #include "MessageSupport.h" #include "Messaging.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "TaskGeneral.h" #include "Utilities.h" /** * @addtogroup SubstitutionPumps * @{ */ // ********** private definitions ********** #define SUBSTITUTION_PUMP_DATA_PUBLISH_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the Substitution pump is monitored. #define SUBSTITUTION_PUMP_CONTROL_INTERVAL ( 100 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the Substitution pump is controlled. #define SUBSTITUTION_PUMP_DATA_PUBLISH_COUNTER_START_COUNT 6 ///< Data publish counter start count. #define SUBSTITUTION_PUMP_VOLUME_PER_REV 1.25F ///< Volume output every revolution (mL). #define SUBSTITUTION_PUMP_ZERO_REVOLUTION_COUNT 0 ///< Revolution count reached to Zero. #define SUBSTITUTION_PUMP_ZERO_FLOW_RATE 0xFFFFFFFF ///< Pulse width value when zero flow rate or pump is off #define SUBSTITUTION_PUMP_STEP_PER_REV 200.0F ///< Number of steps for every revolution. #define SUBSTITUTION_PUMP_MICRO_STEPS_PER_STEP 64.0F ///< Number of micro-steps ( fractions of step) per step. #define SUBSTITUTION_PUMP_VOLUME_TO_REVOLUTION ( ( 1.0F / SUBSTITUTION_PUMP_VOLUME_PER_REV ) * \ ( SUBSTITUTION_PUMP_STEP_PER_REV * \ SUBSTITUTION_PUMP_MICRO_STEPS_PER_STEP ) ) ///< Convert volume in to number of revolutions needed. #define SUBSTITUTION_PUMP_STEP_PERIOD_RESOLUTION ( 0.50F / ( US_PER_SECOND * SEC_PER_MIN ) ) ///< Convert step period resolution (0.50 us) to minute. #define SUBSTITUTION_PUMP_FORWARD_DIR 0x1 ///< Concentrate pump forward direction configuration. #define SUBSTITUTION_PUMP_REVERSE_DIR 0x0 ///< Concentrate pump reverse direction configuration. #define SUBSTITUTION_PUMP_CONTROL_EIGHTH_STEP 0x04 ///< Substitution pump control 1/8th step. #define SUBSTITUTION_PUMP_CONTROL_REVERSE_DIR 0x00 ///< Substitution pump control reverse direction. #define SUBSTITUTION_PUMP_CONTROL_FORWARD_DIR 0x08 ///< Substitution pump control forward direction. #define SUBSTITUTION_PUMP_CONTROL_ENABLE 0x00 ///< Substitution pump control enable pump. #define SUBSTITUTION_PUMP_CONTROL_DISABLE 0x10 ///< Substitution pump control disable pump. #define SUBSTITUTION_PUMP_CONTROL_NOT_RESET 0x20 ///< Substitution pump control not reset. #define SUBSTITUTION_PUMP_CONTROL_SLEEP_OFF 0x40 ///< Substitution pump control sleep off. #define SUBSTITUTION_PUMP_TRANS_TO_RAMP_SPEED_THRESHOLD_MLPM 10.0F ///< Substitution pump transition to ramp to target speed threshold in mL/min. #define SUBSTITUTION_PUMP_RAMP_SPEED_INCREMENT 5.0F ///< Speed increase (mL/min) when controlling Substitution pump to target step speed. static const U32 SUBSTITUTION_PUMP_CONTROL_FORWARD = SUBSTITUTION_PUMP_CONTROL_SLEEP_OFF | SUBSTITUTION_PUMP_CONTROL_NOT_RESET | SUBSTITUTION_PUMP_CONTROL_ENABLE | SUBSTITUTION_PUMP_CONTROL_FORWARD_DIR | SUBSTITUTION_PUMP_CONTROL_EIGHTH_STEP; static const U32 SUBSTITUTION_PUMP_CONTROL_STOP = SUBSTITUTION_PUMP_CONTROL_SLEEP_OFF | SUBSTITUTION_PUMP_CONTROL_NOT_RESET | SUBSTITUTION_PUMP_CONTROL_DISABLE | SUBSTITUTION_PUMP_CONTROL_FORWARD_DIR | SUBSTITUTION_PUMP_CONTROL_EIGHTH_STEP; /// Enumeration of Substitution pump states. typedef enum SubstituionPumpState { SUBSTITUTION_PUMP_OFF_STATE = 0, ///< Substitution pump off state. SUBSTITUTION_PUMP_RAMP_TO_TARGET_SPEED_STATE, ///< Substitution pump ramp to target state. SUBSTITUTION_PUMP_CONTROL_TARGET_SPEED_STATE, ///< Substitution pump on state. NUM_OF_SUBSTITUTION_PUMP_STATES ///< Number of Substitution pump states. } SUBSTITUTION_PUMP_STATE_T; /// Substitution pump data structure typedef struct { U32 controlTimerCounter; ///< Timer counter to perform control on Substitution pump. SUBSTITUTION_PUMP_STATE_T execState; ///< Concentrate pump execute current state. BOOL hasTurnOnPumpsBeenRequested; ///< Flag indicates a request to turn Substitution pumps on. F32 currentPumpSpeed; ///< Current controlled Substitution pumps' speed (mL/min). U32 togglePeriodCount; ///< Converted pump speed (mL/min) to toggle period counts (0.5 uS increment counts per step). U08 direction; ///< Substitution pump motor direction. U08 controlSet; ///< Substitution pump control set. (Used in DVT). } SUBSTITUTION_PUMP_T; /// Payload record structure for Substitution pump start/stop request typedef struct { U32 pumpID; ///< Substitution pump ID U32 startStop; ///< Substitution pump start:1,stop :0. F32 speed; ///< Speed in ml/min F32 volume; ///< Target volume in ml } SUB_PUMP_START_STOP_CMD_PAYLOAD_T; // ********** private data ********** static OVERRIDE_U32_T substitutionPumpDataPublishInterval; ///< Substitution pump data publish interval. static SUBSTITUTION_PUMP_T substitutionPumps[ NUM_OF_SUB_PUMPS ]; ///< Array of Substitution pumps' data structure. static OVERRIDE_F32_T pumpTargetSpeed[ NUM_OF_SUB_PUMPS ]; ///< Target concentrate pumps' speed (mL/min). // ********** private function prototypes ********** static void stopSubstitutionPump( SUBSTITUTION_PUMPS_T pumpId ); static SUBSTITUTION_PUMP_STATE_T handleSubstitutionPumpOffState( SUBSTITUTION_PUMPS_T pumpId ); static SUBSTITUTION_PUMP_STATE_T handleSubstitutionPumpControlTargetSpeedState( SUBSTITUTION_PUMPS_T pumpId ); static BOOL stepSubstitutionPumpToTargetSpeed( SUBSTITUTION_PUMPS_T pumpId ); static void checkSubstitutionPumpControlSet( SUBSTITUTION_PUMPS_T pumpId ); static void publishSubstitutionPumpData( void ); /*********************************************************************//** * @brief * The initSubstitutionPump function initializes the SubstitutionPumps unit. * @details \b Inputs: none * @details \b Outputs: SubstitutionPumps unit variables initialized * @return none *************************************************************************/ void initSubstitutionPump( void ) { SUBSTITUTION_PUMPS_T pumpId; for ( pumpId = SUBPUMPS_FIRST; pumpId < NUM_OF_SUB_PUMPS; pumpId++ ) { substitutionPumps[ pumpId ].controlTimerCounter = 0; substitutionPumps[ pumpId ].execState = SUBSTITUTION_PUMP_OFF_STATE; substitutionPumps[ pumpId ].hasTurnOnPumpsBeenRequested = FALSE; substitutionPumps[ pumpId ].direction = SUBSTITUTION_PUMP_FORWARD_DIR; substitutionPumps[ pumpId ].controlSet = SUBSTITUTION_PUMP_CONTROL_FORWARD_DIR; } setFPGAD92PumpRevolutionCount( SUBSTITUTION_PUMP_ZERO_FLOW_RATE ); setFPGAD92PumpControl( SUBSTITUTION_PUMP_CONTROL_STOP ); substitutionPumpDataPublishInterval.data = SUBSTITUTION_PUMP_DATA_PUBLISH_INTERVAL; substitutionPumpDataPublishInterval.ovInitData = SUBSTITUTION_PUMP_DATA_PUBLISH_INTERVAL; substitutionPumpDataPublishInterval.ovData = 0; substitutionPumpDataPublishInterval.override = OVERRIDE_RESET; } /*********************************************************************//** * @brief * The execSubstitutionPumpController function executes the substitution pump controller. * @details \b Inputs: execState * @details \b Outputs: execState * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid pump state is seen. * @return none *************************************************************************/ void execSubstitutionPumpController( void ) { SUBSTITUTION_PUMPS_T pumpId; for ( pumpId = SUBPUMPS_FIRST; pumpId < NUM_OF_SUB_PUMPS; pumpId++ ) { switch ( substitutionPumps[ pumpId ].execState ) { case SUBSTITUTION_PUMP_OFF_STATE: substitutionPumps[ pumpId ].execState = handleSubstitutionPumpOffState( pumpId ); break; case SUBSTITUTION_PUMP_CONTROL_TARGET_SPEED_STATE: substitutionPumps[ pumpId ].execState = handleSubstitutionPumpControlTargetSpeedState( pumpId ); break; // The switch case is in a for loop so the default case cannot be covered in VectorCAST default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_CONCENTRATE_PUMP_EXEC_INVALID_STATE, pumpId ) substitutionPumps[ pumpId ].execState = SUBSTITUTION_PUMP_OFF_STATE; break; } } } /*********************************************************************//** * @brief * The handleSubstitutionPumpOffState function turns on a given substitution * pumps and switch to on state upon request. * @details \b Inputs: none * @details \b Outputs: pump turned on * @param pumpId ID of the substitution pump * @return state *************************************************************************/ static SUBSTITUTION_PUMP_STATE_T handleSubstitutionPumpOffState( SUBSTITUTION_PUMPS_T pumpId ) { SUBSTITUTION_PUMP_STATE_T state = SUBSTITUTION_PUMP_OFF_STATE; if ( TRUE == substitutionPumps[ pumpId ].hasTurnOnPumpsBeenRequested ) { U08 controlSet = substitutionPumps[ pumpId ].controlSet; setFPGAD92PumpRevolutionCount( SUBSTITUTION_PUMP_ZERO_FLOW_RATE ); setFPGAD92PumpControl( controlSet ); state = SUBSTITUTION_PUMP_CONTROL_TARGET_SPEED_STATE; } else { setFPGAD92PumpRevolutionCount( SUBSTITUTION_PUMP_ZERO_FLOW_RATE ); setFPGAD92PumpControl( SUBSTITUTION_PUMP_CONTROL_STOP ); } return state; } /*********************************************************************//** * @brief * The handleSubstitutionPumpControlTargetSpeedState function turns off given * substitution pumps switch to off state upon request. While in on state, * the function controls substitution pumps to a target step speed. * @details \b Inputs: currentPumpSpeed[] * @details \b Outputs: control given substitution pumps to target step speed * @param pumpId ID of the substitution pump * @return state *************************************************************************/ static SUBSTITUTION_PUMP_STATE_T handleSubstitutionPumpControlTargetSpeedState( SUBSTITUTION_PUMPS_T pumpId ) { SUBSTITUTION_PUMP_STATE_T state = SUBSTITUTION_PUMP_CONTROL_TARGET_SPEED_STATE; F32 targetToCurreSpeedDiffMLPM = fabs( getSubstitutionPumpTargetSpeed( pumpId ) - substitutionPumps[ pumpId ].currentPumpSpeed ); if ( ++substitutionPumps[ pumpId ].controlTimerCounter >= SUBSTITUTION_PUMP_CONTROL_INTERVAL ) { substitutionPumps[ pumpId ].controlTimerCounter = 0; stepSubstitutionPumpToTargetSpeed( pumpId ); } if ( targetToCurreSpeedDiffMLPM >= SUBSTITUTION_PUMP_TRANS_TO_RAMP_SPEED_THRESHOLD_MLPM ) { // If the requested target speed is greater than the threshold, transition back to ramp state regardless of the status of the // control interval stepSubstitutionPumpToTargetSpeed( pumpId ); state = SUBSTITUTION_PUMP_RAMP_TO_TARGET_SPEED_STATE; } //Stop the pump if measured rev count reaches zero if ( FALSE == substitutionPumps[ pumpId ].hasTurnOnPumpsBeenRequested ) { state = SUBSTITUTION_PUMP_OFF_STATE; stopSubstitutionPump( pumpId ); } return state; } /*********************************************************************//** * @brief * The stopSubstitutionPump function sets the given substitution pump step speed * to zero and turns off substitution pump. Also parks the pump if requested. * @details \b Inputs: none * @details \b Outputs: substitutionPumps * @param pumpId ID of the substitution pump * @return none *************************************************************************/ static void stopSubstitutionPump( SUBSTITUTION_PUMPS_T pumpId ) { substitutionPumps[ pumpId ].hasTurnOnPumpsBeenRequested = FALSE; // Just to make sure pump is in Off state. substitutionPumps[ pumpId ].currentPumpSpeed = 0.0F; // set target rate to zero // Disable the motor when stopping, to take next revolution count // Send zero rate command to stop the pump setFPGAD92PumpControl( SUBSTITUTION_PUMP_CONTROL_STOP ); setFPGAD92PumpSetStepSpeed( SUBSTITUTION_PUMP_ZERO_FLOW_RATE ); } /*********************************************************************//** * @brief * The stepSubstitutionPumpToTargetSpeed function steps current step speed * toward target speed for the given concentrate pump,with predefined step increase. * @details \b Inputs: none * @details \b Outputs: currentPumpSpeed[] * @param pumpId concentrate pump id to increase current step speed * @return TRUE if the pump has reached to target otherwise, FALSE *************************************************************************/ static BOOL stepSubstitutionPumpToTargetSpeed( SUBSTITUTION_PUMPS_T pumpId ) { F32 speedIncrease; BOOL hasTgtBeenReached = FALSE; F32 currentToTargetDiff = fabs( getSubstitutionPumpTargetSpeed( pumpId ) - substitutionPumps[ pumpId ].currentPumpSpeed ); if ( currentToTargetDiff > NEARLY_ZERO ) { if ( currentToTargetDiff > SUBSTITUTION_PUMP_RAMP_SPEED_INCREMENT ) { speedIncrease = SUBSTITUTION_PUMP_RAMP_SPEED_INCREMENT; } else { speedIncrease = currentToTargetDiff; hasTgtBeenReached = TRUE; } // Subtract current speed when target speed is smaller if ( getSubstitutionPumpTargetSpeed( pumpId ) < substitutionPumps[ pumpId ].currentPumpSpeed ) { speedIncrease *= -1.0F; } substitutionPumps[ pumpId ].currentPumpSpeed += speedIncrease; // If the pump's target speed is set to be 0, do not ramp down set it to zero immediately if ( getSubstitutionPumpTargetSpeed( pumpId ) < NEARLY_ZERO ) { substitutionPumps[ pumpId ].currentPumpSpeed = 0.0F; } } if ( substitutionPumps[ pumpId ].currentPumpSpeed > NEARLY_ZERO ) { F32 timePerStep; F32 stepPeriodCounts; timePerStep = SUBSTITUTION_PUMP_VOLUME_PER_REV / ( substitutionPumps[ pumpId ].currentPumpSpeed * SUBSTITUTION_PUMP_STEP_PER_REV ); stepPeriodCounts = timePerStep / ( SUBSTITUTION_PUMP_STEP_PERIOD_RESOLUTION * SUBSTITUTION_PUMP_MICRO_STEPS_PER_STEP ); substitutionPumps[ pumpId ].togglePeriodCount = (U32)( stepPeriodCounts + FLOAT_TO_INT_ROUNDUP_OFFSET ); } else { substitutionPumps[ pumpId ].togglePeriodCount = SUBSTITUTION_PUMP_ZERO_FLOW_RATE; } // Check if the control set bit is set as desired and if not, correct it checkSubstitutionPumpControlSet( pumpId ); setFPGAD92PumpSetStepSpeed( substitutionPumps[ pumpId ].togglePeriodCount ); return hasTgtBeenReached; } /*********************************************************************//** * @brief * The checkSubstitutionPumpControlSet function monitors the status of the * given substitution pumps control set bit and if they are different from the * required set bit, they are set again. * @details \b Inputs: substitutionPumps * @details \b Outputs: none * @param pumpId pump id to check its control set bit * @return none *************************************************************************/ static void checkSubstitutionPumpControlSet( SUBSTITUTION_PUMPS_T pumpId ) { U08 controlSetBits = getFPGAD92PumpControlStatus(); if ( controlSetBits != substitutionPumps[ pumpId ].controlSet ) { setFPGAD92PumpControl( substitutionPumps[ pumpId ].controlSet ); } } /*********************************************************************//** * @brief * The getSubstitutionPumpTargetSpeed function gets the current target speed for the given * substituion pump. * @details \b Inputs: pumpTargetSpeed * @details \b Outputs: none * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid pump ID is seen. * @param pumpId substitution pump id to get the current target speed * @return the current target speed for the given concentrate pump. *************************************************************************/ F32 getSubstitutionPumpTargetSpeed( SUBSTITUTION_PUMPS_T pumpId ) { F32 speed = 0.0F; if ( pumpId < NUM_OF_SUB_PUMPS ) { speed = getF32OverrideValue( &pumpTargetSpeed[ pumpId ] ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_CONCENTRATE_PUMP_INVALID_PUMP_ID, pumpId ); } return speed; } /*********************************************************************//** * @brief * The requestSubstitutionPumpOn function requests the module to turn on * the concentrate pumps. * @details \b Inputs: none * @details \b Outputs: concentratePumps[],acidConcentratePumpParkPersistenceClear, * bicarbConcentratePumpParkPersistenceClear, ufPumpParkPersistenceClear * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid pump ID is seen. * @param pumpId concentrate pump id * @return none *************************************************************************/ void requestSubstitutionPumpOn( SUBSTITUTION_PUMPS_T pumpId ) { if ( pumpId < NUM_OF_SUB_PUMPS ) { substitutionPumps[ pumpId ].hasTurnOnPumpsBeenRequested = TRUE; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_CONCENTRATE_PUMP_INVALID_PUMP_ID, pumpId ); } } /*********************************************************************//** * @brief * The requestSubstitutionPumpOff function requests the module to turn off * the concentrate pumps. * @details \b Inputs: none * @details \b Outputs: concentratePumps[] * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid pump ID is seen. * @param pumpId ID of concentrate pump * @param park TRUE if pump should be parked, FALSE if not * @return none *************************************************************************/ void requestSubstitutionPumpOff( SUBSTITUTION_PUMPS_T pumpId ) { if ( pumpId < NUM_OF_SUB_PUMPS ) { substitutionPumps[ pumpId ].hasTurnOnPumpsBeenRequested = FALSE; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_CONCENTRATE_PUMP_INVALID_PUMP_ID, pumpId ); } } /*********************************************************************//** * @brief * The setSubstitutionPumpTargetSpeed function sets the target step speed based on * given speed in mL/min to specified concentrate pump. * @details \b Inputs: none * @details \b Outputs: concentratePumps[] * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid pump ID is seen. * @param pumpId pump id to set step speed * @param targetSpeed_ml_min target speed in mL/min * @param targetVolume_ml dosing volume to be delivered in ml. * @return none *************************************************************************/ void setSubstitutionPumpTargetSpeed( SUBSTITUTION_PUMPS_T pumpId, F32 targetSpeed_ml_min, F32 Vol ) { if ( pumpId < NUM_OF_SUB_PUMPS ) { if ( targetSpeed_ml_min >= 0.0 ) { substitutionPumps[ pumpId ].direction = SUBSTITUTION_PUMP_FORWARD_DIR; substitutionPumps[ pumpId ].controlSet = SUBSTITUTION_PUMP_CONTROL_FORWARD; } else { substitutionPumps[ pumpId ].direction = SUBSTITUTION_PUMP_REVERSE_DIR; substitutionPumps[ pumpId ].controlSet = SUBSTITUTION_PUMP_CONTROL_FORWARD; targetSpeed_ml_min *= -1.0; } /* * If 0.0 <= speed <= 200 set it * If speed < 0.0 set to 0 * else speed > 200 set to 200 */ pumpTargetSpeed[ pumpId ].data = targetSpeed_ml_min; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_CONCENTRATE_PUMP_INVALID_PUMP_ID, pumpId ); } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testConcentratePumpDataPublishIntervalOverride function overrides the * concentrate pump data publish interval. * @details \b Inputs: none * @details \b Outputs: concentratePumpDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the concentrate pump data broadcast interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSubstitutionPumpDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &substitutionPumpDataPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testConcentratePumpTargetSpeedOverride function overrides the target * speed value of given concentrate pump id. * @details \b Inputs: none * @details \b Outputs: pumpTargetSpeed * @param message Override message from Dialin which includes an ID of * the pump to override and the state to override the pump to. * @return TRUE if override successful, FALSE if not * @note pump traget speed range should be between 3.0F and 45.0F. *************************************************************************/ BOOL testSubstituionPumpTargetSpeedOverride( MESSAGE_T *message ) { BOOL result = f32ArrayOverride( message, &pumpTargetSpeed[ 0 ], NUM_OF_SUB_PUMPS - 1 ); return result; } /*********************************************************************//** * @brief * The testSubstitutionPumpStartStopOverride function starts a given substituion pump * at mentioned speed/flowrate ( ml/min) or stops the substituion pump. * @details \b Inputs: tester logged in * @details \b Outputs: substituionPumps[] * @param message set message from Dialin which includes the substituion pump to set * and the state to set the substituion pump to. * @return TRUE if set request is successful, FALSE if not *************************************************************************/ BOOL testSubstitutionPumpStartStopOverride( MESSAGE_T *message ) { BOOL result = FALSE; // Verify tester has logged in with DD if ( TRUE == isTestingActivated() ) { // Verify payload length is valid if ( sizeof( SUB_PUMP_START_STOP_CMD_PAYLOAD_T ) == message->hdr.payloadLen ) { SUB_PUMP_START_STOP_CMD_PAYLOAD_T payload; memcpy( &payload, message->payload, sizeof(SUB_PUMP_START_STOP_CMD_PAYLOAD_T) ); if ( (SUBSTITUTION_PUMPS_T)payload.pumpID < NUM_OF_SUB_PUMPS ) { // Handle start command if ( ( TRUE == payload.startStop ) && ( payload.volume > 0.0 ) ) { setSubstitutionPumpTargetSpeed( (SUBSTITUTION_PUMPS_T)payload.pumpID, payload.speed, payload.volume ); requestSubstitutionPumpOn ( (SUBSTITUTION_PUMPS_T)payload.pumpID ); result = TRUE; } //Handle stop command if ( FALSE == payload.startStop ) { requestSubstitutionPumpOff( (SUBSTITUTION_PUMPS_T)payload.pumpID ); result = TRUE; } } } } return result; } /**@}*/