/************************************************************************** * * Copyright (c) 2025-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 DryBiCart.c * * @author (original) Sameer poyil * @date (original) 23-Oct-2025 * * @author (original) Sameer poyil * @date (original) 23-Oct-2025 * ***************************************************************************/ #include "Common.h" #include "DDDefs.h" #include "DryBiCart.h" #include "Level.h" #include "Messaging.h" #include "OperationModes.h" #include "PressureSensor.h" #include "PersistentAlarm.h" #include "Pressure.h" #include "TaskGeneral.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup DryBiCart * @{ */ // ********** private definitions ********** #define DRY_BICART_DATA_PUBLISH_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the balancing chamber data published. #define DRY_BICART_FILL_MAX_PRESSURE 12.0F ///< Maximum pressure that drybicart can withstand. #define DRY_BICART_FILL_COMPLETE_PRESSURE 10.0F ///< Maximum pressure reached to inidcate the drybicart fill being completed. #define DRY_BICART_FILL_INITIATE_PRESSURE 5.0F ///< Minimum pressure required to initiate the drybicart fill process. #define DRY_BICART_VENT_COMPLETE_PRESSURE 0.0F ///< Pressure reached to inidcate the drybicart venting being completed. #define DRY_BICART_VENT_TIME_MS ( 1 * MS_PER_SECOND ) ///< Wait time to vent dry bibag gas before actuating Bicarb chamber(F) venting. #define DRY_BICART_VENT_MAX_TIME_MS ( 10 * MS_PER_SECOND ) ///< Max time to vent both bicart and Chamber F. #define DRY_BICART_FILL_DURATION_DIFF_MS 500 ///< Fill duration difference between last and current fill cycle. #define DRY_BICART_DEFAULT_MAX_FILL_CYCLE_CNT 10 ///< Default max fill cycle allowed for dry bicart fill/mix with water. #define DRY_BICART_MAX_FILL_CYCLE_CNT 30 ///< Max fill cycle allowed (by override) for dry bicart fill/mix with water. /// Payload record structure for dry bicart fill request typedef struct { U32 startStop; ///< Dry bicart fill request start:1 and stop: 0 } DRY_BICART_FILL_START_CMD_PAYLOAD_T; // ********** private data ********** static DRY_BICART_FILL_EXEC_STATE_T dryBiCartFillExecState; ///< Current state of dry bicart fill executive state machine. static BICARB_CHAMBER_FILL_EXEC_STATE_T bicarbChamberFillExecState; ///< Current state of bicarb chamber fill executive state machine. static U32 dryBiCartFillStartTime; ///< Dry bicart fill start time. static U32 biCartFillCycleCounter; ///< Number of drybicart fill cycle static OVERRIDE_U32_T biCartMaxFillCycleCount; ///< Maximum number of drybicart fill cycle ( overrideable) static U32 lastFillDurationInMS; ///< Previous time duration to fill the bicart fill. static U32 currentFillDurationInMS; ///< Current time duartion to fill the bicart fill. static OVERRIDE_U32_T dryBiCartFillRequested; ///< Start/stop Dry bicart fill. static OVERRIDE_U32_T bicarbChamberFillRequested; ///< Start/stop Bicarb chamber fill. static OVERRIDE_U32_T dryBiCartDataPublishInterval; ///< Dry bicart data publish interval. static U32 dryBiCartDataPublicationTimerCounter; ///< Used to schedule drybicart data publication to CAN bus. // ********** private function prototypes ********** static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillWaterStartState(void); static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillWaterEndState(void); static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartDegasStartState(void); static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartDegasEndState(void); static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillDurationCheckState(void); static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillCompleteState(void); static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberCheckLevelState( void ); static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberFillState( void ); static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberPressureCheckState( void ); static void publishDryBicartData( void ); static U32 getDryBicartFillDataPublishInterval( void ); /*********************************************************************//** * @brief * The The initDryBiCart function initializes the DryBiCart module. * @details \b Inputs: none * @details \b Outputs: unit variables initialized * @return none *************************************************************************/ void initDryBiCart( void ) { dryBiCartFillExecState = DRY_BICART_START_STATE; bicarbChamberFillExecState = BICARB_CHAMBER_START_STATE; dryBiCartDataPublishInterval.data = DRY_BICART_DATA_PUBLISH_INTERVAL; dryBiCartDataPublishInterval.ovData = DRY_BICART_DATA_PUBLISH_INTERVAL; dryBiCartDataPublishInterval.ovInitData = 0; dryBiCartDataPublishInterval.override = OVERRIDE_RESET; biCartMaxFillCycleCount.data = DRY_BICART_MAX_FILL_CYCLE_CNT; biCartMaxFillCycleCount.ovData = DRY_BICART_MAX_FILL_CYCLE_CNT; biCartMaxFillCycleCount.ovInitData = 0; biCartMaxFillCycleCount.override = OVERRIDE_RESET; dryBiCartFillRequested.data = FALSE; dryBiCartFillRequested.ovData = FALSE; dryBiCartFillRequested.ovInitData = FALSE; dryBiCartFillRequested.override = 0; bicarbChamberFillRequested.data = FALSE; bicarbChamberFillRequested.ovData = FALSE; bicarbChamberFillRequested.ovInitData = FALSE; bicarbChamberFillRequested.override = 0; dryBiCartFillStartTime = 0; lastFillDurationInMS = 0; currentFillDurationInMS = 0; dryBiCartDataPublicationTimerCounter = 0; biCartFillCycleCounter = 0; } /*********************************************************************//** * @brief * The transitionToDryBicart function prepares for transition to * dry bicart fill/degas operations. * @details \b Inputs: none * @details \b Outputs: dryBiCartFillExecState,dryBiCartDataPublishInterval, * biCartFillCycleCounter, dryBiCartFillStartTime * @return none *************************************************************************/ void transitionToDryBicart( void ) { initDryBiCart(); } /*********************************************************************//** * @brief * The execDryBicart function executes the set of dry bicart related state machines. * @details \b Inputs: none * @details \b Outputs: none * @return none. *************************************************************************/ void execDryBicart( void ) { // Dry bicart fill exec execDryBicartFillMode(); //Fill Bicarb chamber F execBicarbChamberFillMode(); //Publish dry bicart data publishDryBicartData(); } /*********************************************************************//** * @brief * The execDryBicartFillMode function executes the dry bicart fill (mixing water * with dry sodium bicarbonate ) state machine. * @details \b Inputs: dryBiCartFillExecState * @details \b Outputs: dryBiCartFillExecState * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong dry bicart fill state * invoked. * @return current state. *************************************************************************/ U32 execDryBicartFillMode( void ) { // execute drybicart fill state machine switch ( dryBiCartFillExecState ) { case DRY_BICART_START_STATE: if ( TRUE == getU32OverrideValue( &dryBiCartFillRequested ) ) { dryBiCartFillExecState = DRY_BICART_FILL_WATER_START_STATE; } break; case DRY_BICART_FILL_WATER_START_STATE: dryBiCartFillExecState = handleDryBicartFillWaterStartState(); break; case DRY_BICART_FILL_WATER_END_STATE: dryBiCartFillExecState = handleDryBicartFillWaterEndState(); break; case DRY_BICART_DEGAS_START_STATE: dryBiCartFillExecState = handleDryBicartDegasStartState(); break; case DRY_BICART_DEGAS_END_STATE: dryBiCartFillExecState = handleDryBicartDegasEndState(); break; case DRY_BICART_FILL_DURATION_CHECK_STATE: dryBiCartFillExecState = handleDryBicartFillDurationCheckState(); break; case DRY_BICART_FILL_COMPLETE_STATE: dryBiCartFillExecState = handleDryBicartFillCompleteState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_DRY_BICART_FILL_INVALID_EXEC_STATE, dryBiCartFillExecState ); break; } return dryBiCartFillExecState; } /*********************************************************************//** * @brief * The handleDryBicartFillWaterStartState function initiates the water fill * into dry bicarbonate cartridge. * @details \b Inputs: none * @details \b Outputs: valve states * @return the next drybicart fill state. *************************************************************************/ static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillWaterStartState( void ) { DRY_BICART_FILL_EXEC_STATE_T state = DRY_BICART_FILL_WATER_START_STATE; // Close vent valves and descaling valve setValveState( D80_VALV, VALVE_STATE_CLOSED ); setValveState( D81_VALV, VALVE_STATE_CLOSED ); setValveState( D64_VALV, VALVE_STATE_CLOSED ); setValveState( D85_VALV, VALVE_STATE_CLOSED ); //Open D65 setValveState( D65_VALV, VALVE_STATE_OPEN ); //Initiate timer to measure length of fill dryBiCartFillStartTime = getMSTimerCount(); state = DRY_BICART_FILL_WATER_END_STATE; return state; } /*********************************************************************//** * @brief * The handleDryBicartFillWaterEndState function closes the valve opened * for Dry bicart fill. * @details \b Inputs: D66 pressure sensor reading * @details \b Outputs: valve states * @return the next drybicart fill state. *************************************************************************/ static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillWaterEndState( void ) { DRY_BICART_FILL_EXEC_STATE_T state = DRY_BICART_FILL_WATER_END_STATE; F32 d66Pressure = getFilteredPressure( D66_PRES ); // Close dry bicart inlet water if bicart is filled with water if ( d66Pressure >= DRY_BICART_FILL_COMPLETE_PRESSURE ) { setValveState( D65_VALV, VALVE_STATE_CLOSED ); lastFillDurationInMS = currentFillDurationInMS; currentFillDurationInMS = calcTimeSince( dryBiCartFillStartTime ); //Increment number of fill cycle biCartFillCycleCounter++; state = DRY_BICART_DEGAS_START_STATE; } //TODO:Handle timeout alarm for opening D65 valve too long return state; } /*********************************************************************//** * @brief * The handleDryBicartDegasStartState function actuates the vent valve * present in the Bicart assembly. * @details \b Inputs: none * @details \b Outputs: valve states * @return the next drybicart fill state. *************************************************************************/ static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartDegasStartState( void ) { DRY_BICART_FILL_EXEC_STATE_T state = DRY_BICART_DEGAS_START_STATE; //Vent bicart setValveState( D85_VALV, VALVE_STATE_OPEN ); //Initiate timer to measure length of bicart vent dryBiCartFillStartTime = getMSTimerCount(); state = DRY_BICART_DEGAS_END_STATE; return state; } /*********************************************************************//** * @brief * The handleDryBicartDegasEndState function open vent valve present in * the F Chamber (Bicarb) and closes the vent valve once pressure is met. * @details \b Inputs: D66 pressure sensor reading * @details \b Outputs: valve states * @return the next drybicart fill state. *************************************************************************/ static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartDegasEndState( void ) { DRY_BICART_FILL_EXEC_STATE_T state = DRY_BICART_DEGAS_END_STATE; F32 d66Pressure = getFilteredPressure( D66_PRES ); if ( TRUE == didTimeout( dryBiCartFillStartTime, DRY_BICART_VENT_TIME_MS ) ) { //Vent chamber F setValveState( D64_VALV, VALVE_STATE_OPEN ); dryBiCartFillStartTime = getMSTimerCount(); } // D66 pressure drops or time out if ( ( d66Pressure <= DRY_BICART_VENT_COMPLETE_PRESSURE ) || ( TRUE == didTimeout( dryBiCartFillStartTime, DRY_BICART_VENT_MAX_TIME_MS ) ) ) { setValveState( D64_VALV, VALVE_STATE_CLOSED ); setValveState( D85_VALV, VALVE_STATE_CLOSED ); state = DRY_BICART_FILL_DURATION_CHECK_STATE; } //TODO: Alarm when vent timeout exceeded, but pressure not dropped below expected PSI (0 psi) return state; } /*********************************************************************//** * @brief * The handleDryBicartFillDurationCheckState function checks the fill duration * between last and current fill and exits if the duration is reduced over period * or number of fill cycle done. * @details \b Inputs: none * @details \b Outputs: valve states * @return the next drybicart fill state. *************************************************************************/ static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillDurationCheckState( void ) { DRY_BICART_FILL_EXEC_STATE_T state = DRY_BICART_FILL_DURATION_CHECK_STATE; U32 diffInFillDuration = calcTimeBetween( currentFillDurationInMS ,lastFillDurationInMS ); U32 dryBiCartMaxFillCount = getU32OverrideValue( &biCartMaxFillCycleCount ); F32 d66Pressure = getFilteredPressure( D66_PRES ); // Fill time shortens over the period or number of fill cycle exceeded if ( ( diffInFillDuration <= DRY_BICART_FILL_DURATION_DIFF_MS ) || ( biCartFillCycleCounter >= dryBiCartMaxFillCount ) ) { state = DRY_BICART_FILL_COMPLETE_STATE; } else { // Stop drycart fill if fill request flag is set to false. if ( FALSE == getU32OverrideValue( &dryBiCartFillRequested ) ) { state = DRY_BICART_START_STATE; } else { // transition to next fill cycle and degas state = DRY_BICART_FILL_WATER_START_STATE; } } return state; } /*********************************************************************//** * @brief * The handleDryBicartFillCompleteState function terminal for the dry bicart * fill states. * @details \b Inputs: none * @details \b Outputs: valve states * @return the next drybicart fill state. *************************************************************************/ static DRY_BICART_FILL_EXEC_STATE_T handleDryBicartFillCompleteState( void ) { DRY_BICART_FILL_EXEC_STATE_T state = DRY_BICART_FILL_COMPLETE_STATE; //TODO : Do nothing or any cleanup later return state; } /*********************************************************************//** * @brief * The getCurrentDryBiCartFillExecState function returns the current state * of the dry bicart fill state. * @details \b Inputs: dryBiCartFillExecState * @details \b Outputs: none * @return the current state of dry bicart fill states. *************************************************************************/ DRY_BICART_FILL_EXEC_STATE_T getCurrentDryBiCartFillExecState( void ) { return dryBiCartFillExecState; } /*********************************************************************//** * @brief * The getDryBicartFillDataPublishInterval function gets the dry bicart * fill data publish interval. * @details \b Inputs: dryBiCartDataPublishInterval * @details \b Outputs: none * @return the interval at dry bicart fill data being published. *************************************************************************/ static U32 getDryBicartFillDataPublishInterval( void ) { U32 result = getU32OverrideValue( &dryBiCartDataPublishInterval ); return result; } /*********************************************************************//** * @brief * The execBicarbChamberFillMode function executes the bicarb chamber fill * (delivering bicarbonate from Bicart assembly to chamber F)state machine. * @details \b Inputs: bicarbChamberFillExecState * @details \b Outputs: bicarbChamberFillExecState * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong bicarb chamber * fill state invoked. * @return current state. *************************************************************************/ U32 execBicarbChamberFillMode( void ) { // execute bicarb chamber fill state machine switch ( bicarbChamberFillExecState ) { case BICARB_CHAMBER_START_STATE: if ( TRUE == getU32OverrideValue( &bicarbChamberFillRequested ) ) { bicarbChamberFillExecState = BICARB_CHAMBER_CHECK_LEVEL_STATE; } break; case BICARB_CHAMBER_CHECK_LEVEL_STATE: bicarbChamberFillExecState = handleBicarbChamberCheckLevelState(); break; case BICARB_CHAMBER_FILL_STATE: bicarbChamberFillExecState = handleBicarbChamberFillState(); break; case BICARB_CHAMBER_PRESSURE_CHECK_STATE: bicarbChamberFillExecState = handleBicarbChamberPressureCheckState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_BICARB_CHAMBER_FILL_INVALID_EXEC_STATE, bicarbChamberFillExecState ); break; } return bicarbChamberFillExecState; } /*********************************************************************//** * @brief * The handleBicarbChamberCheckLevelState function checks the bicarb level(D63) * in the chamber F and decides to open the bicarb inlet valve (D80). * @details \b Inputs: D63 level * @details \b Outputs: valve states * @return the next bicarb chamber fill state. *************************************************************************/ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberCheckLevelState( void ) { BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CHAMBER_CHECK_LEVEL_STATE; LEVEL_STATE_T bicarbChamberLevel = getLevelStatus( D63_LEVL ); // Close vent valves and descaling valve setValveState( D81_VALV, VALVE_STATE_CLOSED ); setValveState( D64_VALV, VALVE_STATE_CLOSED ); setValveState( D85_VALV, VALVE_STATE_CLOSED ); // TODO : Confirm D80 open on chamber low or end of balancing chamber switching? if ( LEVEL_STATE_LOW == bicarbChamberLevel ) { // Open the Bicarb chamber inlet valve setValveState( D80_VALV, VALVE_STATE_OPEN ); state = BICARB_CHAMBER_FILL_STATE; } return state; } /*********************************************************************//** * @brief * The handleBicarbChamberFillState function checks the D66 pressure & * decides to open D65 and Close the inlet valve (D80) once level reached * @details \b Inputs: D66 pressure sensor reading * @details \b Outputs: valve states * @return the next bicarb chamber fill state. *************************************************************************/ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberFillState( void ) { BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CHAMBER_FILL_STATE; F32 d66Pressure = getFilteredPressure( D66_PRES ); LEVEL_STATE_T bicarbChamberLevel = getLevelStatus( D63_LEVL ); // Once level reached, close the valve if ( LEVEL_STATE_HIGH == bicarbChamberLevel ) { setValveState( D80_VALV, VALVE_STATE_CLOSED ); state = BICARB_CHAMBER_START_STATE; } // Open water inlet valve to fill the bicart else if ( d66Pressure <= DRY_BICART_FILL_INITIATE_PRESSURE ) { setValveState( D65_VALV, VALVE_STATE_OPEN ); state = BICARB_CHAMBER_PRESSURE_CHECK_STATE; } return state; } /*********************************************************************//** * @brief * The handleBicarbChamberPressureCheckStartState function actuates the vent valve * present in the Bicart assembly. * @details \b Inputs: none * @details \b Outputs: valve states * @return the next bicarb chamber fill state. *************************************************************************/ static BICARB_CHAMBER_FILL_EXEC_STATE_T handleBicarbChamberPressureCheckState( void ) { BICARB_CHAMBER_FILL_EXEC_STATE_T state = BICARB_CHAMBER_PRESSURE_CHECK_STATE; F32 d66Pressure = getFilteredPressure( D66_PRES ); LEVEL_STATE_T bicarbChamberLevel = getLevelStatus( D63_LEVL ); // Once level reached, close the valve if ( LEVEL_STATE_HIGH == bicarbChamberLevel ) { setValveState( D80_VALV, VALVE_STATE_CLOSED ); } // Close water inlet valve as fill is complete. if ( d66Pressure >= DRY_BICART_FILL_COMPLETE_PRESSURE ) { setValveState( D65_VALV, VALVE_STATE_CLOSED ); state = BICARB_CHAMBER_START_STATE; } return state; } /*********************************************************************//** * @brief * The publishDryBicartData function broadcasts the dry bicart * data at defined interval. * @details \b Inputs: dryBiCartDataPublicationTimerCounter * @details \b Outputs: DD dry bicart data broadcast message sent * @details \b Message \Sent: MSG_ID_DD_DRY_BICART_DATA to publish the dry bicart * data. * @return none *************************************************************************/ static void publishDryBicartData( void ) { if ( ++dryBiCartDataPublicationTimerCounter >= getDryBicartFillDataPublishInterval() ) { DRY_BICART_DATA_T data; data.dryBiCartFillExecState = (U32)dryBiCartFillExecState; data.bicarbChamberFillExecState = (U32)bicarbChamberFillExecState; data.dryBiCartFillCycleCounter = biCartFillCycleCounter; data.dryBiCartMaxFillCycleCount = getU32OverrideValue( &biCartMaxFillCycleCount ); data.dryBiCartFillRequest = getU32OverrideValue( &dryBiCartFillRequested ); data.dryBiCartLastFillTime = lastFillDurationInMS; data.dryBiCartCurrentFillTime = currentFillDurationInMS; broadcastData( MSG_ID_DD_DRY_BICART_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( DRY_BICART_DATA_T ) ); dryBiCartDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testDDDryBiCartDataPublishIntervalOverride function overrides the * DD dry bicart data publish interval. * @details \b Inputs: dryBiCartDataPublishInterval * @details \b Outputs: dryBiCartDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the DD dry bicart data publish interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testDryBiCartDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &dryBiCartDataPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testDryBiCartFillCycleMaxCountOverride function sets the override value * of the max dry bicart fill cycle count. * @details Inputs: biCartMaxFillCycleCount * @details Outputs: biCartMaxFillCycleCount * @param message Override message from Dialin which includes the override * value to override the max dry bicart fill cycle count. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testDryBiCartFillCycleMaxCountOverride( MESSAGE_T *message ) { BOOL result = u32Override( message, &biCartMaxFillCycleCount, 0, DRY_BICART_MAX_FILL_CYCLE_CNT ); return result; } /*********************************************************************//** * @brief * The testDryBiCartFillRequestOverride function starts/stops the dry bicart * fill. * @details \b Inputs: tester logged in * @details \b Outputs: dryBiCartFillRequested * @param message set message from Dialin which includes the dry bicart fill * start/stop. * @return TRUE if set request is successful, FALSE if not *************************************************************************/ BOOL testDryBiCartFillRequestOverride( MESSAGE_T *message ) { BOOL result = u32Override( message, &dryBiCartFillRequested, FALSE, TRUE ); return result; } /*********************************************************************//** * @brief * The testDryBiCartFillRequestOverride function starts/stops the bicarb * chamber F fill. * @details \b Inputs: tester logged in * @details \b Outputs: bicarbChamberFillRequested * @param message set message from Dialin which includes the bicarb chamber * fill start/stop. * @return TRUE if set request is successful, FALSE if not *************************************************************************/ BOOL testBiCarbChamberFillRequestOverride( MESSAGE_T *message ) { BOOL result = u32Override( message, &bicarbChamberFillRequested, FALSE, TRUE ); return result; } /**@}*/