Index: firmware/App/Controllers/#ROPump.c# =================================================================== diff -u --- firmware/App/Controllers/#ROPump.c# (revision 0) +++ firmware/App/Controllers/#ROPump.c# (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,987 @@ +/************************************************************************** +* +* 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 ROPump.c +* +* @author (last) Michael Garthwaite +* @date (last) 15-Apr-2026 +* +* @author (original) Michael Garthwaite +* @date (original) 08-Sep-2025 +* +***************************************************************************/ +#include // for log() + +#include "Flow.h" +#include "FPOperationModes.h" +//#include "NVDataMgmt.h" +#include "Messaging.h" +#include "MessageSupport.h" +#include "PersistentAlarm.h" +#include "PIControllers.h" +#include "PIDControllers.h" +#include "Pressure.h" +#include "ROPump.h" +#include "SafetyShutdown.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "Utilities.h" +#include "Valves.h" + +/** + * @addtogroup ROPump + * @{ + */ + +// ********** private definitions ********** + +#define RO_PUMP_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the RO Pump data is published on the CAN bus. +#define ROP_CONTROL_INTERVAL ( 300 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the RO pump is controlled. + +#define ROP_FLOW_CONTROL_P_COEFFICIENT 0.00010F ///< P term for RO pump flow control. +#define ROP_FLOW_CONTROL_I_COEFFICIENT 0.000075F ///< I term for RO pump flow control. +#define ROP_FLOW_CONTROL_D_COEFFICIENT 0.00070F ///< D term for RO pump flow control +#define ROP_MIN_FLOW_TO_CONTROL_PCT 0.75F +#define ROP_PRESSURE_CONTROL_P_COEFFICIENT 0.15F ///< P term for RO pump pressure control. +#define ROP_PRESSURE_CONTROL_I_COEFFICIENT 0.65F ///< I term for RO pump pressure control. + +#define ROP_FLOW_TO_PWM_SLOPE_NO_RECOVERY 0.1621F ///< Slope of flow to PWM line equation for permeate flush. +#define ROP_FLOW_TO_PWM_INTERCEPT_NO_RECOVERY -10.86F ///< Intercept of flow to PWM line equation for permeate flush. +#define ROP_FLOW_TO_PWM_SLOPE_MED_RECOVERY 0.3537F ///< Slope of flow to PWM line equation for permeate flush. +#define ROP_FLOW_TO_PWM_INTERCEPT_MED_RECOVERY 94.559F ///< Intercept of flow to PWM line equation for permeate flush. +#define ROP_FLOW_TO_PWM_SLOPE_MAX_RECOVER 0.2046F ///< Slope of flow to PWM line equation for max RO recovery. +#define ROP_FLOW_TO_PWM_INTERCEPT_MAX_RECOVER 157.0F ///< Intercept of flow to PWM line equation for max RO recovery. +#define ROP_PRESSURE_TO_PWM_SLOPE 0.5F ///< Slope of pressure to PWM line equation. +#define ROP_PRESSURE_TO_PWM_INTERCEPT 0.0F ///< Intercept of pressure to PWM line equation. + +#define DATA_PUBLISH_COUNTER_START_COUNT 10 ///< Data publish counter start count. + +/// PWM line equation for pressure. converted to percentage. +#define ROP_PRESSURE_TO_PWM_PCT(pres) ( ( ROP_PRESSURE_TO_PWM_SLOPE * pres + ROP_PRESSURE_TO_PWM_INTERCEPT ) / MAX_FLUID_PUMP_PWM_DUTY_CYCLE ) +/// PWM log equation for flow converted to percentage. +#define ROP_FLOW_TO_PWM_LOG_PCT(slope, intercept, flow) ( ( slope * log(flow) + intercept ) / MAX_FLUID_PUMP_PWM_DUTY_CYCLE ) +/// PWM line equation for flow converted to percentage. +#define ROP_FLOW_TO_PWM_LIN_PCT(slope, intercept, flow) ( ( slope * flow + intercept ) / MAX_FLUID_PUMP_PWM_DUTY_CYCLE ) + +#define FP_FLOW_RATE_BELOW_TARGET_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Timeout for flow rate below 75% of target flow rate +#define FP_FLOW_RATE_BELOW_TARGET_CLEAR_MS ( 10 * MS_PER_SECOND ) ///< Clear timeout for flow rate below target flow rate +#define PERMEATE_FLOW_LOW_OUT_OF_RANGE_EXEMPT_MS ( 10 * MS_PER_SECOND ) ///< Permeate flow low alarm exempt for 10 seconds after starting from Off + +// ********** private data ********** + +static RO_PUMP_STATE_T roPumpState; ///< Current state of pump controller state machine. +static BOOL isROPumpOn; ///< Flag indicating whether pump is currently running. +static BOOL stopPumpRequest; ///< Flag indicating pump stop is requested. +static U32 roPumpDataPublicationTimerCounter; ///< Used to schedule RO pump data publication to CAN bus. +static OVERRIDE_U32_T roPumpDataPublishInterval; ///< Interval (in ms) at which to publish boost pump data to CAN bus. +static U32 roControlTimerCounter; ///< Determines when to perform control on RO pump. +static OVERRIDE_U32_T targetROPumpFlowRate; ///< Target RO flow rate (in L/min). +static OVERRIDE_F32_T targetROPumpPressure; ///< Target RO max allowed pressure (in PSI). +static F32 roPumpDutyCyclePctSet; ///< Currently set RO pump PWM duty cycle. +static OVERRIDE_F32_T roPumpOpenLoopTargetDutyCycle; ///< Target RO pump open loop PWM. +static BOOL roPumpStartControl; ///< boolean to determine when closed loop flow control starts +static U32 timeSinceP12Started; ///< Time when RO pump is started from off state. + +// ********** private function prototypes ********** + +static RO_PUMP_STATE_T handleROPumpOffState( void ); +static RO_PUMP_STATE_T handleROPumpControlToTargetFlowState( void ); +static RO_PUMP_STATE_T handleROPumpControlToTargetPressureState( void ); +static RO_PUMP_STATE_T handleROPumpOpenLoopState( void ); + +static U32 getROPumpLowFlowExemptTimer( void ); +static F32 roPumpPresToPWM( F32 targetPressure ); +static F32 roPumpFlowToPWM( U32 targetFlow ); +static void stopROPump( void ); +static void publishROPumpData( void ); + +/*********************************************************************//** + * @brief + * The initROPump function initializes the RO Pump module. + * @details \b Inputs: none + * @details \b Outputs: RO Pump controller unit initialized + * @return none + *************************************************************************/ +void initROPump( void ) +{ + initFluidPump(); + + // Initialize RO pump PI controller to flow + initializePIDController( PID_CONTROLLER_ID_RO_PUMP_FLOW, MIN_RO_FLOWRATE_MLPM, ROP_FLOW_CONTROL_P_COEFFICIENT, ROP_FLOW_CONTROL_I_COEFFICIENT, + ROP_FLOW_CONTROL_D_COEFFICIENT, MIN_RO_FLOWRATE_MLPM, MAX_RO_FLOWRATE_MLPM, FALSE, 0 ); + + // Initialize RO pump PI controller to target pressure + initializePIController( PI_CONTROLLER_ID_RO_PUMP_PRES, MIN_FLUID_PUMP_DUTY_CYCLE_PCT, ROP_PRESSURE_CONTROL_P_COEFFICIENT, ROP_PRESSURE_CONTROL_I_COEFFICIENT, + MIN_FLUID_PUMP_DUTY_CYCLE_PCT, MAX_FLUID_PUMP_DUTY_CYCLE_PCT, FALSE, 0 ); + + roPumpState = RO_PUMP_OFF_STATE; + isROPumpOn = FALSE; + stopPumpRequest = FALSE; + roControlTimerCounter = 0; + roPumpDutyCyclePctSet = 0.0F; + roPumpStartControl = FALSE; + roPumpDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; + roPumpDataPublishInterval.data = RO_PUMP_DATA_PUB_INTERVAL; + roPumpDataPublishInterval.ovData = RO_PUMP_DATA_PUB_INTERVAL; + roPumpDataPublishInterval.ovInitData = 0; + roPumpDataPublishInterval.override = OVERRIDE_RESET; + targetROPumpFlowRate.data = 0; + targetROPumpFlowRate.ovData = 0; + targetROPumpFlowRate.ovInitData = 0; + targetROPumpFlowRate.override = OVERRIDE_RESET; + targetROPumpPressure.data = 0.0F; + targetROPumpPressure.ovData = 0.0F; + targetROPumpPressure.ovInitData = 0.0F; + targetROPumpPressure.override = OVERRIDE_RESET; + roPumpOpenLoopTargetDutyCycle.data = 0.0; + roPumpOpenLoopTargetDutyCycle.ovData = 0.0; + roPumpOpenLoopTargetDutyCycle.ovInitData = 0.0; + roPumpOpenLoopTargetDutyCycle.override = OVERRIDE_RESET; + timeSinceP12Started = 0; + + stopROPump(); +} + +/*********************************************************************//** + * @brief + * The execROPumpController function executes the RO pump controller. + * @details \b Alarm: ALARM_ID_FP_SOFTWARE_FAULT if pump is in an invalid state + * @details \b Inputs: roPumpState + * @details \b Outputs: roPumpState + * @return none + *************************************************************************/ +void execROPumpController( void ) +{ + // Update RO pump feedback from FPGA + readFluidPumps(); + + switch ( roPumpState ) + { + case RO_PUMP_OFF_STATE: + roPumpState = handleROPumpOffState(); + break; + + case RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE: + roPumpState = handleROPumpControlToTargetFlowState(); + break; + + case RO_PUMP_CONTROL_TO_TARGET_PRESSURE_STATE: + roPumpState = handleROPumpControlToTargetPressureState(); + break; + + case RO_PUMP_OPEN_LOOP_STATE: + roPumpState = handleROPumpOpenLoopState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_FP_SOFTWARE_FAULT, FP_FAULT_ID_FP_INVALID_PUMP_DUTY_CYCLE_SELECTED, roPumpState ) + roPumpState = RO_PUMP_OFF_STATE; + break; + } + + // Publish RO pump data on interval + publishROPumpData(); +} + +/*********************************************************************//** + * @brief + * The handleROPumpOffState function handles the off state of the RO pump + * state machine. + * @details \b Inputs: isROPumpOn, roPumpControlMode, roPumpOpenLoopTargetDutyCycle + * @details \b Outputs: isROPumpOn, roPumpDutyCyclePctSet + * @return next state + *************************************************************************/ +static RO_PUMP_STATE_T handleROPumpOffState( void ) +{ + RO_PUMP_STATE_T state = RO_PUMP_OFF_STATE; + isROPumpOn = FALSE; + timeSinceP12Started = 0; + + // If there is a target flow set, transition to the PI controller and control to flow + if ( getTargetROPumpFlowRateMLPM() > 0 ) + { + // Set pump to on + isROPumpOn = TRUE; + roPumpDutyCyclePctSet = roPumpFlowToPWM( getTargetROPumpFlowRateMLPM() ); + resetPIDController( PID_CONTROLLER_ID_RO_PUMP_FLOW, roPumpDutyCyclePctSet, 0.0F ); + setFluidPumpPctToPWMDutyCycle( P12_PUMP, roPumpDutyCyclePctSet ); + roPumpStartControl = FALSE; + state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; + } + // If there is a target pressure set, transition to the PI controller and control to pressure. + else if ( getTargetROPumpPressure() > 0.0F ) + { + // Set pump to on + isROPumpOn = TRUE; + roPumpDutyCyclePctSet = roPumpPresToPWM( getTargetROPumpPressure() ); + resetPIController( PI_CONTROLLER_ID_RO_PUMP_PRES, roPumpDutyCyclePctSet, 0.0F ); + setFluidPumpPctToPWMDutyCycle( P12_PUMP, roPumpDutyCyclePctSet ); + state = RO_PUMP_CONTROL_TO_TARGET_PRESSURE_STATE; + } + // If the target duty cycle is greater than zero (minimum is 10%) and the mode has been set to open + // loop, set the duty cycle + else if ( getTargetROPumpDutyCyclePCT() > 0.0F ) + { + roPumpDutyCyclePctSet = getTargetROPumpDutyCyclePCT(); + setFluidPumpPctToPWMDutyCycle( P12_PUMP, roPumpDutyCyclePctSet ); + isROPumpOn = TRUE; + state = RO_PUMP_OPEN_LOOP_STATE; + } + + if ( state != RO_PUMP_OFF_STATE ) + { + timeSinceP12Started = getMSTimerCount(); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleROPumpOpenLoopState function handles the open loop control + * state of the RO pump state machine. + * @details \b Inputs: stopPumpRequest, roPumpControlMode, + * roPumpDutyCyclePctSet + * @details \b Outputs: roPumpState, stopPumpRequest + * @return next state + *************************************************************************/ +static RO_PUMP_STATE_T handleROPumpOpenLoopState( void ) +{ + RO_PUMP_STATE_T state = RO_PUMP_OPEN_LOOP_STATE; + + if ( TRUE == stopPumpRequest ) + { + stopPumpRequest = FALSE; + signalROPumpHardStop(); + } + // If there is a target flow set, transition to the PI controller and control to flow + if ( getTargetROPumpFlowRateMLPM() > 0 ) + { + ///transition to closed loop + if ( getCurrentFPOperationMode() == FP_MODE_GENP ) + { + roPumpDutyCyclePctSet = getCurrentROPumpDutyCyclePCT(); + } + else + { + roPumpDutyCyclePctSet = roPumpFlowToPWM( getTargetROPumpFlowRateMLPM() ); + } + resetPIDController( PID_CONTROLLER_ID_RO_PUMP_FLOW, roPumpDutyCyclePctSet, 0.0F ); + state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; + } + // If there is a target pressure set, transition to the PI controller and control to pressure. + else if ( getTargetROPumpPressure() > 0.0F ) + { + //transition to closed loop + resetPIController( PI_CONTROLLER_ID_RO_PUMP_PRES, getTargetROPumpPressure(), 0.0F ); + state = RO_PUMP_CONTROL_TO_TARGET_PRESSURE_STATE; + } + else if ( (F32)getTargetROPumpDutyCyclePCT() == 0.0F ) + { + signalROPumpHardStop(); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleROPumpControlToTargetFlowState function handles the control to + * target flow state of the RO pump controller state machine. + * @details \b Inputs: roPumpPWMDutyCyclePctSet, roControlTimerCounter, roPumpControlMode + * @details \b Outputs: roPumpPWMDutyCyclePctSet, roControlTimerCounter + * @return next state of the controller state machine + *************************************************************************/ +static RO_PUMP_STATE_T handleROPumpControlToTargetFlowState( void ) +{ + RO_PUMP_STATE_T state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; + F32 currentFlowRate = 0.0F; + F32 minRequiredFlowRate = 0.0F; + + // Check if need to switch control modes + if ( getTargetROPumpPressure() > 0.0F ) + { + // Transition to target pressure + resetPIController( PI_CONTROLLER_ID_RO_PUMP_PRES, getTargetROPumpPressure(), 0 ); + state = RO_PUMP_CONTROL_TO_TARGET_PRESSURE_STATE; + } + else if ( getTargetROPumpDutyCyclePCT() > 0.0F ) + { + roPumpDutyCyclePctSet = getTargetROPumpDutyCyclePCT(); + setFluidPumpPctToPWMDutyCycle( P12_PUMP, roPumpDutyCyclePctSet ); + state = RO_PUMP_OPEN_LOOP_STATE; + } + // Control at set interval or shut off + else if ( (F32)getTargetROPumpFlowRateMLPM() == 0.0F ) + { + signalROPumpHardStop(); + roPumpStartControl = FALSE; + } + else if ( ++roControlTimerCounter >= ROP_CONTROL_INTERVAL ) + { + currentFlowRate = getFilteredFlow( P16_FLOW ); + minRequiredFlowRate = (F32)getTargetROPumpFlowRateMLPM() * ROP_MIN_FLOW_TO_CONTROL_PCT; + + // P16 flow seems to lag in current Leahi HW. We will wait till we hit a % of target flow before we start changing control. + if( ( TRUE == roPumpStartControl ) || ( currentFlowRate >= ( minRequiredFlowRate ) ) ) + { + roPumpDutyCyclePctSet = runPIDController( PID_CONTROLLER_ID_RO_PUMP_FLOW, (F32)getTargetROPumpFlowRateMLPM(), currentFlowRate ); + roPumpDutyCyclePctSet = MIN( roPumpDutyCyclePctSet, ( MAX_FLUID_PUMP_PWM_DUTY_CYCLE * MAX_FLUID_PUMP_DUTY_CYCLE_PCT ) ); + setFluidPumpPctToPWMDutyCycle( P12_PUMP, roPumpDutyCyclePctSet ); + + if ( FALSE == roPumpStartControl ) + { + roPumpStartControl = TRUE; + } + } + roControlTimerCounter = 0; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleROPumpControlToTargetPressureState function handles the control + * to target pressure state of the RO pump controller state machine. + * @details \b Inputs: roPumpPWMDutyCyclePctSet, roControlTimerCounter, roPumpControlMode + * @details \b Outputs: roPumpPWMDutyCyclePctSet, roControlTimerCounter + * @return next state of the controller state machine + *************************************************************************/ +static RO_PUMP_STATE_T handleROPumpControlToTargetPressureState( void ) +{ + RO_PUMP_STATE_T state = RO_PUMP_CONTROL_TO_TARGET_PRESSURE_STATE; + + // Check if we are changing control + if ( getTargetROPumpFlowRateMLPM() > 0 ) + { + resetPIDController( PID_CONTROLLER_ID_RO_PUMP_FLOW, roPumpDutyCyclePctSet, 0 ); + state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; + } + else if ( getTargetROPumpDutyCyclePCT() > 0.0F ) + { + setFluidPumpPctToPWMDutyCycle( P12_PUMP, getTargetROPumpDutyCyclePCT() ); + roPumpDutyCyclePctSet = getTargetROPumpDutyCyclePCT(); + state = RO_PUMP_OPEN_LOOP_STATE; + } + // Control at set interval or shut off + else if ( getTargetROPumpPressure() == 0.0F ) + { + signalROPumpHardStop(); + } + else if ( ++roControlTimerCounter >= ROP_CONTROL_INTERVAL ) + { + roPumpDutyCyclePctSet = runPIController( PI_CONTROLLER_ID_RO_PUMP_PRES, getTargetROPumpPressure(), getFilteredPressure( P17_PRES ) ); + setFluidPumpPctToPWMDutyCycle( P12_PUMP, roPumpDutyCyclePctSet ); + roControlTimerCounter = 0; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The setROPumpTargetFlowRateMLPM function sets a new target flow rate for the + * RO pump. + * @details \b Inputs: roPumpOpenLoopTargetDutyCycle + * @details \b Outputs: targetROPumpFlowRate, roPumpControlMode, roPumpDutyCyclePctSet, + * roControlTimerCounter, isROPumpOn + * @param roFlowRate which is target RO flow rate in mL/min + * @param firmwareCall indicates whether the call initiated by firmware + * internally or by dialin + * @return TRUE if new target flow rate is set successfully, FALSE if not + *************************************************************************/ +BOOL setROPumpTargetFlowRateMLPM( U32 roFlowRate, BOOL firmwareCall ) +{ + BOOL result = FALSE; + BOOL skipSet = FALSE; + + // First of all, the flow rate must be in range + if ( roFlowRate <= MAX_RO_FLOWRATE_MLPM ) + { + if ( firmwareCall == TRUE ) // firmware call + { + // set the target flow and pressure to zero if there are no active override + if ( ( targetROPumpFlowRate.override == OVERRIDE_RESET ) && ( targetROPumpPressure.override == OVERRIDE_RESET ) && ( roPumpOpenLoopTargetDutyCycle.override == OVERRIDE_RESET ) ) + { + targetROPumpPressure.data = 0; + roPumpOpenLoopTargetDutyCycle.data = 0.0F; + } + // skip the firmware set call if any override is active + else + { + result = TRUE; + skipSet = TRUE; + } + } + if ( skipSet != TRUE ) + { + targetROPumpFlowRate.data = roFlowRate; + // Get the initial guess of the duty cycle + roPumpDutyCyclePctSet = roPumpFlowToPWM( getTargetROPumpFlowRateMLPM() ); + roControlTimerCounter = 0; + isROPumpOn = TRUE; + result = TRUE; + } + } + // Requested flow rate is out of range + else + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_FP_SOFTWARE_FAULT, FP_FAULT_ID_FP_PUMP_INVALID_FLOW_RATE_SET, roFlowRate ) + } + + return result; +} + +/*********************************************************************//** + * @brief + * The setROPumpTargetPressure function sets a new target pressure for the + * RO pump. + * @details \b Inputs: roPumpOpenLoopTargetDutyCycle, targetROPumpFlowRate + * @details \b Outputs: roPumpOpenLoopTargetDutyCycle, targetROPumpFlowRate, isROPumpOn, + * roControlTimerCounter, roPumpDutyCyclePctSet, roPumpControlMode, targetROPumpPressure + * @param roPressure which is target RO pressure + * @param firmwareCall indicates whether the call initiated by firmware + * internally or by dialin + * @return TRUE if new target flow rate is set successfully, FALSE if not + *************************************************************************/ +BOOL setROPumpTargetPressure( F32 roPressure, BOOL firmwareCall ) +{ + BOOL result = FALSE; + BOOL skipSet = FALSE; + + // First of all, the pressure must be in range + if ( ( roPressure <= MAX_RO_PRESSURE_PSI ) && ( roPressure >= MIN_RO_PRESSURE_PSI ) ) + { + if ( firmwareCall == TRUE ) // firmware call + { + // set the target flow and pressure to zero if there are no active override + if ( ( targetROPumpFlowRate.override == OVERRIDE_RESET ) && ( targetROPumpPressure.override == OVERRIDE_RESET ) && ( roPumpOpenLoopTargetDutyCycle.override == OVERRIDE_RESET ) ) + { + targetROPumpFlowRate.data = 0; + roPumpOpenLoopTargetDutyCycle.data = 0.0F; + } + // skip the firmware set call if any override is active + else + { + result = TRUE; + skipSet = TRUE; + } + } + if ( skipSet != TRUE ) + { + targetROPumpPressure.data = roPressure; + // Get the initial guess of the duty cycle + roPumpDutyCyclePctSet = roPumpPresToPWM( getTargetROPumpPressure() ); + roControlTimerCounter = 0; + isROPumpOn = TRUE; + result = TRUE; + } + } + // Requested flow rate is out of range + else + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_FP_SOFTWARE_FAULT, FP_FAULT_ID_FP_PUMP_INVALID_PRESSURE_SELECTED, roPressure ) + } + + return result; +} + +/*********************************************************************//** + * @brief + * The setROPumpTargetDutyCycle function sets the duty cycle that the + * pump should run. + * @details \b Inputs: roPumpOpenLoopTargetDutyCycle, roPumpPWMDutyCyclePct, + * targetROPumpPressure, targetROPumpFlowRate + * @details \b Outputs: roPumpOpenLoopTargetDutyCycle + * roPumpControlMode, targetROPumpFlowRate, targetROPumpPressure + * @param duty which is the duty cycle + * @param firmwareCall indicates whether the call initiated by firmware + * internally or by dialin + * @return none + *************************************************************************/ +BOOL setROPumpTargetDutyCycle( F32 dutyCycle, BOOL firmwareCall ) +{ + BOOL result = FALSE; + BOOL skipSet = FALSE; + + if ( ( dutyCycle >= MIN_FLUID_PUMP_DUTY_CYCLE_PCT ) && ( dutyCycle <= MAX_FLUID_PUMP_DUTY_CYCLE_PCT ) ) + { + if ( firmwareCall == TRUE ) // firmware call + { + // set the target flow and pressure to zero if there are no active override + if ( ( targetROPumpFlowRate.override == OVERRIDE_RESET ) && ( targetROPumpPressure.override == OVERRIDE_RESET ) && ( roPumpOpenLoopTargetDutyCycle.override == OVERRIDE_RESET ) ) + { + targetROPumpFlowRate.data = 0; + targetROPumpPressure.data = 0.0F; + } + // skip the firmware set call if any override is active + else + { + result = TRUE; + skipSet = TRUE; + } + } + if ( skipSet != TRUE ) + { + // Set the new duty cycle of the pump + roPumpOpenLoopTargetDutyCycle.data = dutyCycle; + result = TRUE; + // stop RO Pump if duty cycle is set to zero + if ( dutyCycle == 0.0F ) + { + if ( ( targetROPumpFlowRate.data == 0 ) && ( targetROPumpPressure.data == 0.0F ) ) + { + signalROPumpHardStop(); + } + } + else + { + setFluidPumpPctToPWMDutyCycle( P12_PUMP, getTargetROPumpDutyCyclePCT() ); + } + } + } + else + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_FP_SOFTWARE_FAULT, FP_FAULT_ID_FP_INVALID_PUMP_DUTY_CYCLE_SELECTED, dutyCycle ) + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getTargetRODutyCyclePCT function gets the target RO pump + * pwm. + * @details \b Inputs: roPumpOpenLoopTargetDutyCycle + * @details \b Outputs: none + * @return the current target RO pwm between 0 and 0.99 + *************************************************************************/ +F32 getTargetROPumpDutyCyclePCT( void ) +{ + F32 dutyCycle = getF32OverrideValue( &roPumpOpenLoopTargetDutyCycle ); + + return dutyCycle; +} + +/*********************************************************************//** + * @brief + * The getCurrentRODutyCyclePCT function gets the current RO pump + * pwm. + * @details \b Inputs: none + * @details \b Outputs: none + * @return the current feedback RO pwm between 0 and 0.99 + *************************************************************************/ +F32 getCurrentROPumpDutyCyclePCT( void ) +{ + F32 dutyCyclePct = convertDutyCycleCntToPct( getFluidPumpReadPWMDutyCycle( P12_PUMP ) ); + + return dutyCyclePct; +} + +/*********************************************************************//** + * @brief + * The getTargetROPumpPressure function gets the current target RO pump + * pressure. + * @details \b Inputs: targetROPumpPressure + * @details \b Outputs: none + * @return the current target RO pressure in PSI + *************************************************************************/ +F32 getTargetROPumpPressure( void ) +{ + F32 pressure = getF32OverrideValue( &targetROPumpPressure ); + + return pressure; +} + +/*********************************************************************//** + * @brief + * The getTargetROPumpFlowRateMLPM function gets the current target RO pump + * flow rate. + * @details \b Inputs: targetROPumpFlowRate + * @details \b Outputs: none + * @return the current target RO flow rate (in mL/min). + *************************************************************************/ +U32 getTargetROPumpFlowRateMLPM( void ) +{ + U32 flowRate = getU32OverrideValue( &targetROPumpFlowRate ); + + return flowRate; +} + +/*********************************************************************//** + * @brief + * The isROPumpLowflowAlarmExempt function gets the flag whether or not + * to exempt the low flow alarm. + * @details \b Inputs: none + * @details \b Outputs: none + * @return TRUE if low flow alarm is exempt, FALSE if not. + *************************************************************************/ +BOOL isROPumpLowflowAlarmExempt( void ) +{ + BOOL result = FALSE; + + if ( ( roPumpState == RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE ) && ( FALSE == didTimeout( getROPumpLowFlowExemptTimer(), PERMEATE_FLOW_LOW_OUT_OF_RANGE_EXEMPT_MS ) ) ) + { + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getROPumpLowFlowExemptTimer function gets the timer when RO pump + * starts from Off state. + * @details \b Inputs: none + * @details \b Outputs: none + * @return the timer when RO pump started from Off state (in ms). + *************************************************************************/ +static U32 getROPumpLowFlowExemptTimer( void ) +{ + return timeSinceP12Started; +} + +/*********************************************************************//** + * @brief + * The roPumpPresToPWM function calculates the duty cycle for the given target + * pressure. + * @details \b Inputs: none + * @details \b Outputs: none + * @param targetPressure target pressure value to control in PSI + * @return the current target RO pump PWM in a percentage. + *************************************************************************/ +static F32 roPumpPresToPWM( F32 targetPressure ) +{ + F32 dutyCyclePct = ROP_PRESSURE_TO_PWM_PCT( targetPressure ); + + return dutyCyclePct; +} + +/*********************************************************************//** + * @brief + * The roPumpFlowToPWM function calculates the duty cycle for the given target + * flow rate. + * @details \b Inputs: none + * @details \b Outputs: none + * @param targetFlow target flow value to control in in mL/min + * @return the current target RO pump PWM in a percentage. + *************************************************************************/ +static F32 roPumpFlowToPWM( U32 targetFlow ) +{ + F32 dutyCyclePct = 0.0F; + RECOVERY_STATE_T recovery = getRecoveryStatesConfig(); + + if ( FP_PRE_GENP_PERMEATE_FLUSH == getCurrentFPSubMode() && ( FP_MODE_PGEN == getCurrentFPOperationMode() ) ) + { + dutyCyclePct = ROP_FLOW_TO_PWM_LIN_PCT( ROP_FLOW_TO_PWM_SLOPE_NO_RECOVERY, ROP_FLOW_TO_PWM_INTERCEPT_NO_RECOVERY, targetFlow ); + } + else + { + switch( recovery ) + { + case NO_RECOVERY: + dutyCyclePct = ROP_FLOW_TO_PWM_LIN_PCT( ROP_FLOW_TO_PWM_SLOPE_NO_RECOVERY, ROP_FLOW_TO_PWM_INTERCEPT_NO_RECOVERY, targetFlow ); + break; + + case MEDIUM_RECOVERY: + dutyCyclePct = ROP_FLOW_TO_PWM_LIN_PCT( ROP_FLOW_TO_PWM_SLOPE_MED_RECOVERY, ROP_FLOW_TO_PWM_INTERCEPT_MED_RECOVERY, targetFlow ); + break; + + case MAX_RECOVERY: + default: + dutyCyclePct = ROP_FLOW_TO_PWM_LIN_PCT( ROP_FLOW_TO_PWM_SLOPE_MAX_RECOVER, ROP_FLOW_TO_PWM_INTERCEPT_MAX_RECOVER, targetFlow ); + break; + } + } + + return dutyCyclePct; +} + +/*********************************************************************//** + * @brief + * The signalROPumpHardStop function stops the P12 pump immediately and + * resets all the variables associated with the P12 pump run. + * @details \b Inputs: targetROPumpFlowRate, targetROPumpPressure + * @details \b Outputs: roPumpState, roPumpDutyCyclePctSet, roControlTimerCounter, + * roPumpOpenLoopTargetDutyCycle, targetROPumpFlowRate, targetROPumpPressure + * @return none + *************************************************************************/ +void signalROPumpHardStop( void ) +{ + if( targetROPumpFlowRate.data > 0 ) + { + targetROPumpFlowRate.data = 0; + resetPIDController( PID_CONTROLLER_ID_RO_PUMP_FLOW, MIN_FLUID_PUMP_DUTY_CYCLE_PCT, 0.0F ); + } + + if( targetROPumpPressure.data > 0.0F ) + { + targetROPumpPressure.data = 0.0F; + resetPIController( PI_CONTROLLER_ID_RO_PUMP_PRES, MIN_FLUID_PUMP_DUTY_CYCLE_PCT, 0.0F ); + } + + roPumpState = RO_PUMP_OFF_STATE; + roPumpDutyCyclePctSet = 0.0F; + roControlTimerCounter = 0; + roPumpOpenLoopTargetDutyCycle.data = 0.0F; + + stopROPump(); +} + +/*********************************************************************//** + * @brief + * The stopROPump function sets the P12 pump duty cycle to zero. + * @details \b Inputs: none + * @details \b Outputs: isROPumpOn, roPumpPWMDutyCyclePctSet + * @return none + *************************************************************************/ +static void stopROPump( void ) +{ + isROPumpOn = FALSE; + // Set the new duty cycle of the pump + setFluidPumpPWMDutyCycle( P12_PUMP, 0 ); +} + +/*********************************************************************//** + * @brief + * The isROPumpRunning function returns the on/off status of P12 pump. + * @details \b Inputs: isROPumpOn + * @details \b Outputs: none + * @return isROPumpOn the boolean flag that is TRUE if the pump is on and + * FALSE if it is off + *************************************************************************/ +BOOL isROPumpRunning( void ) +{ + return isROPumpOn; +} + +/*********************************************************************//** + * @brief + * The publishROPumpData function publishes p12 pump data at the set interval. + * @details \b Message \b Sent: MSG_ID_FP_RO_PUMP_DATA + * @details \b Inputs: roPumpDataPublicationTimerCounter + * @details \b Outputs: roPumpDataPublicationTimerCounter + * @return none + *************************************************************************/ +static void publishROPumpData( void ) +{ + // publish RO pump data on interval + if ( ++roPumpDataPublicationTimerCounter >= getU32OverrideValue( &roPumpDataPublishInterval ) ) + { + RO_PUMP_DATA_T pumpData; + + pumpData.p12PumpState = (U32)roPumpState; + pumpData.p12PumpDutyCycle = (U32)getFluidPumpPWMDutyCycle( P12_PUMP ); + pumpData.p12PumpFBDutyCycle = (U32)getFluidPumpReadPWMDutyCycle( P12_PUMP ); + pumpData.p12PumpSpeed = getFluidPumpRPM( P12_PUMP ); + pumpData.p12TargetPressure = getTargetROPumpPressure(); + pumpData.p12TargetFlow = getTargetROPumpFlowRateMLPM(); + pumpData.p12TargetDutyCycle = getTargetROPumpDutyCyclePCT(); + pumpData.p12PumpDutyCyclePct = convertDutyCycleCntToPct( (U32)getFluidPumpPWMDutyCycle( P12_PUMP ) ); + pumpData.p12PumpFBDutyCyclePct = convertDutyCycleCntToPct( (U32)getFluidPumpReadPWMDutyCycle( P12_PUMP ) ); + + broadcastData( MSG_ID_FP_RO_PUMP_DATA, COMM_BUFFER_OUT_CAN_FP_BROADCAST, (U08*)&pumpData, sizeof( RO_PUMP_DATA_T ) ); + roPumpDataPublicationTimerCounter = 0; + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testROPumpDataPublishIntervalOverride function overrides the RO pump + * data publish interval. + * @details \b Inputs: roPumpDataPublishInterval + * @details \b Outputs: roPumpDataPublishInterval + * @param message Override message from Dialin which includes the value + * that override ro pump data publish interval with (in ms) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testROPumpDataPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = u32BroadcastIntervalOverride( message, &roPumpDataPublishInterval, TASK_GENERAL_INTERVAL ); + + return result; +} + +/*********************************************************************//** + * @brief + * The testROPumpTargetPressureOverride function overrides the RO pump + * data publish interval. + * @details \b Inputs: targetROPumpPressure, targetROPumpFlowRate, + * roPumpOpenLoopTargetDutyCycle + * @details \b Outputs: targetROPumpPressure, targetROPumpFlowRate, + * roPumpOpenLoopTargetDutyCycle + * @param message Override message from Dialin which includes the value + * of the target pressure + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testROPumpTargetPressureOverride( MESSAGE_T *message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + OVERRIDE_TYPE_T ovType = getOverridePayloadFromMessage( message, &payload ); + BOOL result = FALSE; + // reset target pressure override + if ( ( ovType == OVERRIDE_RESET_OVERRIDE ) && ( targetROPumpPressure.override != OVERRIDE_RESET ) ) + { + // Restore previous flow, duty cycle and pressure values + targetROPumpPressure.data = targetROPumpPressure.ovInitData; + targetROPumpPressure.ovInitData = 0.0F; + targetROPumpPressure.ovData = targetROPumpPressure.ovInitData; + targetROPumpPressure.override = OVERRIDE_RESET; + if ( targetROPumpFlowRate.override == OVERRIDE_RESET && targetROPumpFlowRate.ovInitData != 0 ) + { + targetROPumpFlowRate.data = targetROPumpFlowRate.ovInitData; + } + if ( roPumpOpenLoopTargetDutyCycle.override == OVERRIDE_RESET && roPumpOpenLoopTargetDutyCycle.ovInitData != 0.0F ) + { + roPumpOpenLoopTargetDutyCycle.data = roPumpOpenLoopTargetDutyCycle.ovInitData; + } + } + // set target pressure override + else if ( ovType == OVERRIDE_OVERRIDE ) + { + targetROPumpPressure.ovData = payload.state.f32; + targetROPumpPressure.override = OVERRIDE_KEY; + if ( targetROPumpPressure.ovInitData == 0.0F ) + { + targetROPumpPressure.ovInitData = targetROPumpPressure.data; + } + handleFluidPumpF32Data( &roPumpOpenLoopTargetDutyCycle ); + handleFluidPumpU32Data( &targetROPumpFlowRate ); + } + result = setROPumpTargetPressure(getTargetROPumpPressure(), FALSE); + + return result; +} + +/*********************************************************************//** + * @brief + * The testROPumpTargetFlowOverride function overrides the RO pump + * data publish interval. + * @details \b Inputs: targetROPumpFlowRate, roPumpOpenLoopTargetDutyCycle, + * targetROPumpPressure + * @details \b Outputs: targetROPumpFlowRate, roPumpOpenLoopTargetDutyCycle, + * targetROPumpPressure + * @param message Override message from Dialin which includes the value + * of the target flow + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testROPumpTargetFlowOverride( MESSAGE_T *message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + OVERRIDE_TYPE_T ovType = getOverridePayloadFromMessage( message, &payload ); + BOOL result = FALSE; + // reset target flow rate override + if ( ( ovType == OVERRIDE_RESET_OVERRIDE ) && ( targetROPumpFlowRate.override != OVERRIDE_RESET ) ) + { + // Restore previous flow, duty cycle and pressure values + targetROPumpFlowRate.data = targetROPumpFlowRate.ovInitData; + targetROPumpFlowRate.ovInitData = 0; + targetROPumpFlowRate.ovData = targetROPumpFlowRate.ovInitData; + targetROPumpFlowRate.override = OVERRIDE_RESET; + if ( roPumpOpenLoopTargetDutyCycle.override == OVERRIDE_RESET && roPumpOpenLoopTargetDutyCycle.ovInitData != 0.0F ) + { + roPumpOpenLoopTargetDutyCycle.data = roPumpOpenLoopTargetDutyCycle.ovInitData; + } + if ( targetROPumpPressure.override == OVERRIDE_RESET && targetROPumpPressure.ovInitData != 0.0F ) + { + targetROPumpPressure.data = targetROPumpPressure.ovInitData; + } + } + // set target flow rate override + else if ( ovType == OVERRIDE_OVERRIDE ) + { + targetROPumpFlowRate.ovData = payload.state.u32; + targetROPumpFlowRate.override = OVERRIDE_KEY; + if ( targetROPumpFlowRate.ovInitData == 0 ) + { + targetROPumpFlowRate.ovInitData = targetROPumpFlowRate.data; + } + handleFluidPumpF32Data( &roPumpOpenLoopTargetDutyCycle ); + handleFluidPumpF32Data( &targetROPumpPressure ); + } + result = setROPumpTargetFlowRateMLPM(getTargetROPumpFlowRateMLPM(), FALSE); + + return result; +} + +/*********************************************************************//** + * @brief + * The testROPumpTargetDutyCycleOverride function overrides the RO pump + * duty cycle. + * @details \b Inputs: roPumpOpenLoopTargetDutyCycle, targetROPumpPressure, + * targetROPumpFlowRate + * @details \b Outputs: roPumpOpenLoopTargetDutyCycle, targetROPumpPressure, + * targetROPumpFlowRate + * @param message Override message from Dialin which includes the value + * of the target flow + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testROPumpTargetDutyCycleOverride( MESSAGE_T *message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + OVERRIDE_TYPE_T ovType = getOverridePayloadFromMessage( message, &payload ); + BOOL result = FALSE; + // reset target duty cycle override + if ( ( ovType == OVERRIDE_RESET_OVERRIDE ) && ( roPumpOpenLoopTargetDutyCycle.override != OVERRIDE_RESET ) ) + { + // Restore previous flow, duty cycle and pressure values + roPumpOpenLoopTargetDutyCycle.data = roPumpOpenLoopTargetDutyCycle.ovInitData; + roPumpOpenLoopTargetDutyCycle.ovInitData = 0.0F; + roPumpOpenLoopTargetDutyCycle.ovData = roPumpOpenLoopTargetDutyCycle.ovInitData; + roPumpOpenLoopTargetDutyCycle.override = OVERRIDE_RESET; + if ( targetROPumpFlowRate.override == OVERRIDE_RESET && targetROPumpFlowRate.ovInitData != 0 ) + { + targetROPumpFlowRate.data = targetROPumpFlowRate.ovInitData; + } + if ( targetROPumpPressure.override == OVERRIDE_RESET && targetROPumpPressure.ovInitData != 0 ) + { + targetROPumpPressure.data = targetROPumpPressure.ovInitData; + } + } + // set target duty cycle override + else if ( ovType == OVERRIDE_OVERRIDE ) + { + roPumpOpenLoopTargetDutyCycle.ovData = payload.state.f32; + roPumpOpenLoopTargetDutyCycle.override = OVERRIDE_KEY; + if ( roPumpOpenLoopTargetDutyCycle.ovInitData == 0.0F ) + { + roPumpOpenLoopTargetDutyCycle.ovInitData = roPumpOpenLoopTargetDutyCycle.data; + } + handleFluidPumpU32Data( &targetROPumpFlowRate ); + handleFluidPumpF32Data( &targetROPumpPressure ); + } + result = setROPumpTargetDutyCycle(getTargetROPumpDutyCyclePCT(), FALSE); + + return result; +} + +/*********************************************************************//** + * @brief + * The testBoostPumpHardStopOverride function stops the RO pump. + * @details \b Inputs: roPumpOpenLoopTargetDutyCycle + * @details \b Outputs: roPumpOpenLoopTargetDutyCycle + * @param message Override message from Dialin which includes the value + * of the target flow + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testROPumpHardStop( MESSAGE_T *message ) +{ + BOOL result = TRUE; + + signalROPumpHardStop(); + + return result; +} + +/**@}*/ Index: firmware/App/Controllers/HemoDiaFiltration.c =================================================================== diff -u --- firmware/App/Controllers/HemoDiaFiltration.c (revision 0) +++ firmware/App/Controllers/HemoDiaFiltration.c (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,373 @@ +/************************************************************************** +* +* 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 HemoDiaFiltration.c +* +* @author (last) Steve Jarpe +* @date (last) 23-May-2026 +* +* @author (original) Steve Jarpe +* @date (original) 23-May-2026 +* +***************************************************************************/ + +#include "SubstitutionPump.h" +#include "PIControllers.h" +#include "Pressure.h" +#include "Messaging.h" +#include "OperationModes.h" +#include "TaskGeneral.h" +#include "TDInterface.h" +#include "TestSupport.h" +#include "Hemodiafiltration.h" + +/** + * @addtogroup Hemodiafiltration + * @{ + */ + +// ********** private definitions ********** + +#define HDF_DATA_PUBLISH_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the hemodiafiltration data published. +#define SECONDS_PER_MINUTE 60 ///< seconds per minute +#define HDF_CONTROL_PERIOD_SECONDS 2 ///< Interval at which the hemodiafiltration flow rate adjustment is executed in seconds +#define HDF_CONTROL_PERIOD ( HDF_CONTROL_PERIOD_SECONDS * MS_PER_SECOND ) ///< Interval at which the hemodiafiltration flow rate adjustment is executed in milliseconds + +#define HDF_CONTROL_INTERVAL ( HDF_CONTROL_PERIOD / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the peroidic hemodiafiltration flow rate is calculated. +#define HDF_MAX_FILTRATION_FRACTION 0.35F ///< HDF maximum filtration fraction (Qs+Quf)/Qb +#define HDF_MAX_TMP 400.0F ///< HDF maximum TMP - trans-membrane pressure = (Pb - Pd) +#define HDF_MAX_RATE 200.0F ///< Maximum substitution flow rate +#define ZERO_RATE 0.0F ///< Zero value. +#define SUB_PUMP_PRESSURE_CONTROL_P_COEFFICIENT 0.5F ///< P term for Sub pump pressure control. +#define SUB_PUMP_PRESSURE_CONTROL_I_COEFFICIENT 5.0F ///< I term for Sub pump pressure control. +// ********** private data ********** + +static HDF_EXEC_STATE_T hdfExecState; ///< Current hemodiafiltration executive state. +static BOOL isHemodiafiltrationRequested; ///< Flag indicating hemodiafiltration request. +static U32 hdfControlTimerCounter; ///< Counter (in task interval) to initiate the periodic hemodiafiltration rate recalculation. +static F32 currentHDFrate; ///< current HDF rate in ml/min +static F32 TMP_target; +static F32 currentHDFvolume; ///< current HDF delivered substitution volume +static U32 hdfDataPublicationTimerCounter; ///< Used to schedule hemodiafiltration data publication to CAN bus. +static BOOL isHDFRateUpdated; ///< flag indicating to update HDF rate needed. +static OVERRIDE_U32_T hdfDataPublishInterval; ///< Hemodiafiltration data publish interval. + +// ********** private function prototypes ********** + +static HDF_EXEC_STATE_T handleHDFRunningState( void ); +static HDF_EXEC_STATE_T handleHDFPausedState( void ); +static void updateHDFRequest( void ); +static void UpdateHDFCompensation( void ); +static void publishHemodiafiltrationData( void ); + +/*********************************************************************//** + * @brief + * The initHemodiafiltration function initializes the hemodiafiltration unit. + * @details \b Inputs: none + * @details \b Outputs: unit variables initialized + * @return none + *************************************************************************/ +void initHemodiafiltration( void ) +{ + // Initialize substitution pump PI controller to target pressure + initializePIController( PI_CONTROLLER_ID_SUB_PUMP_PRES, ZERO_RATE, SUB_PUMP_PRESSURE_CONTROL_P_COEFFICIENT, SUB_PUMP_PRESSURE_CONTROL_I_COEFFICIENT, + ZERO_RATE, HDF_MAX_RATE, FALSE, 0 ); + + hdfExecState = DD_HDF_PAUSED; + hdfDataPublishInterval.data = HDF_DATA_PUBLISH_INTERVAL; + hdfDataPublishInterval.ovData = HDF_DATA_PUBLISH_INTERVAL; + hdfDataPublishInterval.ovInitData = 0; + hdfDataPublishInterval.override = OVERRIDE_RESET; + isHemodiafiltrationRequested = FALSE; + currentHDFCompCounter = 0; + currentHDFrate = 0.0; + currentHDFvolume = 0.0; + hdfDataPublicationTimerCounter = 0; + isHDFRateUpdated = FALSE; +} + +/*********************************************************************//** + * @brief + * The transitionToHemodiafiltration function prepares for transition to + * hemodiafiltration. + * @details \b Inputs: none + * @details \b Outputs: isHemodiafiltrationRequested,hdfDataPublicationTimerCounter + * @return none + *************************************************************************/ +void transitionToHemodiafiltration( void ) +{ + initHemodiafiltration(); +} + +/*********************************************************************//** + * @brief + * The execHDFControl function executes the hemodiafiltration state machine. + * @details \b Inputs: hdfExecState + * @details \b Outputs: hdfExecState + * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong hemodiafiltration + * state invoked. + * @details Warning: The compensated HDF should be caclulated first and then + * call 'updateHDFRequest' function. + * @return current state. + *************************************************************************/ +U32 execHDFControl( void ) +{ + else if ( TRUE == isHDFRateUpdated ) + { + //get updated HDF rate + compHDFrate = getTDHDFRate(); + + // Update HDF rate + setConcentratePumpTargetSpeed( D76_PUMP, compHDFrate, DOSING_CONT_VOLUME ); + isHDFRateUpdated = FALSE; + } + + // Calculate HDF volume and determine HDF pause/run + updateHDFRequest(); + + // execute current hemodiafiltration exec state + switch ( hdfExecState ) + { + case DD_HDF_PAUSED: + hdfExecState = handleHDFPausedState(); + break; + + case DD_HDF_RUNNING: + hdfExecState = handleHDFRunningState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_HDF_INVALID_EXEC_STATE, hdfExecState ) + hdfExecState = DD_HDF_PAUSED; + break; + } + + //Publish hemodiafiltration data + publishHemodiafiltrationData(); + + return hdfExecState; +} + +/*********************************************************************//** + * @brief + * The handleHDFPausedState function handles the hemodiafiltration + * paused state. + * @details \b Inputs:isHemodiafiltrationRequested + * @details \b Outputs: HDF state + * @return next HDF state. + *************************************************************************/ +static HDF_EXEC_STATE_T handleHDFPausedState( void ) +{ + HDF_EXEC_STATE_T state = DD_HDF_PAUSED; + + if ( TRUE == isHemodiafiltrationRequested ) + { + setSubstitutionPumpTargetSpeed( D92_PUMP, currentHDFrate ); + requestSubstitutionPumpOn( D92_PUMP ); + resetPIDController( PI_CONTROLLER_ID_SUB_PUMP_PRES, currentHDFrate, 0.0F ); + hdfControlTimerCounter = 0; + + //Tranistion to run state + state = DD_HDF_RUNNING; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHDFPausedState function handles the hemodiafiltration + * running state. + * @details \b Inputs:isHemodiafiltrationRequested + * @details \b Outputs: HDF state + * @return next HDF state. + *************************************************************************/ +static HDF_EXEC_STATE_T handleHDFRunningState( void ) +{ + HDF_EXEC_STATE_T state = DD_HDF_RUNNING; + + if ( TRUE != isHemodiafiltrationRequested ) + { + requestSubstitutionPumpOff( D92_PUMP); + state = DD_HDF_PAUSED; + } + else + { + UpdateHDFRateAndVolume(); + } + return state; +} + +/*********************************************************************//** + * @brief + * The updateHDFRequest function updates the hemodiafiltration requested + * flag to true or false based on the calculated HDF rate. + * @details \b Inputs: compensated HDF + * @details \b Outputs: isHemodiafiltrationRequested + * @return none. + *************************************************************************/ +static void updateHDFRequest( void ) +//need to understand this... +{ + // update latest HDF run/pause request + if ( currentHDFrate > ZERO_RATE ) + { + isHemodiafiltrationRequested = TRUE; + } + else + { + isHemodiafiltrationRequested = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The signalHDFRateUpdate function sets the flag to udpate the + * HDF rate. + * @details \b Inputs: none + * @details \b Outputs: isHDFRateUpdated + * @return none. + *************************************************************************/ +void signalHDFRateUpdate( void ) +{ + isHDFRateUpdated = TRUE; +} + +/*********************************************************************//** + * @brief + * The getTMP function gets the TMP (trans-membrane pressure) + * is within the (limits) + * @details \b Inputs: Inputs: D41 and H14 pressure + * @details \b Outputs: Calculated TMP + * @return none. + *************************************************************************/ +static F32 getTMP( void ) +{ +F32 d41Pressure = getFilteredPressure( D41_PRES ); +F32 h14Pressure = ; //figure out how to get TD pressures + + return h14Pressure - d41Pressure; +} + +/*********************************************************************//** + * @brief + * The CheckHDFRate function checks whether the hemodiafiltration rate + * is within the (limits) + * @details \b Inputs: proposed HDF rate + * @details \b Outputs: updated HDF rate + * @return none. + *************************************************************************/ +static F32 CheckHDFRate( F32 proposed_rate ) +{ + F32 Qb = ; //get blood flow rate + F32 Quf = getTDUFRate(); + F32 max_sub_rate = HDF_MAX_FILTRATION_FRACTION * Qb - Quf; + + if (max_sub_rate > HDF_MAX_RATE) + { + max_sub_rate = HDF_MAX_RATE; + } + if (proposed_rate > max_sub_rate) + { + return max_sub_rate; + } + else + { + return proposed_rate; + } +} + +/*********************************************************************//** + * @brief + * The UpdateHDFCompensation function updates the hemodiafiltration rate + * based on the trans-membrane-pressure + * @details \b Inputs: D41 and H14 temperature + * @details \b Outputs: updated HDF rate + * @return none. + *************************************************************************/ + +static void UpdateHDFRateAndVolume( void ) +{ + if ( ( ++hdfControlTimerCounter >= HDF_CONTROL_INTERVAL ) || ( TRUE == isHDFRateUpdated ) ) + // get current TMP + currentHDFrate = runPIController( PI_CONTROLLER_ID_SUB_PUMP_PRES, TMP_target, TMP_current_value ); + + // Update HDF rate + setSubstitutionPumpTargetSpeed( D92_PUMP, currentHDFrate ) + hdfControlTimerCounter = 0; + currentHDFvolume += ((F32) HDF_CONTROL_PERIOD_SECONDS / (F32) SECONDS_PER_MINUTE) * currentHDFrate; //ml + + hdfControlTimerCounter = 0; + isHDFRateUpdated = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The getCurrentHDFExecState function returns the current state + * of the hemodiafiltration. + * @details \b Inputs: hdfExecState + * @details \b Outputs: none + * @return the current state of HDF execution state. + *************************************************************************/ +HDF_EXEC_STATE_T getCurrentHDFExecState( void ) +{ + return hdfExecState; +} + +/*********************************************************************//** + * @brief + * The publishHemodiafiltrationData function broadcasts the hemodiafiltration + * data at defined interval. + * @details \b Inputs: hdfDataPublicationTimerCounter + * @details \b Outputs: DD hemodiafiltration data broadcast message sent + * @details \b Message \Sent: MSG_ID_DD_HDF_DATA to publish the hemodiafiltration + * data. + * @return none + *************************************************************************/ +static void publishHemodiafiltrationData( void ) +{ + if ( ++hdfDataPublicationTimerCounter >= getU32OverrideValue( &hdfDataPublishInterval ) ) + { + HDF_DATA_T data; + + data.hdfExecState = (U32)hdfExecState; + data.hdfRate = getTDHDFRate(); + data.hdfcurrentrate = currentHDFrate; + data.isHDFRequested = (U32)isHemodiafiltrationRequested; + data.hdfRequestedVolume; = getTDHDFVolume(); + data.hdfCurrentVolume; = CurrentHDFvolume; + broadcastData( MSG_ID_DD_HDF_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( HDF_DATA_T ) ); + + hdfDataPublicationTimerCounter = 0; + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testDDHDFDataPublishIntervalOverride function overrides the + * DD hemodiafiltration data publish interval. + * @details \b Inputs: hdfDataPublishInterval + * @details \b Outputs: hdfDataPublishInterval + * @param Override message from Dialin which includes the interval + * (in ms) to override the DD hemodiafiltration data publish interval to. + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testDDHDFDataPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = u32BroadcastIntervalOverride( message, &hdfDataPublishInterval, TASK_GENERAL_INTERVAL ); + + return result; +} + +/**@}*/ Index: firmware/App/Controllers/HemoDiaFiltration.c~ =================================================================== diff -u --- firmware/App/Controllers/HemoDiaFiltration.c~ (revision 0) +++ firmware/App/Controllers/HemoDiaFiltration.c~ (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,337 @@ +/************************************************************************** +* +* 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 Ultrafiltration.c +* +* @author (last) Vinayakam Mani +* @date (last) 14-Apr-2026 +* +* @author (original) Vinayakam Mani +* @date (original) 28-Jul-2025 +* +***************************************************************************/ + +#include "ConcentratePumps.h" +#include "Messaging.h" +#include "OperationModes.h" +#include "TaskGeneral.h" +#include "Temperature.h" +#include "TDInterface.h" +#include "TestSupport.h" +#include "Ultrafiltration.h" + +/** + * @addtogroup Ultrafiltration + * @{ + */ + +// ********** private definitions ********** + +#define UF_DATA_PUBLISH_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the ultrafiltration data published. +#define UF_COMPENSATION_PERIOD ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Interval at which the ultrafiltration compenstaion executed. +#define UF_COMP_INTERVAL ( UF_COMPENSATION_PERIOD / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the peroidic ultrafiltration compensation calculated. +#define COMP_SLOPE -0.000376F ///< UF Temperature compensation slope factor +#define COMP_INTERCEPT 1.007269F ///< UF temperature compensation intercept factor +#define ZERO_RATE 0.0F ///< Zero value. + +// ********** private data ********** + +static UF_EXEC_STATE_T ufExecState; ///< Current ultrafiltration executive state. +static BOOL isUltrafiltrationRequested; ///< Flag indicating ultrafiltration request. +static U32 currentUFCompCounter; ///< Counter (in task interval) to initiate the periodic ultrafiltration temperature compensation. +static F32 compUFrate; ///< compensated UF rate +static U32 ufDataPublicationTimerCounter; ///< Used to schedule ultrafiltration data publication to CAN bus. +static BOOL isUFRateUpdated; ///< flag indicating to update UF rate needed. +static OVERRIDE_U32_T ufDataPublishInterval; ///< Ultrafiltration data publish interval. + +// ********** private function prototypes ********** + +static UF_EXEC_STATE_T handleUFRunningState( void ); +static UF_EXEC_STATE_T handleUFPausedState( void ); +static void updateUFRequest( void ); +static void UpdateUFCompensation( void ); +static void publishUltrafiltrationData( void ); + +/*********************************************************************//** + * @brief + * The initUltrafiltration function initializes the ultrafiltration unit. + * @details \b Inputs: none + * @details \b Outputs: unit variables initialized + * @return none + *************************************************************************/ +void initUltrafiltration( void ) +{ + ufExecState = DD_UF_PAUSED; + ufDataPublishInterval.data = UF_DATA_PUBLISH_INTERVAL; + ufDataPublishInterval.ovData = UF_DATA_PUBLISH_INTERVAL; + ufDataPublishInterval.ovInitData = 0; + ufDataPublishInterval.override = OVERRIDE_RESET; + isUltrafiltrationRequested = FALSE; + currentUFCompCounter = 0; + compUFrate = getTDUFRate(); + ufDataPublicationTimerCounter = 0; + isUFRateUpdated = FALSE; +} + +/*********************************************************************//** + * @brief + * The transitionToUltrafiltration function prepares for transition to + * ultrafiltration. + * @details \b Inputs: none + * @details \b Outputs: isUltrafiltrationRequested,ufDataPublicationTimerCounter + * @return none + *************************************************************************/ +void transitionToUltrafiltration( void ) +{ + initUltrafiltration(); +} + +/*********************************************************************//** + * @brief + * The execUFControl function executes the ultrafiltration state machine. + * @details \b Inputs: ufExecState + * @details \b Outputs: ufExecState + * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong ultrafiltration + * state invoked. + * @details Warning: The compensated UF should be caclulated first and then + * call 'updateUFRequest' function. + * @return current state. + *************************************************************************/ +U32 execUFControl( void ) +{ + // Trimmer heater enabled, hence UF temp compensation is optional + if ( getTestConfigStatus( TEST_CONFIG_DD_DISABLE_UF_TEMP_COMPENSATION ) != TRUE ) + { + // Compensate balancing error at defined interval + UpdateUFCompensation(); + } + else if ( TRUE == isUFRateUpdated ) + { + //get updated UF rate + compUFrate = getTDUFRate(); + + // Update UF rate + setConcentratePumpTargetSpeed( D76_PUMP, compUFrate, DOSING_CONT_VOLUME ); + isUFRateUpdated = FALSE; + } + + // Calculate UF volume and determine UF pause/run + updateUFRequest(); + + // execute current ultrafiltration exec state + switch ( ufExecState ) + { + case DD_UF_PAUSED: + ufExecState = handleUFPausedState(); + break; + + case DD_UF_RUNNING: + ufExecState = handleUFRunningState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_UF_INVALID_EXEC_STATE, ufExecState ) + ufExecState = DD_UF_PAUSED; + break; + } + + //Publish ultrafiltration data + publishUltrafiltrationData(); + + return ufExecState; +} + +/*********************************************************************//** + * @brief + * The handleUFPausedState function handles the ultrafiltration + * paused state. + * @details \b Inputs:isUltrafiltrationRequested + * @details \b Outputs: UF state + * @return next UF state. + *************************************************************************/ +static UF_EXEC_STATE_T handleUFPausedState( void ) +{ + UF_EXEC_STATE_T state = DD_UF_PAUSED; + + if ( TRUE == isUltrafiltrationRequested ) + { + // start with TD UF rate + compUFrate = getTDUFRate(); + currentUFCompCounter = 0; + + setConcentratePumpTargetSpeed( D76_PUMP, compUFrate, DOSING_CONT_VOLUME ); + requestConcentratePumpOn( D76_PUMP ); + + //Tranistion to run state + state = DD_UF_RUNNING; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleUFPausedState function handles the ultrafiltration + * running state. + * @details \b Inputs:isUltrafiltrationRequested + * @details \b Outputs: UF state + * @return next UF state. + *************************************************************************/ +static UF_EXEC_STATE_T handleUFRunningState( void ) +{ + UF_EXEC_STATE_T state = DD_UF_RUNNING; + + if ( TRUE != isUltrafiltrationRequested ) + { + requestConcentratePumpOff( D76_PUMP, FALSE ); + state = DD_UF_PAUSED; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The updateUFRequest function updates the ultrafiltration requested + * flag to true or false based on the compensated UF rate. + * @details \b Inputs: compensated UF + * @details \b Outputs: isUltrafiltrationRequested + * @return none. + *************************************************************************/ +static void updateUFRequest( void ) +{ + // update latest UF run/pause request + if ( compUFrate > ZERO_RATE ) + { + isUltrafiltrationRequested = TRUE; + } + else + { + isUltrafiltrationRequested = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The signalUFRateUpdate function sets the flag to udpate the + * UF rate. + * @details \b Inputs: none + * @details \b Outputs: isUFRateUpdated + * @return none. + *************************************************************************/ +void signalUFRateUpdate( void ) +{ + isUFRateUpdated = TRUE; +} + +/*********************************************************************//** + * @brief + * The UpdateUFCompensation function updates the ultrafiltration rate + * based on the dialysate temperature compensation. + * @details \b Inputs: D4 and D50 temperature + * @details \b Outputs: updated UF rate + * @return none. + *************************************************************************/ +static void UpdateUFCompensation( void ) +{ + if ( ( ++currentUFCompCounter >= UF_COMP_INTERVAL ) || ( TRUE == isUFRateUpdated ) ) + { + F32 freshDensity = 0.0F; + F32 spentDensity = 0.0F; + F32 compFreshFlowrate = 0.0F; + F32 compSpentFlowrate = 0.0F; + F32 balancingError = 0.0F; + + // Fresh side dialysate density + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) + { + freshDensity = ( COMP_SLOPE * getFilteredTemperatureValue( D4_TEMP ) ) + COMP_INTERCEPT; + } + else + { + freshDensity = ( COMP_SLOPE * getFilteredTemperatureValue( D99_TEMP ) ) + COMP_INTERCEPT; + } + spentDensity = ( COMP_SLOPE * getFilteredTemperatureValue( D50_TEMP ) ) + COMP_INTERCEPT; // spent side dialysate density + compFreshFlowrate = getTDDialysateFlowrate() * freshDensity; // Qd * fresh density + compSpentFlowrate = getTDDialysateFlowrate() * spentDensity; // Qd * spent density + balancingError = compFreshFlowrate - compSpentFlowrate; // Error in g/min + + //Update compensate UF rate with the balancing chamber error + compUFrate = getTDUFRate() + balancingError; + + // Update UF rate + setConcentratePumpTargetSpeed( D76_PUMP, compUFrate, DOSING_CONT_VOLUME ); + + currentUFCompCounter = 0; + isUFRateUpdated = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The getCurrentUFExecState function returns the current state + * of the ultrafiltration. + * @details \b Inputs: ufExecState + * @details \b Outputs: none + * @return the current state of UF execution state. + *************************************************************************/ +UF_EXEC_STATE_T getCurrentUFExecState( void ) +{ + return ufExecState; +} + +/*********************************************************************//** + * @brief + * The publishUltrafiltrationData function broadcasts the ultrafiltration + * data at defined interval. + * @details \b Inputs: ufDataPublicationTimerCounter + * @details \b Outputs: DD ultrafiltration data broadcast message sent + * @details \b Message \Sent: MSG_ID_DD_UF_DATA to publish the ultrafiltration + * data. + * @return none + *************************************************************************/ +static void publishUltrafiltrationData( void ) +{ + if ( ++ufDataPublicationTimerCounter >= getU32OverrideValue( &ufDataPublishInterval ) ) + { + UF_DATA_T data; + + data.ufExecState = (U32)ufExecState; + data.ufRate = getTDUFRate(); + data.compUFrate = compUFrate; + data.isUFRequested = (U32)isUltrafiltrationRequested; + + broadcastData( MSG_ID_DD_UF_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( UF_DATA_T ) ); + + ufDataPublicationTimerCounter = 0; + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testDDUFDataPublishIntervalOverride function overrides the + * DD ultrafiltration data publish interval. + * @details \b Inputs: ufDataPublishInterval + * @details \b Outputs: ufDataPublishInterval + * @param Override message from Dialin which includes the interval + * (in ms) to override the DD ultrafiltration data publish interval to. + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testDDUFDataPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = u32BroadcastIntervalOverride( message, &ufDataPublishInterval, TASK_GENERAL_INTERVAL ); + + return result; +} + +/**@}*/ Index: firmware/App/Controllers/HemoDiaFiltration.h =================================================================== diff -u --- firmware/App/Controllers/HemoDiaFiltration.h (revision 0) +++ firmware/App/Controllers/HemoDiaFiltration.h (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,58 @@ +/************************************************************************** +* +* 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 HemoDiaFiltration.h +* +* @author (last) Steve Jarpe +* @date (last) 23-May-2026 +* +* @author (original) Steve Jarpe +* @date (original) 23-May-2026 +* +***************************************************************************/ + +#ifndef __HEMO_DIAFILTRATION_H__ +#define ___HEMO_DIAFILTRATION_H__ + +#include "DDCommon.h" +#include "DDDefs.h" + +/** + * @defgroup Hemodiafiltration Hemodiafiltration + * @brief Hemodiafiltration control unit. Performs hemodiafiltration functions via a state machine. + * + * @addtogroup Hemodiafiltration + * @{ + */ + +// ********** public definitions ********** + +/// ultrafiltration data structure +typedef struct +{ + U32 hdfExecState; ///< Hemodiafiltration execution state + F32 hdfRequestedVolume; ///< Hemodiafiltration volume from TD + F32 hdfRequestedRate; ///< Hemodiafiltration rate from TD + F32 hdfRequestedTMP; ///< Hemodiafiltration TMP target from TD + F32 hdfCurrentVolume; ///< Current hemodiafiltration volume + F32 hdfCurrentRate; ///< Current HDF rate + U32 isHDFRequested; ///< Hemodiafiltration run or pause request +} UF_DATA_T; + +// ********** public function prototypes ********** + +void initHemodiafiltration( void ); // Initialize hemodiafiltration unit +void transitionToHemodiafiltration( void ); // Prepares for transition to hemodiafiltration execution +U32 execHDFControl( void ); // Execute the hemodiafiltration state machine +UF_EXEC_STATE_T getCurrentHDFExecState( void ); // Get the current state of the hemodifiltration execution +void signalHDFRateUpdate( void ); // Update HDF rate when there is a change in rate + +BOOL testDDHDFDataPublishIntervalOverride( MESSAGE_T *message ); // To override the HDF data publish interval + +/**@}*/ + +#endif Index: firmware/App/Controllers/HemoDiaFiltration.h~ =================================================================== diff -u --- firmware/App/Controllers/HemoDiaFiltration.h~ (revision 0) +++ firmware/App/Controllers/HemoDiaFiltration.h~ (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,55 @@ +/************************************************************************** +* +* 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 Ultrafiltration.h +* +* @author (last) Vinayakam Mani +* @date (last) 30-Jul-2025 +* +* @author (original) Vinayakam Mani +* @date (original) 28-Jul-2025 +* +***************************************************************************/ + +#ifndef __ULTRA_FILTERATION_H__ +#define __ULTRA_FILTERATION_H__ + +#include "DDCommon.h" +#include "DDDefs.h" + +/** + * @defgroup Ultrafiltration Ultrafiltration + * @brief Ultrafiltration control unit. Performs ultrafiltration functions via a state machine. + * + * @addtogroup Ultrafiltration + * @{ + */ + +// ********** public definitions ********** + +/// ultrafiltration data structure +typedef struct +{ + U32 ufExecState; ///< Ultrafiltration execution state + F32 ufRate; ///< Ultrafiltration rate from TD + F32 compUFrate; ///< Compensated UF rate + U32 isUFRequested; ///< Ultrafiltration run or puase request +} UF_DATA_T; + +// ********** public function prototypes ********** + +void initUltrafiltration( void ); // Initialize ultrafiltration unit +void transitionToUltrafiltration( void ); // Prepares for transition to ultrafiltration execution +U32 execUFControl( void ); // Execute the ultrafiltration state machine +UF_EXEC_STATE_T getCurrentUFExecState( void ); // Get the current state of the balancing chamber execution +void signalUFRateUpdate( void ); // Update UF rate when there is a change in rate + +BOOL testDDUFDataPublishIntervalOverride( MESSAGE_T *message ); // To override the UF data publish interval + +/**@}*/ + +#endif Index: firmware/App/Monitors/Switches.c =================================================================== diff -u --- firmware/App/Monitors/Switches.c (revision 0) +++ firmware/App/Monitors/Switches.c (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,225 @@ +/************************************************************************** +* +* 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. +* +* @file Switches.c +* +* @author (last) N/A +* @date (last) N/A +* +* @author (original) Steve Jarpe +* @date (original) 22 May 2026 +* +***************************************************************************/ + +#include "FpgaDD.h" +#include "Messaging.h" +//#include "NVDataMgmt.h" +#include "Switches.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "MsgDefs.h" + +/** + * @addtogroup Switches + * @{ + */ + +// ********** private definitions ********** + +#define SWITCHES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the switches data is published on the CAN bus. +#define SWITCHES_DEBOUNCE_TIME_MS ( MS_PER_SECOND / 4 ) ///< Switches FPGA status check interval. +#define DATA_PUBLISH_COUNTER_START_COUNT 90 ///< Data publish counter start count. + +// ********** private data ********** + +static U32 switchesDataPublicationCounter; ///< Switches data publication counter. +static OVERRIDE_U32_T switchesDataPublishInterval; ///< Interval (in ms) at which to publish switches data to CAN bus. +static OVERRIDE_U32_T switchesStatus[ NUM_OF_SWITCHES ]; ///< Debounced switch states. +static U32 switchDebounceStartTimes[ NUM_OF_SWITCHES ]; ///< Switch debounce start times. + +// ********** private function prototypes ********** + +static void publishSwitchesData( void ); + +/*********************************************************************//** + * @brief + * The initSwitches function initializes the switches unit. + * @details \b Inputs: none + * @details \b Outputs: Switches monitor unit initialized + * @return none + *************************************************************************/ +void initSwitches( void ) +{ + U32 i; + + switchesDataPublicationCounter = DATA_PUBLISH_COUNTER_START_COUNT; + + switchesDataPublishInterval.data = SWITCHES_DATA_PUB_INTERVAL; + switchesDataPublishInterval.ovData = SWITCHES_DATA_PUB_INTERVAL; + switchesDataPublishInterval.ovInitData = 0; + switchesDataPublishInterval.override = OVERRIDE_RESET; + + // Initialize all the switches + for ( i = 0; i < NUM_OF_SWITCHES; i++ ) + { + switchesStatus[ i ].data = (U32)STATE_CLOSED; + switchesStatus[ i ].ovData = (U32)STATE_CLOSED; + switchesStatus[ i ].ovInitData = (U32)STATE_CLOSED; + switchesStatus[ i ].override = OVERRIDE_RESET; + switchDebounceStartTimes[ i ] = 0; + } +} + +/*********************************************************************//** + * @brief + * The execSwitches function executes the switches monitor executive. + * Switch state changes are debounced for 250ms before formalizing them. + * @details \b Inputs: switchesStatus[], switchDebounceStartTimes[] + * @details \b Outputs: switchesStatus[], switchDebounceStartTimes[] + * @return none + *************************************************************************/ +void execSwitches( void ) +{ + U32 i; + OPN_CLS_STATE_T currentSwitchStates[ NUM_OF_SWITCHES ]; + + // Get current state of each switch + currentSwitchStates[ D101_SWCH ] = ( FALSE == getD101HDFCapConnectorStatus() ? STATE_OPEN : STATE_CLOSED ); + currentSwitchStates[ D102_SWCH ] = ( FALSE == getD102HDFCapConnectorStatus() ? STATE_OPEN : STATE_CLOSED ); + currentSwitchStates[ D103_SWCH ] = ( FALSE == getD103HDFCapParkedStatus() ? STATE_OPEN : STATE_CLOSED ); + + // Debounce each switch + for ( i = 0; i < NUM_OF_SWITCHES; i++ ) + { +#ifndef _RELEASE_ +// if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_SWITCHES_MONITOR ) ) +// { +// switchesStatus[ i ].status.data = STATE_CLOSED; +// } +// else +#endif + { + // Check if the current switch status is not the same as the recorded data + if ( (U32)currentSwitchStates[ i ] != switchesStatus[ i ].data ) + { + // If the debounce time is 0, start the timer + if ( 0 == switchDebounceStartTimes[ i ] ) + { + switchDebounceStartTimes[ i ] = getMSTimerCount(); + } + // If the debounce time has been elapsed, update the switch status to the new status + else if ( TRUE == didTimeout( switchDebounceStartTimes[ i ], SWITCHES_DEBOUNCE_TIME_MS ) ) + { + // If the bit is 0, the door switch is open, because it is normally open switch + switchDebounceStartTimes[ i ] = 0; + switchesStatus[ i ].data = currentSwitchStates[ i ]; + } + } + else + { + switchDebounceStartTimes[ i ] = 0; + } + } + } + + + // Handle publishing switches data + publishSwitchesData(); +} + +/*********************************************************************//** + * @brief + * The getSwitchState function returns the state of a given switch. + * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT if given switch ID is invalid. + * @details \b Inputs: switchesStatus + * @details \b Outputs: switchesStatus + * @param switchId ID of switch to get state for + * @return switch state of given switch + *************************************************************************/ +OPN_CLS_STATE_T getSwitchState( SWITCH_T switchId ) +{ + U32 state = 0; + + if ( switchId < NUM_OF_SWITCHES ) + { + state = getU32OverrideValue( &switchesStatus[ switchId ] ); + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_DD_INVALID_SWITCH_ID, (U32)switchId ) + } + + return (OPN_CLS_STATE_T)state; +} + +/*********************************************************************//** + * @brief + * The publishSwitchesData function broadcasts the switches data at the + * publication interval. + * @details \b Message \b Sent: MSG_ID_DD_SWITCHES_DATA + * @details \b Inputs: switchesDataPublicationCounter, switchesStatus[] + * @details \b Outputs: switchesDataPublicationCounter + * @return none + *************************************************************************/ +static void publishSwitchesData( void ) +{ + if ( ++switchesDataPublicationCounter >= getU32OverrideValue( &switchesDataPublishInterval ) ) + { + SWITCHES_DATA_T data; + + switchesDataPublicationCounter = 0; + data.d101Sw1 = (U32)getSwitchState( D101_SWCH ); + data.d102Sw2 = (U32)getSwitchState( D102_SWCH ); + data.d103CapParked = (U32)getSwitchState( D103_SWCH ); + + broadcastData( MSG_ID_DD_SWITCHES_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( SWITCHES_DATA_T ) ); + //broadcastData( MSG_ID_TD_BATTERY_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( SWITCHES_DATA_T ) ); + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testSwitchesDataPublishIntervalOverride function overrides the interval + * at which the DD switches data is published. + * @details \b Inputs: none + * @details \b Outputs: switchesDataPublishInterval + * @param message Override message from Dialin which includes the interval + * (in ms) to override the switches broadcast interval to. + * @return TRUE if override request is successful, FALSE if not + *************************************************************************/ +BOOL testSwitchesDataPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = u32BroadcastIntervalOverride( message, &switchesDataPublishInterval, TASK_GENERAL_INTERVAL ); + + return result; +} + +/*********************************************************************//** + * @brief + * The testSwitchOverride function overrides the state for a given switch. + * @details \b Inputs: none + * @details \b Outputs: switchesStatus[] + * @param message Override message from Dialin which includes an ID of + * the switch to override and the state to override the switch to. + * @return TRUE if override request is successful, FALSE if not + *************************************************************************/ +BOOL testSwitchOverride( MESSAGE_T *message ) +{ + BOOL result = u32ArrayOverride( message, &switchesStatus[0], NUM_OF_SWITCHES - 1, 0, NUM_OF_OPN_CLS_STATES - 1 ); + + return result; +} + +/**@}*/ + + Index: firmware/App/Monitors/Switches.c~ =================================================================== diff -u --- firmware/App/Monitors/Switches.c~ (revision 0) +++ firmware/App/Monitors/Switches.c~ (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,270 @@ +/************************************************************************** +* +* 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. +* +* @file Switches.c +* +* @author (last) Sean Nash +* @date (last) 03-Jun-2025 +* +* @author (original) Sean Nash +* @date (original) 19-Sep-2024 +* +***************************************************************************/ + +#include "FpgaTD.h" +#include "Messaging.h" +//#include "NVDataMgmt.h" +#include "Switches.h" +#include "TaskGeneral.h" +#include "Timers.h" + +/** + * @addtogroup Switches + * @{ + */ + +// ********** private definitions ********** + +#define SWITCHES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the switches data is published on the CAN bus. +#define SWITCHES_DEBOUNCE_TIME_MS ( MS_PER_SECOND / 4 ) ///< Switches FPGA status check interval. +#define DATA_PUBLISH_COUNTER_START_COUNT 90 ///< Data publish counter start count. + +// ********** private data ********** + +static U32 switchesDataPublicationCounter; ///< Switches data publication counter. +static OVERRIDE_U32_T switchesDataPublishInterval; ///< Interval (in ms) at which to publish switches data to CAN bus. +static OVERRIDE_U32_T switchesStatus[ NUM_OF_SWITCHES ]; ///< Debounced switch states. +static U32 switchDebounceStartTimes[ NUM_OF_SWITCHES ]; ///< Switch debounce start times. +static BOOL requireDoorClosed; ///< Flag indicates whether door is required to be closed in current state. + +// ********** private function prototypes ********** + +static void publishSwitchesData( void ); +static void handleSwitchAlarms( void ); + +/*********************************************************************//** + * @brief + * The initSwitches function initializes the switches unit. + * @details \b Inputs: none + * @details \b Outputs: Switches monitor unit initialized + * @return none + *************************************************************************/ +void initSwitches( void ) +{ + U32 i; + + switchesDataPublicationCounter = DATA_PUBLISH_COUNTER_START_COUNT; + requireDoorClosed = FALSE; + + switchesDataPublishInterval.data = SWITCHES_DATA_PUB_INTERVAL; + switchesDataPublishInterval.ovData = SWITCHES_DATA_PUB_INTERVAL; + switchesDataPublishInterval.ovInitData = 0; + switchesDataPublishInterval.override = OVERRIDE_RESET; + + // Initialize all the switches + for ( i = 0; i < NUM_OF_SWITCHES; i++ ) + { + switchesStatus[ i ].data = (U32)STATE_CLOSED; + switchesStatus[ i ].ovData = (U32)STATE_CLOSED; + switchesStatus[ i ].ovInitData = (U32)STATE_CLOSED; + switchesStatus[ i ].override = OVERRIDE_RESET; + switchDebounceStartTimes[ i ] = 0; + } +} + +/*********************************************************************//** + * @brief + * The execSwitches function executes the switches monitor executive. + * Switch state changes are debounced for 250ms before formalizing them. + * @details \b Inputs: switchesStatus[], switchDebounceStartTimes[] + * @details \b Outputs: switchesStatus[], switchDebounceStartTimes[] + * @return none + *************************************************************************/ +void execSwitches( void ) +{ + U32 i; + OPN_CLS_STATE_T currentSwitchStates[ NUM_OF_SWITCHES ]; + + // Get current state of each switch + currentSwitchStates[ H9_SWCH ] = ( FALSE == getH9FrontDoorClosedStatus() ? STATE_OPEN : STATE_CLOSED ); + + // Debounce each switch + for ( i = 0; i < NUM_OF_SWITCHES; i++ ) + { +#ifndef _RELEASE_ +// if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_SWITCHES_MONITOR ) ) +// { +// switchesStatus[ i ].status.data = STATE_CLOSED; +// } +// else +#endif + { + // Check if the current switch status is not the same as the recorded data + if ( (U32)currentSwitchStates[ i ] != switchesStatus[ i ].data ) + { + // If the debounce time is 0, start the timer + if ( 0 == switchDebounceStartTimes[ i ] ) + { + switchDebounceStartTimes[ i ] = getMSTimerCount(); + } + // If the debounce time has been elapsed, update the switch status to the new status + else if ( TRUE == didTimeout( switchDebounceStartTimes[ i ], SWITCHES_DEBOUNCE_TIME_MS ) ) + { + // If the bit is 0, the door switch is open, because it is normally open switch + switchDebounceStartTimes[ i ] = 0; + switchesStatus[ i ].data = currentSwitchStates[ i ]; + } + } + else + { + switchDebounceStartTimes[ i ] = 0; + } + } + } + + // Check for switch alarms + handleSwitchAlarms(); + + // Handle publishing switches data + publishSwitchesData(); +} + +/*********************************************************************//** + * @brief + * The handleSwitchAlarms function checks for switch related alarms. + * @details \b Alarm: ALARM_ID_TD_CARTRIDGE_DOOR_OPENED if door is open when + * it should be closed. + * @details \b Inputs: requireDoorClosed, switchesStatus + * @details \b Outputs: switchesStatus + * @return none + *************************************************************************/ +static void handleSwitchAlarms( void ) +{ +#ifndef _RELEASE_ +// if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SWITCHES_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + // Check for door closed alarm + if ( TRUE == requireDoorClosed ) + { + if ( getSwitchState( H9_SWCH ) != STATE_CLOSED ) + { + activateAlarmNoData( ALARM_ID_TD_CARTRIDGE_DOOR_OPENED ); + } + } + + // Handle clearing alarm conditions + if ( STATE_CLOSED == getSwitchState( H9_SWCH ) ) + { + clearAlarmCondition( ALARM_ID_TD_CARTRIDGE_DOOR_OPENED ); + } + } +} + +/*********************************************************************//** + * @brief + * The getSwitchState function returns the state of a given switch. + * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given switch ID is invalid. + * @details \b Inputs: switchesStatus + * @details \b Outputs: switchesStatus + * @param switchId ID of switch to get state for + * @return switch state of given switch + *************************************************************************/ +OPN_CLS_STATE_T getSwitchState( SWITCH_T switchId ) +{ + U32 state = 0; + + if ( switchId < NUM_OF_SWITCHES ) + { + state = getU32OverrideValue( &switchesStatus[ switchId ] ); + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TD_INVALID_SWITCH_ID, (U32)switchId ) + } + + return (OPN_CLS_STATE_T)state; +} + +/*********************************************************************//** + * @brief + * The doorClosedRequired function sets flags that determine whether door + * and pump tracks should be closed/locked in current state. Associated + * alarms will be triggered if switches not in required state. + * @details \b Inputs: none + * @details \b Outputs: requireDoorClosed + * @param door is door required to be closed in current state? + * @return none + *************************************************************************/ +void doorClosedRequired( BOOL door ) +{ + requireDoorClosed = door; +} + +/*********************************************************************//** + * @brief + * The publishSwitchesData function broadcasts the switches data at the + * publication interval. + * @details \b Message \b Sent: MSG_ID_TD_SWITCHES_DATA + * @details \b Inputs: switchesDataPublicationCounter, switchesStatus[] + * @details \b Outputs: switchesDataPublicationCounter + * @return none + *************************************************************************/ +static void publishSwitchesData( void ) +{ + if ( ++switchesDataPublicationCounter >= getU32OverrideValue( &switchesDataPublishInterval ) ) + { + SWITCHES_DATA_T data; + + switchesDataPublicationCounter = 0; + data.h9Door = (U32)getSwitchState( H9_SWCH ); + broadcastData( MSG_ID_TD_SWITCHES_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( SWITCHES_DATA_T ) ); + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testSwitchesDataPublishIntervalOverride function overrides the interval + * at which the TD switches data is published. + * @details \b Inputs: none + * @details \b Outputs: switchesDataPublishInterval + * @param message Override message from Dialin which includes the interval + * (in ms) to override the switches broadcast interval to. + * @return TRUE if override request is successful, FALSE if not + *************************************************************************/ +BOOL testSwitchesDataPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = u32BroadcastIntervalOverride( message, &switchesDataPublishInterval, TASK_GENERAL_INTERVAL ); + + return result; +} + +/*********************************************************************//** + * @brief + * The testSwitchOverride function overrides the state for a given switch. + * @details \b Inputs: none + * @details \b Outputs: switchesStatus[] + * @param message Override message from Dialin which includes an ID of + * the switch to override and the state to override the switch to. + * @return TRUE if override request is successful, FALSE if not + *************************************************************************/ +BOOL testSwitchOverride( MESSAGE_T *message ) +{ + BOOL result = u32ArrayOverride( message, &switchesStatus[0], NUM_OF_SWITCHES - 1, 0, NUM_OF_OPN_CLS_STATES - 1 ); + + return result; +} + +/**@}*/ + + Index: firmware/App/Monitors/Switches.h =================================================================== diff -u --- firmware/App/Monitors/Switches.h (revision 0) +++ firmware/App/Monitors/Switches.h (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,62 @@ +/************************************************************************** +* +* 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. +* +* @file Switches.h +* +* @author (last) N/A +* @date (last) N/A +* +* @author (original) Steve Jarpe +* @date (original) 22-May-2026 +* +***************************************************************************/ + +#ifndef APP_SWITCHES_H_ +#define APP_SWITCHES_H_ + +#include "DDCommon.h" + +/** + * @defgroup Switches Switches + * @brief Switches monitor unit. Provides functions for monitoring the status + * of the DD switch positions. + * + * @addtogroup Switches + * @{ + */ + +// ********** public definitions ********** + +/// TD switches enumeration +typedef enum switch_names +{ + D101_SWCH = 0, ///< HDF cap closed connector on + D102_SWCH, ///< HDF cap open connector off + D103_SWCH, ///< HDF cap parked switch + NUM_OF_SWITCHES ///< Number of switches +} SWITCH_T; + +/// TD switches data publish structure +typedef struct +{ + U32 d101Sw1; ///< HDF cap closed connector on switch + U32 d102Sw2; ///< HDF cap open connector off switch + U32 d103CapParked; ///< HDF cap parked switch +} SWITCHES_DATA_T; + +// ********** public function prototypes ********** + +void initSwitches( void ); + +void execSwitches( void ); + +OPN_CLS_STATE_T getSwitchState( SWITCH_T switchId ); + +BOOL testSwitchesDataPublishIntervalOverride( MESSAGE_T *message ); +BOOL testSwitchOverride( MESSAGE_T *message ); + +#endif /* APP_CONTROLLERS_SWITCHES_H_ */ Index: firmware/App/Monitors/Switches.h~ =================================================================== diff -u --- firmware/App/Monitors/Switches.h~ (revision 0) +++ firmware/App/Monitors/Switches.h~ (revision eca350134b358d583fe4364a117d744684d28788) @@ -0,0 +1,59 @@ +/************************************************************************** +* +* 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. +* +* @file Switches.h +* +* @author (last) Sean Nash +* @date (last) 20-Jan-2025 +* +* @author (original) Sean Nash +* @date (original) 19-Sep-2024 +* +***************************************************************************/ + +#ifndef APP_SWITCHES_H_ +#define APP_SWITCHES_H_ + +#include "TDCommon.h" + +/** + * @defgroup Switches Switches + * @brief Switches monitor unit. Provides functions for monitoring the status + * of the TD switch positions. + * + * @addtogroup Switches + * @{ + */ + +// ********** public definitions ********** + +/// TD switches enumeration +typedef enum switch_names +{ + H9_SWCH = 0, ///< Front door switch + NUM_OF_SWITCHES ///< Number of switches +} SWITCH_T; + +/// TD switches data publish structure +typedef struct +{ + U32 h9Door; ///< Front door +} SWITCHES_DATA_T; + +// ********** public function prototypes ********** + +void initSwitches( void ); + +void execSwitches( void ); + +OPN_CLS_STATE_T getSwitchState( SWITCH_T switchId ); +void doorClosedRequired( BOOL door ); + +BOOL testSwitchesDataPublishIntervalOverride( MESSAGE_T *message ); +BOOL testSwitchOverride( MESSAGE_T *message ); + +#endif /* APP_CONTROLLERS_SWITCHES_H_ */ Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -rfd897db8177752330ad08d877e0a13620513dbdc -reca350134b358d583fe4364a117d744684d28788 --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision fd897db8177752330ad08d877e0a13620513dbdc) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision eca350134b358d583fe4364a117d744684d28788) @@ -157,6 +157,7 @@ SW_FAULT_ID_PI_CTRL_INVALID_FEED_FORWARD_LIMIT = 126, SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED3 = 127, SW_FAULT_ID_DIALYSATE_MIX_EXEC_INVALID_STATE = 128, + SW_FAULT_ID_DD_INVALID_SWITCH_ID = 129, NUM_OF_SW_FAULT_IDS } DD_SW_FAULT_ID_T; Index: firmware/App/Services/FpgaDD.c =================================================================== diff -u -rfd897db8177752330ad08d877e0a13620513dbdc -reca350134b358d583fe4364a117d744684d28788 --- firmware/App/Services/FpgaDD.c (.../FpgaDD.c) (revision fd897db8177752330ad08d877e0a13620513dbdc) +++ firmware/App/Services/FpgaDD.c (.../FpgaDD.c) (revision eca350134b358d583fe4364a117d744684d28788) @@ -148,6 +148,10 @@ #define DIENER_1000_PUMP_INTERCEPT 0.07040F ///< Speed adjustment intercept factor (Diener 1000 pump) #define DIENER_1000_PUMP_SPEED_FULL_SCALE 3150 ///< Maximum speed factor (Diener 1000 pump) +#define FPGA_D101_SWITCH1_BIT 0x01 ///< D101 connector/cap sensor 1 bit mask. +#define FPGA_D102_SWITCH2_BIT 0x02 ///< D102 connector/cap sensor 2 bit mask. +#define FPGA_D103_CAP_PARKED_BIT 0x04 ///< D103 cap parked bit mask. + #pragma pack(push,1) /// FPGA header struct. typedef struct @@ -3932,4 +3936,62 @@ { return fpgaSensorReadings.fpgaP18CalMemCounter; } + +/*********************************************************************//** + * The getD101HDFCapConnectorStatus function returns the cap connector switch 1 status + * bit 0 (0x01). + * @details \b Inputs: fpgaSensorReadings + * @details \b Outputs: none + * @return TRUE if sensor is covered, FALSE if not. + *************************************************************************/ +BOOL getD101HDFCapConnectorStatus( void ) +{ + BOOL result = TRUE; + + if ( ( fpgaSensorReadings.fpgaHallSensorStatus & FPGA_D101_SWITCH1_BIT ) != 0 ) + { + result = FALSE; + } + + return result; +} + +/*********************************************************************//** + * The getD102HDFCapConnectorStatus function returns the cap connector switch 2 status + * bit 1 (0x02). + * @details \b Inputs: fpgaSensorReadings + * @details \b Outputs: none + * @return TRUE if sensor is covered, FALSE if not. + *************************************************************************/ +BOOL getD102HDFCapConnectorStatus( void ) +{ + BOOL result = TRUE; + + if ( ( fpgaSensorReadings.fpgaHallSensorStatus & FPGA_D102_SWITCH2_BIT ) != 0 ) + { + result = FALSE; + } + + return result; +} + +/*********************************************************************//** + * The getD103HDFCapParkedStatus function returns the cap parked switch status + * bit 2 (0x04). + * @details \b Inputs: fpgaSensorReadings + * @details \b Outputs: none + * @return TRUE if door is parked, FALSE if not. + *************************************************************************/ +BOOL getD103HDFCapParkedStatus( void ) +{ + BOOL result = TRUE; + + if ( ( fpgaSensorReadings.fpgaHallSensorStatus & FPGA_D103_CAP_PARKED_BIT ) != 0 ) + { + result = FALSE; + } + + return result; +} + /**@}*/ Index: firmware/App/Services/FpgaDD.h =================================================================== diff -u -rb04db69f541f245e543df343257bcbdb73fbbc3d -reca350134b358d583fe4364a117d744684d28788 --- firmware/App/Services/FpgaDD.h (.../FpgaDD.h) (revision b04db69f541f245e543df343257bcbdb73fbbc3d) +++ firmware/App/Services/FpgaDD.h (.../FpgaDD.h) (revision eca350134b358d583fe4364a117d744684d28788) @@ -318,6 +318,11 @@ U16 getFPGAP40PumpPWM( void ); U16 getFPGAP40PumpTachCount( void ); +// HDF switches +BOOL getD101HDFCapConnectorStatus( void ); +BOOL getD102HDFCapConnectorStatus( void ); +BOOL getD103HDFCapParkedStatus( void ); + /**@}*/ #endif Index: firmware/App/Tasks/TaskGeneral.c =================================================================== diff -u -r6d0bd19fb192dcd272fa773e8833862cc8a8f750 -reca350134b358d583fe4364a117d744684d28788 --- firmware/App/Tasks/TaskGeneral.c (.../TaskGeneral.c) (revision 6d0bd19fb192dcd272fa773e8833862cc8a8f750) +++ firmware/App/Tasks/TaskGeneral.c (.../TaskGeneral.c) (revision eca350134b358d583fe4364a117d744684d28788) @@ -30,6 +30,7 @@ #include "PermeateTank.h" #include "RinsePump.h" #include "ROPump.h" +#include "Switches.h" #include "SystemCommDD.h" #include "TaskGeneral.h" #include "WatchdogMgmt.h" @@ -114,7 +115,7 @@ execBoostPumpController(); // Manage switches monitor - //execSwitches(); + execSwitches(); // Heaters controller execHeatersControl(); Index: firmware/source/sys_main.c =================================================================== diff -u -r89c8709e3b27648926fbb20f25c9a67cbeb99adc -reca350134b358d583fe4364a117d744684d28788 --- firmware/source/sys_main.c (.../sys_main.c) (revision 89c8709e3b27648926fbb20f25c9a67cbeb99adc) +++ firmware/source/sys_main.c (.../sys_main.c) (revision eca350134b358d583fe4364a117d744684d28788) @@ -98,6 +98,7 @@ #include "StateFlushFilterDefeatured.h" #include "StateFlushPermeate.h" #include "StateInletPressureCheck.h" +#include "Switches.h" #include "SystemCommDD.h" #include "TaskBG.h" #include "TDInterface.h" @@ -208,6 +209,7 @@ initUltrafiltration(); initRinsePump(); initDryBiCart(); + initSwitches(); // FP Modules