/************************************************************************** * * 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 MixingControl.c * * @author (last) Sameer Kalliadan Poyil * @date (last) 05-Jun-2026 * * @author (original) Sameer Kalliadan Poyil * @date (original) 05-Jun-2026 * ***************************************************************************/ #include "BalancingChamber.h" #include "Common.h" #include "ConcentratePumps.h" #include "Conductivity.h" #include "DialysatePumps.h" #include "DDDefs.h" #include "DryBiCart.h" #include "Level.h" #include "Messaging.h" #include "MixingControl.h" #include "ModeGenDialysate.h" #include "OperationModes.h" #include "PIControllers.h" #include "PressureSensor.h" #include "PersistentAlarm.h" #include "Pressure.h" #include "TaskGeneral.h" #include "TDInterface.h" #include "TemperatureSensors.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup MixingControl * @{ */ // ********** private definitions ********** #define MIXING_CONTROL_DATA_PUBLISH_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the mixing control data published. // drybicarb mixing #define BICARB_VOL_CONTROL_P_COEFFICIENT ( 0.00008484F * 2 ) ///< Bicarb proportional gain (kp) #define BICARB_VOL_CONTROL_I_COEFFICIENT ( 0.00033936F / 4 ) ///< Bicarb integral gain. (ki) #define MIN_BICARB_VOLUME_ML 0.868686869 ///< Minimum bicarb volume in mL #define MAX_BICARB_VOLUME_ML 1.8F ///< Maximum bicarb volume in mL #define ACID_VOL_CONTROL_P_COEFFICIENT ( 0.00000997F / 2 ) ///< Acid proportional gain (kp) #define ACID_VOL_CONTROL_I_COEFFICIENT ( 0.00003988F / 5 ) ///< Acid integral gain. (ki) #define MIN_ACID_VOLUME_ML 0.3F ///< Minimum acid volume in mL #define MAX_ACID_VOLUME_ML 1.0F ///< Maximum acid volume in mL/min. #define BICARB_TARGET_CONDUCTIVITY 2714.0F ///< Target bicarb conductivity #define BICARB_DELTA_CONDUCTIVITY 500.0F ///< Delta bicarb conductivity #define DIALYSATE_TARGET_CONDUCTIVITY 3734.87F ///< Target dialysate conductivity #define DIALYSATE_DELTA_CONDUCTIVITY 700.0F ///< Delta dialysate conductivity // this is for reference only , it can be removed later #define ACID_TYPE_1K_2_5_CA 11192.55F ///< standard acid conductivity for 1K #define ACID_TYPE_2K_2_5_CA 11313.62F ///< standard acid conductivity for 2K #define ACID_TYPE_3K_2_5_CA 11435.68F ///< standard acid conductivity for 3K #define MIX_NO_FEED_FORWARD 0.0F ///< Feed forward term for dialysate closed loop control #define BICARB_MIX_CONTROL_INTERVAL ( 15 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the bicarb mix is controlled. #define ACID_MIX_CONTROL_INTERVAL ( 9 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the dialysate mix is controlled. #define BICARB_DEADBAND_CONTROL 15.0F ///< Bicarb dead band control #define ACID_DEADBAND_CONTROL 50.0F ///< Acid dead band control // Proportioning Ratios: 1 Part Acid : 1.72 Parts Bicarb : 42.28 Parts Water // Total parts = 1 + 1.72 + 42.28 = 45.0 #define STD_45X_CONC_MIX_ACID_PART 1.0F ///< Acid portion of 45X concentration mix #define STD_45X_CONC_MIX_BICARB_PART 1.72F ///< Bicarb portion of 45X concentration mix #define STD_45X_CONC_MIX_WATER_PART 42.28F ///< Water portion of 45X concentration mix #define TOTAL_STD_45X_MIX_PART ( STD_45X_CONC_MIX_ACID_PART + STD_45X_CONC_MIX_BICARB_PART + \ STD_45X_CONC_MIX_WATER_PART ) ///< Total 45X mix #define ACID_PCT_IN_DIALYSTATE ( ( STD_45X_CONC_MIX_ACID_PART / TOTAL_STD_45X_MIX_PART ) ) ///< Acid percent in Dialysate #define BICARB_PCT_IN_DIALYSATE ( ( STD_45X_CONC_MIX_BICARB_PART / TOTAL_STD_45X_MIX_PART) ) ///< Bicarb Percent in Dialysate #define WATER_PCT_IN_DIALYSATE ( ( STD_45X_CONC_MIX_WATER_PART / TOTAL_STD_45X_MIX_PART) ) ///< Water percent in Dialysate #define RO_WATER_VOLUME_ML ( BAL_CHAMBER_FILL_VOLUME_ML * WATER_PCT_IN_DIALYSATE ) ///< Water volume #define ACID_MIX_VOLUME_ML ( BAL_CHAMBER_FILL_VOLUME_ML * ACID_PCT_IN_DIALYSTATE ) ///< Acid volume #define BICARB_MIX_VOLUME_ML ( BAL_CHAMBER_FILL_VOLUME_ML * BICARB_PCT_IN_DIALYSATE ) ///< Bicarb volume #define STD_DILUTION_ACID_POST_MIX_NA 100.0F ///< Acid post mix Sodium #define STD_DILUTION_BICARB_POST_MIX_NA 37.0F ///< Bicarb post mix Sodium #define STD_DILUTION_BICARB_POST_MIX_CHO3 33.0F ///< Post mix bicarbonate #define DIALYSATE_TARGET_STD_NA_SETTINGS 137.0F ///< Standard target sodium #define DIALYSATE_TARGET_STD_BICARB_SETTING 33.0F ///< Standard bicarb #define DIALYSATE_TARGET_USER_ADJ_SODIUM_SETTING getTDSodiumConcentration() ///< User Sodium #define DIALYSATE_TARGET_USER_ADJ_BICARB_SETTING getTDBicarbonateConcentration() ///< User bicarb #define BICARB_PERCENT_CHANGE ( 1 + ( ( DIALYSATE_TARGET_USER_ADJ_BICARB_SETTING - DIALYSATE_TARGET_STD_BICARB_SETTING) /\ DIALYSATE_TARGET_STD_BICARB_SETTING ) ) ///< Bicarb percentage change #define BICARB_PERCENT_CHANGE_NA ( STD_DILUTION_BICARB_POST_MIX_NA * BICARB_PERCENT_CHANGE ) ///< Percent change of sodium #define BICARB_PERCENT_CHANGE_NAHCO3 ( STD_DILUTION_BICARB_POST_MIX_CHO3 * BICARB_PERCENT_CHANGE ) ///< Percentage change of bicarbonate #define ACID_TARGET_CONCENTRATE_NA ( DIALYSATE_TARGET_USER_ADJ_SODIUM_SETTING - BICARB_PERCENT_CHANGE_NA ) ///< Acid Target concentrate sodium #define ACID_PERCENT_CHANGE ( ( 1 + ( ACID_TARGET_CONCENTRATE_NA - STD_DILUTION_ACID_POST_MIX_NA ) / STD_DILUTION_ACID_POST_MIX_NA ) ) ///< Acid percentage change #define FINAL_NA_ADJ_MIX ( ACID_TARGET_CONCENTRATE_NA + BICARB_PERCENT_CHANGE_NA ) ///< Final adjusted sodium #define FINAL_NAHCO3_ADJ_MIX ( BICARB_PERCENT_CHANGE_NAHCO3 ) #define FINAL_ACID_MIX ( ACID_MIX_VOLUME_ML * ACID_PERCENT_CHANGE ) ///< Final Acid Mix #define FINAL_BICARB_MIX ( BICARB_MIX_VOLUME_ML * BICARB_PERCENT_CHANGE ) ///< Final bicarb mix #define BICARB_CONDUCTIVITY_PRE ( STD_DILUTION_BICARB_POST_MIX_NA * BICARB_PERCENT_CHANGE * getTDBicarbConversionFactor() ) ///< Theoretical bicarb conductivity pre mix #define BICARB_CONDUCTIVITY_POST ( STD_DILUTION_BICARB_POST_MIX_CHO3 * BICARB_PERCENT_CHANGE * getTDBicarbConversionFactor() ) ///< Theoretical bicarb conductivity post mix #define ACID_CONDUCTIVITY_POST ( STD_DILUTION_ACID_POST_MIX_NA * ACID_PERCENT_CHANGE * getTDAcidConversionFactor() ) ///< Theoretical Acid conductivity post mix #define TOTAL_CONDUCTIVITY ( ACID_CONDUCTIVITY_POST + BICARB_CONDUCTIVITY_POST ) ///< Theoretical Dialysate conductivity /// Enumeration of dialysate Mixing id. typedef enum DialysateMixingID { BICARB_MIX_ID = 0, ///< Bicarb mixing id DIALYSATE_MIX_ID_FIRST = BICARB_MIX_ID, ///< First dialysate mixing in list ACID_MIX_ID, ///< Acid mix id NUM_OF_DIALYSATE_MIXING_ID ///< Number of dialysate mix ids } DIALYSATE_MIXING_ID_T; /// Enumeration of dialysate Mixing states. typedef enum DialysateMixing_States { DIALYSATE_MIXING_OPEN_LOOP_STATE = 0, ///< Dialysate open loop state DIALYSATE_MIXING_RAMP_UP_STATE, ///< Dialysate mixing ramp up state DIALYSATE_MIXING_CONTROL_TO_TARGET_STATE, ///< Dialysate mixing control to target state NUM_OF_DIALYSATE_MIXING_STATES ///< Number of dialysate mixing states } DIALYSATE_MIXING_STATE_T; /// Dialysate Mixing state machine data structure typedef struct { U32 controlTimerCounter; ///< Timer counter to perform control on dialysate mixing. DIALYSATE_MIXING_STATE_T dialysateMixingState; //< Current state of dialysate mixing controller state machine. } DIALYSATE_MIXING_DATA_T; // ********** private data ********** // publish static OVERRIDE_U32_T mixingControlDataPublishInterval; ///< Mixing Control data publish interval. static U32 mixingControlDataPublicationTimerCounter; ///< Used to schedule mixingControl data publication to CAN bus. // mixing static OVERRIDE_F32_T mixingControlAcidVolume; ///< Acid concentrate volume in ml ( overrideable). static OVERRIDE_F32_T mixingControlBicarbVolume; ///< Bicarb concentrate volume in ml ( overrideable). static OVERRIDE_F32_T mixingControlAcidVolumeKpGain; ///< Acid kp gain. static OVERRIDE_F32_T mixingControlAcidVolumeKiGain; ///< Acid ki gain. static OVERRIDE_F32_T mixingControlBicarbVolumeKpGain; ///< Bicarb kp gain static OVERRIDE_F32_T mixingControlBicarbVolumeKiGain; ///< Acid ki gain. static DIALYSATE_MIXING_DATA_T dialysateMix[ NUM_OF_DIALYSATE_MIXING_ID ]; ///< Array of dialysate mixing data structure. static OVERRIDE_F32_T mixingControlBicarbTargetConductivity; ///< Target bicarb conductivity static OVERRIDE_F32_T mixingControlBicarbDeltaConductivity; ///< Target delta conductivity static OVERRIDE_F32_T mixingControlDialysateTargetConductivity; ///< Dialysate mix conductivity static OVERRIDE_F32_T mixingControlDialysateDeltaConductivity; ///< Dialysate delta conductivity static OVERRIDE_F32_T mixingControlStdMixingBCVolume; ///< Standard mixing balancing chamber volume static OVERRIDE_F32_T mixingControlTargetAdjNaSettings; ///< Adjusted Sodium static OVERRIDE_F32_T mixingControlTargetAdjBicarbSettings; ///< Adjusted bicarb static OVERRIDE_U32_T mixingControlBicarbControlInterval; ///< Bicarb mix control interval static OVERRIDE_U32_T mixingControlAcidControlInterval; ///< Acid mix control interval static PI_CONTROLLER_SIGNALS_DATA bicarbControlSignals; ///< Bicarb closed loop control signal data static PI_CONTROLLER_SIGNALS_DATA acidControlSignals; ///< Acid closed loop control signal data // ********** private function prototypes ********** // Mixing control loop static DIALYSATE_MIXING_STATE_T handleDialysateMixOpenLoopState( DIALYSATE_MIXING_ID_T mixId ); static DIALYSATE_MIXING_STATE_T handleDialysateMixRampToTargetState( DIALYSATE_MIXING_ID_T mixId ); static DIALYSATE_MIXING_STATE_T handleDialysateMixControlToTargetState( DIALYSATE_MIXING_ID_T mixId ); static void setBicarbMixVol( F32 targetValue ); static void setAcidMixVol( F32 targetValue ); static F32 getBicarbDeltaConductivity( void ); static F32 getBicarbTargetConductivity( void ); static F32 getDialysateDeltaConductivity( void ); static F32 getDialysateTargetConductivity( void ); static F32 getTotalConductivity( void ); static F32 getAcidConducivityPost( void ); static F32 getBicarbConductivityPost( void ); static F32 getBicarbConductivityPre( void ); static F32 getBicarbKpGainCoefficient( void ); static F32 getBicarbKiGainCoefficient( void ); static F32 getAcidKpGainCoefficient( void ); static F32 getAcidKiGainCoefficient( void ); static U32 getMixingControlBicarbControlInterval( void ); static U32 getMixingControlAcidControlInterval( void ); // publish the status static void publishMixingControlData( void ); static U32 getMixingControlDataPublishInterval( void ); /*********************************************************************//** * @brief * The The initMixingControl function initializes the mixing controller * module. * @details \b Inputs: none * @details \b Outputs: unit variables initialized * @return none *************************************************************************/ void initMixingControl( void ) { DIALYSATE_MIXING_ID_T mixId; for ( mixId = DIALYSATE_MIX_ID_FIRST; mixId < NUM_OF_DIALYSATE_MIXING_ID; mixId++ ) { dialysateMix[ mixId ].dialysateMixingState = DIALYSATE_MIXING_OPEN_LOOP_STATE; dialysateMix[ mixId ].controlTimerCounter = 0; } mixingControlDataPublishInterval.data = MIXING_CONTROL_DATA_PUBLISH_INTERVAL; mixingControlDataPublishInterval.ovData = MIXING_CONTROL_DATA_PUBLISH_INTERVAL; mixingControlDataPublishInterval.ovInitData = 0; mixingControlDataPublishInterval.override = OVERRIDE_RESET; mixingControlAcidVolume.data = DEFAULT_ACID_VOLUME_ML; mixingControlAcidVolume.ovData = DEFAULT_ACID_VOLUME_ML; mixingControlAcidVolume.ovInitData = 0.0F; mixingControlAcidVolume.override = OVERRIDE_RESET; mixingControlBicarbVolume.data = DEFAULT_BICARB_VOLUME_ML; mixingControlBicarbVolume.ovData = DEFAULT_BICARB_VOLUME_ML; mixingControlBicarbVolume.ovInitData = 0.0F; mixingControlBicarbVolume.override = OVERRIDE_RESET; mixingControlAcidVolumeKpGain.data = ACID_VOL_CONTROL_P_COEFFICIENT; mixingControlAcidVolumeKpGain.ovData = ACID_VOL_CONTROL_P_COEFFICIENT; mixingControlAcidVolumeKpGain.ovInitData = 0.0F; mixingControlAcidVolumeKpGain.override = OVERRIDE_RESET; mixingControlAcidVolumeKiGain.data = ACID_VOL_CONTROL_I_COEFFICIENT; mixingControlAcidVolumeKiGain.ovData = ACID_VOL_CONTROL_I_COEFFICIENT; mixingControlAcidVolumeKiGain.ovInitData = 0.0F; mixingControlAcidVolumeKiGain.override = OVERRIDE_RESET; mixingControlBicarbVolumeKpGain.data = BICARB_VOL_CONTROL_P_COEFFICIENT; mixingControlBicarbVolumeKpGain.ovData = BICARB_VOL_CONTROL_P_COEFFICIENT; mixingControlBicarbVolumeKpGain.ovInitData = 0.0F; mixingControlBicarbVolumeKpGain.override = OVERRIDE_RESET; mixingControlBicarbVolumeKiGain.data = BICARB_VOL_CONTROL_I_COEFFICIENT; mixingControlBicarbVolumeKiGain.ovData = BICARB_VOL_CONTROL_I_COEFFICIENT; mixingControlBicarbVolumeKiGain.ovInitData = 0.0F; mixingControlBicarbVolumeKiGain.override = OVERRIDE_RESET; mixingControlBicarbTargetConductivity.data = BICARB_TARGET_CONDUCTIVITY; mixingControlBicarbTargetConductivity.ovData = BICARB_TARGET_CONDUCTIVITY; mixingControlBicarbTargetConductivity.ovInitData = 0.0F; mixingControlBicarbTargetConductivity.override = OVERRIDE_RESET; mixingControlBicarbDeltaConductivity.data = BICARB_DELTA_CONDUCTIVITY; mixingControlBicarbDeltaConductivity.ovData = BICARB_DELTA_CONDUCTIVITY; mixingControlBicarbDeltaConductivity.ovInitData = 0.0F; mixingControlBicarbDeltaConductivity.override = OVERRIDE_RESET; mixingControlDialysateTargetConductivity.data = DIALYSATE_TARGET_CONDUCTIVITY; mixingControlDialysateTargetConductivity.ovData = DIALYSATE_TARGET_CONDUCTIVITY; mixingControlDialysateTargetConductivity.ovInitData = 0.0F; mixingControlDialysateTargetConductivity.override = OVERRIDE_RESET; mixingControlDialysateDeltaConductivity.data = DIALYSATE_DELTA_CONDUCTIVITY; mixingControlDialysateDeltaConductivity.ovData = DIALYSATE_DELTA_CONDUCTIVITY; mixingControlDialysateDeltaConductivity.ovInitData = 0.0F; mixingControlDialysateDeltaConductivity.override = OVERRIDE_RESET; mixingControlStdMixingBCVolume.data = BAL_CHAMBER_FILL_VOLUME_ML; mixingControlStdMixingBCVolume.ovData = BAL_CHAMBER_FILL_VOLUME_ML; mixingControlStdMixingBCVolume.ovInitData = 0.0F; mixingControlStdMixingBCVolume.override = OVERRIDE_RESET; mixingControlTargetAdjNaSettings.data = DIALYSATE_TARGET_USER_ADJ_SODIUM_SETTING; mixingControlTargetAdjNaSettings.ovData = DIALYSATE_TARGET_USER_ADJ_SODIUM_SETTING; mixingControlTargetAdjNaSettings.ovInitData = 0.0F; mixingControlTargetAdjNaSettings.override = OVERRIDE_RESET; mixingControlTargetAdjBicarbSettings.data = DIALYSATE_TARGET_USER_ADJ_BICARB_SETTING; mixingControlTargetAdjBicarbSettings.ovData = DIALYSATE_TARGET_USER_ADJ_BICARB_SETTING; mixingControlTargetAdjBicarbSettings.ovInitData = 0.0F; mixingControlTargetAdjBicarbSettings.override = OVERRIDE_RESET; mixingControlBicarbControlInterval.data = BICARB_MIX_CONTROL_INTERVAL; mixingControlBicarbControlInterval.ovData = BICARB_MIX_CONTROL_INTERVAL; mixingControlBicarbControlInterval.ovInitData = 0; mixingControlBicarbControlInterval.override = OVERRIDE_RESET; mixingControlAcidControlInterval.data = ACID_MIX_CONTROL_INTERVAL; mixingControlAcidControlInterval.ovData = ACID_MIX_CONTROL_INTERVAL; mixingControlAcidControlInterval.ovInitData = 0; mixingControlAcidControlInterval.override = OVERRIDE_RESET; mixingControlDataPublicationTimerCounter = 0; } /*********************************************************************//** * @brief * The transitionToMixingControl function prepares for transition to * mixing in open loop/ closed loop operations. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ void transitionToMixingControl( void ) { initMixingControl(); } /*********************************************************************//** * @brief * The execDialysateCompositionMixingController function adjusts the volume of acid and bicarb based on the target * conductivity of bicarb and dialysate solution * @details \b Inputs: dialysateMix[ mixId ].dialysateMixingState * @details \b Outputs:dialysateMix[ mixId ].dialysateMixingState * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if invalid dialysate mixing * state machine found. * @return none *************************************************************************/ void execDialysateCompositionMixingController( void ) { DIALYSATE_MIXING_ID_T mixId; for ( mixId = DIALYSATE_MIX_ID_FIRST; mixId < NUM_OF_DIALYSATE_MIXING_ID; mixId++ ) { switch ( dialysateMix[ mixId ].dialysateMixingState ) { case DIALYSATE_MIXING_OPEN_LOOP_STATE: dialysateMix[ mixId ].dialysateMixingState = handleDialysateMixOpenLoopState( mixId ); break; case DIALYSATE_MIXING_RAMP_UP_STATE: dialysateMix[ mixId ].dialysateMixingState = handleDialysateMixRampToTargetState( mixId ); break; case DIALYSATE_MIXING_CONTROL_TO_TARGET_STATE: dialysateMix[ mixId ].dialysateMixingState = handleDialysateMixControlToTargetState( mixId ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSATE_MIX_EXEC_INVALID_STATE, mixId ) dialysateMix[ mixId ].dialysateMixingState = DIALYSATE_MIXING_OPEN_LOOP_STATE; break; } } } /*********************************************************************//** * @brief * The execMixingControl function executes the dialysate and bicarb open loop /closed loop * related state machines * @details \b Inputs: none * @details \b Outputs: none * @return none. *************************************************************************/ void execMixingControl( void ) { if( getCurrentBalancingChamberExecState() > BAL_CHAMBER_STATE_IDLE ) { // closed loop bicarb and acid mixing controller execDialysateCompositionMixingController(); } // Publish mixing control data publishMixingControlData(); } /*********************************************************************//** * @brief * TThe getBicarbMixVol function gets the bicarb mix volume provided by the controller * @details \b Inputs: mixingControlBicarbVolume * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ F32 getBicarbMixVol( void ) { F32 result = getF32OverrideValue( &mixingControlBicarbVolume ); return result; } /*********************************************************************//** * @brief * The getAcidMixVol function gets acid mix volume provided by the controller * @details \b Inputs: mixingControlAcidVolume * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ F32 getAcidMixVol( void ) { F32 result = getF32OverrideValue( &mixingControlAcidVolume ); return result; } /*********************************************************************//** * @brief * The setAcidMixVol function sets acid mix volume provided by the controller * flag value to be True. * @details \b Inputs: none * @details \b Outputs: mixingControlAcidMixVolume * @param targetValue acid mix volume * @return TRUE if successful, FALSE if not. *************************************************************************/ static void setAcidMixVol( F32 targetValue ) { mixingControlAcidVolume.data = targetValue; } /*********************************************************************//** * @brief * The setBicarbMixVol function sets bicarb Mix volume provided by the controller * @details \b Inputs: none * @details \b Outputs: mixingControlBicarbMixVolume * @param targetValue bicarb Mix volume * @return TRUE if successful, FALSE if not. *************************************************************************/ static void setBicarbMixVol( F32 targetValue ) { mixingControlBicarbVolume.data = targetValue; } /*********************************************************************//** * @brief * The getBicarbDeltaConductivity function gets the delta target conductivity * @details \b Inputs: mixingControlBicarbDeltaConductivity * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getBicarbDeltaConductivity( void ) { F32 result = getF32OverrideValue( &mixingControlBicarbDeltaConductivity ); return result; } /*********************************************************************//** * @brief * The getBicarbTargetConductivity function gets the target conductivity * @details \b Inputs: mixingControlBicarbTargetConductivity * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getBicarbTargetConductivity( void ) { F32 result = getF32OverrideValue( &mixingControlBicarbTargetConductivity ); return result; } /*********************************************************************//** * @brief * The getDialysateDeltaConductivity function gets the acid bicarb delta target conductivity * @details \b Inputs: mixingControlDialysateDeltaConductivity * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getDialysateDeltaConductivity( void ) { F32 result = getF32OverrideValue( &mixingControlDialysateDeltaConductivity ); return result; } /*********************************************************************//** * @brief * The getDialysateTargetConductivity function gets the acid bicarb delta target conductivity * @details \b Inputs: mixingControlDialysateTargetConductivity * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getDialysateTargetConductivity( void ) { F32 result = getF32OverrideValue( &mixingControlDialysateTargetConductivity ); return result; } /*********************************************************************//** * @brief * The getBicarbKpGainCoefficient function gets the Kp Gain for closed loop * bicarb mix volume control * @details \b Inputs: mixingControlBicarbMixVolumeKpGain * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getBicarbKpGainCoefficient( void ) { F32 result = getF32OverrideValue( &mixingControlBicarbVolumeKpGain ); return result; } /*********************************************************************//** * @brief * The getBicarbKiGainCoefficient function gets the Kp Gain for closed loop * bicarb mix volume control * @details \b Inputs: mixingControlBicarbMixVolumeKiGain * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getBicarbKiGainCoefficient( void ) { F32 result = getF32OverrideValue( &mixingControlBicarbVolumeKiGain ); return result; } /*********************************************************************//** * @brief * The getAcidKpGainCoefficient function gets the Kp Gain for closed loop * acid mix volume control * @details \b Inputs: mixingControlAcidMixVolumeKpGain * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getAcidKpGainCoefficient( void ) { F32 result = getF32OverrideValue( &mixingControlAcidVolumeKpGain ); return result; } /*********************************************************************//** * @brief * The getAcidKiGainCoefficient function gets the Ki Gain for closed loop * acid mix volume control * @details \b Inputs: mixingControlAcidMixVolumeKiGain * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static F32 getAcidKiGainCoefficient( void ) { F32 result = getF32OverrideValue( &mixingControlAcidVolumeKiGain ); return result; } /*********************************************************************//** * @brief * The getmixingControlBicarbMixControlInterval function gets the bicart mix control interval * @details \b Inputs: mixingControlBicarbMixControlInterval * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static U32 getMixingControlBicarbControlInterval( void ) { U32 result = getU32OverrideValue( &mixingControlBicarbControlInterval ); return result; } /*********************************************************************//** * @brief * The getMixingControlAcidControlInterval function gets the acid mix control interval * @details \b Inputs: mixingControlAcidControlInterval * @details \b Outputs: none * @return TRUE if successful, FALSE if not. *************************************************************************/ static U32 getMixingControlAcidControlInterval( void ) { U32 result = getU32OverrideValue( &mixingControlAcidControlInterval ); return result; } static F32 getTotalConductivity( void ) { return TOTAL_CONDUCTIVITY; } static F32 getAcidConducivityPost( void ) { return ACID_CONDUCTIVITY_POST; } static F32 getBicarbConductivityPost( void ) { return BICARB_CONDUCTIVITY_PRE; } static F32 getBicarbConductivityPre( void ) { return BICARB_CONDUCTIVITY_PRE; } /*********************************************************************//** * @brief * The getmixingControlFillDataPublishInterval function gets the mix controller * data publish interval. * @details \b Inputs: mixingControlDataPublishInterval * @details \b Outputs: none * @return the interval at mixing control data being published. *************************************************************************/ static U32 getMixingControlDataPublishInterval( void ) { U32 result = getU32OverrideValue( &mixingControlDataPublishInterval ); return result; } /*********************************************************************//** * @brief * The handleDialysateOpenLoopState function handles the dialysate open loop state of * the dialysate mix controller state machine. * @details \b Inputs: D17_COND, D29_COND * @details \b Outputs: none * @param mixId dialysate mix id to run the dialysate mix * @return next state for the controller state machine *************************************************************************/ static DIALYSATE_MIXING_STATE_T handleDialysateMixOpenLoopState( DIALYSATE_MIXING_ID_T mixId ) { DIALYSATE_MIXING_STATE_T result; if ( BICARB_MIX_ID == mixId ) { F32 measuredBicarbConductivity = getFilteredConductivity( D17_COND ); F32 bicarbConductivity = getBicarbTargetConductivity(); F32 bicarbDeltaConductivity = getBicarbDeltaConductivity(); result = DIALYSATE_MIXING_OPEN_LOOP_STATE; if ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DOSING_OPEN_LOOP_CONTROL ) ) { // if measured conductivity crossed (targetConductivity- deltaConuctivity) , switch to closed loop if ( measuredBicarbConductivity >= ( bicarbConductivity - bicarbDeltaConductivity ) ) { result = DIALYSATE_MIXING_RAMP_UP_STATE; } } } else if ( ACID_MIX_ID == mixId ) { F32 measuredAcidBicarbMixConductivity = getFilteredConductivity( D27_COND ); F32 DialysateConductivity = getDialysateTargetConductivity(); F32 DialysateDeltaConductivity = getDialysateTargetConductivity(); result = DIALYSATE_MIXING_OPEN_LOOP_STATE; if ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DOSING_OPEN_LOOP_CONTROL ) ) { // if measured conductivity crossed (targetConductivity- deltaConuctivity) , switch to closed loop if ( measuredAcidBicarbMixConductivity >= ( DialysateConductivity - DialysateDeltaConductivity ) ) { result = DIALYSATE_MIXING_RAMP_UP_STATE; } } } return result; } /*********************************************************************//** * @brief * The handleDialysatePumpRampToTargetState function handles the dialysate ramp to target state of * the dialysate mix controller state machine. * @details \b Inputs: none * @details \b Outputs: none * @param mixId mix id to * @return next state for the controller state machine *************************************************************************/ static DIALYSATE_MIXING_STATE_T handleDialysateMixRampToTargetState( DIALYSATE_MIXING_ID_T mixId ) { DIALYSATE_MIXING_STATE_T state = DIALYSATE_MIXING_RAMP_UP_STATE; if ( BICARB_MIX_ID == mixId ) { resetPIController( PI_CONTROLLER_ID_BICARB_VOL, DEFAULT_BICARB_VOLUME_ML, MIX_NO_FEED_FORWARD ); state = DIALYSATE_MIXING_CONTROL_TO_TARGET_STATE; } else if ( ACID_MIX_ID == mixId ) { resetPIController( PI_CONTROLLER_ID_ACID_VOL, DEFAULT_ACID_VOLUME_ML, MIX_NO_FEED_FORWARD ); state = DIALYSATE_MIXING_CONTROL_TO_TARGET_STATE; } return state; } /*********************************************************************//** * @brief * The handleDialysatePumpControlToTargetState function handles the control to * target state of the dialysate mix controller state machine.. * @details \b Inputs: D17_COND, D29_COND, controlTimerCounter * @details \b Outputs:none * @return next state of the controller state machine *************************************************************************/ static DIALYSATE_MIXING_STATE_T handleDialysateMixControlToTargetState( DIALYSATE_MIXING_ID_T mixId ) { DIALYSATE_MIXING_STATE_T state = DIALYSATE_MIXING_CONTROL_TO_TARGET_STATE; if ( BICARB_MIX_ID == mixId ) { // control interval for bicarb if ( ( ++dialysateMix[ mixId ].controlTimerCounter >= getMixingControlBicarbControlInterval() ) ) { F32 bicarbMixVol; dialysateMix[ mixId ].controlTimerCounter = 0; // Control based on the measured and target conductivity PI_CONTROLLER_SIGNALS_DATA debugBicarbControl; F32 measuredBicarbConductivity = getFilteredConductivity( D17_COND ); F32 bicarbConductivity = getBicarbConductivityPre();//getBicarbTargetConductivity(); if ( fabs ( bicarbConductivity - measuredBicarbConductivity ) > BICARB_DEADBAND_CONTROL ) { bicarbMixVol = runPIController( PI_CONTROLLER_ID_BICARB_VOL, getBicarbConductivityPre() /*getBicarbTargetConductivity()*/, measuredBicarbConductivity ); //Set bicarb mix vol only if its above dead band threshold, else use the previous set vlaue setBicarbMixVol( bicarbMixVol ); } else { //use the previous value } debugBicarbControl = getDebugPIControllerSignals( PI_CONTROLLER_ID_BICARB_VOL ); memcpy( (void*)&bicarbControlSignals, (void*)&debugBicarbControl, sizeof(PI_CONTROLLER_SIGNALS_DATA) ); } } if ( ACID_MIX_ID == mixId ) { // control interval for acid if ( ( ++dialysateMix[ mixId ].controlTimerCounter >= getMixingControlAcidControlInterval( ) ) ) { F32 acidMixVol; dialysateMix[ mixId ].controlTimerCounter = 0; F32 measuredDialysateConductivity = getFilteredConductivity( D27_COND ); F32 DialysateConductivity = getTotalConductivity();//getDialysateTargetConductivity(); PI_CONTROLLER_SIGNALS_DATA debugAcidControl; if ( fabs ( DialysateConductivity - measuredDialysateConductivity ) > ACID_DEADBAND_CONTROL ) { acidMixVol = runPIController( PI_CONTROLLER_ID_ACID_VOL, getTotalConductivity() /*DialysateConductivity*/, measuredDialysateConductivity ); // set acid mix volume if its above dead band threshold , else use previous set value setAcidMixVol( acidMixVol ); } else { // use the previous value } debugAcidControl = getDebugPIControllerSignals( PI_CONTROLLER_ID_ACID_VOL ); memcpy( (void*)&acidControlSignals, (void*)&debugAcidControl, sizeof(PI_CONTROLLER_SIGNALS_DATA) ); } } return state; } /*********************************************************************//** * @brief * The publishMixingControlData function broadcasts the Mixing Controller * data at defined interval. * @details \b Inputs: mixingControlDataPublicationTimerCounter * @details \b Outputs: none * @details \b Message \Sent: MSG_ID_DD_MIXING_CONTROL_DATA to publish the mixing control * data. * @return none *************************************************************************/ static void publishMixingControlData( void ) { if ( ++mixingControlDataPublicationTimerCounter >= getMixingControlDataPublishInterval( ) ) { MIXING_CONTROL_DATA_T data; data.bicarbMixingState = (U32)dialysateMix[ BICARB_MIX_ID ].dialysateMixingState; data.bicarbControlInterval = (U32)dialysateMix[ BICARB_MIX_ID ].controlTimerCounter; data.acidMixingState = (U32)dialysateMix[ ACID_MIX_ID ].dialysateMixingState; data.acidControlInterval = (U32)dialysateMix[ ACID_MIX_ID ].controlTimerCounter; data.bicarbVolume = getBicarbMixVol(); data.acidVolume = getAcidMixVol(); // broadcast theretical pre and post conductivity // TODO: remove after feature testing memcpy( &data.bicarbData, &bicarbControlSignals, sizeof( PI_CONTROLLER_SIGNALS_DATA ) ); // TODO: remove after feature testing memcpy( &data.acidData , &acidControlSignals , sizeof( PI_CONTROLLER_SIGNALS_DATA ) ); // TODO: remove after feature testing data.bicarbKPgain = getBicarbKpGainCoefficient(); // TODO: remove after feature testing data.bicarbKIgain = getBicarbKiGainCoefficient(); // TODO: remove after feature testing data.acidKPgain = getAcidKpGainCoefficient(); // TODO: remove after feature testing data.aciKIgain = getAcidKiGainCoefficient(); broadcastData( MSG_ID_DD_MIXING_CONTROL_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( MIXING_CONTROL_DATA_T ) ); mixingControlDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testDDmixingControlDataPublishIntervalOverride function overrides the * DD mixing control data publish interval. * @details \b Inputs: mixingControlDataPublishInterval * @details \b Outputs: mixingControlDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the DD mixing control data publish interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &mixingControlDataPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testmixingControlAcidMixVolumeOverride function sets the override value * of the acid concentrate mixing volume. * @details Inputs: mixingControlAcidMixVolume * @details Outputs: mixingControlAcidMixVolume * @param message Override message from Dialin which includes the override * value to override the acid concentrate mixing volume. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlAcidMixVolumeOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlAcidVolume ); return result; } /*********************************************************************//** * @brief * The testmixingControlBicarbMixVolumeOverride function sets the override value * of the bicarb concentrate mixing volume. * @details Inputs: mixingControlBicarbMixVolume * @details Outputs: mixingControlBicarbMixVolume * @param message Override message from Dialin which includes the override * value to override the bicarb concentrate mixing volume. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlBicarbMixVolumeOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlBicarbVolume ); return result; } /*********************************************************************//** * @brief * The testmixingControlBicarbMixVolControlKpGainOverride function sets the override value * of the Kp gain coefficient for closed loop bicarb mixing volume control * @details Inputs: mixingControlBicarbMixVolumeKpGain * @details Outputs: mixingControlBicarbMixVolumeKpGain * @param message Override message from Dialin which includes the override * value to override the bicarb concentrate mixing volume kp gain. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlBicarbVolControlKpGainOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlBicarbVolumeKpGain ); initializePIController( PI_CONTROLLER_ID_BICARB_VOL, 0.0F,\ getBicarbKpGainCoefficient(), getBicarbKiGainCoefficient(),\ MIN_BICARB_VOLUME_ML, MAX_BICARB_VOLUME_ML, FALSE, MIX_NO_FEED_FORWARD ); return result; } /*********************************************************************//** * @brief * The testmixingControlBicarbMixVolControlKiGainOverride function sets the override value * of the Ki gain coefficient for closed loop bicarb concentrate mixing volume control * @details Inputs: mixingControlBicarbMixVolumeKiGain * @details Outputs: mixingControlBicarbMixVolumeKiGain * @param message Override message from Dialin which includes the override * value to override the bicarb concentrate mixing volume ki gain. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlBicarbVolControlKiGainOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlBicarbVolumeKiGain ); initializePIController( PI_CONTROLLER_ID_BICARB_VOL, 0.0F,\ getBicarbKpGainCoefficient(), getBicarbKiGainCoefficient(),\ MIN_BICARB_VOLUME_ML, MAX_BICARB_VOLUME_ML, FALSE, MIX_NO_FEED_FORWARD ); return result; } /*********************************************************************//** * @brief * The testmixingControlAcidMoxVolControlKpGainOverride function sets the override value * of the Kp gain coefficient for closed loop acid concentrate mixing volume control * @details Inputs: mixingControlAcidMixVolumeKpGain * @details Outputs: mixingControlAcidMixVolumeKpGain * @param message Override message from Dialin which includes the override * value to override the acid concentrate mixing volume kp gain. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlAcidVolControlKpGainOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlAcidVolumeKpGain ); initializePIController( PI_CONTROLLER_ID_ACID_VOL, 0.0F, \ getAcidKpGainCoefficient(), getAcidKiGainCoefficient(),\ MIN_ACID_VOLUME_ML, MAX_ACID_VOLUME_ML, FALSE, MIX_NO_FEED_FORWARD ); return result; } /*********************************************************************//** * @brief * The testmixingControlAcidMixVolControlKiGainOverride function sets the override value * of the Ki gain coefficient for closed loop acid concentrate mixing volume control * @details Inputs: mixingControlAcidMixVolumeKiGain * @details Outputs: mixingControlAcidMixVolumeKiGain * @param message Override message from Dialin which includes the override * value to override the bicarb concentrate mixing volume ki gain. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlAcidVolControlKiGainOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlAcidVolumeKiGain ); initializePIController( PI_CONTROLLER_ID_ACID_VOL, 0.0F, \ getAcidKpGainCoefficient(), getAcidKiGainCoefficient(),\ MIN_ACID_VOLUME_ML, MAX_ACID_VOLUME_ML, FALSE, MIX_NO_FEED_FORWARD ); return result; } /*********************************************************************//** * @brief * The testMixingControlBicarbTargetConductivityOverride function sets the override value * of target bicarb conductivity for closed loop control * @details Inputs: mixingControlBicarbTargetConductivity * @details Outputs: mixingControlBicarbTargetConductivity * @param message Override message from Dialin which includes the override * value to override the bicarb concentrate target conductivity. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlBicarbTargetConductivityOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlBicarbTargetConductivity ); return result; } /*********************************************************************//** * @brief * The testMixingControlBicarbDeltaConductivityOverride function sets the override value * of bicarb delta conductivity for closed loop control * @details Inputs: mixingControlBicarbDeltaConductivity * @details Outputs: mixingControlBicarbDeltaConductivity * @param message Override message from Dialin which includes the override * value to override the bicarb concentrate delta target conductivity. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlBicarbDeltaConductivityOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlBicarbDeltaConductivity ); return result; } /*********************************************************************//** * @brief * The testmixingControlAcidBicarbTargetConductivityOverride function sets the override value * of acid bicarb mix target conductivity for closed loop control * @details Inputs: mixingControlAcidBicarbMixTargetConductivity * @details Outputs: mixingControlAcidBicarbMixTargetConductivity * @param message Override message from Dialin which includes the override * value to override the acid bicarb mix target conductivity. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlDialysateTargetConductivityOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlDialysateTargetConductivity ); return result; } /*********************************************************************//** * @brief * The testmixingControlAcidBicarbDeltaConductivityOverride function sets the override value * of delta target conductivity for acid bicarb mix closed loop control * @details Inputs: mixingControlDeltaConductivity * * @details Outputs: mixingControlDeltaConductivity * @param message Override message from Dialin which includes the override * value to override the acid bicarb mix target delta conductivity. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlDialysateDeltaConductivityOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlDialysateDeltaConductivity ); return result; } /*********************************************************************//** * @brief * The testmixingControlBicarbMixControlIntervalOverride function sets the override value * of bicarb mix control interval * @details Inputs: mixingControlBicarbMixControlInterval * @details Outputs: mixingControlBicarbMixControlInterval * @param message Override message from Dialin which includes the override * value to override the bicart lower pressure. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlBicarbControlIntervalOverride( MESSAGE_T *message ) { BOOL result = u32Override( message, &mixingControlBicarbControlInterval, 0, BICARB_MIX_CONTROL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testmixingControlAcidMixControlIntervalOverride function sets the override value * of acid mix control interval * @details Inputs: mixingControlAcidMixControlInterval * @details Outputs: mixingControlAcidMixControlInterval * @param message Override message from Dialin which includes the override * value to override the bicart lower pressure. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlAcidControlIntervalOverride( MESSAGE_T *message ) { BOOL result = u32Override( message, &mixingControlAcidControlInterval, 0, BICARB_MIX_CONTROL_INTERVAL ); return result; } /*********************************************************************//** * @brief * The testmixingControlStdMixingBCVolumeOverride function sets the override value * of acid mix control interval * @details Inputs: mixingControlStdMixingBCVolume * @details Outputs: mixingControlStdMixingBCVolume * @param message Override message from Dialin which includes the override * value to override the bicart lower pressure. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlStdMixingBCVolumeOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlStdMixingBCVolume ); return result; } /*********************************************************************//** * @brief * The testmixingControlTargetAdjNASettingOverride function sets the override value * of acid mix control interval * @details Inputs: mixingControlTargetAdjNaSettings * @details Outputs: mixingControlTargetAdjNaSettings * @param message Override message from Dialin which includes the override * value to override the bicart lower pressure. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlTargetAdjNASettingOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlTargetAdjNaSettings ); return result; } /*********************************************************************//** * @brief * The testmixingControlAcidMixControlIntervalOverride function sets the override value * of acid mix control interval * @details Inputs: mixingControlTargetAdjBicarbSettings * @details Outputs: mixingControlTargetAdjBicarbSettings * @param message Override message from Dialin which includes the override * value to override the bicart lower pressure. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMixingControlTargetAdjBicarbSettingOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, &mixingControlTargetAdjBicarbSettings ); return result; } /**@}*/