Index: firmware/App/Controllers/DialInFlow.c =================================================================== diff -u -rf1352c811e40f323e8a3e40903123999d42b80af -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision f1352c811e40f323e8a3e40903123999d42b80af) +++ firmware/App/Controllers/DialInFlow.c (.../DialInFlow.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -166,10 +166,10 @@ #define DIR_DI_PUMP_SPI5_PORT_MASK 0x00000100 ///< Pin on unused SPI5 peripheral (ENA) - re-purposed as output GPIO to set dialysate inlet pump direction. #define DIP_ROTOR_HALL_SENSOR_NHET_ID 0x0000001E ///< NHET pin number associated with DPi rotor hall sensor input // DialIn pump stop and direction macros -#define SET_DIP_DIR() {mibspiREG5->PC3 |= DIR_DI_PUMP_SPI5_PORT_MASK;} ///< Macro for setting the dialysate inlet pump direction pin high. -#define CLR_DIP_DIR() {mibspiREG5->PC3 &= ~DIR_DI_PUMP_SPI5_PORT_MASK;} ///< Macro for setting the dialysate inlet pump direction pin low. -#define SET_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) ///< Macro for setting the dialysate inlet pump stop pin low. -#define CLR_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) ///< Macro for setting the dialysate inlet pump stop pin high. +#define SET_DIP_DIR() {mibspiREG5->PC3 |= DIR_DI_PUMP_SPI5_PORT_MASK;} ///< Macro for setting the dialysate inlet pump direction pin high (forward). +#define CLR_DIP_DIR() {mibspiREG5->PC3 &= ~DIR_DI_PUMP_SPI5_PORT_MASK;} ///< Macro for setting the dialysate inlet pump direction pin low (reverse). +#define SET_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) ///< Macro for setting the dialysate inlet pump disable pin low (disable). +#define CLR_DIP_STOP() gioSetBit( gioPORTA, STOP_DI_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) ///< Macro for setting the dialysate inlet pump disable pin high (enable). // ********** private data ********** Index: firmware/App/Controllers/DialOutFlow.c =================================================================== diff -u -rf1352c811e40f323e8a3e40903123999d42b80af -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Controllers/DialOutFlow.c (.../DialOutFlow.c) (revision f1352c811e40f323e8a3e40903123999d42b80af) +++ firmware/App/Controllers/DialOutFlow.c (.../DialOutFlow.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -152,10 +152,10 @@ #define STOP_DO_PUMP_GIO_PORT_PIN 6U ///< GIO port A pin used for pump controller direction pin. #define DOP_ROTOR_HALL_SENSOR_NHET_ID 0x0000000E ///< NHET pin number associated with DPo rotor hall sensor input // Dialysate outlet pump stop and direction macros -#define SET_DOP_STOP() {mibspiREG1->PC3 &= ~STOP_DO_PUMP_MIBSPI1_PORT_MASK;} ///< Macro sets pump controller run/stop signal to stop. -#define CLR_DOP_STOP() {mibspiREG1->PC3 |= STOP_DO_PUMP_MIBSPI1_PORT_MASK;} ///< Macro sets pump controller run/stop signal to run. -#define SET_DOP_DIR() gioSetBit( gioPORTA, STOP_DO_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) ///< Macro sets pump controller direction to forward direction. -#define CLR_DOP_DIR() gioSetBit( gioPORTA, STOP_DO_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) ///< Macro sets pump controller direction to reverse direction. +#define SET_DOP_STOP() {mibspiREG1->PC3 &= ~STOP_DO_PUMP_MIBSPI1_PORT_MASK;} ///< Macro sets pump controller disable signal low (active low). +#define CLR_DOP_STOP() {mibspiREG1->PC3 |= STOP_DO_PUMP_MIBSPI1_PORT_MASK;} ///< Macro sets pump controller disable signal high (active low). +#define SET_DOP_DIR() gioSetBit( gioPORTA, STOP_DO_PUMP_GIO_PORT_PIN, PIN_SIGNAL_HIGH ) ///< Macro sets pump controller direction to high (forward direction). +#define CLR_DOP_DIR() gioSetBit( gioPORTA, STOP_DO_PUMP_GIO_PORT_PIN, PIN_SIGNAL_LOW ) ///< Macro sets pump controller direction to low (reverse direction). // ********** private data ********** Index: firmware/App/Controllers/SyringePump.c =================================================================== diff -u -r175b20f03478dd1beea58d399993acb5bb32f471 -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision 175b20f03478dd1beea58d399993acb5bb32f471) +++ firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -7,8 +7,8 @@ * * @file SyringePump.c * -* @author (last) Sean Nash -* @date (last) 24-Aug-2023 +* @author (last) Darren Cox +* @date (last) 19-Sep-2023 * * @author (original) Sean Nash * @date (original) 04-Mar-2021 @@ -69,7 +69,7 @@ #define SYRINGE_PUMP_RATE_ALARM_PERSISTENCE 3000 ///< Alarm persistence period (in ms) for syringe pump speed check alarms. #define SYRINGE_PUMP_DIR_ALARM_PERSISTENCE 3000 ///< Alarm persistence period (in ms) for syringe pump direction check alarms. #define SYRINGE_PUMP_OFF_ALARM_PERSISTENCE 1000 ///< Alarm persistence period (in ms) for syringe pump off check alarms. -#define SYRINGE_PUMP_OFF_ERROR_MAX_CNT 10 ///< Maximum number of syringe pump not stopped errors within time window before alarm triggered. Do not exceed MAX_TIME_WINDOWED_COUNT. +#define SYRINGE_PUMP_OFF_ERROR_MAX_CNT 5 ///< Maximum number of syringe pump not stopped errors within time window before alarm triggered. Do not exceed MAX_TIME_WINDOWED_COUNT. #define SYRINGE_PUMP_OFF_ERROR_TIME_WIN_MS ( 1 * MS_PER_SECOND ) ///< Time window for Syringe Pump not stopped error. #define SYRINGE_PUMP_PRIMING_TIMEOUT_MS ( 5 * MS_PER_SECOND ) ///< Timeout for syringe pump prime operation. @@ -156,6 +156,8 @@ /// Interval (ms/task time) at which the syringe pump speed is calculated (every 40 ms). #define SYRINGE_PUMP_SPEED_CALC_INTERVAL ( 40 / TASK_PRIORITY_INTERVAL ) +/// Interval (ms/task time) at which the syringe pump position is checked when pump off state (every 50 ms). +#define SYRINGE_PUMP_STOP_POSITION_CHECK_INTERVAL ( 50 / TASK_PRIORITY_INTERVAL ) /// Number of syringe pump encoder positions kept in buffer to hold last 1 second of position data. #define SYRINGE_PUMP_SPEED_CALC_BUFFER_LEN ( MS_PER_SECOND / SYRINGE_PUMP_SPEED_CALC_INTERVAL / TASK_PRIORITY_INTERVAL ) @@ -217,9 +219,11 @@ static S32 syringePumpVolumeStartPosition; ///< Start position for the current volume calculation. static S32 syringePumpHomePositionOffset; ///< FPGA reported position when at home postion. static S32 syringePumpLastPosition; ///< Position previously recorded. +static S32 syringePumpStopLastPosition; ///< Syringe pump stop position previously recorded. static S32 syringePumpLastPositions[ SYRINGE_PUMP_SPEED_CALC_BUFFER_LEN ]; ///< Last encoder positions for the syringe pump. static U32 syringePumpMotorSpeedCalcIdx; ///< Index into 1 second buffer of syringe pump encoder positions. static U32 syringePumpSpeedCalcTimerCounter; ///< Used to calculate measured rate from change in position over time. +static U32 syringePumpStopPositionTimerCounter; ///< Used to check change in encoder position when pump stopped. static MOTOR_DIR_T syringePumpControllerMeasuredDirection; ///< Measured direction of syringe pump per controller. static MOTOR_DIR_T syringePumpEncoderMeasuredDirection; ///< Measured direction of syringe pump per encoder position relative to previous. static F32 syringePumpVolumeRequired; ///< Volume required for complete therapy (in mL). @@ -310,12 +314,14 @@ syringePumpVolumeStartPosition = 0; syringePumpHomePositionOffset = 0; syringePumpLastPosition = 0; + syringePumpStopLastPosition = 0; syringePumpVolumeRequired = 0.0; syringePumpControllerMeasuredDirection = MOTOR_DIR_FORWARD; syringePumpEncoderMeasuredDirection = MOTOR_DIR_FORWARD; syringePumpDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; syringePumpSpeedCalcTimerCounter = 0; syringePumpRampTimerCtr = 0; + syringePumpStopPositionTimerCounter = 0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpStateStartTime = getMSTimerCount(); @@ -392,10 +398,9 @@ { if ( HEPARIN_STATE_DISPENSING == heparinDeliveryState ) { + stopSyringePump(); accepted = TRUE; heparinDeliveryState = HEPARIN_STATE_PAUSED; - stopSyringePump(); - sendTreatmentLogEventData( HEPARIN_START_RESUME_EVENT, HEPARIN_STATE_DISPENSING, HEPARIN_STATE_PAUSED ); } else { @@ -408,7 +413,6 @@ { accepted = TRUE; startHeparinContinuous(); - sendTreatmentLogEventData( HEPARIN_START_RESUME_EVENT, HEPARIN_STATE_PAUSED, HEPARIN_STATE_DISPENSING ); } else { @@ -515,6 +519,14 @@ } if ( ( HEPARIN_STATE_INITIAL_BOLUS == heparinDeliveryState ) || ( HEPARIN_STATE_DISPENSING == heparinDeliveryState ) ) { + if ( HEPARIN_STATE_INITIAL_BOLUS == heparinDeliveryState ) + { + sendTreatmentLogEventData( HEPARIN_BOLUS_END_EVENT, syringePumpSetRate, 0.0 ); + } + else + { + sendTreatmentLogEventData( HEPARIN_STOP_PAUSE_EVENT, syringePumpSetRate, 0.0 ); + } heparinDeliveryState = HEPARIN_STATE_STOPPED; } } @@ -1296,25 +1308,33 @@ * of the syringe pump control state machine. * @details Inputs: syringePumpRetractRequested, syringePumpSeekRequested, * syringePumpPrimeRequested, syringePumpBolusRequested, syringePumpContinuousRequested, - * syringePumpDACVrefSetRequested + * syringePumpDACVrefSetRequested,syringePumpStopPositionTimerCounter,syringePumpStopLastPosition * @details Outputs: Pump speed and direction set if pump command initiated * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpOffState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_OFF_STATE; - // Check position is not changing while stopped - if ( ( syringePumpLastPosition != getSyringePumpPosition() ) && ( TRUE == incTimeWindowedCount( TIME_WINDOWED_COUNT_SYRINGE_PUMP_OFF_ERROR ) ) ) + // Check the encoder position every 50ms interval after pump off state transition. + if ( ++syringePumpStopPositionTimerCounter >= SYRINGE_PUMP_STOP_POSITION_CHECK_INTERVAL ) { -#ifndef _RELEASE_ - if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) -#endif + // Check position is not changing while stopped + if ( ( syringePumpStopLastPosition != getSyringePumpPosition() ) && ( TRUE == incTimeWindowedCount( TIME_WINDOWED_COUNT_SYRINGE_PUMP_OFF_ERROR ) ) ) { - activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_NOT_STOPPED_ERROR ); - activateSafetyShutdown(); + #ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) + #endif + { + activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_NOT_STOPPED_ERROR ); + activateSafetyShutdown(); + } } + // last pump stop position value + syringePumpStopLastPosition = getSyringePumpPosition(); + syringePumpStopPositionTimerCounter = 0; } + // Reset persistence for direction alarms while pump is off isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, FALSE ); isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, FALSE ); @@ -1609,7 +1629,7 @@ if ( TRUE == didTimeout( syringePumpStateStartTime, SYRINGE_PUMP_PRIMING_TIMEOUT_MS ) ) { stopPump = TRUE; - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_OCCLUSION, (F32)SYRINGE_PUMP_PRIMING_TIMEOUT_MS, 0.0F ) + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_PRIME_TIMEOUT, (F32)SYRINGE_PUMP_PRIMING_TIMEOUT_MS, 0.0F ) } // Check for stall @@ -1678,7 +1698,6 @@ if ( TRUE == stopPump ) { stopSyringePump(); - sendTreatmentLogEventData( HEPARIN_BOLUS_END_EVENT, syringePumpSetRate, 0.0 ); result = SYRINGE_PUMP_OFF_STATE; } @@ -1726,7 +1745,6 @@ if ( TRUE == stopPump ) { stopSyringePump(); - sendTreatmentLogEventData( HEPARIN_STOP_PAUSE_EVENT, syringePumpSetRate, 0.0 ); result = SYRINGE_PUMP_OFF_STATE; } Index: firmware/App/Modes/ModePreTreat.c =================================================================== diff -u -r3f4105248e6c49ab487ec065f9a4843d72a16a2a -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Modes/ModePreTreat.c (.../ModePreTreat.c) (revision 3f4105248e6c49ab487ec065f9a4843d72a16a2a) +++ firmware/App/Modes/ModePreTreat.c (.../ModePreTreat.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -110,7 +110,7 @@ #ifndef _RELEASE_ const PRE_TREATMENT_RESERVOIR_VAR_T reservoirPrimingDisabled = {FALSE,FALSE,FALSE,TRUE,FALSE,(0)}; #endif -const PRE_TREATMENT_RESERVOIR_VAR_T reservoirPretreatmentInit = {FALSE,FALSE,FALSE,FALSE,FALSE,PRE_TREATMENT_FLUSH_COUNT}; +const PRE_TREATMENT_RESERVOIR_VAR_T reservoirPretreatmentInit = {FALSE,FALSE,FALSE,FALSE,FALSE,PRE_TREATMENT_FLUSH_COUNT}; ///< Pretreatment reservoir fill plan record. // ********** private function prototypes ********** Index: firmware/App/Modes/ModeTreatment.c =================================================================== diff -u -rb687ab5673dc5da53537f538056ea12842b3f6ce -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision b687ab5673dc5da53537f538056ea12842b3f6ce) +++ firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -8,7 +8,7 @@ * @file ModeTreatment.c * * @author (last) Darren Cox -* @date (last) 23-Aug-2023 +* @date (last) 19-Sep-2023 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -1036,6 +1036,7 @@ setTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME, ( presMaxUFVolumeML / (F32)ML_PER_LITER ) ); setTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION, ( presTreatmentTimeSecs / SEC_PER_MIN ) ); setDialysisParams( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ), getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), presMaxUFVolumeML, presUFRate ); + signalInitiatePressureStabilization( USE_NORMAL_STABILIZATION_PERIOD ); } else { @@ -1091,7 +1092,7 @@ * @details Inputs: current operating mode, treatment states and parameters * @details Outputs: response message sent * @param uFVolume New ultrafiltration volume requested by the user - * @return TRUE if new UF voluem is valid, FALSE if not. + * @return TRUE if new UF volume is valid, FALSE if not. *************************************************************************/ BOOL verifyUFSettingsChange( F32 uFVolume ) { @@ -1103,6 +1104,7 @@ S32 txSecRem = CALC_TREAT_TIME_REMAINING_IN_SECS(); S32 txMinEla = CALC_ELAPSED_TREAT_TIME_IN_SECS() / SEC_PER_MIN; F32 txMinRem = (F32)txSecRem / (F32)SEC_PER_MIN; + F32 colUFVol = getUltrafiltrationReferenceVolume(); // How much UF volume have we taken so far? // Reset pending UF/time settings changes to current values in case request is rejected pendingUFVolumeChange = presMaxUFVolumeML; @@ -1112,12 +1114,11 @@ // Check if we are in an appropriate treatment state for settings adjustment if ( ( MODE_TREA == currMode ) && ( currentTreatmentState > TREATMENT_START_STATE ) && ( currentTreatmentState < TREATMENT_END_STATE ) && - ( uFVolume <= MAX_UF_VOLUME_ML ) && + ( uFVolume <= MAX_UF_VOLUME_ML ) && ( uFVolume > colUFVol ) && ( CALC_TREAT_TIME_REMAINING_IN_SECS() >= PREVENT_UF_VOL_CHANGE_IF_NEARLY_DONE_SEC ) ) { DIALYSIS_STATE_T currDialysisState = getDialysisState(); UF_STATE_T currUFState = getUltrafiltrationState(); - F32 colUFVol = getUltrafiltrationReferenceVolume(); // How much UF volume have we taken so far? F32 remUFVol = uFVolume - colUFVol; // What would remaining UF volume be after subtracting UF volume already taken F32 remUFVolCap = RANGE( remUFVol, 0.0F, (F32)MAX_UF_VOLUME_ML ); // Enforce valid range on remaining UF volume F32 uFRate = remUFVolCap / txMinRem; // What UF rate would be if user selected to adjust it @@ -1166,7 +1167,9 @@ { rejectReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } - else if ( uFVolume > MAX_UF_VOLUME_ML ) + // UF volume cannot be set lower than the UF volume that has already been collected + else if ( ( uFVolume > MAX_UF_VOLUME_ML ) || + ( uFVolume <= colUFVol ) ) { rejectReason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; } Index: firmware/App/Modes/Prime.c =================================================================== diff -u -rc505a4234cdecb0433432090640528e7ca09f5c7 -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Modes/Prime.c (.../Prime.c) (revision c505a4234cdecb0433432090640528e7ca09f5c7) +++ firmware/App/Modes/Prime.c (.../Prime.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -7,8 +7,8 @@ * * @file Prime.c * -* @author (last) James Walter Taylor -* @date (last) 18-Aug-2023 +* @author (last) Vinayakam Mani +* @date (last) 29-Aug-2023 * * @author (original) Quang Nguyen * @date (original) 08-Dec-2020 @@ -40,7 +40,6 @@ #define MAX_PRIME_TIME ( 30 * SEC_PER_MIN ) ///< Maximum prime time (in seconds). #define PRIME_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the prime data is published on the CAN bus. -#define BLOOD_PUMP_FAST_FLOW_RATE_PURGE_AIR_ML_MIN 300 ///< Blood pump fast flow rate to fill fluid. #define BLOOD_PUMP_SLOW_FLOW_RATE_PURGE_AIR_ML_MIN 150 ///< Blood pump slow flow rate after fluid reach lower level of air trap sensor. #define BLOOD_PUMP_SALINE_FLOW_RATE_PURGE_AIR_ML_MIN 200 ///< Blood pump very slow flow rate during prime saline dialyzer state #define BLOOD_PUMP_SLOW_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN 150 ///< Blood pump slow flow rate during prime recirculate blood circuit state. @@ -130,7 +129,6 @@ static BOOL primeStartRequested; ///< Flag indicates user requesting to start prime. static BOOL primeResumeRequested; ///< Flag indicates user requesting prime resume. -static BOOL primeFirstPurgePass; ///< Flag indicates to transition to a faster purge speed. static U32 noAirDetectedStartTime; ///< starting time when detecting no air. static U32 purgeAirTimeOutStartTime; ///< Starting time for purge air state time out. @@ -196,7 +194,6 @@ void transitionToPrime( void ) { primeStartTime = getMSTimerCount(); - primeFirstPurgePass = TRUE; setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); @@ -460,7 +457,7 @@ * @brief * The purgeAirValvesBloodPumpControl function controls valves and blood pump * to purge air. - * @details Inputs: primeFirstPurgePass + * @details Inputs: None * @details Outputs: run blood pump, close VDI, VDO, VBA and VBV valves, open VBT valve * @return current state (sub-mode) *************************************************************************/ @@ -474,14 +471,10 @@ signalDialOutPumpHardStop(); signalDialInPumpHardStop(); - if ( FALSE == primeFirstPurgePass ) - { - setBloodPumpTargetFlowRate( BLOOD_PUMP_FAST_FLOW_RATE_PURGE_AIR_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - } - else - { - setBloodPumpTargetFlowRate( BLOOD_PUMP_SLOW_FLOW_RATE_PURGE_AIR_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - } + + // Set the Blood Pump rate to 150 mL/min that helps to avoid entering saline bubbles in to PBo line + setBloodPumpTargetFlowRate( BLOOD_PUMP_SLOW_FLOW_RATE_PURGE_AIR_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + } /*********************************************************************//** @@ -635,8 +628,8 @@ * @brief * The handlePrimePurgeAirState function checks for air trap level and moves * to blood circuit circulation state if fluid is detected at upper sensor. - * @details Inputs: air trap levels, primeFirstPurgePass, purgeAirTimeOutStartTime - * @details Outputs: runs blood pump, control valves to trap air, primeFirstPurgePass + * @details Inputs: air trap levels, purgeAirTimeOutStartTime + * @details Outputs: runs blood pump, control valves to trap air * noAirDetectedStartTime * @return current state *************************************************************************/ @@ -661,9 +654,6 @@ setBloodPumpTargetFlowRate( BLOOD_PUMP_FAST_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - // Once the dialyzer is no longer dry from the first pass of purge air, we can set flow to the fast rate - // until we are in a new priming state. - primeFirstPurgePass = FALSE; noAirDetectedStartTime = getMSTimerCount(); primeDialyzerBubbleClearState = PRIME_BUBBLE_CLEAR_COMPLETE_STATE; state = HD_PRIME_SALINE_CIRC_BLOOD_CIRCUIT_STATE; Index: firmware/App/Services/AlarmMgmt.c =================================================================== diff -u -rb687ab5673dc5da53537f538056ea12842b3f6ce -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Services/AlarmMgmt.c (.../AlarmMgmt.c) (revision b687ab5673dc5da53537f538056ea12842b3f6ce) +++ firmware/App/Services/AlarmMgmt.c (.../AlarmMgmt.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -7,8 +7,8 @@ * * @file AlarmMgmt.c * -* @author (last) Sean Nash -* @date (last) 09-Aug-2023 +* @author (last) Vinayakam Mani +* @date (last) 11-Sep-2023 * * @author (original) Sean Nash * @date (original) 07-Nov-2019 @@ -1735,7 +1735,7 @@ * the correct 32-bit key. A Dialin user must also be logged into HD. * @details Inputs: none * @details Outputs: alarmIsActive[], alarmStartedAt[] - * @param key 32-bit supervior alarm key required to perform this function + * @param key 32-bit supervisor alarm key required to perform this function * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testClearAllAlarms( U32 key ) @@ -1750,6 +1750,10 @@ { ALARM_ID_T a; + // clear the flags when Dialin clears alarms + alarmNoRetrigger = FALSE; + resumeBlockedByAlarmProperty = FALSE; + // Clear all active alarms for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) { Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -r0caa659858cdbd6b887dda7417ffbbd3123f17f6 -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 0caa659858cdbd6b887dda7417ffbbd3123f17f6) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -7,8 +7,8 @@ * * @file Reservoirs.c * -* @author (last) Michael Garthwaite -* @date (last) 18-Aug-2023 +* @author (last) Dara Navaei +* @date (last) 19-Sep-2023 * * @author (original) Dara Navaei * @date (original) 21-Nov-2021 @@ -63,6 +63,7 @@ #define DIA_FLOW_TO_FILL_FLOW_CONSTANT 2.0F ///< Dialysate flow rate to fill flow rate constant. #define MAX_RESERVOIR_VOL_BEFORE_SWITCH_ML 1900.0F ///< Maximum reservoir volume before we switch reservoirs (in mL). +#define FILL_VOLUME_INTERCEPT_ML_QD_ABOVE_400_MLPM 1100 ///< Fill volume intercept in milliliters for Qd above 400 mL/min. // ********** private data ********** @@ -98,6 +99,7 @@ static U32 previousDialysateFlowMLP; ///< Previous dialysate flow rate in mL/min. static F32 previousUFFlowMLP; ///< Previous ultrafiltration flow rate in mL/min. static DG_MIXING_RATIOS_T ratios; ///< Mixing ratios and fill prep time in milliseconds structure. +static U32 prevTargetFillVolumeML[ NUM_OF_DG_RESERVOIRS ]; ///< Previous target fill volume in milliliters. static const F32 RESERVOIR_DILUTION_RATIO = MAX_RESERVOIR_DILUTION / ( 1.0 - MAX_RESERVOIR_DILUTION ); ///< Reservoir dilution ratio. @@ -111,6 +113,7 @@ static F32 getReservoirRecirculationMaxPercent( void ); static void publishReservoirData( void ); static BOOL isDialysateTempAlarmActive( void ); +static U32 getTargetFillVolumeBasedOnDialysateFlowML( void ); static TREATMENT_RESERVOIR_MGMT_STATE_T handleReservoirMgmtStartState( void ); static TREATMENT_RESERVOIR_MGMT_STATE_T handleReservoirMgmtDrainState( void ); @@ -128,7 +131,7 @@ * volSpentMl, reservoirsPublicationCounter, dilutionLevelPct, dgOpMode, dgSubMode, * timeReservoirInUse, volSpentUFML, activeReservoir, recirculationLevelPct, * reservoirSwitchStartTimeMS, timeWaitToFillMS, targetFillFlowLPM, ratios - * previousDialysateFlowRate + * previousDialysateFlowRate, prevTargetFillVolumeML * @return none *************************************************************************/ void initReservoirs( void ) @@ -156,6 +159,10 @@ ratios.acidMixingRatio = 0.0F; ratios.bicarbMixingRatio = 0.0F; ratios.timeFillPrepMS = 0; + + // Initialize the previous reservoir fill volume + prevTargetFillVolumeML[ DG_RESERVOIR_1 ] = FILL_RESERVOIR_TO_VOLUME_ML; + prevTargetFillVolumeML[ DG_RESERVOIR_2 ] = FILL_RESERVOIR_TO_VOLUME_ML; } /*********************************************************************//** @@ -175,8 +182,9 @@ * @brief * The execReservoirs function executes the state machine for the treatment * reservoir management during treatment mode. - * @details Inputs: reservoirsState - * @details Outputs: reservoirsState, timeReservoirInUse, volSpentML, recirculationLevelPct + * @details Inputs: reservoirsState, prevTargetFillVolumeML + * @details Outputs: reservoirsState, timeReservoirInUse, volSpentML, + * recirculationLevelPct * @return none *************************************************************************/ void execReservoirs( void ) @@ -199,7 +207,7 @@ volSpentML += ( flowRateMLPerMS * msSinceLastVolumeCalc ); timeReservoirInUse++; // Check the recirculation level - recirculationLevelPct = volSpentML / (F32)FILL_RESERVOIR_TO_VOLUME_ML; + recirculationLevelPct = volSpentML / (F32)prevTargetFillVolumeML[ getDGActiveReservoir() ]; } // Update the reservoir start time @@ -307,7 +315,7 @@ /*********************************************************************//** * @brief * The getFillTimeMS function calculates the fill time in milliseconds. - * @details Inputs: ratios + * @details Inputs: targetFillFlowRate, prevTargetFillVolumeML * @details Outputs: none * @return target maximum fill time *************************************************************************/ @@ -319,7 +327,7 @@ F32 totalTargetFillFlow = targetFillFlowRate + ( targetFillFlowRate * ratios.acidMixingRatio ) + ( targetFillFlowRate * ratios.bicarbMixingRatio ); // Time fill = Fill volume / Qfill. Qfill is converted from L/min to mL/min and the time is converted from minutes to milliseconds - U32 timeFillMS = ( FILL_RESERVOIR_TO_VOLUME_ML / ( ML_PER_LITER * totalTargetFillFlow ) ) * SEC_PER_MIN * MS_PER_SECOND; + U32 timeFillMS = ( getTargetFillVolumeBasedOnDialysateFlowML() / ( ML_PER_LITER * totalTargetFillFlow ) ) * SEC_PER_MIN * MS_PER_SECOND; // Add the prepare time in the DG mode fill to the calculated fill time in milliseconds U32 timeTotalFillMS = timeFillMS + ratios.timeFillPrepMS; @@ -333,27 +341,29 @@ * flow has changed the function sends the new active reservoir cycle time * to the DG. * @details Inputs: previousDialysateFlowMLP, previousUFFlowMLP - * @details Outputs: previousDialysateFlowMLP, previousUFFlowMLP, volSpentUFML, timeDepleteMS + * @details Outputs: previousDialysateFlowMLP, previousUFFlowMLP, volSpentUFML, + * timeDepleteMS, prevTargetFillVolumeML * @return none *************************************************************************/ static void calculateActiveReservoirCycleTime( void ) { + U32 activeRsrvrTgtVolML = prevTargetFillVolumeML[ getDGActiveReservoir() ]; U32 dialysateFlowMLP = getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ); - F32 fillTimeMS = ( (F32)FILL_RESERVOIR_TO_VOLUME_ML / ( getTargetFillFlowRateLPM() * ML_PER_LITER ) ) * SEC_PER_MIN * MS_PER_SECOND; + F32 fillTimeMS = ( (F32)activeRsrvrTgtVolML / ( getTargetFillFlowRateLPM() * ML_PER_LITER ) ) * SEC_PER_MIN * MS_PER_SECOND; F32 targetUFFlowMLP = getCurrentUFSetRate(); - F32 timeDepletionMS = ( (F32)FILL_RESERVOIR_TO_VOLUME_ML / dialysateFlowMLP ) * SEC_PER_MIN * MS_PER_SECOND; + F32 timeDepletionMS = ( (F32)activeRsrvrTgtVolML / dialysateFlowMLP ) * SEC_PER_MIN * MS_PER_SECOND; F32 timeTotalCycleMS = fillTimeMS + RESERVOIR_FRESH_SETTLE_TIME_MS + RESERVOIR_CYCLE_EXTRA_MARGIN_TIME_MS + ratios.timeFillPrepMS; F32 timeReservoirCycleMS = 0.0F; F32 timeUFDepletionMS = NEARLY_INFINITY; - F32 volFreshML = FILL_RESERVOIR_TO_VOLUME_ML - volSpentML; + F32 volFreshML = activeRsrvrTgtVolML - volSpentML; F32 timeFreshRemainingMS = ( volFreshML / (F32)dialysateFlowMLP ) * SEC_PER_MIN * MS_PER_SECOND; - F32 volMaxUFML = FILL_RESERVOIR_TO_VOLUME_ML * MAX_RESERVOIR_DILUTION; + F32 volMaxUFML = activeRsrvrTgtVolML * MAX_RESERVOIR_DILUTION; // Check if target UF flow is not zero to consider it in the calculations too if ( targetUFFlowMLP > NEARLY_ZERO ) { // If UF is not 0, the active reservoir cycle time is minimum of UF depletion and fill time - timeUFDepletionMS = ( ( (F32)FILL_RESERVOIR_TO_VOLUME_ML * RESERVOIR_DILUTION_RATIO ) / targetUFFlowMLP ) * SEC_PER_MIN * MS_PER_SECOND; + timeUFDepletionMS = ( ( (F32)activeRsrvrTgtVolML * RESERVOIR_DILUTION_RATIO ) / targetUFFlowMLP ) * SEC_PER_MIN * MS_PER_SECOND; // Calculate the ultra-filtration remaining volume // Using the ultra-filtration remaining volume and the ultra-filtration target flow rate calculate the time // The depletion time in milliseconds is the minimum time of the fresh remaining time and the depletion remaining time @@ -518,29 +528,33 @@ TREATMENT_RESERVOIR_MGMT_STATE_T state = TREATMENT_RESERVOIR_MGMT_WAIT_TO_FILL_STATE; // Get the target fill flow rate in L/min - F32 targetFillFlowRateLPM = getTargetFillFlowRateLPM(); + F32 targetFillFlowRateLPM = getTargetFillFlowRateLPM(); + DG_RESERVOIR_ID_T inactiveRsrvr = getDGInactiveReservoir(); + DG_RESERVOIR_ID_T activeRsrvr = getDGActiveReservoir(); // Get the ultra-filtration volume milliliters // Get the dilution level in percent = spent ultra-filtration volume / target fill volume in milliliters volSpentUFML = getReservoirUltrafiltrationVol( activeReservoir ); - dilutionLevelPct = volSpentUFML / (F32)FILL_RESERVOIR_TO_VOLUME_ML; + dilutionLevelPct = volSpentUFML / (F32)prevTargetFillVolumeML[ activeRsrvr ]; // Check if the dilution level has exceeded the limit or the spent volume is more than the amount of volume in the reservoir // If it has, trigger the fill command. This if should not happen and the predictive dilution level should trigger just in time // in advance - if ( ( dilutionLevelPct >= MAX_RESERVOIR_DILUTION ) || ( volSpentML >= (F32)FILL_RESERVOIR_TO_VOLUME_ML ) ) + if ( ( dilutionLevelPct >= MAX_RESERVOIR_DILUTION ) || ( volSpentML >= (F32)prevTargetFillVolumeML[ activeRsrvr ] ) ) { if ( DG_GEN_IDLE_MODE_STATE_FLUSH_WATER == dgSubMode ) { - cmdStartDGFill( FILL_RESERVOIR_TO_VOLUME_ML, targetFillFlowRateLPM ); + prevTargetFillVolumeML[ inactiveRsrvr ] = getTargetFillVolumeBasedOnDialysateFlowML(); + cmdStartDGFill( prevTargetFillVolumeML[ inactiveRsrvr ], targetFillFlowRateLPM ); } } // If we have active dialysate temp alarms, we want to fill immediately. else if ( TRUE == isDialysateTempAlarmActive() ) { if ( DG_GEN_IDLE_MODE_STATE_FLUSH_WATER == dgSubMode ) { - cmdStartDGFill( FILL_RESERVOIR_TO_VOLUME_ML, targetFillFlowRateLPM ); + prevTargetFillVolumeML[ inactiveRsrvr ] = getTargetFillVolumeBasedOnDialysateFlowML(); + cmdStartDGFill( prevTargetFillVolumeML[ inactiveRsrvr ], targetFillFlowRateLPM ); } } else @@ -553,7 +567,8 @@ // If the wait time has elapsed, trigger a fill command if ( timeWaitToFillMS <= 0 ) { - cmdStartDGFill( FILL_RESERVOIR_TO_VOLUME_ML, targetFillFlowRateLPM ); + prevTargetFillVolumeML[ inactiveRsrvr ] = getTargetFillVolumeBasedOnDialysateFlowML(); + cmdStartDGFill( prevTargetFillVolumeML[ inactiveRsrvr ], targetFillFlowRateLPM ); } } @@ -618,25 +633,25 @@ static TREATMENT_RESERVOIR_MGMT_STATE_T handleReservoirMgmtWaitForFillSettleState( void ) { TREATMENT_RESERVOIR_MGMT_STATE_T state = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE; - DG_RESERVOIR_ID_T active = getDGActiveReservoir(); + DG_RESERVOIR_ID_T active = getDGActiveReservoir(); + DG_RESERVOIR_ID_T inactiveRsrvr = getDGInactiveReservoir(); // Get the ultra-filtration volume milliliters // Get the dilution level in percent = spent ultra-filtration volume / target fill volume in milliliters volSpentUFML = getReservoirUltrafiltrationVol( activeReservoir ); - dilutionLevelPct = volSpentUFML / (F32)FILL_RESERVOIR_TO_VOLUME_ML; + dilutionLevelPct = volSpentUFML / (F32)prevTargetFillVolumeML[ active ]; // Wait for the reservoir to settle and then send the commands to switch the active reservoir if ( ( TRUE == didTimeout( reservoirSwitchStartTimeMS, RESERVOIR_FRESH_SETTLE_TIME_MS ) ) && - ( ( dilutionLevelPct >= MAX_RESERVOIR_DILUTION ) || ( volSpentML >= (F32)FILL_RESERVOIR_TO_VOLUME_ML ) || ( TRUE == isDialysateTempAlarmActive() ) ) ) + ( ( dilutionLevelPct >= MAX_RESERVOIR_DILUTION ) || ( volSpentML >= (F32)getTargetFillVolumeBasedOnDialysateFlowML() ) || ( TRUE == isDialysateTempAlarmActive() ) ) ) { DG_SWITCH_RSRVRS_CMD_T rsrvrCmd; - DG_RESERVOIR_ID_T inactiveRes = getDGInactiveReservoir(); - rsrvrCmd.reservoirID = (U32)inactiveRes; + rsrvrCmd.reservoirID = (U32)inactiveRsrvr; rsrvrCmd.useLastTrimmerHeaterDC = TRUE; // Signal dialysis sub-mode to capture baseline volume for next reservoir. - setStartReservoirVolume( inactiveRes ); + setStartReservoirVolume( inactiveRsrvr ); // Command DG to switch reservoirs cmdSetDGActiveReservoir( &rsrvrCmd ); @@ -651,7 +666,6 @@ reservoirSwitchStartTimeMS = getMSTimerCount(); state = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE; - } return state; @@ -708,4 +722,25 @@ return result; } +/*********************************************************************//** + * @brief + * The getTargetFillVolumeBasedOnDialysateFlowML function calculates the + * target fill volume in milliliters. + * @details Inputs: none + * @details Outputs: none + * @return Fill target volume in milliliters + *************************************************************************/ +static U32 getTargetFillVolumeBasedOnDialysateFlowML( void ) +{ + U32 targetFillVolumeML = FILL_RESERVOIR_TO_VOLUME_ML; + U32 dialysateFlowMLP = getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ); + + if ( dialysateFlowMLP >= RESERVOIR_FLOW_400_MLP ) + { + targetFillVolumeML = dialysateFlowMLP + FILL_VOLUME_INTERCEPT_ML_QD_ABOVE_400_MLPM; + } + + return targetFillVolumeML; +} + /**@}*/ Index: firmware/App/Services/SystemCommMessages.c =================================================================== diff -u -r85814f50ffa07b1794e9b1c40d3e558554a7fb77 -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision 85814f50ffa07b1794e9b1c40d3e558554a7fb77) +++ firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -7,8 +7,8 @@ * * @file SystemCommMessages.c * -* @author (last) Michael Garthwaite -* @date (last) 25-Aug-2023 +* @author (last) Vinayakam Mani +* @date (last) 30-Aug-2023 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -1719,6 +1719,30 @@ /*********************************************************************//** * @brief + * The sendDGParkConcentratePumpsCommand function constructs a DG concetentrate + * pumps park request msg to the DG and queues the msg for transmit on the appropriate CAN channel. + * @details Inputs: none + * @details Outputs: DG park concentrate pumps command msg constructed and queued.. + * @return TRUE if msg successfully queued for transmit, FALSE if not + *************************************************************************/ +BOOL sendDGParkConcentratePumpsCommand( void ) +{ + BOOL result; + MESSAGE_T msg; + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_PARK_CONCENTRATE_PUMPS_CMD_REQUEST; + 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 sendDGStartFlushModeCommand function constructs a DG start/stop * flush mode command message and queues the msg for transmit on the * appropriate CAN channel. Index: firmware/App/Services/SystemCommMessages.h =================================================================== diff -u -r3f4105248e6c49ab487ec065f9a4843d72a16a2a -re2cf7feff54dad3fc5be72619fa64b5421fc6f9f --- firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision 3f4105248e6c49ab487ec065f9a4843d72a16a2a) +++ firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision e2cf7feff54dad3fc5be72619fa64b5421fc6f9f) @@ -65,23 +65,27 @@ #define ACK_NOT_REQUIRED FALSE ///< Macro for functions that want to know if an outgoing message requires acknowledgement from receiver. #pragma pack(push, 1) +/// Payload record structure for ACK response. typedef struct { U08 acknowledgement; ///< Acknowledgement Byte } ACK_RESPONSE_PAYLOAD_T; +/// Payload record structure for off button response. typedef struct { U08 userRequest; ///< request to confirm, cancel, or reject off button request } UI_OFF_BUTTON_RESPONSE_PAYLOAD_T; #pragma pack(pop) +/// Payload record structure for UI response. typedef struct { BOOL accepted; ///< Accepted/Rejected U32 rejectionReason; ///< Rejection reason if not accepted. } UI_RESPONSE_PAYLOAD_T; +/// Payload record structure for UF change response. typedef struct { BOOL accepted; ///< Accepted/Rejected @@ -94,6 +98,7 @@ F32 oldUFRate; ///< Previous UF Rate in mL/min. } UF_SETTINGS_CHANGE_RESPONSE_PAYLOAD_T; +/// Payload record structure for UF change confirmation response. typedef struct { BOOL accepted; ///< Accepted/Rejected @@ -103,6 +108,7 @@ F32 ufRate; ///< UF rate in mL. } UF_SETTINGS_CONFIRMATION_RESPONSE_PAYLOAD_T; +/// Payload record structure for treatment duration change response. typedef struct { BOOL accepted; ///< Accepted/Rejected @@ -111,6 +117,7 @@ F32 volume; ///< UF Volume in mL. } TREATMENT_TIME_CHANGE_RESPONSE_PAYLOAD_T; +/// Payload record structure for blood / dialysate rate change response. typedef struct { BOOL accepted; ///< Accepted/Rejected @@ -119,6 +126,7 @@ U32 dialRate; ///< new dialysate flow rate } BLOOD_DIAL_RATE_CHANGE_RESPONSE_PAYLOAD_T; +/// Payload record structure for event messages. typedef struct { U32 event; ///< Event ID @@ -128,6 +136,7 @@ EVENT_DATAS_T data2; ///< Data field 2 } EVENT_PAYLOAD_T; +/// Payload record structure for treatment parameter broadcast messages. typedef struct { U32 minTreatmentTime; ///< Minimum treatment duration (in minutes) @@ -222,18 +231,9 @@ // MSG_ID_DG_TEMPERATURE_DATA: void handleDGTemperatureData( MESSAGE_T *message ); -// MSG_ID_RO_PUMP_DATA: -void handleROPumpData( MESSAGE_T *message ); - // MSG_ID_DG_FLOW_SENSORS_DATA void handleDialysateFlowData( MESSAGE_T *message ); -// MSG_ID_DRAIN_PUMP_DATA: -void handleDrainPumpData( MESSAGE_T *message ); - -// MSG_ID_DG_PRESSURES_DATA: -void handleDGPressuresData( MESSAGE_T *message ); - // MSG_ID_DG_RESERVOIR_DATA: void handleDGReservoirData( MESSAGE_T *message ); @@ -773,9 +773,6 @@ // MSG_ID_SUPER_CLEAR_ALARMS_CMD void handleTestSuperClearAlarmsRequest( MESSAGE_T *message ); -// MSG_ID_HD_REQUEST_CALIBRATION_DATA -void handleTestHDCalibrationDataRequest( MESSAGE_T *message ); - // MSG_ID_HD_SET_CALIBRATION_RECORD void handleSetHDCalibrationRecord( MESSAGE_T *message );