/************************************************************************** * * Copyright (c) 2024-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 PistonPumpControl.c * * @author (last) Vinayakam Mani * @date (last) 07-Jan-2025 * * @author (original) Vinayakam Mani * @date (original) 07-Jan-2025 * ***************************************************************************/ #include "ConcentratePumps.h" #include "FpgaDD.h" #include "Messaging.h" #include "PistonPumpControl.h" #include "TaskGeneral.h" #include "Valves.h" #ifdef __PUMPTEST__ /** * @addtogroup PistonPumpControl * @{ */ // ********** private definitions ********** #define PISTON_PUMP_CONTROL_DATA_PUBLISH_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the piston pump control data published. #define PISTON_PUMP_FILL_PERIOD ( 4000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the piston pump filling cycle time #define PISTON_PUMP_DISPENSE_PERIOD ( 4000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the piston pump dispense cycle time #define PISTON_PUMP_MIN_SPEED 3.0F ///< Piston pump minimum speed in ml/min. #define PISTON_PUMP_MAX_SPEED 48.0F ///< Maximum speed for piston pump in mL/min /// Enumeration of piston pump states. typedef enum PistonPumpState { PISTON_PUMP_OFF_STATE = 0, ///< Piston pump off state. PISTON_PUMP_FILL_START_STATE, ///< Piston pump fill start state. PISTON_PUMP_FILL_COMPLETE_STATE, ///< Piston pump fill complete state. PISTON_PUMP_DISPENSE_START_STATE, ///< Piston pump dispense start state. PISTON_PUMP_DISPENSE_COMPLETE_STATE, ///< Piston pump dispense complete state. NUM_OF_PISTON_PUMP_STATES ///< Number of piston pump states. } PISTON_PUMP_EXEC_STATE_T; /// Payload record structure for piston pump start/stop request typedef struct { U32 pumpId; ///< Piston pump ID (0:Acid ,1:Bicarb, 2: UF pump) U32 startStop; ///< Piston pump start:1,stop :0. U32 cycleCount; ///< Piston pump cycle count F32 volume; ///< Target volume in ml F32 speed; ///< Speed range from 0.3 ml/min to 48.0 ml/min. } PISTON_PUMP_START_STOP_CMD_PAYLOAD_T; // ********** private data ********** static PISTON_PUMP_EXEC_STATE_T pistonPumpExecState[ NUM_OF_PISTON_PUMPS ]; ///< Current piston pump control executive state. static F32 pistonPumpSetSpeed[ NUM_OF_PISTON_PUMPS ]; ///< Current piston pump set speed. static F32 pistonPumpVolumeinMl[ NUM_OF_PISTON_PUMPS ]; ///< Current piston pump set volume to deliver. static U32 pistonPumpFillPeriod; ///< Piston pump fill period static U32 pistonPumpDispensePeriod; ///< Piston pump dispense period static U32 pistonPumpCycleCount[ NUM_OF_PISTON_PUMPS ]; ///< Number of piston pump (fill and dispense) cycle count static U32 currentPistonPumpSwitchingCounter[ NUM_OF_PISTON_PUMPS ]; ///< Counter (in task interval) to monitor the piston pump fill/drain operation. static U32 pistonPumpControlDataPublicationTimerCounter; ///< Used to schedule piston pump control data publication to CAN bus. static OVERRIDE_U32_T pistonPumpControlDataPublishInterval; ///< Piston Pump control data publish interval. // ********** private function prototypes ********** static void pistonPumpInletOutletValveControl( PISTON_PUMPS_T pumpId, VALVE_STATE_NAMES_T inletValveState, VALVE_STATE_NAMES_T outletValveState ); static PISTON_PUMP_EXEC_STATE_T handlePistonPumpOffState( PISTON_PUMPS_T pumpId ); static PISTON_PUMP_EXEC_STATE_T handlePistonPumpFillStartState( PISTON_PUMPS_T pumpId ); static PISTON_PUMP_EXEC_STATE_T handlePistonPumpFillCompleteState( PISTON_PUMPS_T pumpId ); static PISTON_PUMP_EXEC_STATE_T handlePistonPumpDispenseStartState( PISTON_PUMPS_T pumpId ); static PISTON_PUMP_EXEC_STATE_T handlePistonPumpDispenseCompleteState( PISTON_PUMPS_T pumpId ); static U32 getPistonPumpControlDataPublishInterval( void ); static void publishPistonPumpControlData( void ); /*********************************************************************//** * @brief * The initPistonPump function initializes the piston pump control * ( Dosing and ultrafilteraion control) unit. * @details \b Inputs: none * @details \b Outputs: unit variables initialized. * @return none *************************************************************************/ void initPistonPump( void ) { pistonPumpExecState[ PISTONPUMPS_ACID ] = PISTON_PUMP_OFF_STATE; pistonPumpExecState[ PISTONPUMPS_BICARB ] = PISTON_PUMP_OFF_STATE; pistonPumpExecState[ PISTONPUMPS_UF ] = PISTON_PUMP_OFF_STATE; currentPistonPumpSwitchingCounter[ PISTONPUMPS_ACID ] = 0; currentPistonPumpSwitchingCounter[ PISTONPUMPS_BICARB ] = 0; currentPistonPumpSwitchingCounter[ PISTONPUMPS_UF ] = 0; pistonPumpCycleCount[ PISTONPUMPS_ACID ] = 0; pistonPumpCycleCount[ PISTONPUMPS_BICARB ] = 0; pistonPumpCycleCount[ PISTONPUMPS_UF ] = 0; pistonPumpVolumeinMl[ PISTONPUMPS_ACID ] = 0.0F; pistonPumpVolumeinMl[ PISTONPUMPS_BICARB ] = 0.0F; pistonPumpVolumeinMl[ PISTONPUMPS_UF ] = 0.0F; pistonPumpSetSpeed[ PISTONPUMPS_ACID ] = PISTON_PUMP_MIN_SPEED; pistonPumpSetSpeed[ PISTONPUMPS_BICARB ] = PISTON_PUMP_MIN_SPEED; pistonPumpSetSpeed[ PISTONPUMPS_UF ] = PISTON_PUMP_MIN_SPEED; pistonPumpControlDataPublishInterval.data = PISTON_PUMP_CONTROL_DATA_PUBLISH_INTERVAL; pistonPumpControlDataPublishInterval.ovData = PISTON_PUMP_CONTROL_DATA_PUBLISH_INTERVAL; pistonPumpControlDataPublishInterval.ovInitData = 0; pistonPumpControlDataPublishInterval.override = OVERRIDE_RESET; pistonPumpFillPeriod = PISTON_PUMP_FILL_PERIOD; pistonPumpDispensePeriod = PISTON_PUMP_DISPENSE_PERIOD; pistonPumpControlDataPublicationTimerCounter = 0; } /*********************************************************************//** * @brief * The execPistonPumpController function executes the piston pump control state machine. * @details \b Inputs: pistonPumpExecState * @details \b Outputs: pistonPumpExecState * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong piston pump control state invoked. * @return current state. *************************************************************************/ void execPistonPumpController( void ) { PISTON_PUMPS_T pumpId; for ( pumpId = PISTONPUMPS_FIRST; pumpId < NUM_OF_PISTON_PUMPS; pumpId++ ) { // Increment free running counter currentPistonPumpSwitchingCounter [ pumpId ]++; switch ( pistonPumpExecState[pumpId] ) { case PISTON_PUMP_OFF_STATE: pistonPumpExecState[ pumpId ] = handlePistonPumpOffState( pumpId ); break; case PISTON_PUMP_FILL_START_STATE: pistonPumpExecState[ pumpId ] = handlePistonPumpFillStartState( pumpId ); break; case PISTON_PUMP_FILL_COMPLETE_STATE: pistonPumpExecState[ pumpId ] = handlePistonPumpFillCompleteState( pumpId ); break; case PISTON_PUMP_DISPENSE_START_STATE: pistonPumpExecState[ pumpId ] = handlePistonPumpDispenseStartState( pumpId ); break; case PISTON_PUMP_DISPENSE_COMPLETE_STATE: pistonPumpExecState[ pumpId ] = handlePistonPumpDispenseCompleteState( pumpId ); break; #ifndef _VECTORCAST_ // 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_PISTON_PUMP_EXEC_INVALID_STATE, pumpId ) pistonPumpExecState[ pumpId ] = PISTON_PUMP_OFF_STATE; break; #endif } } //Publish piston control pump data publishPistonPumpControlData(); } /*********************************************************************//** * @brief * The pistonPumpInletOutletValveControl function actuates the inlet and * Outlet valve for given pump ( dosing/UF ). * @details \b Inputs: none * @details \b Outputs: valve states * @param pumpId pump id to configure valve settings * @param inletValveState Inlet Valve open/close state * @param outletValveState Outlet Valve open/close state * @return none. *************************************************************************/ static void pistonPumpInletOutletValveControl( PISTON_PUMPS_T pumpId, VALVE_STATE_NAMES_T inletValveState, VALVE_STATE_NAMES_T outletValveState ) { switch ( pumpId ) { case PISTONPUMPS_ACID: // TODO : Valves assocaited to Acid concentrate Pumps setValveState( D14_VALV, inletValveState ); setValveState( D52_VALV, outletValveState ); break; case PISTONPUMPS_BICARB: // TODO: Valves assocaited to Bicarb concentrate Pumps setValveState( D8_VALV, inletValveState ); setValveState( D54_VALV, outletValveState ); break; case PISTONPUMPS_UF: // TODO: Valves assocaited to Ultrafilteration setValveState( D53_VALV, inletValveState ); setValveState( D34_VALV, outletValveState ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_PISTON_PUMP_INVALID_PUMP_ID, pumpId ); break; } } /*********************************************************************//** * @brief * The startPistonPumpCycle function sets the given piston pump parameters * for dosing/UF operations. * @details \b Inputs: none * @details \b Outputs: piston pump parameters. * @param pumpId pump id to configure pump parameters * @param cycleCount Number of piston pump cycle. * @param targetVolume_ml Volume to be filled/dispensed * @param speed The flow rate in ml/min * @return none. *************************************************************************/ void startPistonPumpCycle( PISTON_PUMPS_T pumpId, U32 cycleCount, F32 targetVolume_ml, F32 speed ) { if ( pumpId < NUM_OF_PISTON_PUMPS ) { pistonPumpCycleCount[ pumpId ] = cycleCount; pistonPumpVolumeinMl[ pumpId ] = targetVolume_ml; pistonPumpSetSpeed[ pumpId ] = speed; pistonPumpExecState[ pumpId ] = PISTON_PUMP_OFF_STATE; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_PISTON_PUMP_INVALID_PUMP_ID, pumpId ); } } /*********************************************************************//** * @brief * The stopPistonPumpCycle function stops the given piston pump * @details \b Inputs: none * @details \b Outputs: pistonPumpCycleCount,pistonPumpVolumeinMl,pistonPumpSetSpeed, * pistonPumpExecState. * @param pumpId pump id to stop the pump * @return none. *************************************************************************/ void stopPistonPumpCycle( PISTON_PUMPS_T pumpId ) { if ( pumpId < NUM_OF_PISTON_PUMPS ) { pistonPumpCycleCount[ pumpId ] = 0; pistonPumpVolumeinMl[ pumpId ] = 0.0F; pistonPumpSetSpeed[ pumpId ] = 0.0F; pistonPumpExecState[ pumpId ] = PISTON_PUMP_OFF_STATE; // Close Inlet and Outlet valve pistonPumpInletOutletValveControl( pumpId, VALVE_STATE_CLOSED, VALVE_STATE_CLOSED ); //Stop the pump requestConcentratePumpOff( (CONCENTRATE_PUMPS_T)pumpId, FALSE ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_PISTON_PUMP_INVALID_PUMP_ID, pumpId ); } } /*********************************************************************//** * @brief * The handlePistonPumpOffState function handles the piston pump * Off state to handle piston pump control initiation when asked to. * @details \b Inputs: pump Id * @details \b Outputs: valve states and motor control * @param pumpId pump id to start/stop the pump * @return next piston pump control state. *************************************************************************/ static PISTON_PUMP_EXEC_STATE_T handlePistonPumpOffState( PISTON_PUMPS_T pumpId ) { PISTON_PUMP_EXEC_STATE_T state = PISTON_PUMP_OFF_STATE; // Initiate piston pump control when cycle count is more than 1. // Will redefine the off state later if required if ( pistonPumpCycleCount[ pumpId ] > 0 ) { state = PISTON_PUMP_FILL_START_STATE; pistonPumpCycleCount[ pumpId ]--; } return state; } /*********************************************************************//** * @brief * The handlePistonPumpFillStartState function handles the inlet and outlet * valve control and start the piston pumps with the set speed and volume * @details \b Inputs: pump Id * @details \b Outputs: Valve actuation and motor control * @param pumpId pump id to actuate valves pair and control stepper motor * @return next piston pump control state. *************************************************************************/ static PISTON_PUMP_EXEC_STATE_T handlePistonPumpFillStartState( PISTON_PUMPS_T pumpId ) { PISTON_PUMP_EXEC_STATE_T state = PISTON_PUMP_FILL_START_STATE; currentPistonPumpSwitchingCounter [ pumpId ] = 0; // Open Inlet and Outlet valve pistonPumpInletOutletValveControl( pumpId, VALVE_STATE_OPEN, VALVE_STATE_CLOSED ); // start stepper motor with the expected quantity setConcentratePumpTargetSpeed( (CONCENTRATE_PUMPS_T)pumpId, pistonPumpSetSpeed[ pumpId ], pistonPumpVolumeinMl[ pumpId ] ); requestConcentratePumpOn( (CONCENTRATE_PUMPS_T)pumpId ); //Move to next state state = PISTON_PUMP_FILL_COMPLETE_STATE; return state; } /*********************************************************************//** * @brief * The handlePistonPumpFillCompleteState function check for the piston pump * fill complete and close the currently opened valve. * @details \b Inputs: currentPistonPumpSwitchingCounter,pistonPumpFillPeriod * @details \b Outputs: currentPistonPumpSwitchingCounter * @param pumpId pump id to actuate valves pair and control stepper motor * @return next piston pump control state. *************************************************************************/ static PISTON_PUMP_EXEC_STATE_T handlePistonPumpFillCompleteState( PISTON_PUMPS_T pumpId ) { PISTON_PUMP_EXEC_STATE_T state = PISTON_PUMP_FILL_COMPLETE_STATE; // TODO: The FPGA status register for step revolution count - downcounter to be monitered here // to confirm the motor stop. Once FPGA status register issue is fixed, we can bring that here. // Till then go with some guess timing for filling and dispensing. if ( currentPistonPumpSwitchingCounter[ pumpId ] >= pistonPumpFillPeriod ) { // Close Inlet and Outlet valve pistonPumpInletOutletValveControl( pumpId, VALVE_STATE_CLOSED, VALVE_STATE_CLOSED ); //Stop the pump requestConcentratePumpOff( (CONCENTRATE_PUMPS_T)pumpId, FALSE ); //Move to next state state = PISTON_PUMP_DISPENSE_START_STATE; } return state; } /*********************************************************************//** * @brief * The handlePistonPumpDispenseStartState function handles the inlet and outlet * valve control for dispensing fluid and start the piston pumps with the set * speed and volume * @details \b Inputs: pump Id * @details \b Outputs: Valve actuation and motor control * @param pumpId pump id to actuate valves pair and control stepper motor * @return next piston pump control state. *************************************************************************/ static PISTON_PUMP_EXEC_STATE_T handlePistonPumpDispenseStartState( PISTON_PUMPS_T pumpId ) { PISTON_PUMP_EXEC_STATE_T state = PISTON_PUMP_DISPENSE_START_STATE; currentPistonPumpSwitchingCounter[ pumpId ] = 0; F32 pumpSpeed = pistonPumpSetSpeed[ pumpId ] * -1.0 ; // This is to rotate motor in reverse direction // Close Inlet and Open Outlet valve pistonPumpInletOutletValveControl( pumpId, VALVE_STATE_CLOSED, VALVE_STATE_OPEN ); // start stepper motor with the expected quantity setConcentratePumpTargetSpeed( (CONCENTRATE_PUMPS_T)pumpId, pumpSpeed, pistonPumpVolumeinMl[ pumpId ] ); requestConcentratePumpOn( (CONCENTRATE_PUMPS_T)pumpId ); //Move to next state state = PISTON_PUMP_DISPENSE_COMPLETE_STATE; return state; } /*********************************************************************//** * @brief * The handlePistonPumpDispenseCompleteState function check for the piston pump * dispense complete and close the currently opened valve. * @details \b Inputs: currentPistonPumpSwitchingCounter,pistonPumpDispensePeriod * @details \b Outputs: currentPistonPumpSwitchingCounter * @param pumpId pump id to actuate valves pair and control stepper motor * @return next piston pump control state. *************************************************************************/ static PISTON_PUMP_EXEC_STATE_T handlePistonPumpDispenseCompleteState( PISTON_PUMPS_T pumpId ) { PISTON_PUMP_EXEC_STATE_T state = PISTON_PUMP_DISPENSE_COMPLETE_STATE; // TODO: The FPGA status register for step revolution count - downcounter to be monitered here // to confirm the motor stop. Once FPGA status register issue is fixed, we can bring that here. // Till then go with some guess timing for filling and dispensing. if ( currentPistonPumpSwitchingCounter[ pumpId ] >= pistonPumpDispensePeriod ) { // Close Inlet and Outlet valve pistonPumpInletOutletValveControl( pumpId, VALVE_STATE_CLOSED, VALVE_STATE_CLOSED ); //Stop the pump requestConcentratePumpOff( (CONCENTRATE_PUMPS_T)pumpId, FALSE ); //Move to next state state = PISTON_PUMP_OFF_STATE; } return state; } /*********************************************************************//** * @brief * The getPistonPumpControlDataPublishInterval function gets the piston pump * control data publish interval. * @details \b Inputs: pistonPumpControlDataPublishInterval * @details \b Outputs: none * @return the interval at piston pump control data being published. *************************************************************************/ static U32 getPistonPumpControlDataPublishInterval( void ) { U32 result = pistonPumpControlDataPublishInterval.data; if ( OVERRIDE_KEY == pistonPumpControlDataPublishInterval.override ) { result = pistonPumpControlDataPublishInterval.ovData; } return result; } /*********************************************************************//** * @brief * The publishPistonPumpControlData function broadcasts the piston pump * control execution data at defined interval. * @details \b Inputs: pistonPumpControlDataPublicationTimerCounter * @details \b Outputs: DD piston pump control data broadcast message sent * @details \b Message \Sent: MSG_ID_DD_PISTON_PUMP_CONTROL_DATA to publish the balancing * chamber data. * @return none *************************************************************************/ static void publishPistonPumpControlData( void ) { if ( ++pistonPumpControlDataPublicationTimerCounter >= getPistonPumpControlDataPublishInterval() ) { PISTON_PUMP_CONTROL_DATA_T data; data.acidPumpControlExecState = (U32)pistonPumpExecState[ PISTONPUMPS_ACID ]; data.bicarbPumpControlExecState = (U32)pistonPumpExecState[ PISTONPUMPS_BICARB ]; data.ufPumpControlExecState = (U32)pistonPumpExecState[ PISTONPUMPS_UF ]; data.acidPistonPumpSwCounter = currentPistonPumpSwitchingCounter[ PISTONPUMPS_ACID ]; data.bicarbPistonPumpSwCounter = currentPistonPumpSwitchingCounter[ PISTONPUMPS_BICARB ]; data.ufPistonPumpSwCounter = currentPistonPumpSwitchingCounter[ PISTONPUMPS_UF ]; data.acidPistonPumpCycleCount = pistonPumpCycleCount[ PISTONPUMPS_ACID ]; data.bicarbPistonPumpCycleCount = pistonPumpCycleCount[ PISTONPUMPS_BICARB ]; data.ufPistonPumpCycleCount = pistonPumpCycleCount[ PISTONPUMPS_UF ]; broadcastData( MSG_ID_DD_PISTON_PUMP_CONTROL_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( PISTON_PUMP_CONTROL_DATA_T ) ); pistonPumpControlDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testDDPistonPumpControlDataPublishIntervalOverride function overrides the * DD piston pump data publish interval. * @details \b Inputs: pistonPumpControlDataPublishInterval * @details \b Outputs: pistonPumpControlDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the DD piston pump data publish interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testDDPistonPumpControlDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &pistonPumpControlDataPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testDDPistonPumpStartStopOverride function sets the override * of start/stop piston pump. * @details Inputs: piston pump parameters * @details Outputs: valve and motor control * @param message Override message from Dialin which includes the override * value to override the piston pump control parameters. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testDDPistonPumpStartStopOverride( MESSAGE_T *message ) { BOOL result = FALSE; // Verify tester has logged in with TD if ( TRUE == isTestingActivated() ) { // Verify payload length is valid if ( sizeof( PISTON_PUMP_START_STOP_CMD_PAYLOAD_T ) == message->hdr.payloadLen ) { PISTON_PUMP_START_STOP_CMD_PAYLOAD_T payload; memcpy( &payload, message->payload, sizeof(PISTON_PUMP_START_STOP_CMD_PAYLOAD_T) ); if ( (PISTON_PUMPS_T)payload.pumpId < NUM_OF_PISTON_PUMPS ) { // Handle start command if ( ( TRUE == payload.startStop ) && ( ( payload.speed >= PISTON_PUMP_MIN_SPEED ) && ( payload.speed <= PISTON_PUMP_MAX_SPEED ) ) && ( payload.volume > 0.0 ) && ( payload.cycleCount > 0 ) ) { startPistonPumpCycle( (PISTON_PUMPS_T)payload.pumpId, payload.cycleCount, payload.volume, payload.speed ); result = TRUE; } //Handle stop command if ( FALSE == payload.startStop ) { stopPistonPumpCycle( (PISTON_PUMPS_T)payload.pumpId ); result = TRUE; } } } } return result; } #endif /**@}*/