Index: firmware/App/Services/TxParams.c =================================================================== diff -u -rda59fa4a98dbc11c37677e92a66aa940d251678f -rd387dc3cd21e6f1fb7e161032f30eb49a9c1b48e --- firmware/App/Services/TxParams.c (.../TxParams.c) (revision da59fa4a98dbc11c37677e92a66aa940d251678f) +++ firmware/App/Services/TxParams.c (.../TxParams.c) (revision d387dc3cd21e6f1fb7e161032f30eb49a9c1b48e) @@ -181,15 +181,22 @@ static BOOL validTreatParamsReceived = FALSE; ///< Flag indicates user has provided treatment parameters. static BOOL treatParamsConfirmed = FALSE; ///< Flag indicates user has confirmed the treatment parameters. +static BOOL isolatedUFSettingsPending = FALSE; ///< Flag indicates isolated UF settings are staged for confirmation. +static U32 isolatedUFDurationPendingMin = 0; ///< Staged isolated UF duration in minutes. +static F32 isolatedUFVolumePendingMl = 0.0F; ///< Staged isolated UF volume goal in mL. // ********** private function prototypes ********** static void resetAllTreatmentParameters( void ); +static void resetIsolatedUFSettingsPending( void ); static BOOL isTreatmentParamNotApplicable( TREATMENT_PARAM_T param ); static BOOL isNotApplicableTreatmentParamZero( TREATMENT_PARAM_T param ); static BOOL checkTreatmentParamsInRange( U32 *reasons ); static BOOL checkTreatmentParamsDependencies( U32 *reasons ); static BOOL checkUFDependencies( U32 *reasons ); +static REQUEST_REJECT_REASON_CODE_T validateIsolatedUFSettings( U32 durationMin, F32 volumeGoalMl ); +static U32 calculateIsolatedUFMaxVolumeMl( U32 durationMin ); +static BOOL isPostTreatmentIsolatedUFRequest( void ); static void extractTreatmentParamsFromPayload( TREATMENT_PARAMS_DATA_RESPONSE_PAYLOAD_T payload ); //static void checkPressureParamsRange( TREATMENT_PARAMS_DATA_RESPONSE_PAYLOAD_T* txParams ); static void sendTreatmentParamsResponse( BOOL rejected, U32 *reasons ); @@ -240,10 +247,27 @@ origTreatmentParams.venousPressureLimitAsymmetric_mmHg = 0; origTreatmentParams.tmpLimitWindow_mmHg = 0; origTreatmentParams.uFVolume_L = 0.0F; + + resetIsolatedUFSettingsPending(); } /*********************************************************************//** * @brief + * The resetIsolatedUFSettingsPending function clears staged isolated UF + * settings awaiting user confirmation. + * @details \b Inputs: none + * @details \b Outputs: isolatedUFSettingsPending cleared + * @return none + *************************************************************************/ +static void resetIsolatedUFSettingsPending( void ) +{ + isolatedUFSettingsPending = FALSE; + isolatedUFDurationPendingMin = 0; + isolatedUFVolumePendingMl = 0.0F; +} + +/*********************************************************************//** + * @brief * The setTreatmentParameterU32 function sets a given unsigned integer * treatment parameter to a given value. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given param is invalid @@ -497,15 +521,15 @@ if ( TRUE == treatParamsConfirmed ) { U32 setTxDuration = stagedParams[ TREATMENT_PARAM_TREATMENT_DURATION ].uInt; - TREATMENT_PARAM_RANGE_BROADCAST_PAYLOAD_T payload; + TREATMENT_PARAM_BROADCAST_PAYLOAD_T payload; payload.minTreatmentTime = treatmentParameters[ TREATMENT_PARAM_TREATMENT_DURATION ].minimum.uInt; payload.maxTreatmentTime = treatmentParameters[ TREATMENT_PARAM_TREATMENT_DURATION ].maximum.uInt; payload.minUFVolume = 0.0F; payload.maxUFVolume = MIN( (F32)setTxDuration * MAX_UF_RATE_ML_MIN, (F32)MAX_UF_VOLUME_ML ); payload.minDialRate = treatmentParameters[ TREATMENT_PARAM_DIALYSATE_FLOW ].minimum.uInt; payload.maxDialRate = treatmentParameters[ TREATMENT_PARAM_DIALYSATE_FLOW ].maximum.uInt; - sendMessage( MSG_ID_TD_TREATMENT_PARAM_RANGES, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&payload), sizeof( TREATMENT_PARAM_RANGE_BROADCAST_PAYLOAD_T ) ); + sendMessage( MSG_ID_TD_TREATMENT_PARAM_RANGES, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&payload), sizeof( TREATMENT_PARAM_BROADCAST_PAYLOAD_T ) ); } result = ( TRUE == paramsAreInvalid ? FALSE : TRUE ); @@ -537,30 +561,27 @@ result = ( ( ( param == TREATMENT_PARAM_HDF_DILUTION ) || ( param == TREATMENT_PARAM_SUBST_FLUID_VOLUME ) ) ? TRUE : FALSE ); break; - // TODO uncomment in phase 3 when ISO UF feature is required -// case TREATMENT_MODALITY_ISOLATED_UF: -// switch ( param ) -// { -// // Treatment parameters that are not applicable for isolated UF -// case TREATMENT_PARAM_HDF_DILUTION: -// case TREATMENT_PARAM_SUBST_FLUID_VOLUME: -// case TREATMENT_PARAM_DIALYSATE_FLOW: -// case TREATMENT_PARAM_DIALYSATE_TEMPERATURE: -// case TREATMENT_PARAM_ACID_CONCENTRATE: -// case TREATMENT_PARAM_ACID_CONCENTRATE_CONV_FACTOR: -// case TREATMENT_PARAM_SODIUM: -// case TREATMENT_PARAM_BICARBONATE: -// case TREATMENT_PARAM_DRY_BICARB_CART_SIZE: -// result = TRUE; -// break; -// -// // Treatment parameters applicable for isolated UF -// default: -// result = FALSE; -// break; -// } -// break; + case TREATMENT_MODALITY_ISOLATED_UF: + switch ( param ) + { + case TREATMENT_PARAM_HDF_DILUTION: + case TREATMENT_PARAM_SUBST_FLUID_VOLUME: + case TREATMENT_PARAM_DIALYSATE_FLOW: + case TREATMENT_PARAM_DIALYSATE_TEMPERATURE: + case TREATMENT_PARAM_ACID_CONCENTRATE: + case TREATMENT_PARAM_ACID_CONCENTRATE_CONV_FACTOR: + case TREATMENT_PARAM_SODIUM: + case TREATMENT_PARAM_BICARBONATE: + case TREATMENT_PARAM_DRY_BICARB_CART_SIZE: + result = TRUE; + break; + default: + result = FALSE; + break; + } + break; + default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_TREATMENT_MODALITY, (U32)txType ) break; @@ -819,6 +840,285 @@ /*********************************************************************//** * @brief + * The calculateIsolatedUFRateMlMin function calculates isolated UF rate. + * @details \b Inputs: durationMin, volumeGoalMl + * @details \b Outputs: none + * @param durationMin Isolated UF duration in minutes. + * @param volumeGoalMl Isolated UF volume goal in mL. + * @return UF rate in mL/min. + *************************************************************************/ +F32 calculateIsolatedUFRateMlMin( U32 durationMin, F32 volumeGoalMl ) +{ + F32 ufRate = 0.0F; + + if ( durationMin > 0U ) + { + ufRate = volumeGoalMl / (F32)durationMin; + } + + return ufRate; +} + +/*********************************************************************//** + * @brief + * The isPostTreatmentIsolatedUFRequest function determines whether isolated UF + * is being requested after prescribed HD/HDF treatment time has elapsed. + * @details \b Inputs: getTreatmentState, isTreatmentCompleted + * @details \b Outputs: none + * @return TRUE if post-treatment isolated UF entry applies, FALSE otherwise. + *************************************************************************/ +static BOOL isPostTreatmentIsolatedUFRequest( void ) +{ + BOOL result = FALSE; + + if ( ( TREATMENT_END_STATE == getTreatmentState() ) && + ( TRUE == isTreatmentCompleted() ) && + ( TREATMENT_MODALITY_ISOLATED_UF != getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_MODALITY ) ) ) + { + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The validateIsolatedUFSettings function validates isolated UF duration, + * volume goal, and derived rate before UI confirmation. + * @details \b Inputs: durationMin, volumeGoalMl + * @details \b Outputs: none + * @param durationMin Isolated UF duration in minutes. + * @param volumeGoalMl Isolated UF volume goal in mL. + * @return Rejection reason code, or REQUEST_REJECT_REASON_NONE if valid. + *************************************************************************/ +static REQUEST_REJECT_REASON_CODE_T validateIsolatedUFSettings( U32 durationMin, F32 volumeGoalMl ) +{ + REQUEST_REJECT_REASON_CODE_T reason = REQUEST_REJECT_REASON_NONE; + F32 volumeGoalL = volumeGoalMl / (F32)ML_PER_LITER; + F32 ufRateMlMin = calculateIsolatedUFRateMlMin( durationMin, volumeGoalMl ); + U32 elapsedTreatmentMin = getActualTreatmentTimeSecs() / SEC_PER_MIN; + BOOL postTreatmentRequest = isPostTreatmentIsolatedUFRequest(); + BOOL validTreatmentState = FALSE; + + if ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) + { + validTreatmentState = TRUE; + } + else if ( TRUE == postTreatmentRequest ) + { + validTreatmentState = TRUE; + } + + if ( MODE_TREA != getCurrentOperationMode() ) + { + reason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; + } + else if ( FALSE == validTreatmentState ) + { + reason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + } + else if ( ( durationMin < getU32TreatmentParamLowerRangeLimit( TREATMENT_PARAM_TREATMENT_DURATION ) ) || + ( durationMin > getU32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_TREATMENT_DURATION ) ) ) + { + reason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; + } + else if ( ( FALSE == postTreatmentRequest ) && ( durationMin <= elapsedTreatmentMin ) ) + { + reason = REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_CURRENT; + } + else if ( ( volumeGoalL < getF32TreatmentParamLowerRangeLimit( TREATMENT_PARAM_UF_VOLUME ) ) || + ( volumeGoalL > getF32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_UF_VOLUME ) ) ) + { + reason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; + } + else if ( ( ufRateMlMin < MIN_UF_RATE_ML_MIN ) || ( ufRateMlMin > MAX_UF_RATE_ML_MIN ) ) + { + reason = REQUEST_REJECT_REASON_UF_RATE_OUT_OF_RANGE; + } + + return reason; +} + +/*********************************************************************//** + * @brief + * The calculateIsolatedUFMaxVolumeMl function calculates the maximum isolated + * UF volume for a given duration based on max UF rate and configured UF max. + * @details \b Inputs: durationMin + * @details \b Outputs: none + * @param durationMin Isolated UF duration in minutes. + * @return Maximum isolated UF volume in mL. + *************************************************************************/ +static U32 calculateIsolatedUFMaxVolumeMl( U32 durationMin ) +{ + F32 maxVolumeMl = MIN( (F32)durationMin * MAX_UF_RATE_ML_MIN, (F32)MAX_UF_VOLUME_ML ); + + return (U32)maxVolumeMl; +} + +/*********************************************************************//** + * @brief + * The handleIsolatedUFDurationChangeRequest function handles a UI isolated UF + * duration change request. + * @details \b Message \b Sent: MSG_ID_TD_ISOLATED_UF_DURATION_CHANGE_RESPONSE + * @details \b Inputs: message + * @details \b Outputs: isolatedUFDurationPendingMin + * @param message Duration change request message from UI. + * @return TRUE if the request is valid and staged, FALSE otherwise. + *************************************************************************/ +BOOL handleIsolatedUFDurationChangeRequest( MESSAGE_T *message ) +{ + BOOL accepted = FALSE; + REQUEST_REJECT_REASON_CODE_T reason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + ISOLATED_UF_DURATION_CHANGE_RESPONSE_PAYLOAD_T response; + U32 durationMin = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); + F32 volumeGoalMl = ( TRUE == isolatedUFSettingsPending ? isolatedUFVolumePendingMl : + getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) * (F32)ML_PER_LITER ); + + if ( sizeof( ISOLATED_UF_DURATION_CHANGE_REQUEST_PAYLOAD_T ) == message->hdr.payloadLen ) + { + ISOLATED_UF_DURATION_CHANGE_REQUEST_PAYLOAD_T request; + + memcpy( &request, message->payload, sizeof( ISOLATED_UF_DURATION_CHANGE_REQUEST_PAYLOAD_T ) ); + durationMin = request.mDuration; + reason = validateIsolatedUFSettings( durationMin, volumeGoalMl ); + + if ( REQUEST_REJECT_REASON_NONE == reason ) + { + isolatedUFSettingsPending = TRUE; + isolatedUFDurationPendingMin = durationMin; + isolatedUFVolumePendingMl = volumeGoalMl; + accepted = TRUE; + } + } + + response.mAccepted = (U32)accepted; + response.mReason = (U32)reason; + response.mVolumeMax = calculateIsolatedUFMaxVolumeMl( durationMin ); + sendMessage( MSG_ID_TD_ISOLATED_UF_DURATION_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&response), sizeof( ISOLATED_UF_DURATION_CHANGE_RESPONSE_PAYLOAD_T ) ); + + return accepted; +} + +/*********************************************************************//** + * @brief + * The handleIsolatedUFVolumeGoalChangeRequest function handles a UI isolated + * UF volume goal change request. + * @details \b Message \b Sent: MSG_ID_TD_ISOLATED_UF_VOLUME_GOAL_CHANGE_RESPONSE + * @details \b Inputs: message + * @details \b Outputs: isolatedUFVolumePendingMl + * @param message Volume goal change request message from UI. + * @return TRUE if the request is valid and staged, FALSE otherwise. + *************************************************************************/ +BOOL handleIsolatedUFVolumeGoalChangeRequest( MESSAGE_T *message ) +{ + BOOL accepted = FALSE; + REQUEST_REJECT_REASON_CODE_T reason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + ISOLATED_UF_VOLUME_GOAL_CHANGE_RESPONSE_PAYLOAD_T response; + U32 durationMin = ( TRUE == isolatedUFSettingsPending ? isolatedUFDurationPendingMin : + getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ) ); + F32 volumeGoalMl = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) * (F32)ML_PER_LITER; + + if ( sizeof( ISOLATED_UF_VOLUME_GOAL_CHANGE_REQUEST_PAYLOAD_T ) == message->hdr.payloadLen ) + { + ISOLATED_UF_VOLUME_GOAL_CHANGE_REQUEST_PAYLOAD_T request; + + memcpy( &request, message->payload, sizeof( ISOLATED_UF_VOLUME_GOAL_CHANGE_REQUEST_PAYLOAD_T ) ); + durationMin = request.mDuration; + volumeGoalMl = (F32)request.mVolume; + reason = validateIsolatedUFSettings( durationMin, volumeGoalMl ); + + if ( REQUEST_REJECT_REASON_NONE == reason ) + { + isolatedUFSettingsPending = TRUE; + isolatedUFDurationPendingMin = durationMin; + isolatedUFVolumePendingMl = volumeGoalMl; + accepted = TRUE; + } + } + + response.mAccepted = (U32)accepted; + response.mReason = (U32)reason; + response.mVolume = (U32)volumeGoalMl; + response.mDuration = durationMin; + response.mRate = (U32)calculateIsolatedUFRateMlMin( durationMin, volumeGoalMl ); + sendMessage( MSG_ID_TD_ISOLATED_UF_VOLUME_GOAL_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&response), sizeof( ISOLATED_UF_VOLUME_GOAL_CHANGE_RESPONSE_PAYLOAD_T ) ); + + return accepted; +} + +/*********************************************************************//** + * @brief + * The handleIsolatedUFConfirmRequest function handles UI confirmation of + * staged isolated UF settings. + * @details \b Message \b Sent: MSG_ID_TD_ISOLATED_UF_CONFIRM_RESPONSE + * @details \b Inputs: isolatedUFSettingsPending + * @details \b Outputs: treatment parameters updated if confirmed + * @param message Confirmation request message from UI. + * @return TRUE if confirmation is accepted, FALSE otherwise. + *************************************************************************/ +BOOL handleIsolatedUFConfirmRequest( MESSAGE_T *message ) +{ + BOOL accepted = FALSE; + REQUEST_REJECT_REASON_CODE_T reason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + BOOL confirmed = TRUE; + ISOLATED_UF_CONFIRM_RESPONSE_PAYLOAD_T response; + + if ( sizeof( ISOLATED_UF_CONFIRM_REQUEST_PAYLOAD_T ) == message->hdr.payloadLen ) + { + ISOLATED_UF_CONFIRM_REQUEST_PAYLOAD_T request; + + memcpy( &request, message->payload, sizeof( ISOLATED_UF_CONFIRM_REQUEST_PAYLOAD_T ) ); + confirmed = ( request.mConfirmed != 0U ? TRUE : FALSE ); + } + + if ( ( 0U == message->hdr.payloadLen ) || ( sizeof( ISOLATED_UF_CONFIRM_REQUEST_PAYLOAD_T ) == message->hdr.payloadLen ) ) + { + if ( TRUE == confirmed ) + { + if ( TRUE == isolatedUFSettingsPending ) + { + reason = validateIsolatedUFSettings( isolatedUFDurationPendingMin, isolatedUFVolumePendingMl ); + + if ( REQUEST_REJECT_REASON_NONE == reason ) + { + BOOL activeUpdated = setIsolatedUFSettings( isolatedUFDurationPendingMin, isolatedUFVolumePendingMl ); + + if ( TRUE == activeUpdated ) + { + signalStartIsolatedUF( ( TRUE == isPostTreatmentIsolatedUFRequest() ? FALSE : TRUE ) ); + accepted = TRUE; + resetIsolatedUFSettingsPending(); + } + else + { + reason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + } + } + } + else + { + accepted = TRUE; + reason = REQUEST_REJECT_REASON_NONE; + resetIsolatedUFSettingsPending(); + + // Post-treatment: UI decline (0x9F mConfirmed=0) is the exit to POST until full END workflow exists. + if ( TRUE == isPostTreatmentIsolatedUFRequest() ) + { + signalEndTreatment(); + } + } + } + + response.mAccepted = (U32)accepted; + response.mReason = (U32)reason; + sendMessage( MSG_ID_TD_ISOLATED_UF_CONFIRM_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&response), sizeof( ISOLATED_UF_CONFIRM_RESPONSE_PAYLOAD_T ) ); + + return accepted; +} + +/*********************************************************************//** + * @brief * The resetTreatmentParameters function resets the Treatment Parameters * session flags and parameter values. * @details Inputs: none