/************************************************************************** * * Copyright (c) 2020-2024 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file PermeateTank.c * * @author (last) Sean Nash * @date (last) 12-Nov-2024 * * @author (original) Sean Nash * @date (original) 12-Nov-2024 * ***************************************************************************/ #include "FPOperationModes.h" #include "Level.h" #include "Messaging.h" #include "PermeateTank.h" #include "TaskGeneral.h" #include "TDInterface.h" #include "Timers.h" #include "Utilities.h" #include "Valves.h" /** * @addtogroup FPPermeateTank * @{ */ // ********** private definitions ********** #define PERMEATE_TANK_PUMP_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) for permeate tank broadcast #define PERMEATE_TANK_PUBLISH_COUNTER_START_COUNT 9 ///< Publishing counter offset #define PERMEATE_TANK_FULL_SWITCH_MS ( 1 * MS_PER_SECOND ) ///< State switch timeout in full state (in ms) #define PERMEATE_TANK_EMPTY_LEVEL_VOL_ML 0 ///< Permeate tank volume in empty level ( in ml ) #define PERMEATE_TANK_LOW_LEVEL_VOL_ML 957 ///< Permeate tank volume in low level ( in ml ) #define PERMEATE_TANK_HIGH_LEVEL_VOL_ML 1345 ///< Permeate tank volume in high level ( in ml ) #define PERMEATE_TANK_FULL_LEVEL_VOL_ML 1532 ///< Permeate tank volume in full level ( in ml ) #define RINSE_PUMP_TARGET_FLOW 228.89 ///< Rinse pump rate // ********** private data ********** static PERMEATE_TANK_STATE_T permeateTankControllerState; ///< Current state of permeate controller state machine. static U32 permeateTankDataPublicationTimerCounter; ///< Used to schedule permeate tank data publication to CAN bus. static OVERRIDE_U32_T permeateTankPublishInterval; ///< Interval (in ms) at which to publish permeate tank data to CAN bus. static BOOL pendingStartPermeateTankController; ///< Flag indicates an air trap controller start request is pending. static BOOL pendingStopPermeateTankController; ///< Flag indicates an air trap controller stop request is pending. static U32 tankFullAlarmTimer; ///< Time stamp to track alarm timeout during tank full. static U32 tankFillAlarmTimer; ///< Time stamp to track alarm timeout during tank fill. static U32 tankFullDelayTime; ///< Time stamp to track delay before valve switch. static F32 tankFullAlarmTimeout; ///< Permeate tank full alarm timeout static F32 tankFillAlarmTimeout; ///< Permeate tank fill alarm timeout // ********** private function prototypes ********** static PERMEATE_TANK_STATE_T handlePermeateTankManualControlState( void ); static PERMEATE_TANK_STATE_T handlePermeateTankFillState( void ); static PERMEATE_TANK_STATE_T handlePermeateTankFullState( void ); static void setPermeateTankTransition( PERMEATE_TANK_STATE_T state ); static void publishPermeateTankData( void ); /*********************************************************************//** * @brief * The initPermeateTank function initializes the permeate tank controller unit. * @details \b Inputs: none * @details \b Outputs: permeate tank controller unit initialized * @return none *************************************************************************/ void initPermeateTank( void ) { resetPermeateTank(); permeateTankDataPublicationTimerCounter = PERMEATE_TANK_PUBLISH_COUNTER_START_COUNT; permeateTankPublishInterval.data = PERMEATE_TANK_PUMP_DATA_PUB_INTERVAL; permeateTankPublishInterval.ovData = PERMEATE_TANK_PUMP_DATA_PUB_INTERVAL; permeateTankPublishInterval.ovInitData = PERMEATE_TANK_PUMP_DATA_PUB_INTERVAL; permeateTankPublishInterval.override = OVERRIDE_RESET; tankFullDelayTime = 0; tankFullAlarmTimer = 0; tankFillAlarmTimer = 0; tankFullAlarmTimeout = 0.0F; tankFillAlarmTimeout = 0.0F; } /*********************************************************************//** * @brief * The resetPermeateTank function resets certain parts of the permeate tank module * between water generation. * @details \b Inputs: none * @details \b Outputs: Permeate Tank controller reset. * @return none *************************************************************************/ void resetPermeateTank( void ) { permeateTankControllerState = PERMEATE_TANK_INIT_STATE; pendingStartPermeateTankController = FALSE; pendingStopPermeateTankController = FALSE; } /*********************************************************************//** * @brief * The startPermeateTankControl function requests a start to permeate tank control. * @details \b Inputs: permeateTankControllerState * @details \b Outputs: pendingStartPermeateTankController * @return none *************************************************************************/ void startPermeateTankControl( void ) { if ( FALSE == isPermeateTankControlling() ) { pendingStartPermeateTankController = TRUE; } } /*********************************************************************//** * @brief * The endPermeateTankControl function requests a stop to permeate tank control. * @details \b Inputs: permeateTankControllerState * @details \b Outputs: pendingStopPermeateTankController * @return none *************************************************************************/ void endPermeateTankControl( void ) { if ( TRUE == isPermeateTankControlling() ) { pendingStopPermeateTankController = TRUE; } } /*********************************************************************//** * @brief * The isPermeateTankControlling function determines whether the permeate tank is * currently controlling. * @details \b Inputs: permeateTankControllerState * @details \b Outputs: none * @return TRUE if air trap is currently controlling, FALSE if not. *************************************************************************/ BOOL isPermeateTankControlling( void ) { BOOL result = FALSE; if ( permeateTankControllerState > PERMEATE_TANK_MANUAL_CONTROL_STATE ) { result = TRUE; } return result; } /*********************************************************************//** * @brief * The getPermeateTankState function returns the current state of the * permeate tank controller. * @details \b Inputs: permeateTankControllerState * @details \b Outputs: none * @return the current state of permeate tank *************************************************************************/ PERMEATE_TANK_STATE_T getPermeateTankState( void ) { return permeateTankControllerState; } /*********************************************************************//** * @brief * The getTankFullAlarmTimeout function returns the tank full alarm timeout * @details \b Inputs: none * @details \b Outputs: tankFullAlarmTimeout * @return the alarm timeout for tank full *************************************************************************/ F32 getTankFullAlarmTimeout( void ) { F32 expectedDeprimeTime = 0.0F; F32 expectedTankFullTime = 0.0F; F32 ddConsumptionRate = getTDDialysateFlowrate() + RINSE_PUMP_TARGET_FLOW; expectedTankFullTime = ( ( PERMEATE_TANK_HIGH_LEVEL_VOL_ML - PERMEATE_TANK_LOW_LEVEL_VOL_ML ) / ddConsumptionRate ) * SEC_PER_MIN; expectedDeprimeTime = ( ( PERMEATE_TANK_LOW_LEVEL_VOL_ML - PERMEATE_TANK_EMPTY_LEVEL_VOL_ML ) / ddConsumptionRate ) * SEC_PER_MIN; tankFullAlarmTimeout = expectedTankFullTime + expectedDeprimeTime; return tankFullAlarmTimeout; } /*********************************************************************//** * @brief * The getTankFillAlarmTimeout function returns the tank fill alarm timeout * @details \b Inputs: none * @details \b Outputs: tankFillAlarmTimeout * @return the alarm timeout for tank fill *************************************************************************/ F32 getTankFillAlarmTimeout( void ) { F32 expectedOverfillTime = 0.0F; F32 expectedTankFillTime = 0.0F; F32 ddConsumptionRate = getTDDialysateFlowrate() + RINSE_PUMP_TARGET_FLOW; expectedTankFillTime = ( ( PERMEATE_TANK_HIGH_LEVEL_VOL_ML - PERMEATE_TANK_LOW_LEVEL_VOL_ML ) / ddConsumptionRate ) * SEC_PER_MIN; expectedOverfillTime = ( ( PERMEATE_TANK_FULL_LEVEL_VOL_ML - PERMEATE_TANK_HIGH_LEVEL_VOL_ML ) / ddConsumptionRate ) * SEC_PER_MIN; tankFillAlarmTimeout = ( expectedTankFillTime + ( expectedOverfillTime / 2 ) ); return tankFillAlarmTimeout; } /*********************************************************************//** * @brief * The execPermeateTankController function executes the air trap control state machine. * @details \b Alarm: ALARM_ID_FP_SOFTWARE_FAULT if current permeate tank control * state is invalid. * @details \b Message \b Sent: MSG_ID_TD_EVENT if air trap valve closed due to fault. * @details \b Inputs: permeateTankControllerState * @details \b Outputs: permeateTankControllerState * @return none *************************************************************************/ void execPermeateTankController( void ) { PERMEATE_TANK_STATE_T prevState = permeateTankControllerState; // If we have faulted, close valve and go to manual control if ( FP_MODE_FAUL == getCurrentFPOperationMode() ) { endPermeateTankControl(); } // Execute air trap state machine switch( permeateTankControllerState ) { case PERMEATE_TANK_INIT_STATE: permeateTankControllerState = PERMEATE_TANK_MANUAL_CONTROL_STATE; break; case PERMEATE_TANK_MANUAL_CONTROL_STATE: permeateTankControllerState = handlePermeateTankManualControlState(); break; case PERMEATE_TANK_FILL_STATE: permeateTankControllerState = handlePermeateTankFillState(); break; case PERMEATE_TANK_FULL_STATE: permeateTankControllerState = handlePermeateTankFullState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_FP_SOFTWARE_FAULT, (U32)FP_FAULT_ID_FP_INVALID_PERMEATE_TANK_STATE, (U32)permeateTankControllerState ) permeateTankControllerState = PERMEATE_TANK_INIT_STATE; break; } if ( prevState != permeateTankControllerState ) { setPermeateTankTransition( permeateTankControllerState ); } // Publish permeate tank data if due publishPermeateTankData(); } /*********************************************************************//** * @brief * The handlePermeateTankManualControlState function handles the manual control * state of the permeate tank. * @details \b Inputs: pendingStartPermeateTankController * @details \b Outputs: pendingStartPermeateTankController * @return next state *************************************************************************/ static PERMEATE_TANK_STATE_T handlePermeateTankManualControlState( void ) { PERMEATE_TANK_STATE_T state = PERMEATE_TANK_MANUAL_CONTROL_STATE; // Transition to valve control states when requested if ( TRUE == pendingStartPermeateTankController ) { pendingStartPermeateTankController = FALSE; state = PERMEATE_TANK_FILL_STATE; } return state; } /*********************************************************************//** * @brief * The handleAirTrapRaiseLevelState function handles the raise level state * of the permeate tank. * @details \b Message \b Sent: MSG_ID_TD_EVENT if raising level completed * @details \b Inputs: pendingStopPermeateTankController * @details \b Outputs: pendingStopPermeateTankController * @return next state *************************************************************************/ static PERMEATE_TANK_STATE_T handlePermeateTankFillState( void ) { PERMEATE_TANK_STATE_T state = PERMEATE_TANK_FILL_STATE; LEVEL_STATE_T level = getLevelStatus( P25_LEVL ); if ( TRUE == didTimeout( tankFillAlarmTimer, getTankFillAlarmTimeout() ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_FP_GEN_PERMEATE_TANK_FILL_TIMEOUT, level ) } // Transition to manual valve control state when requested if ( TRUE == pendingStopPermeateTankController ) { pendingStopPermeateTankController = FALSE; state = PERMEATE_TANK_MANUAL_CONTROL_STATE; } else if ( LEVEL_STATE_HIGH == level ) { state = PERMEATE_TANK_FULL_STATE; } return state; } /*********************************************************************//** * @brief * The handlePermeateTankFullState function handles the lower level state * of the permeate tank. * @details \b Inputs: tankFullAlarmTimeout, pendingStopPermeateTankController, tankFullDelayTime * @details \b Outputs: pendingStopPermeateTankController * @return next state *************************************************************************/ static PERMEATE_TANK_STATE_T handlePermeateTankFullState( void ) { PERMEATE_TANK_STATE_T state = PERMEATE_TANK_FULL_STATE; LEVEL_STATE_T level = getLevelStatus( P25_LEVL ); if ( TRUE == didTimeout( tankFullAlarmTimer, getTankFullAlarmTimeout() ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_FP_GEN_PERMEATE_TANK_FULL_TIMEOUT, level ) } // Transition to manual valve control state when requested if ( TRUE == pendingStopPermeateTankController ) { pendingStopPermeateTankController = FALSE; state = PERMEATE_TANK_MANUAL_CONTROL_STATE; } else if ( level == LEVEL_STATE_LOW ) { if ( TRUE == didTimeout( tankFullDelayTime, PERMEATE_TANK_FULL_SWITCH_MS ) ) { state = PERMEATE_TANK_FILL_STATE; } } return state; } /*********************************************************************//** * @brief * The setPermeateTankTransition function sets the actuators and variables * for the state transition in permeate tank controller. * @details Inputs: Valve states * @details Outputs: Actuate valves * @param state permeate tank state enum * @return none *************************************************************************/ static void setPermeateTankTransition( PERMEATE_TANK_STATE_T state ) { // Execute on running state switch( state ) { case PERMEATE_TANK_INIT_STATE: case PERMEATE_TANK_MANUAL_CONTROL_STATE: break; case PERMEATE_TANK_FILL_STATE: setValveState( M4_VALV,VALVE_STATE_OPEN ); setValveState( M12_VALV, VALVE_STATE_OPEN ); setValveState( P6_VALV, VALVE_STATE_CLOSED ); setValveState( P11_VALV, VALVE_STATE_OPEN ); setValveState( P33_VALV, VALVE_STATE_OPEN ); // TODO - Change valves to handle RO rejection config setValveState( P34_VALV, VALVE_STATE_CLOSED ); // Current set to Medium recovery for alpha HW setValveState( P37_VALV, VALVE_STATE_CLOSED ); setValveState( P39_VALV, VALVE_STATE_OPEN ); tankFillAlarmTimer = getMSTimerCount(); break; case PERMEATE_TANK_FULL_STATE: setValveState( M4_VALV, VALVE_STATE_CLOSED ); setValveState( M12_VALV, VALVE_STATE_CLOSED ); setValveState( P6_VALV, VALVE_STATE_OPEN ); setValveState( P11_VALV, VALVE_STATE_OPEN ); setValveState( P33_VALV, VALVE_STATE_OPEN ); setValveState( P34_VALV, VALVE_STATE_CLOSED ); setValveState( P37_VALV, VALVE_STATE_CLOSED ); setValveState( P39_VALV, VALVE_STATE_CLOSED ); tankFullDelayTime = getMSTimerCount(); tankFullAlarmTimer = getMSTimerCount(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_FP_SOFTWARE_FAULT, FP_FAULT_ID_FP_INVALID_GENP_STATE, state ) break; } } /*********************************************************************//** * @brief * The publishPermeateTankData function publishes permeate tank at the set interval. * @details \b Message \b Sent: MSG_ID_FP_PERMEATE_TANK_DATA * @details \b Inputs: permeateTankDataPublicationTimerCounter * @details \b Outputs: permeateTankDataPublicationTimerCounter * @return none *************************************************************************/ static void publishPermeateTankData( void ) { // publish RO pump data on interval if ( ++permeateTankDataPublicationTimerCounter >= getU32OverrideValue( &permeateTankPublishInterval ) ) { PERMEATE_TANK_DATA_T data; data.tankState = permeateTankControllerState; broadcastData( MSG_ID_FP_PERMEATE_TANK_DATA, COMM_BUFFER_OUT_CAN_FP_BROADCAST, (U08*)&data, sizeof( PERMEATE_TANK_DATA_T ) ); permeateTankDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testPermeateTankDataPublishIntervalOverride function overrides the * permeate tank data publish interval. * @details \b Inputs: permeateTankPublishInterval * @details \b Outputs: permeateTankPublishInterval * @param message Override message from Dialin which includes the value * that override permeate tank data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testPermeateTankDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &permeateTankPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /**@}*/