Index: firmware/App/Controllers/BloodFlow.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Controllers/BloodFlow.c (.../BloodFlow.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Controllers/BloodFlow.c (.../BloodFlow.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -112,6 +112,8 @@ #define BP_PWM_FROM_ML_PER_MIN(rate) ( (rate) * BP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * BP_GEAR_RATIO * BP_MOTOR_RPM_TO_PWM_DC_FACTOR + BP_PWM_ZERO_OFFSET ) /// Conversion from PWM duty cycle % to commanded pump motor speed. #define BP_PWM_TO_MOTOR_SPEED_RPM(pwm) ( ((pwm) - BP_PWM_ZERO_OFFSET) * 4000.0F ) +/// Conversion macro from mL/min to estimated PWM duty cycle %. +#define BP_ML_PER_MIN_FROM_PWM(pwm) ( ( ( pwm - BP_PWM_ZERO_OFFSET ) / ( BP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * BP_GEAR_RATIO * BP_MOTOR_RPM_TO_PWM_DC_FACTOR ) ) ) /// Measured blood flow is filtered w/ moving average. #define SIZE_OF_ROLLING_AVG ( ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) * 1 ) @@ -1634,5 +1636,26 @@ return result; } + +/*********************************************************************//** + * @brief + * The testSetDialInPumpTargetDutyCycle function resets the override of the + * measured dialIn pump motor current. + * @details Inputs: none + * @details Outputs: adcDialInPumpMCCurrentmA + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testSetBloodPumpTargetDutyCycle( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + setBloodPumpTargetFlowRate( (S32)BP_ML_PER_MIN_FROM_PWM( value ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + result = TRUE; + } + + return result; +} /**@}*/ Index: firmware/App/Controllers/DialInFlow.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -110,6 +110,9 @@ #define DIP_PWM_FROM_ML_PER_MIN(rate) ( ( ( (rate) - 49.121F ) / 684.73F ) + DIP_PWM_ZERO_OFFSET ) /// Conversion from PWM duty cycle % to commanded pump motor speed. PWM range is 10% to 90%. RPM range is 0 to 3200. 3200 / 0.8 = 4000. #define DIP_PWM_TO_MOTOR_SPEED_RPM(pwm) ( ((pwm) - DIP_PWM_ZERO_OFFSET) * 4000.0F ) +// Macro converts PWM to estimate flow rate needed to achieve it. +#define DIP_ML_PER_MIN_FROM_PWM(pwm) ( (( pwm - DIP_PWM_ZERO_OFFSET) * 684.73 ) + 49.121 ) + /// Measured dialIn flow is filtered w/ moving average. #define SIZE_OF_ROLLING_AVG 10 @@ -261,10 +264,11 @@ * @details Outputs: targetDialInFlowRate, dialInPumpdirection, dialInPumpPWMDutyCyclePct * @param flowRate new target dialIn flow rate * @param dir new dialIn flow direction - * @param mode new control mode + * @param mode new control mode + * @param pwm PWM duty cycle to set pump controller to (optional for open loop) * @return TRUE if new flow rate & dir are set, FALSE if not *************************************************************************/ -BOOL setDialInPumpTargetFlowRate( U32 flowRate, MOTOR_DIR_T dir, PUMP_CONTROL_MODE_T mode ) +BOOL setDialInPumpTargetFlowRate( U32 flowRate, MOTOR_DIR_T dir, PUMP_CONTROL_MODE_T mode, F32 pwm ) { BOOL result = FALSE; @@ -274,7 +278,7 @@ S32 dirFlowRate = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); // Don't interrupt pump control unless rate or mode is changing - if ( ( dirFlowRate != targetDialInFlowRate ) || ( mode != dialInPumpControlMode ) ) + if ( ( dirFlowRate != targetDialInFlowRate ) || ( mode != dialInPumpControlMode ) || ( fabs(pwm) > NEARLY_ZERO ) ) { BOOL isFlowRateInRange = ( flowRate <= MAX_DIAL_IN_FLOW_RATE ? TRUE : FALSE ); @@ -292,8 +296,15 @@ targetDialInFlowRate = dirFlowRate; dialInPumpDirection = dir; dialInPumpControlMode = mode; - // Set PWM duty cycle target to an estimated initial target to ramp to based on target flow rate - then we will control to flow when ramp completed - dialInPumpPWMDutyCyclePct = ( 0 == flowRate ? DIP_PWM_ZERO_OFFSET : DIP_PWM_FROM_ML_PER_MIN( (F32)flowRate ) ); + // Set PWM duty cycle target to an estimated initial target to ramp to based on target flow rate - then we will control to flow when ramp completed + if ( PUMP_CONTROL_MODE_CLOSED_LOOP == mode || fabs(pwm) < NEARLY_ZERO ) + { + dialInPumpPWMDutyCyclePct = ( 0 == flowRate ? DIP_PWM_ZERO_OFFSET : DIP_PWM_FROM_ML_PER_MIN( (F32)flowRate ) ); + } + else // Dialin command to open loop w/ set PWM duty cycle + { + dialInPumpPWMDutyCyclePct = pwm; + } switch ( dialInPumpState ) { @@ -403,7 +414,7 @@ { dipStopAtHomePosition = TRUE; dipHomeStartTime = getMSTimerCount(); - result = setDialInPumpTargetFlowRate( DIP_HOME_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + result = setDialInPumpTargetFlowRate( DIP_HOME_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP, 0.0F ); } return result; @@ -1407,7 +1418,7 @@ } else { - result = setDialInPumpTargetFlowRate( abs(value), dir, (PUMP_CONTROL_MODE_T)ctrlMode ); + result = setDialInPumpTargetFlowRate( abs(value), dir, (PUMP_CONTROL_MODE_T)ctrlMode, 0.0F ); } } } @@ -1638,6 +1649,27 @@ } return result; -} +} +/*********************************************************************//** + * @brief + * The testSetDialInPumpTargetDutyCycle function resets the override of the + * measured dialIn pump motor current. + * @details Inputs: none + * @details Outputs: adcDialInPumpMCCurrentmA + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testSetDialInPumpTargetDutyCycle( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + setDialInPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP, value ); + result = TRUE; + } + + return result; +} + /**@}*/ Index: firmware/App/Controllers/DialOutFlow.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Controllers/DialOutFlow.c (.../DialOutFlow.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Controllers/DialOutFlow.c (.../DialOutFlow.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -105,9 +105,11 @@ #define DOP_PWM_ZERO_OFFSET 0.1F ///< 10% PWM duty cycle = zero speed. /// Macro converts a flow rate to an estimated PWM duty cycle %. -#define DOP_PWM_FROM_ML_PER_MIN(rate) ( ( (rate) * 0.0009 ) + 0.0972 + DOP_PWM_ZERO_OFFSET ) +#define DOP_PWM_FROM_ML_PER_MIN(rate) ( ( ( rate ) * 0.0009 ) + 0.0972 + DOP_PWM_ZERO_OFFSET ) /// Conversion from PWM duty cycle % to commanded pump motor speed. -#define DOP_PWM_TO_MOTOR_SPEED_RPM(pwm) ( ((pwm) - DOP_PWM_ZERO_OFFSET) * 4000.0F ) +#define DOP_PWM_TO_MOTOR_SPEED_RPM(pwm) ( ( ( pwm ) - DOP_PWM_ZERO_OFFSET) * 4000.0F ) +/// Macro converts a PWM to an estimated flow rate. +#define DOP_ML_PER_MIN_FROM_PWM(pwm) ( ( ( pwm - DOP_PWM_ZERO_OFFSET - 0.0972 ) / 0.0009 ) ) #define PUMP_DIR_ERROR_COUNT_MASK 0x3F ///< Bit mask for pump direction error counter. #define DOP_MIN_DIR_CHECK_SPEED_RPM 10.0F ///< Minimum motor speed before we check pump direction. @@ -1643,4 +1645,26 @@ return result; } +/*********************************************************************//** + * @brief + * The testSetDialInPumpTargetDutyCycle function resets the override of the + * measured dialIn pump motor current. + * @details Inputs: none + * @details Outputs: adcDialInPumpMCCurrentmA + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testSetDialOutPumpTargetDutyCycle( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + setDialOutPumpTargetRate( (S32)DOP_ML_PER_MIN_FROM_PWM( value ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + result = TRUE; + } + + return result; +} + + /**@}*/ Index: firmware/App/Modes/BloodPrime.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Modes/BloodPrime.c (.../BloodPrime.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Modes/BloodPrime.c (.../BloodPrime.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -143,7 +143,7 @@ // start blood and dialysate inlet pumps setBloodPumpTargetFlowRate( (U32)bloodPrimeRampFlowRate_mL_min, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP, 0.0F ); cmdStartDGTrimmerHeater(); // Start air trap control Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -238,7 +238,7 @@ } #endif - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, mode ); + setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, mode, 0.0F ); setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // Start Heparin pump as appropriate @@ -327,7 +327,7 @@ mode = PUMP_CONTROL_MODE_OPEN_LOOP; } #endif - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, mode ); + setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, mode, 0.0F ); setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); } @@ -867,7 +867,7 @@ salineBolusStartRequested = FALSE; // Cmd all pumps to stop setBloodPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - setDialInPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP, 0.0F ); setDialOutPumpTargetRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); stopSyringePump(); // Begin saline bolus @@ -909,9 +909,9 @@ #endif // Start dialysate inlet pump at re-circ rate #ifndef RUN_PUMPS_OPEN_LOOP - setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP, 0.0F ); #else - setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP, 0.0F ); #endif // Begin saline bolus result = SALINE_BOLUS_STATE_IN_PROGRESS; Index: firmware/App/Modes/Prime.c =================================================================== diff -u -rd28280f1054fc9ddf9304a11373dc9ee963425e3 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Modes/Prime.c (.../Prime.c) (revision d28280f1054fc9ddf9304a11373dc9ee963425e3) +++ firmware/App/Modes/Prime.c (.../Prime.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -7,14 +7,13 @@ * * @file Prime.c * -* @author (last) Michael Garthwaite -* @date (last) 26-Aug-2022 +* @author (last) Dong Nguyen +* @date (last) 27-Sep-2022 * * @author (original) Quang Nguyen * @date (original) 08-Dec-2020 * ***************************************************************************/ - #include "AirTrap.h" #include "AlarmMgmt.h" #include "BloodFlow.h" @@ -52,6 +51,7 @@ #define DIALYZER_VOLUME_SCALE_FACTOR 0.5F ///< Half of the dialyzer total volume. #define NO_AIR_DETECTED_COUNT ( 20 * MS_PER_SECOND ) ///< No air detected time period count. +#define PRIME_SALINE_DIALYZER_TIME_OUT_COUNT ( 60 * MS_PER_SECOND ) ///< Time period count for prime saline dialyzer time out. #define PURGE_AIR_TIME_OUT_COUNT ( 120 * MS_PER_SECOND ) ///< Time period count for purge air time out. #define LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ( 1 * MS_PER_SECOND ) ///< Time load cell reading steady state detection sampling time in seconds. #define PRIME_DIALYSATE_BYPASS_TIME_LIMIT ( 15 * MS_PER_SECOND ) ///< Time limit for priming dialysate bypass circuit. @@ -86,7 +86,8 @@ { 100, 200 }, // DIALYZER_TYPE_BBRAUN_PRO_16H { 120, 257 }, // DIALYZER_TYPE_BBRAUN_PRO_19H { 87, 233 }, // DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F160NRE - { 102, 280 } }; // DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F180NRE + { 102, 280 }, // DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F180NRE + { 113, 348 } }; // DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F200NRE static U32 primeDialysateDialyzerTimeLimit; ///< Time limit in msec for priming dialysate dialyzer path. static U32 primeSalineDialyzerTimeLimit; ///< Time limit in msec for priming saline dialyzer path. @@ -499,7 +500,7 @@ if ( TRUE == didTimeout( purgeAirTimeOutStartTime, PURGE_AIR_TIME_OUT_COUNT ) ) { - activateAlarmNoData( ALARM_ID_HD_PRIME_SALINE_PURGE_AIR_TIME_OUT ); + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_PRIME_SALINE_PURGE_AIR_TIME_OUT, PURGE_AIR_TIME_OUT_COUNT ); } if ( AIR_TRAP_LEVEL_FLUID == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ) ) @@ -731,6 +732,11 @@ state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; } + if ( TRUE == didTimeout( primeSalineDialyzerStartTime, PRIME_SALINE_DIALYZER_TIME_OUT_COUNT ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRIME_SALINE_DIALYZER_TIME_OUT, PRIME_SALINE_DIALYZER_TIME_OUT_COUNT ); // Trigger HD prime saline dialyzer time out alarm. + } + if ( TRUE == doesAlarmStatusIndicateStop() ) { setupForPrimePause(); Index: firmware/App/Modes/Rinseback.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Modes/Rinseback.c (.../Rinseback.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Modes/Rinseback.c (.../Rinseback.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -193,7 +193,7 @@ endAirTrapControl(); // Re-circulate dialysate side of dialyzer w/ heating to maintain temperature - setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP, 0.0F ); cmdStartDGTrimmerHeater(); } Index: firmware/App/Modes/SelfTests.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Modes/SelfTests.c (.../SelfTests.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Modes/SelfTests.c (.../SelfTests.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -769,7 +769,7 @@ havePumpsStarted = TRUE; setBloodPumpTargetFlowRate( PUMP_SELF_TEST_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialInPumpTargetFlowRate( PUMP_SELF_TEST_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialInPumpTargetFlowRate( PUMP_SELF_TEST_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP, 0.0F ); setDialOutPumpTargetRate( PUMP_SELF_TEST_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); runPumpStartTime = getMSTimerCount(); } @@ -1295,7 +1295,7 @@ { F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); - setDialInPumpTargetFlowRate( DIP_FLOW_RATE_SETUP_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialInPumpTargetFlowRate( DIP_FLOW_RATE_SETUP_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP, 0.0F ); fmdIntegratedVolume = 0.0; if ( ( bolusVol > 0.0 ) && ( getSyringePumpVolumeDelivered() < bolusVol ) ) @@ -1471,7 +1471,7 @@ if ( FALSE == cmdResp.rejected ) { - setDialInPumpTargetFlowRate( DIP_FLOW_RATE_FIRST_DISPLACEMENT_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialInPumpTargetFlowRate( DIP_FLOW_RATE_FIRST_DISPLACEMENT_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP, 0.0F ); displacementStartTime = getMSTimerCount(); fmdIntegratedVolume = 0.0; state = WET_SELF_TESTS_FIRST_DISPLACEMENT_STATE; @@ -1609,7 +1609,7 @@ if ( FALSE == cmdResp.rejected ) { - setDialInPumpTargetFlowRate( DIP_FLOW_RATE_SECOND_DISPLACEMENT_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialInPumpTargetFlowRate( DIP_FLOW_RATE_SECOND_DISPLACEMENT_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP, 0.0F ); displacementStartTime = getMSTimerCount(); fmdIntegratedVolume = 0.0; state = WET_SELF_TESTS_SECOND_DISPLACEMENT_STATE; Index: firmware/App/Modes/TreatmentStop.c =================================================================== diff -u -r46b163d19c65e8c21db7b0247bbb1af0dba1ece5 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Modes/TreatmentStop.c (.../TreatmentStop.c) (revision 46b163d19c65e8c21db7b0247bbb1af0dba1ece5) +++ firmware/App/Modes/TreatmentStop.c (.../TreatmentStop.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -145,7 +145,7 @@ { // Re-circulate dialysate side of dialyzer w/ heating to maintain temperature doorClosedRequired( TRUE, TRUE ); - setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP, 0.0F ); cmdStartDGTrimmerHeater(); } Index: firmware/App/Services/SystemComm.c =================================================================== diff -u -raf2cd84d3319b1e298057fe2d329aa7824306507 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision af2cd84d3319b1e298057fe2d329aa7824306507) +++ firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -7,8 +7,8 @@ * * @file SystemComm.c * -* @author (last) Michael Garthwaite -* @date (last) 07-Sep-2022 +* @author (last) Dong Nguyen +* @date (last) 27-Sep-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -53,10 +53,12 @@ #define MSG_NOT_ACKED_TIMEOUT_MS 150 ///< Maximum time for a Denali message that requires ACK to be ACK'd -#define MSG_NOT_ACKED_TIMEOUT_MS_INIT 5000 ///< Maximum time for a Denali message that requires ACK to be ACK'd on the INIT state for the first (UI version request) message of the POST -#define MSG_NOT_ACKED_MAX_RETRIES 3 ///< Maximum number of times a message that requires ACK that was not ACK'd can be re-sent before alarm +#define MSG_NOT_ACKED_MAX_RETRIES 20 ///< Maximum number of times a message that requires ACK that was not ACK'd can be re-sent before alarm #define PENDING_ACK_LIST_SIZE 25 ///< Maximum number of Denali messages that can be pending ACK at any given time - + +#define MAX_FPGA_CLOCK_SPEED_ERRORS 3 ///< maximum number of FPGA clock speed errors within window period before alarm +#define MAX_FPGA_CLOCK_SPEED_ERROR_WINDOW_MS (10 * SEC_PER_MIN * MS_PER_SECOND) ///< FPGA clock speed error window + #pragma pack(push, 1) /// Record for transmitted message that is pending acknowledgment from receiver. @@ -111,10 +113,6 @@ static U32 timeOfLastUICheckIn = 0; ///< Last time UI checked in static volatile BOOL uiDidCommunicate = FALSE; ///< Has UI every sent a message -#ifdef EMC_TEST_BUILD - static U32 badCANCount; // Test code in support of EMC testing -#endif - // ********** private function prototypes ********** static void clearCANXmitBuffers( void ); @@ -147,6 +145,9 @@ // Initialize bad message CRC time windowed count initTimeWindowedCount( TIME_WINDOWED_COUNT_BAD_MSG_CRC, MAX_COMM_CRC_FAILURES, MAX_COMM_CRC_FAILURE_WINDOW_MS ); + + // Initialize FPGA clock speed error time windowed count + initTimeWindowedCount( TIME_WINDOWED_COUNT_FPGA_CLOCK_SPEED_ERROR, MAX_FPGA_CLOCK_SPEED_ERRORS, MAX_FPGA_CLOCK_SPEED_ERROR_WINDOW_MS); // Initialize pending ACK list for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) @@ -227,12 +228,16 @@ /*********************************************************************//** * @brief * The uiCommunicated function determines whether the UI has communicated. - * @details Inputs: uiDidCommunicate + * @details Inputs: none * @details Outputs: none * @return TRUE if UI has communicated since power up, FALSE if not *************************************************************************/ BOOL uiCommunicated( void ) -{ +{ +#ifdef SIMULATE_UI + uiDidCommunicate = TRUE; +#endif + return uiDidCommunicate; } @@ -257,7 +262,7 @@ * @return none *************************************************************************/ void execSystemCommRx( void ) -{ +{ // Parse messages from comm buffers and queue them processIncomingData(); @@ -526,8 +531,9 @@ * @brief * The processIncomingData function parses out messages from the Input * Comm Buffers and adds them to the Received Message Queue. - * @details Inputs: Input Comm Buffers - * @details Outputs: Parsed message(s) added to Received Message Queue + * @details Inputs: none + * @details Outputs:hdIsOnlyCANNode, rcvMsg, dgIsCommunicating, + * timeOfLastDGCheckIn * @return none *************************************************************************/ static void processIncomingData( void ) @@ -572,8 +578,8 @@ // Blank the new message record blankMessageInWrapper( &rcvMsg ); // Copy message header portion of message data to the new message - memcpy( &(rcvMsg.msg.hdr), dataPtr, sizeof(MESSAGE_HEADER_T) ); - dataPtr += sizeof(MESSAGE_HEADER_T); + memcpy( &(rcvMsg.msg.hdr), dataPtr, sizeof( MESSAGE_HEADER_T ) ); + dataPtr += sizeof( MESSAGE_HEADER_T ); // Copy message payload portion of message data to the new message memcpy( &(rcvMsg.msg.payload), dataPtr, rcvMsg.msg.hdr.payloadLen ); dataPtr += rcvMsg.msg.hdr.payloadLen; @@ -591,10 +597,6 @@ else if ( -1 == msgSize ) // Candidate message with bad CRC found? { badCRCDetected = TRUE; -#ifdef EMC_TEST_BUILD - badCANCount++; - broadcastCANErrorCount( badCANCount ); -#endif getFromCommBuffer( MSG_IN_BUFFERS[ i ], data, 1 ); // Consume sync byte so we can re-sync messagesInBuffer = TRUE; // Keep processing this buffer } // Looks like there is a complete message in the comm buffer @@ -702,7 +704,7 @@ { BOOL isThereMsgRcvd = TRUE; // Assume TRUE at first to get into while loop MESSAGE_WRAPPER_T message; - + while ( TRUE == isThereMsgRcvd ) { // See if any messages received @@ -729,10 +731,6 @@ } else // CRC failed { -#ifdef EMC_TEST_BUILD - badCANCount++; - broadcastCANErrorCount( badCANCount ); -#endif checkTooManyBadMsgCRCs(); } } @@ -750,7 +748,9 @@ static void checkForCommTimeouts( void ) { if ( TRUE == uiDidCommunicate ) - { + { + HD_OP_MODE_T opMode = getCurrentOperationMode(); + if ( TRUE == didTimeout( timeOfLastUICheckIn, UI_COMM_TIMEOUT_IN_MS ) ) { #ifndef _RELEASE_ @@ -761,13 +761,21 @@ } } - if ( TRUE == didTimeout( timeOfLastDGCheckIn, DG_COMM_TIMEOUT_IN_MS ) ) + // Only alarm on DG comm loss while in the treatment workflow + if ( MODE_PRET == opMode || MODE_TREA == opMode || MODE_POST == opMode ) { + if ( TRUE == didTimeout( timeOfLastDGCheckIn, DG_COMM_TIMEOUT_IN_MS ) ) + { #ifndef RUN_WITHOUT_DG - activateAlarmNoData( ALARM_ID_DG_COMM_TIMEOUT ); - dgIsCommunicating = FALSE; + activateAlarmNoData( ALARM_ID_DG_COMM_TIMEOUT ); + dgIsCommunicating = FALSE; #endif + } } + else // Otherwise clear the alarm + { + clearAlarm( ALARM_ID_DG_COMM_TIMEOUT ); + } } } @@ -878,13 +886,7 @@ // Find expired messages pending ACK for ( i = 0; i < PENDING_ACK_LIST_SIZE; i++ ) { // Pending ACK expired? - U32 timeoutPeriod = MSG_NOT_ACKED_TIMEOUT_MS; // set the timeout as default - - if ( MODE_INIT == getCurrentOperationMode() ) - { // change it to longer timeout if the HD is in INIT state - timeoutPeriod = MSG_NOT_ACKED_TIMEOUT_MS_INIT; - } - if ( ( TRUE == pendingAckList[ i ].used ) && ( TRUE == didTimeout( pendingAckList[ i ].timeStamp, timeoutPeriod ) ) ) + if ( ( TRUE == pendingAckList[ i ].used ) && ( TRUE == didTimeout( pendingAckList[ i ].timeStamp, MSG_NOT_ACKED_TIMEOUT_MS ) ) ) { // If retries left, reset and resend pending message if ( pendingAckList[ i ].retries > 0 ) { // Re-queue message for transmit @@ -1646,6 +1648,10 @@ handleSetBloodLeakEmbeddedModeCommand( message ); break; + case MSG_ID_HD_SEND_ALARMS_COMMAND: + handleResendAllAlarmsCommand( message ); + break; + case MSG_ID_HD_REQ_CURRENT_TREATMENT_PARAMETERS: handleTestCurrentTreamtmentParametersRequest( message ); break; Index: firmware/App/Services/SystemCommMessages.c =================================================================== diff -u -raf2cd84d3319b1e298057fe2d329aa7824306507 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision af2cd84d3319b1e298057fe2d329aa7824306507) +++ firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -7,8 +7,8 @@ * * @file SystemCommMessages.c * -* @author (last) Darren Cox -* @date (last) 13-Sep-2022 +* @author (last) Dong Nguyen +* @date (last) 27-Sep-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -43,6 +43,7 @@ #include "Valves.h" #include "WatchdogMgmt.h" #include "HDDefs.h" +#include "TaskPriority.h" /** * @addtogroup SystemCommMessages @@ -62,9 +63,9 @@ #pragma pack(pop) // ********** private data ********** - static BOOL testerLoggedIn = FALSE; ///< Flag indicates whether an external tester (connected PC) has sent a valid login message. static volatile U16 nextSeqNo = 1; ///< Value of sequence number to use for next transmitted message. + /// List of message IDs that are requested not to be transmitted. static BLOCKED_MSGS_DATA_T blockedMessagesForXmit = { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -1571,10 +1572,9 @@ * @details Inputs: none * @details Outputs: DG start/stop trimmer heater command msg constructed and queued. * @param start TRUE indicates start heater, FALSE indicates stop heater - * @param trimmerHtrTemp target temperature for trimmer heater * @return TRUE if msg successfully queued for transmit, FALSE if not *************************************************************************/ -BOOL sendDGStartStopTrimmerHeaterCommand( BOOL start, F32 trimmerHtrTemp ) +BOOL sendDGStartStopTrimmerHeaterCommand( BOOL start ) { BOOL result; MESSAGE_T msg; @@ -1583,11 +1583,9 @@ // Create a message record blankMessage( &msg ); msg.hdr.msgID = MSG_ID_DG_START_STOP_TRIMMER_HEATER_CMD; - msg.hdr.payloadLen = sizeof( BOOL ) + sizeof( F32 ); + msg.hdr.payloadLen = sizeof( BOOL ); memcpy( payloadPtr, &start, sizeof( BOOL ) ); - payloadPtr += sizeof( BOOL ); - memcpy( payloadPtr, &trimmerHtrTemp, sizeof( F32 ) ); // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_HD_2_DG, ACK_REQUIRED ); @@ -2005,35 +2003,6 @@ } -#ifdef EMC_TEST_BUILD -/*********************************************************************//** - * @brief - * The broadcastCANErrorCount function handles the CAN error count - * @details Inputs: none - * @details Outputs: message handled - * @param message a pointer to the message to handle - * @return none - *************************************************************************/ -BOOL broadcastCANErrorCount( U32 count ) -{ - BOOL result; - MESSAGE_T msg; - U08 *payloadPtr = msg.payload; - - // Create a message record - blankMessage( &msg ); - msg.hdr.msgID = MSG_ID_CAN_ERROR_COUNT; - msg.hdr.payloadLen = sizeof( U32 ); - - memcpy( payloadPtr, &count, sizeof( U32 ) ); - - // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer - result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_HD_BROADCAST, ACK_NOT_REQUIRED ); - - return result; -} -#endif - // *********************************************************************** // **************** Message Handling Helper Functions ******************** // *********************************************************************** @@ -2272,7 +2241,6 @@ { BOOL result; MESSAGE_T msg; - U08 *payloadPtr = msg.payload; // Create a message record blankMessage( &msg ); @@ -2368,8 +2336,6 @@ memcpy( &payload, message->payload, sizeof( TEMPERATURE_SENSORS_DATA_T ) ); setDialysateTemperatureReadings( payload.inletDialysate, payload.outletRedundant ); } - // TODO - what to do if invalid payload length? - // TODO - how to know if DG stops sending these? } /*********************************************************************//** @@ -3626,38 +3592,6 @@ /*********************************************************************//** * @brief - * The handleHDSetArterialPressureOffsetRequest function handles a request to - * set the arterial pressure offset. - * @details Inputs: none - * @details Outputs: message handled - * @param message a pointer to the message to handle - * @return none - *************************************************************************/ -void handleHDSetArterialPressureOffsetRequest( MESSAGE_T *message ) -{ - TEST_OVERRIDE_PAYLOAD_T payload; - BOOL result = FALSE; - - // Verify payload length - if ( sizeof(TEST_OVERRIDE_PAYLOAD_T) == message->hdr.payloadLen ) - { - memcpy( &payload, message->payload, sizeof(TEST_OVERRIDE_PAYLOAD_T) ); - if ( FALSE == payload.reset ) - { - result = testSetArterialPressureOffsetOverride( payload.state.f32 ); - } - else - { - result = testResetArterialPressureOffsetOverride(); - } - } - - // Respond to request - sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); -} - -/*********************************************************************//** - * @brief * The handleTestBloodPumpRotorMeasuredSpeedOverrideRequest function handles a request to * override the measured blood pump rotor speed (RPM). * @details Inputs: none @@ -7133,6 +7067,30 @@ } /*********************************************************************//** + * @brief + * The sendRequestForDGResendAlarms function sends out the HD request for + * DG re-send all active alarms. + * @details Inputs: none + * @details Outputs: DG alarms re-send request msg constructed and queued + * @return TRUE if msg successfully queued for transmit, FALSE if not + *************************************************************************/ +BOOL sendRequestForDGResendAlarms( void ) +{ + BOOL result; + MESSAGE_T msg; + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_HD_REQUEST_DG_ALARMS; + msg.hdr.payloadLen = 0; + + // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_HD_2_DG, ACK_REQUIRED ); + + return result; +} + +/*********************************************************************//** * @brief * The handleGetHDUsageInfoRecord function handles a request to get the HD * usage information record. @@ -7288,15 +7246,15 @@ /*********************************************************************//** * @brief - * The handleSendBloodLeakEmbeddedModeCommandResponse function sends out + * The sendBloodLeakEmbeddedModeCommandResponse function sends out * the blood leak embedded mode command response. * @details Inputs: none * @details Outputs: blood leak embedded mode command response msg constructed and queued * @param responseLen: the length of the buffer * @param response: pointer to the response buffer * @return TRUE if msg successfully queued for transmit, FALSE if not *************************************************************************/ -BOOL handleSendBloodLeakEmbeddedModeCommandResponse( U32 responseLen, U08* response ) +BOOL sendBloodLeakEmbeddedModeCommandResponse( U32 responseLen, U08* response ) { BOOL result; MESSAGE_T msg; @@ -7318,6 +7276,30 @@ } /*********************************************************************//** +* @brief +* The handleResendAllAlarmsCommand function handles a request to re-send +* all active HD alarms. +* @details Inputs: none +* @details Outputs: message handled +* @param message a pointer to the message to handle +* @return none +*************************************************************************/ +void handleResendAllAlarmsCommand( MESSAGE_T* message ) +{ + BOOL result = FALSE; + + // verify payload length + if ( 0 == message->hdr.payloadLen ) + { + handleResendActiveAlarmsRequest(); + result = TRUE; + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** * @brief * The sendDGServiceModeRequest function constructs a service mode request msg * to the DG and queues the msg for transmit on the appropriate CAN channel. Index: firmware/App/Services/SystemCommMessages.h =================================================================== diff -u -rf89cac0c61f0068c045411504925373c3731c874 -rd4bc221d9817b488c2dcd00868386d7036a5c9a1 --- firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision f89cac0c61f0068c045411504925373c3731c874) +++ firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision d4bc221d9817b488c2dcd00868386d7036a5c9a1) @@ -7,8 +7,8 @@ * * @file SystemCommMessages.h * -* @author (last) Michael Garthwaite -* @date (last) 08-Aug-2022 +* @author (last) Dara Navaei +* @date (last) 22-Sep-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -372,7 +372,7 @@ BOOL sendDGStartStopCommand( BOOL start ); // MSG_ID_DG_START_STOP_TRIMMER_HEATER_CMD -BOOL sendDGStartStopTrimmerHeaterCommand( BOOL start, F32 trimmerHtrTemp ); +BOOL sendDGStartStopTrimmerHeaterCommand( BOOL start ); // MSG_ID_DG_SAMPLE_WATER_CMD BOOL sendDGSampleWaterCommand( SAMPLE_WATER_CMD_T cmd ); @@ -432,11 +432,9 @@ void handleSetHDSoftwareConfigRecord( MESSAGE_T *message ); -#ifdef EMC_TEST_BUILD -// MSG_ID_CAN_ERROR_COUNT -BOOL broadcastCANErrorCount( U32 count ); -#endif - +// MSG_ID_HD_REQUEST_DG_ALARMS +BOOL sendRequestForDGResendAlarms( void ); + // *********** public test support message functions ********** // MSG_TESTER_LOG_IN @@ -810,8 +808,11 @@ void handleSetBloodLeakEmbeddedModeCommand( MESSAGE_T* message ); // MSG_ID_HD_SEND_BLOOD_LEAK_EMB_MODE_RESPONSE -BOOL handleSendBloodLeakEmbeddedModeCommandResponse( U32 responseLen, U08* response ); +BOOL sendBloodLeakEmbeddedModeCommandResponse( U32 responseLen, U08* response ); +// MSG_ID_HD_SEND_ALARMS_COMMAND +void handleResendAllAlarmsCommand( MESSAGE_T* message ); + // MSG_ID_HD_BLOOD_PUMP_SET_PWM void handleTestBloodPumpSetPWM( MESSAGE_T* message );