Index: firmware/App/Modes/ModeTreatment.c =================================================================== diff -u -r5c9531b2816a03c6de30b63281d8a1718fcdd5a2 -r2f9807457197c347c20a24c64492edcf063f3daa --- firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision 5c9531b2816a03c6de30b63281d8a1718fcdd5a2) +++ firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision 2f9807457197c347c20a24c64492edcf063f3daa) @@ -33,6 +33,7 @@ #include "Reservoirs.h" #include "Rinseback.h" #include "RTC.h" +#include "SalineBolus.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" @@ -57,7 +58,9 @@ #define USER_CONFIRM_CHANGE_TIMEOUT_MS ( 60 * MS_PER_SECOND ) ///< Require user to confirm UF volume change within this time. #define PREVENT_UF_VOL_CHANGE_IF_NEARLY_DONE_SEC ( 10 * SEC_PER_MIN ) ///< Prevent UF volume change if treatment within this much time from end of treatment (in seconds). +#define UF_RATE_CONFIRM_MSG_TIMEOUT_MS ( 30 * MS_PER_SECOND ) ///< UF rate confirm message timeout in milliseconds. + /// Interval (ms/task time) at which the treatment time data is published on the CAN bus. #define TREATMENT_TIME_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Interval (ms/task time) at which the treatment state data is published on the CAN bus. @@ -75,6 +78,15 @@ /// Macro to calculate the remaining treatment time in seconds. #define CALC_TREAT_TIME_REMAINING_IN_SECS() ( (S32)presTreatmentTimeSecs - (S32)( treatmentTimeMS / MS_PER_SECOND ) ) +/// Treatment duration requested values +typedef struct +{ + U32 requestedTxDurationMins; ///< Requested treatment duration from the user in minutes. + F32 newUFRateMLPM; ///< New calculated UF rate in mL/Min. + BOOL isUFRateConfInProgress; ///< Boolean flag to indicate whether a new UF rate confirmation has been requested. + U32 rqstStartTimeStamp; ///< Request start time stamp. +} TREATMENT_DURATION_RQST_T; + // ********** private data ********** static TREATMENT_STATE_T currentTreatmentState; ///< Current state (sub-mode) of treatment mode. @@ -133,6 +145,8 @@ static U32 treatmentStartTimeStamp; ///< Treatment start timestampt for logging purpose. static U32 treatmentEndTimeStamp; ///< Treatment end timestampt for logging purpose. +static TREATMENT_DURATION_RQST_T txDurationRequest; ///< Treatment duration request structure. + // ********** private function prototypes ********** static void broadcastTreatmentSettingsRanges( void ); @@ -199,6 +213,10 @@ treatmentStartTimeStamp = getRTCTimestamp(); treatmentEndTimeStamp = 0; + txDurationRequest.requestedTxDurationMins = 0; + txDurationRequest.newUFRateMLPM = 0.0F; + txDurationRequest.isUFRateConfInProgress = FALSE; + // reset dialysate temperature alarm persistences prior to starting a treatment. resetPersistentAlarmTimer( ALARM_ID_HD_DIALYSATE_TEMP_ABOVE_SAFETY_TEMP ); resetPersistentAlarmTimer( ALARM_ID_HD_DIALYSATE_TEMP_ABOVE_TARGET_TEMP ); @@ -221,6 +239,7 @@ // Initialize treatment mode each time we transition to it initTreatmentMode(); initReservoirs(); + initSalineBolus(); // Initialize treatment sub-modes each time we transition to treatment mode initBloodPrime(); initDialysis(); @@ -1048,6 +1067,7 @@ BOOL verifyTreatmentDurationSettingChange( U32 treatmentTime ) { BOOL result = FALSE; + BOOL isResp2UINeeded = TRUE; REQUEST_REJECT_REASON_CODE_T rejectReason = REQUEST_REJECT_REASON_NONE; HD_OP_MODE_T currMode = getCurrentOperationMode(); @@ -1056,38 +1076,72 @@ ( currentTreatmentState > TREATMENT_START_STATE ) && ( currentTreatmentState < TREATMENT_END_STATE ) && ( CALC_ELAPSED_TREAT_TIME_IN_MIN() < treatmentTime ) && ( treatmentTime >= getMinTreatmentTimeInMinutes() ) ) { - // Always adjust UF volume to accommodate treatment time change (not UF rate) - F32 uFVolume = ( (F32)( treatmentTime - CALC_ELAPSED_TREAT_TIME_IN_MIN() ) * presUFRate ) + getUltrafiltrationReferenceVolume(); - F32 uFVolumeL = uFVolume / ML_PER_LITER; - U32 dialVolume = getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ) * treatmentTime; // In mL + // Always keep the UF volume the same and change the UF rate upon changing the treatment time + presMaxUFVolumeML = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) * (F32)ML_PER_LITER; + + // New UF rate is how much UF is left to be done / remaining treatment time + F32 measUFVolML = getTotalMeasuredUFVolumeInMl(); + F32 newUFDiffML = ( ( presMaxUFVolumeML - measUFVolML ) < 0.0F ? 0.0F : ( presMaxUFVolumeML - measUFVolML ) ); + F32 newTxTimeS = (F32)treatmentTime * (F32)SEC_PER_MIN; + F32 elapsedTxTimeS = (F32)treatmentTimeMS / (F32)MS_PER_SECOND; + F32 timeDiffMin = (newTxTimeS - elapsedTxTimeS) / (F32)SEC_PER_MIN; + F32 newUFRateMLPM = newUFDiffML / timeDiffMin; + U32 dialVolume = getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ) * treatmentTime; // In mL + + SEND_EVENT_WITH_2_F32_DATA( HD_EVENT_UF_RATE_TX_DUR, newUFDiffML, newUFRateMLPM ) + SEND_EVENT_WITH_2_F32_DATA( HD_EVENT_UF_RATE_TIME_TX_DUR, elapsedTxTimeS, timeDiffMin ) + // The minimum treatment time is either in the range of the institutional record or the 1-minute treatment config has been requested which is an exception BOOL isMinTxTimeValid = ( ( treatmentTime >= getU32TreatmentParamLowerRangeLimit( TREATMENT_PARAM_TREATMENT_DURATION ) ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_ENABLE_ONE_MINUTE_TREATMENT ) ) ? TRUE : FALSE ); BOOL isTxTimeValid = ( ( TRUE == isMinTxTimeValid ) && ( treatmentTime <= getU32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_TREATMENT_DURATION ) ) ? TRUE : FALSE ); - BOOL isMinUFVolValid = ( ( uFVolumeL >= getF32TreatmentParamLowerRangeLimit( TREATMENT_PARAM_UF_VOLUME ) ) || ( uFVolumeL <= 0.0F ) ? TRUE : FALSE ); - BOOL isUFVolValid = ( ( TRUE == isMinUFVolValid ) && ( uFVolumeL <= getF32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_UF_VOLUME ) ) ? TRUE : FALSE ); + // Only the upper range UF rate is checked here. Per the check above if the UF diff is < 0, it is set to zero so the minimum UF rate is 0 and cannot be negative + BOOL isUFRateValid = ( newUFRateMLPM <= MAX_UF_RATE_ML_MIN ? TRUE : FALSE ); - if ( ( TRUE == isTxTimeValid ) && ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) && ( TRUE == isUFVolValid ) ) + if ( ( TRUE == isTxTimeValid ) && ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) && ( TRUE == isUFRateValid ) ) { result = TRUE; - sendTreatmentLogEventData( TREATMENT_DURATION_CHANGE_EVENT, ( presTreatmentTimeSecs / SEC_PER_MIN ), treatmentTime ); - presMaxUFVolumeML = uFVolume; - presTreatmentTimeSecs = treatmentTime * SEC_PER_MIN; - setTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME, ( presMaxUFVolumeML / (F32)ML_PER_LITER ) ); - setTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION, ( presTreatmentTimeSecs / SEC_PER_MIN ) ); - setDialysisDialInFlowAndUFRate( getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), presMaxUFVolumeML, presUFRate ); - signalInitiatePressureStabilization( USE_NORMAL_STABILIZATION_PERIOD ); + + if ( presMaxUFVolumeML > 0.0F ) + { + GENERIC_CONFIRMATION_REQUEST_T genericConfRequest; + + // Don't send the treatment duration change response to UI because we have UF volume so the UF rate changes and it needs the approval of the user + // If the UF volume in the treatment is 0 then send the response back to UI + isResp2UINeeded = FALSE; + + // Store the proposed treatment duration and the calculated new UF rate until the user approves it + txDurationRequest.requestedTxDurationMins = treatmentTime; + txDurationRequest.newUFRateMLPM = newUFRateMLPM; + txDurationRequest.isUFRateConfInProgress = TRUE; + txDurationRequest.rqstStartTimeStamp = getMSTimerCount(); + + genericConfRequest.requestID = (U32)GENERIC_CONFIRM_ID_UF_RATE_CHANGE_IN_TX_DURATION_CHANGE; + genericConfRequest.requestType = (U32)GENERIC_CONFIRM_CMD_REQUEST_OPEN; + genericConfRequest.rejectReason = 0; + genericConfRequest.genericPayload1 = txDurationRequest.newUFRateMLPM ; // TODO convert to L/hr for UI + genericConfRequest.genericPayload2 = 0.0F; + genericConfRequest.genericPayload3 = 0.0F; + genericConfRequest.genericPayload4 = 0.0F; + + addConfirmationRequest( &genericConfRequest ); + } + else + { + // Update the treatment time since there is not UF rate change that needs approval + presTreatmentTimeSecs = treatmentTime * SEC_PER_MIN; + } } else { if ( FALSE == isTxTimeValid ) { rejectReason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; } - else if ( FALSE == isUFVolValid ) + else if ( FALSE == isUFRateValid ) { - rejectReason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; + rejectReason = REQUEST_REJECT_REASON_UF_RATE_OUT_OF_RANGE; } else { @@ -1101,8 +1155,7 @@ { rejectReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } - else if ( ( currentTreatmentState <= TREATMENT_START_STATE ) || - ( currentTreatmentState >= TREATMENT_END_STATE ) ) + else if ( ( currentTreatmentState <= TREATMENT_START_STATE ) || ( currentTreatmentState >= TREATMENT_END_STATE ) ) { rejectReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } @@ -1115,8 +1168,13 @@ rejectReason = REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_CURRENT; } } - // Send response to request - sendChangeTreatmentDurationResponse( result, rejectReason, presTreatmentTimeSecs / SEC_PER_MIN, presMaxUFVolumeML ); + + if ( TRUE == isResp2UINeeded ) + { + // Send response to request only if there is a rejection + sendChangeTreatmentDurationResponse( result, rejectReason, presTreatmentTimeSecs / SEC_PER_MIN, presMaxUFVolumeML ); + } + // Send new ranges for settings treatmentParamsRangesBroadcastTimerCtr = getU32OverrideValue( &treatmentParamRangesPublishInterval ); broadcastTreatmentSettingsRanges(); @@ -1519,6 +1577,59 @@ /*********************************************************************//** * @brief + * The handleUFRateConfirmationMessageFromUI function handles the UF rate + * message received from UI. It checks whether the accept or reject needs + * to be handled. + * @details Inputs: txDurationRequest + * @details Outputs: presTreatmentTimeSecs, presUFRate, txDurationRequest, + * presMaxUFVolumeML + * @return none + *************************************************************************/ +void handleUFRateConfirmationMessageFromUI( void ) +{ + BOOL result = FALSE; + CONFIRMATION_REQUEST_STATUS_T status = getConfirmationRequestStatus( GENERIC_CONFIRM_ID_UF_RATE_CHANGE_IN_TX_DURATION_CHANGE, + txDurationRequest.newUFRateMLPM, 0.0F, 0.0F, 0.0F ); + + if ( TRUE == txDurationRequest.isUFRateConfInProgress ) + { + if ( ( CONFIRMATION_REQUEST_STATUS_ACCEPTED == status ) || ( CONFIRMATION_REQUEST_STATUS_REJECTED == status ) ) + { + if ( CONFIRMATION_REQUEST_STATUS_ACCEPTED == status ) + { + result = TRUE; + sendTreatmentLogEventData( TREATMENT_DURATION_CHANGE_EVENT, ( presTreatmentTimeSecs / SEC_PER_MIN ), txDurationRequest.requestedTxDurationMins ); + presTreatmentTimeSecs = txDurationRequest.requestedTxDurationMins * SEC_PER_MIN; + presUFRate = txDurationRequest.newUFRateMLPM; + setTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME, ( presMaxUFVolumeML / (F32)ML_PER_LITER ) ); + setTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION, ( presTreatmentTimeSecs / SEC_PER_MIN ) ); + setDialysisDialInFlowAndUFRate( getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), presMaxUFVolumeML, presUFRate ); + signalInitiatePressureStabilization( USE_NORMAL_STABILIZATION_PERIOD ); + } + + // Regardless of accept or reject there is no reject reason. + sendChangeTreatmentDurationResponse( result, 0, ( presTreatmentTimeSecs / SEC_PER_MIN ), presMaxUFVolumeML ); + // Send new ranges for settings + treatmentParamsRangesBroadcastTimerCtr = getU32OverrideValue( &treatmentParamRangesPublishInterval ); + broadcastTreatmentSettingsRanges(); + // Send time/state data immediately for UI update + broadcastTreatmentTimeAndState(); + + // Done with treatment duration changes whether they were accepted or rejected. + txDurationRequest.newUFRateMLPM = 0.0F; + txDurationRequest.requestedTxDurationMins = 0; + txDurationRequest.isUFRateConfInProgress = FALSE; + } + } + + if ( TRUE == didTimeout( txDurationRequest.rqstStartTimeStamp, UF_RATE_CONFIRM_MSG_TIMEOUT_MS ) ) + { + setConfirmationRequestStatus( GENERIC_CONFIRM_ID_UF_RATE_CHANGE_IN_TX_DURATION_CHANGE, CONFIRMATION_REQUEST_STATUS_TIMEOUT ); + } +} + +/*********************************************************************//** + * @brief * The broadcastTreatmentSettingsRanges function computes and broadcasts * updated treatment parameter ranges that the user may change during treatment. * It is assumed that prescription settings have already been set prior to calling