Index: firmware/App/Modes/Rinseback.c =================================================================== diff -u -rccfd15568f1e3d304320c2babb2fd4bcf0413304 -r8bd1ae47aa13a843aa8abd6321ddc050deacb4a6 --- firmware/App/Modes/Rinseback.c (.../Rinseback.c) (revision ccfd15568f1e3d304320c2babb2fd4bcf0413304) +++ firmware/App/Modes/Rinseback.c (.../Rinseback.c) (revision 8bd1ae47aa13a843aa8abd6321ddc050deacb4a6) @@ -37,24 +37,22 @@ // ********** private definitions ********** -#define TARGET_RINSEBACK_VOLUME_ML 300.0 ///< Target rinseback volume to deliver back to the patient (in mL). TODO - get from Systems when available -#define MAX_TOTAL_RINSEBACK_VOLUME_ML 400.0 ///< Maximum total rinseback volume allowed : main + all additionals (in mL). TODO - get from Systems when known -#define TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML 10.0 ///< Target rinseback volume for an additional volume request (in mL). -#define RINSEBACK_FLOW_RATE_ADJ_ML_MIN 25 ///< Adjustment amount (in mL/min) to apply when user requests increase/decrease in flow rate. -#define MIN_RINSEBACK_FLOW_RATE_ML_MIN 50 ///< Minimum rinseback flow rate (in mL/min). -#define MAX_RINSEBACK_FLOW_RATE_ML_MIN 150 ///< Maximum rinseback flow rate (in mL/min). +#define TARGET_RINSEBACK_VOLUME_ML 300.0 ///< Target rinseback volume to deliver back to the patient (in mL). TODO - get from Systems when available +#define MAX_TOTAL_ADDITIONAL_RINSEBACK_VOLUME_ML 300.0 ///< Maximum total additional rinseback volume allowed : all additionals (in mL). +#define MAX_RINSEBACK_VOLUME_ERROR_ML 60.0 ///< Maximum error in total additional rinseback volume (20% of total). +#define TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML 10.0 ///< Target rinseback volume for an additional volume request (in mL). +#define RINSEBACK_FLOW_RATE_ADJ_ML_MIN 25 ///< Adjustment amount (in mL/min) to apply when user requests increase/decrease in flow rate. +#define MIN_RINSEBACK_FLOW_RATE_ML_MIN 50 ///< Minimum rinseback flow rate (in mL/min). +#define MAX_RINSEBACK_FLOW_RATE_ML_MIN 150 ///< Maximum rinseback flow rate (in mL/min). -#ifndef DISABLE_PUMP_FLOW_CHECKS -/// Maximum rinseback volume measured by independent means (as % of target). -static const F32 MAX_RINSEBACK_SAFETY_VOLUME_ML = ( TARGET_RINSEBACK_VOLUME_ML * 1.2 ); -/// Minimum rinseback volume measured by independent means (as % of target). -static const F32 MIN_RINSEBACK_SAFETY_VOLUME_ML = ( TARGET_RINSEBACK_VOLUME_ML * 0.8 ); -#endif - /// Interval at which rinseback progress is to be published to UI. #define RINSEBACK_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Maximum time allowed for rinseback operation until full volume is delivered. Timer is reset whenever BP is running. static const U32 MAX_RINSEBACK_TIME = ( 5 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); +/// Maximum time allowed after rinseback operation completed. Timer is reset whenever BP is running (additional). +static const U32 MAX_RINSEBACK_DONE_TIME = ( 15 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); +/// Warning time after rinseback operation completed. Timer is reset whenever BP is running (additional). +static const U32 RINSEBACK_DONE_WARNING_TIME = ( 13 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); /// Maximum time allowed for each additional rinseback volume delivery. static const U32 MAX_RINSEBACK_ADDITIONAL_TIME = ( 20 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ); /// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. @@ -66,12 +64,11 @@ static U32 rinsebackRate_mL_min; ///< Rinseback rate to use/adjust for this current rinseback only. static U32 rinsebackTimerCtr; ///< Timer counter for time spent in rinseback sub-mode. -static OVERRIDE_F32_T cumulativeRinsebackVolume_mL = { 0.0, 0.0, 0.0, 0 }; ///< Total cumulative rinseback volume (in mL). -static OVERRIDE_F32_T rinsebackVolumeDelivered_Safety = { 0.0, 0.0, 0.0, 0 }; ///< The cumulative independent rinseback volume (in mL) calculated so far. +static OVERRIDE_F32_T cumulativeRinsebackVolume_mL = { 0.0, 0.0, 0.0, 0 }; ///< Total cumulative rinseback volume (in mL) from measured blood flow rate. +static F32 expectedRinsebackVolume_mL = 0.0; ///< Total cumulative rinseback volume (in mL) expected based on target blood flow rate. static F32 targetRinsebackVolumePlusAdditional_mL; ///< Target rinseback volume w/ additional volume(s) added (in mL). static F32 additionalRinsebackVolume_mL; ///< Total volume (in mL) delivered so far for additional volume request. -static S32 rinsebackMotorCount; ///< The cumulative sum of BP motor encoder counts used for independent rinseback volume check. -static U32 rinsebackLastMotorCount; ///< The last BP motor encoder count read for independent rinseback volume check. +static F32 totalAdditionalRinsebackVolume_mL; ///< Total accumulated volume (in mL) delivered so far for all additional volumes combined. static U32 rinsebackAdditionalTimerCtr; ///< Timer counter for duration of an additional rinseback delivery. static U32 rinsebackPublishTimerCtr; ///< Timer counter for determining interval for rinseback status to be published. /// Interval (in task intervals) at which to publish rinseback data to CAN bus. @@ -96,7 +93,6 @@ static void setupForRinsebackStopOrPause( void ); static F32 getRinsebackVolume( void ); -static F32 getRinsebackSafetyVolume( void ); static RINSEBACK_STATE_T handleRinsebackStopInitState( void ); static RINSEBACK_STATE_T handleRinsebackRunState( void ); @@ -132,10 +128,9 @@ targetRinsebackVolumePlusAdditional_mL = TARGET_RINSEBACK_VOLUME_ML; rinsebackTimerCtr = 0; cumulativeRinsebackVolume_mL.data = 0.0; - rinsebackVolumeDelivered_Safety.data = 0.0; + expectedRinsebackVolume_mL = 0.0; additionalRinsebackVolume_mL = 0.0; - rinsebackMotorCount = 0; - rinsebackLastMotorCount = getBloodPumpMotorCount(); + totalAdditionalRinsebackVolume_mL = 0.0; rinsebackAdditionalTimerCtr = 0; rinsebackPublishTimerCtr = 0; resetRinsebackFlags(); @@ -209,8 +204,8 @@ static void setupForRinsebackDelivery( U32 rate ) { // Open VBA and VBV valves to allow flow from saline bag and to patient venous line - setValvePosition( VBA, VALVE_POSITION_B_OPEN ); - setValvePosition( VBV, VALVE_POSITION_B_OPEN ); + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); // draw from saline back instead of patient + setValvePosition( VBV, VALVE_POSITION_B_OPEN ); // return to patient // Start blood pump at rinseback flow rate setBloodPumpTargetFlowRate( rate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // Start air trap leveling control @@ -259,26 +254,6 @@ /*********************************************************************//** * @brief - * The getRinsebackSafetyVolume function gets the calculated independent - * blood prime volume delivered. - * @details Inputs: rinsebackVolumeDelivered_Safety - * @details Outputs: none - * @return the current rinseback safety volume delivered (in mL). - *************************************************************************/ -static F32 getRinsebackSafetyVolume( void ) -{ - F32 result = rinsebackVolumeDelivered_Safety.data; - - if ( OVERRIDE_KEY == rinsebackVolumeDelivered_Safety.override ) - { - result = rinsebackVolumeDelivered_Safety.ovData; - } - - return result; -} - -/*********************************************************************//** - * @brief * The getCurrentRinsebackState function returns the current state of the * rinseback sub-mode. * @details Inputs: rinsebackState @@ -350,7 +325,6 @@ // Has user requested rinseback start? if ( TRUE == startRinsebackRequested ) { - rinsebackLastMotorCount = getBloodPumpMotorCount(); setupForRinsebackDelivery( rinsebackRate_mL_min ); // From moment we start rinseback, we consider blood side of dialyzer no longer fully primed setBloodIsPrimed( FALSE ); @@ -382,7 +356,7 @@ * The handleRinsebackRunState function handles the rinseback run state * operations. * @details Inputs: flags - * @details Outputs: cumulativeRinsebackVolume_mL, rinsebackVolumeDelivered_Safety, flags handled + * @details Outputs: cumulativeRinsebackVolume_mL, flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackRunState( void ) @@ -394,29 +368,22 @@ // Update rinseback volume delivered so far cumulativeRinsebackVolume_mL.data += ( getMeasuredBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); - // update independent calculated safety volume delivered so far - rinsebackMotorCount = u32BiDiffWithWrap( rinsebackLastMotorCount, getBloodPumpMotorCount() ) / BP_HALL_EDGE_COUNTS_PER_REV; - rinsebackVolumeDelivered_Safety.data = ( (F32)rinsebackMotorCount * VOLUME_PER_BP_MOTOR_REV_ML ); // TODO - include upstream pressure compensation to this calc + expectedRinsebackVolume_mL += ( (F32)getTargetBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); // Has user requested to end rinseback? if ( TRUE == endRinsebackRequested ) { setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } // Has rinseback completed? else if ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ) { setRinsebackIsCompleted( TRUE ); setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; -#ifndef DISABLE_PUMP_FLOW_CHECKS - // Check for under-delivery - if ( getRinsebackSafetyVolume() < MIN_RINSEBACK_SAFETY_VOLUME_ML ) - { - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_RINSEBACK_VOLUME_CHECK_FAILURE, TARGET_RINSEBACK_VOLUME_ML, getRinsebackSafetyVolume() ); - } -#endif } // Check for empty saline bag if ( TRUE == isSalineBagEmpty() ) @@ -432,12 +399,12 @@ result = RINSEBACK_PAUSED_STATE; } #ifndef DISABLE_PUMP_FLOW_CHECKS - // Has independent safety volume exceeded safety limit? - else if ( getRinsebackSafetyVolume() > MAX_RINSEBACK_SAFETY_VOLUME_ML ) + // Is rinseback taking too long? + else if ( fabs( expectedRinsebackVolume_mL - getRinsebackVolume() ) > MAX_RINSEBACK_VOLUME_ERROR_ML ) { setRinsebackIsCompleted( TRUE ); setupForRinsebackStopOrPause(); - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_RINSEBACK_VOLUME_CHECK_FAILURE, TARGET_RINSEBACK_VOLUME_ML, getRinsebackSafetyVolume() ); + activateAlarmNoData( ALARM_ID_RINSEBACK_VOLUME_CHECK_FAILURE ); result = RINSEBACK_STOP_STATE; } #endif @@ -495,6 +462,7 @@ // Has user requested to end rinseback? else if ( TRUE == endRinsebackRequested ) { + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } @@ -513,23 +481,33 @@ { RINSEBACK_STATE_T result = RINSEBACK_STOP_STATE; - // If user confirms ready to start re-circulate sub-mode, go there - if ( TRUE == recircRequested ) + // Have we been in this stopped state for too long w/o having delivered full blood volume back to patient? + if ( ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) && ( getRinsebackVolume() < TARGET_RINSEBACK_VOLUME_ML ) ) { - signalRinsebackToRecirc(); + signalGoToTreatmentStopped(); + activateAlarmNoData( ALARM_ID_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); } - // Has rinseback operation exceeded max time w/o delivering full volume? - if ( ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) && ( getRinsebackVolume() < TARGET_RINSEBACK_VOLUME_ML ) ) + // Have we been in this stopped state for too long despite having delivered full blood volume back to patient? + else if ( ( rinsebackTimerCtr > MAX_RINSEBACK_DONE_TIME ) && ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ) ) { signalGoToTreatmentStopped(); activateAlarmNoData( ALARM_ID_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); + clearAlarm( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_WARNING ); } + // Have we been in this stopped state for too long despite having delivered full blood volume back to patient? + else if ( ( RINSEBACK_DONE_WARNING_TIME == rinsebackTimerCtr ) && ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ) ) + { + activateAlarmNoData( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_WARNING ); + } + else if ( TRUE == recircRequested ) + { + signalRinsebackToRecirc(); + } else if ( TRUE == additionalRinsebackRequested ) { additionalRinsebackRequested = FALSE; // deliver additional rinseback volume only if max volume not reached and max time not reached - if ( ( ( getRinsebackVolume() + TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) <= MAX_TOTAL_RINSEBACK_VOLUME_ML ) && - ( ( rinsebackTimerCtr < MAX_RINSEBACK_TIME ) || ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ) ) ) + if ( ( rinsebackTimerCtr < MAX_RINSEBACK_TIME ) || ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ) ) { rinsebackAdditionalTimerCtr = 0; additionalRinsebackVolume_mL = 0.0; @@ -568,31 +546,46 @@ // update additional rinseback volume delivered so far additionalRinsebackVolume_mL += rinsebackVolumeSinceLast; + totalAdditionalRinsebackVolume_mL += rinsebackVolumeSinceLast; cumulativeRinsebackVolume_mL.data += rinsebackVolumeSinceLast; rinsebackAdditionalTimerCtr++; - // Has additional rinseback completed or timed out? - if ( ( additionalRinsebackVolume_mL >= TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) || - ( getRinsebackVolume() >= MAX_TOTAL_RINSEBACK_VOLUME_ML ) || - ( rinsebackAdditionalTimerCtr >= MAX_RINSEBACK_ADDITIONAL_TIME ) ) - { - setupForRinsebackStopOrPause(); - result = RINSEBACK_STOP_STATE; - } // Check for empty saline bag if ( TRUE == isSalineBagEmpty() ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); - setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } + // Has additional rinseback completed + else if ( additionalRinsebackVolume_mL >= TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) + { + result = RINSEBACK_STOP_STATE; + } + // Has additional rinseback timed out? + else if ( rinsebackAdditionalTimerCtr >= MAX_RINSEBACK_ADDITIONAL_TIME ) + { + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); + result = RINSEBACK_STOP_STATE; + } // Has alarm requested stop? else if ( TRUE == doesAlarmStatusIndicateStop() ) { - setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } + // Setup for rinseback stop state if additional rinseback completed or ending. + if ( RINSEBACK_STOP_STATE == result ) + { + setupForRinsebackStopOrPause(); + // If this additional rinseback volume has brought us to "full" rinseback volume, set flag + if ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ) + { + setRinsebackIsCompleted( TRUE ); + } + } + return result; } @@ -716,6 +709,7 @@ { result = TRUE; incrRinsebackFlowRateRequested = TRUE; + rinsebackPublishTimerCtr = getPublishRinsebackInterval(); } else { @@ -750,6 +744,7 @@ { result = TRUE; decrRinsebackFlowRateRequested = TRUE; + rinsebackPublishTimerCtr = getPublishRinsebackInterval(); } else { @@ -861,7 +856,7 @@ if ( RINSEBACK_STOP_STATE == rinsebackState ) { - if ( ( getRinsebackVolume() + TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) <= MAX_TOTAL_RINSEBACK_VOLUME_ML ) + if ( ( totalAdditionalRinsebackVolume_mL + TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) <= MAX_TOTAL_ADDITIONAL_RINSEBACK_VOLUME_ML ) { result = TRUE; additionalRinsebackRequested = TRUE; @@ -901,6 +896,10 @@ { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } + else if ( getRinsebackVolume() < TARGET_RINSEBACK_VOLUME_ML ) + { + *rejReason = REQUEST_REJECT_REASON_RINSEBACK_NOT_COMPLETED; + } else { result = TRUE; @@ -983,26 +982,35 @@ { RINSEBACK_DATA_PAYLOAD_T data; U32 timeout = MAX_RINSEBACK_TIME / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); - U32 countdown = ( rinsebackTimerCtr >= MAX_RINSEBACK_TIME ? 0 : ( MAX_RINSEBACK_TIME - rinsebackTimerCtr ) / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); + U32 countdown = ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ? MAX_RINSEBACK_DONE_TIME : MAX_RINSEBACK_TIME ); + countdown -= rinsebackTimerCtr; + // Handle countdown past zero + if ( countdown > MAX_RINSEBACK_DONE_TIME ) + { + countdown = 0; + } + // Scale to seconds + countdown /= ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + data.targetRinsebackVolumeMl = TARGET_RINSEBACK_VOLUME_ML; rinsebackPublishTimerCtr = 0; // If we have completed rinseback, timeout is no longer in force - indicate by zeroing timeout. Also include any additionals to target. if ( ( rinsebackState > RINSEBACK_PAUSED_STATE ) && ( getRinsebackVolume() >= TARGET_RINSEBACK_VOLUME_ML ) ) { data.targetRinsebackVolumeMl = targetRinsebackVolumePlusAdditional_mL; - timeout = 0; + timeout = MAX_RINSEBACK_DONE_TIME / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); } data.deliveredRinsebackVolumeMl = getRinsebackVolume(); - data.safetyRinsebackVolumeMl = getRinsebackSafetyVolume(); data.rinsebackFlowRateMlMin = rinsebackRate_mL_min; if ( RINSEBACK_RUN_ADDITIONAL_STATE == rinsebackState ) { data.rinsebackFlowRateMlMin = MIN_RINSEBACK_FLOW_RATE_ML_MIN; } data.timeout = timeout; data.countdown = countdown; - broadcastRinsebackData( data ); + data.unused = 0.0; // TODO - remove when UI no longer looks for this field + broadcastData( MSG_ID_HD_RINSEBACK_PROGRESS, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( RINSEBACK_DATA_PAYLOAD_T ) ); } } @@ -1079,51 +1087,6 @@ /*********************************************************************//** * @brief - * The testSetRinsebackSafetyVolumeOverride function overrides the calculated - * rinseback safety volume. - * @details Inputs: none - * @details Outputs: rinsebackVolumeDelivered_Safety - * @param vol override calculated rinseback safety volume (in mL) - * @return TRUE if override successful, FALSE if not - *************************************************************************/ -BOOL testSetRinsebackSafetyVolumeOverride( F32 vol ) -{ - BOOL result = FALSE; - - if ( TRUE == isTestingActivated() ) - { - result = TRUE; - rinsebackVolumeDelivered_Safety.ovData = vol; - rinsebackVolumeDelivered_Safety.override = OVERRIDE_KEY; - } - - return result; -} - -/*********************************************************************//** - * @brief - * The testResetRinsebackSafetyVolumeOverride function resets the override of the - * calculated rinseback safety volume. - * @details Inputs: none - * @details Outputs: rinsebackVolumeDelivered_Safety - * @return TRUE if reset successful, FALSE if not - *************************************************************************/ -BOOL testResetRinsebackSafetyVolumeOverride( void ) -{ - BOOL result = FALSE; - - if ( TRUE == isTestingActivated() ) - { - result = TRUE; - rinsebackVolumeDelivered_Safety.override = OVERRIDE_RESET; - rinsebackVolumeDelivered_Safety.ovData = rinsebackVolumeDelivered_Safety.ovInitData; - } - - return result; -} - -/*********************************************************************//** - * @brief * The testSetRinsebackPublishIntervalOverride function sets the override of the * rinseback data publication interval. * @details Inputs: none