Index: firmware/App/Modes/FPModes/ModeGenPermeate.c =================================================================== diff -u -ra0c144ed9f72066d7a7b1d047b01fb76ca56d667 -rfac466fa1f67f47d717288d64ac71235188a884d --- firmware/App/Modes/FPModes/ModeGenPermeate.c (.../ModeGenPermeate.c) (revision a0c144ed9f72066d7a7b1d047b01fb76ca56d667) +++ firmware/App/Modes/FPModes/ModeGenPermeate.c (.../ModeGenPermeate.c) (revision fac466fa1f67f47d717288d64ac71235188a884d) @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (c) 2024-2024 Diality Inc. - All Rights Reserved. +* 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. @@ -17,6 +17,7 @@ #include "BoostPump.h" #include "FPInterface.h" +#include "Flow.h" #include "FPModeStandby.h" #include "FPOperationModes.h" #include "Level.h" @@ -25,10 +26,12 @@ #include "ModeGenPermeate.h" #include "ModePreGenPermeate.h" #include "PermeateTank.h" +#include "PIControllers.h" #include "ROPump.h" #include "TaskGeneral.h" #include "Timers.h" #include "Valves.h" +#include "WaterQualityMonitor.h" /** * @addtogroup FPGenPermeateMode @@ -37,15 +40,29 @@ // ********** private definitions ********** -#define PRE_GEN_PERMEATE_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the gen permeate mode data published. -#define GEN_PERMEATE_BOOST_PUMP_TGT_PSI 25.0F ///< Pressure target in PSI for the boost pump during generate permeate mode. -#define GEN_PERMEATE_RO_PUMP_TGT_ML 700 ///< Flow target in ml/min for the ro pump during generate permeate mode. +#define PRE_GEN_PERMEATE_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the gen permeate mode data published. +#define GEN_PERMEATE_BOOST_PUMP_TGT_PSI 25.0F ///< Pressure target in PSI for the boost pump during generate permeate mode. +#define GEN_PERMEATE_RO_PUMP_TGT_ML 750 ///< Flow target in ml/min for the ro pump during generate permeate mode. +#define PUMP_REST_TIMEOUT_MS ( 3 * MS_PER_SECOND ) ///< Duraion for open loop control at the start of tank fill/full state ( in ms ) +#define RO_REJECTION_WAIT_TIME_MS ( 8 * MS_PER_SECOND ) ///< RO rejection alarm wait time. 5 seconds for RR to stabilize and 3 seconds for calculating rolling average ( in ms ) +#define MIN_SAMPLES_NEEDED_FOR_DUTY_CYCLE_AVG 10 ///< Minimum number for samples needed for calculating the average duty cycle +#define PERMEATE_FLOW_OUT_OF_RANGE_TIMEOUT_MS ( 12 * MS_PER_SECOND ) ///< Permeate flow low tolerance out of range timeout // ********** private data ********** static FP_GENP_MODE_STATE_T genPermeateState; ///< Currently active generate Permeate state. static U32 genPermeateDataPublicationTimerCounter; ///< Used to schedule generate Permeate data publication to CAN bus. static OVERRIDE_U32_T genPermeateDataPublishInterval; ///< Generate permeate mode data publish interval. +static F32 fillDutySum; ///< Sum of duty cycle values for one fill cycle. +static U32 fillDutyCount; ///< Sample counts for one fill cycle. +static F32 prevFillAvgDutyCycle; ///< Average vale of duty cycle during previous fill state. +static BOOL isFillAvgValid; ///< Flag to check if the average fill duty cycle value is valid or not. +static F32 fullDutySum; ///< Sum of duty cycle values for one full cycle. +static U32 fullDutyCount; ///< Sample counts for one full cycle. +static F32 prevFullAvgDutyCycle; ///< Average vale of duty cycle during previous full state. +static BOOL isFullAvgValid; ///< Flag to check if the average full duty cycle value is valid or not. +static U32 timeInState; ///< Time to wait after reset before starting close loop control (temporary) +static BOOL stateTransitioned; ///< Flag to check if permeate tank state transitioned // ********** private function prototypes ********** @@ -54,6 +71,8 @@ static FP_GENP_MODE_STATE_T handleGenPTankFillState( void ); static void setModeGenPTransition( FP_GENP_MODE_STATE_T state ); static U32 getGenPermeateDataPublishInterval( void ); +static void updateDutyCycleAvg( FP_GENP_MODE_STATE_T state ); +static void calculateDutyCycleAvg( FP_GENP_MODE_STATE_T state ); /*********************************************************************//** * @brief @@ -70,6 +89,16 @@ genPermeateDataPublishInterval.ovInitData = 0; genPermeateDataPublishInterval.override = OVERRIDE_RESET; genPermeateDataPublicationTimerCounter = 0; + fillDutySum = 0.0F; + fillDutyCount = 0; + prevFillAvgDutyCycle = 0.0F; + isFillAvgValid = FALSE; + fullDutySum = 0.0F; + fullDutyCount = 0; + prevFullAvgDutyCycle = 0.0F; + isFullAvgValid = FALSE; + timeInState = 0; + stateTransitioned = FALSE; } /*********************************************************************//** @@ -107,10 +136,12 @@ switch ( genPermeateState ) { case FP_GENP_TANK_FILL_STATE: + updateDutyCycleAvg( FP_GENP_TANK_FILL_STATE ); genPermeateState = handleGenPTankFillState(); break; case FP_GENP_TANK_FULL_STATE: + updateDutyCycleAvg( FP_GENP_TANK_FULL_STATE ); genPermeateState = handleGenPTankFullState(); break; @@ -122,9 +153,20 @@ if ( prevState != genPermeateState ) { + stateTransitioned = TRUE; + calculateDutyCycleAvg( prevState ); setModeGenPTransition( genPermeateState ); SEND_EVENT_WITH_2_U32_DATA( FP_EVENT_GENP_CHANGE, genPermeateState, prevState ) } + + checkInletPressures(); + + checkPermeateHighFlow(); + + checkPermeateLowFlow(); + + checkInletTemperatures(); + //Publish Gen Permeate mode data publishGenPModeData(); @@ -133,29 +175,88 @@ /*********************************************************************//** * @brief + * The requestGenWaterStart function handles an DD request to start (go to gen permeate mode). + * @details \b Inputs: none + * @details \b Outputs: none + * @return TRUE if request accepted, FALSE if not. + *************************************************************************/ +BOOL requestGenWaterStart( void ) +{ + BOOL result = TRUE; + requestNewFPOperationMode( FP_MODE_GENP ); + + return result; +} + +/*********************************************************************//** + * @brief + * The requestGenWaterStop function handles an DD request to stop (go to standby mode). + * @details \b Inputs: none + * @details \b Outputs: none + * @return TRUE if request accepted, FALSE if not. + *************************************************************************/ +BOOL requestGenWaterStop( void ) +{ + BOOL result = TRUE; + signalROPumpHardStop(); + + if ( TRUE == isBoostPumpInstalled() ) + { + signalBoostPumpHardStop(); + } + requestNewFPOperationMode( FP_MODE_STAN ); + + return result; +} + +/*********************************************************************//** + * @brief + * The getCurrentGenPermeateState function returns the current state of the + * gen permeate mode. + * @details \b Inputs: genPermeateState + * @details \b Outputs: genPermeateState + * @return the current state of gen permeate mode + *************************************************************************/ +FP_GENP_MODE_STATE_T getCurrentGenPermeateState( void ) +{ + return genPermeateState; +} + +/*********************************************************************//** + * @brief * The setModeGenPTransition function sets the actuators and variables * for the state transition in generate permeate mode. - * @details Inputs: Valve states, Pump speed + * @details Inputs: none * @details Outputs: Actuate valves, pumps as desired. * @param state gen permeate state enum * @return none *************************************************************************/ static void setModeGenPTransition( FP_GENP_MODE_STATE_T state ) { + F32 initDutyCycle = 0.0F; + // Execute on running state switch( state ) { case FP_GENP_TANK_FILL_STATE: + initDutyCycle = isFillAvgValid ? prevFillAvgDutyCycle : getCurrentROPumpDutyCyclePCT(); + setROPumpTargetDutyCycle( initDutyCycle, TRUE ); + timeInState = getMSTimerCount(); if ( TRUE == isBoostPumpInstalled() ) { setBoostPumpTargetPressure( GEN_PERMEATE_BOOST_PUMP_TGT_PSI ); } break; case FP_GENP_TANK_FULL_STATE: + initDutyCycle = isFullAvgValid ? prevFullAvgDutyCycle : getCurrentROPumpDutyCyclePCT(); + setROPumpTargetDutyCycle( initDutyCycle, TRUE ); + timeInState = getMSTimerCount(); if ( TRUE == isBoostPumpInstalled() ) { - signalBoostPumpHardStop(); + F32 currentDutyCyclePct = getCurrentBoostPumpDutyCyclePCT(); + + setBoostPumpTargetDutyCycle( currentDutyCyclePct ); } break; @@ -168,15 +269,26 @@ /*********************************************************************//** * @brief * The handleGenPTankFillState handles the fill state of gen water. - * @details \b Inputs: none - * @details \b Outputs: none + * @details \b Inputs: timeInState, stateTransitioned + * @details \b Outputs: stateTransitioned * @return the next state of gen water mode *************************************************************************/ static FP_GENP_MODE_STATE_T handleGenPTankFillState( void ) { FP_GENP_MODE_STATE_T state = FP_GENP_TANK_FILL_STATE; PERMEATE_TANK_STATE_T permemeateTankState = getPermeateTankState(); + // Wait for set timeout before transition to closed loop control + if ( TRUE == didTimeout( timeInState, PUMP_REST_TIMEOUT_MS ) && stateTransitioned == TRUE ) + { + stateTransitioned = FALSE; + setROPumpTargetFlowRateMLPM( GEN_PERMEATE_RO_PUMP_TGT_ML, TRUE ); + } + // Wait for RO rejection to stabilize after transition from full to fill + RO Rejection moving average duration + if ( TRUE == didTimeout( timeInState, RO_REJECTION_WAIT_TIME_MS ) ) + { + checkRORejectionRatio(); + } if ( permemeateTankState == PERMEATE_TANK_FULL_STATE ) { state = FP_GENP_TANK_FULL_STATE; @@ -188,15 +300,21 @@ /*********************************************************************//** * @brief * The handleGenPTankFullState handles the full state of gen permeate. - * @details \b Inputs: stateDelayTime - * @details \b Outputs: none + * @details \b Inputs: timeInState, stateTransitioned + * @details \b Outputs: stateTransitioned * @return the next state of gen permeate mode *************************************************************************/ static FP_GENP_MODE_STATE_T handleGenPTankFullState( void ) { FP_GENP_MODE_STATE_T state = FP_GENP_TANK_FULL_STATE; PERMEATE_TANK_STATE_T permemeateTankState = getPermeateTankState(); + // Wait for set timeout before transition to closed loop control + if ( TRUE == didTimeout( timeInState, PUMP_REST_TIMEOUT_MS ) && stateTransitioned == TRUE ) + { + stateTransitioned = FALSE; + setROPumpTargetFlowRateMLPM( GEN_PERMEATE_RO_PUMP_TGT_ML, TRUE ); + } if ( permemeateTankState == PERMEATE_TANK_FILL_STATE ) { state = FP_GENP_TANK_FILL_STATE; @@ -207,15 +325,56 @@ /*********************************************************************//** * @brief - * The getCurrentGenPermeateState function returns the current state of the - * gen permeate mode. - * @details \b Inputs: genPermeateState - * @details \b Outputs: genPermeateState - * @return the current state of gen permeate mode + * The updateDutyCycleAvg function accumulates duty cycle samples for + * states in generate permeate mode. + * @details \b Inputs: none + * @details \b Outputs: fillDutySum, fillDutyCount, fullDutySum, fullDutyCount + * @param state genPermeateState enum + * @return none +*************************************************************************/ +static void updateDutyCycleAvg( FP_GENP_MODE_STATE_T state ) +{ + F32 duty = getCurrentROPumpDutyCyclePCT(); + + if ( state == FP_GENP_TANK_FILL_STATE ) + { + fillDutySum += duty; + fillDutyCount++; + } + else if ( state == FP_GENP_TANK_FULL_STATE ) + { + fullDutySum += duty; + fullDutyCount++; + } +} + +/*********************************************************************//** + * @brief + * The calculateDutyCycleAvg function computes the average duty cycle + * from the accumulated samples collected using updateDutyCyle. + * @details \b Inputs: fillDutyCount, fullDutyCount + * @details \b Outputs: prevFillAvgDutyCycle, isFillAvgValid, fillDutySum, + * fillDutyCount, prevFullAvgDutyCycle, isFullAvgValid, fullDutySum, + * fullDutyCount + * @param state genPermeateState enum + * @return none *************************************************************************/ -FP_GENP_MODE_STATE_T getCurrentGenPermeateState( void ) +static void calculateDutyCycleAvg( FP_GENP_MODE_STATE_T state ) { - return genPermeateState; + if ( state == FP_GENP_TANK_FILL_STATE && fillDutyCount > MIN_SAMPLES_NEEDED_FOR_DUTY_CYCLE_AVG) + { + prevFillAvgDutyCycle = fillDutySum / (F32)fillDutyCount; + isFillAvgValid = TRUE; + fillDutySum = 0.0F; + fillDutyCount = 0; + } + else if ( state == FP_GENP_TANK_FULL_STATE && fullDutyCount > MIN_SAMPLES_NEEDED_FOR_DUTY_CYCLE_AVG ) + { + prevFullAvgDutyCycle = fullDutySum / (F32)fullDutyCount; + isFullAvgValid = TRUE; + fullDutySum = 0.0F; + fullDutyCount = 0; + } } /*********************************************************************//** @@ -258,43 +417,7 @@ } } -/*********************************************************************//** - * @brief - * The requestGenWaterStart function handles an DD request to start (go to gen permeate mode). - * @details \b Inputs: none - * @details \b Outputs: none - * @return TRUE if request accepted, FALSE if not. - *************************************************************************/ -BOOL requestGenWaterStart( void ) -{ - BOOL result = TRUE; - requestNewFPOperationMode( FP_MODE_GENP ); - return result; -} - -/*********************************************************************//** - * @brief - * The requestGenWaterStop function handles an DD request to stop (go to standby mode). - * @details \b Inputs: none - * @details \b Outputs: none - * @return TRUE if request accepted, FALSE if not. - *************************************************************************/ -BOOL requestGenWaterStop( void ) -{ - BOOL result = TRUE; - signalROPumpHardStop(); - - if ( TRUE == isBoostPumpInstalled() ) - { - signalBoostPumpHardStop(); - } - requestNewFPOperationMode( FP_MODE_STAN ); - - return result; -} - - /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/