Index: firmware/App/Services/TxParams.c =================================================================== diff -u -rda59fa4a98dbc11c37677e92a66aa940d251678f -r714fadfb7981731452bda81f53053543bf09392f --- firmware/App/Services/TxParams.c (.../TxParams.c) (revision da59fa4a98dbc11c37677e92a66aa940d251678f) +++ firmware/App/Services/TxParams.c (.../TxParams.c) (revision 714fadfb7981731452bda81f53053543bf09392f) @@ -7,19 +7,21 @@ * * @file TxParams.c * -* @author (last) Raghu Kallala -* @date (last) 01-May-2026 +* @author (last) Vijay Pamula +* @date (last) 07-Jul-2026 * * @author (original) Varshini Nagabooshanam * @date (original) 02-Dec-2025 * ***************************************************************************/ +#include // Used for fabs() and roundf() functions for floating-point comparisons and rounding. #include "AirPump.h" #include "AirTrap.h" #include "BloodFlow.h" #include "Buttons.h" #include "Messaging.h" +#include "ModeStandby.h" #include "ModeTreatment.h" #include "ModePreTreat.h" #include "OperationModes.h" @@ -105,6 +107,7 @@ // ********** private data ********** +/// Treatment parameter properties including data type, range limits and default values. static const TREATMENT_PARAMS_PROPERTIES_T TREAT_PARAMS_PROPERTIES[ NUM_OF_TREATMENT_PARAMS ] = { // type min max default { CRITICAL_DATA_TYPE_U32, {.uInt=0}, {.uInt=2}, {.uInt=0} }, // TREATMENT_PARAM_TREATMENT_MODALITY @@ -133,13 +136,14 @@ { CRITICAL_DATA_TYPE_F32, {.sFlt=0.0}, {.sFlt=8.0}, {.sFlt=0.0} }, // TREATMENT_PARAM_UF_VOLUME }; +/// System configuration treatment parameter properties including data type, range limits and default values. static const TREATMENT_PARAMS_PROPERTIES_T SYS_CONFIG_TREAT_PARAMS_PROPERTIES[ NUM_OF_SYS_CONFIG_TREATMENT_PARAMS ] = { // type min max default - { CRITICAL_DATA_TYPE_U32, {.uInt=120}, {.uInt=200}, {.uInt=120} }, // TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW - { CRITICAL_DATA_TYPE_U32, {.uInt=100}, {.uInt=200}, {.uInt=100} }, // TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW - { CRITICAL_DATA_TYPE_U32, {.uInt=20}, {.uInt=35}, {.uInt=20} }, // TREATMENT_PARAM_VEN_PRES_LIMIT_ASYMMETRIC - { CRITICAL_DATA_TYPE_U32, {.uInt=40}, {.uInt=100}, {.uInt=40} }, // TREATMENT_PARAM_TMP_PRES_LIMIT_WINDOW + { CRITICAL_DATA_TYPE_U32, {.uInt=50}, {.uInt=100}, {.uInt=50} }, // TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW + { CRITICAL_DATA_TYPE_U32, {.uInt=50}, {.uInt=100}, {.uInt=50} }, // TREATMENT_PARAM_VEN_PRES_HIGH_LIMIT_WINDOW + { CRITICAL_DATA_TYPE_U32, {.uInt=20}, {.uInt=35}, {.uInt=35} }, // TREATMENT_PARAM_VEN_PRES_LOW_LIMIT_WINDOW + { CRITICAL_DATA_TYPE_U32, {.uInt=20}, {.uInt=50}, {.uInt=20} }, // TREATMENT_PARAM_TMP_PRES_LIMIT_WINDOW { CRITICAL_DATA_TYPE_U32, {.uInt=100}, {.uInt=500}, {.uInt=200} }, // TREATMENT_PARAM_RINSEBACK_FLOW_RATE { CRITICAL_DATA_TYPE_U32, {.uInt=300}, {.uInt=1000}, {.uInt=300} }, // TREATMENT_PARAM_PRIME_DISCARD_VOLUME { CRITICAL_DATA_TYPE_U32, {.uInt=70}, {.uInt=140}, {.uInt=90} }, // TREATMENT_PARAM_SYSTOLIC_BP_LOW_ALARM_LIMIT @@ -181,18 +185,40 @@ 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. +static BOOL validTreatmentDurationReceived; ///< Flag indicates valid treatment duration was validated. +static U32 validatedTreatmentDuration_min; ///< Last validated treatment duration in minutes. +static U32 validatedHeparinDeliveryDuration_min; ///< Last validated Heparin delivery duration in minutes. +static F32 validatedUFVolumeGoalL; ///< Last validated UF volume goal in L. +static F32 validatedUFRateLhr; ///< Last validated UF rate in L/hr. +static BOOL validUFVolumeReceived; ///< Flag indicates valid UF volume was validated. // ********** 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 REQUEST_REJECT_REASON_CODE_T validateIsolatedUFDuration( U32 durationMin ); +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 ); +static BOOL isPressureLimitValueValid( U32 value, U32 minValue, U32 maxValue ); +static BOOL isArterialPressureLimitWindowValid( U32 value ); +static BOOL isVenousPressureLimitWindowValid( U32 value ); +static BOOL isVenousAsymmetricPressureLimitWindowValid( U32 value ); +static BOOL isTmpPressureLimitWindowValid( U32 value ); +static BOOL isVitalsAlarmLimitValueValid(U32 value, SYS_CONFIG_TREATMENT_PARAM_T param ); +static BOOL isVitalsMeasurementIntervalValid( U32 value, TREATMENT_PARAM_T param ); + //static void getInstitutionalRecordEdgeValue( TREATMENT_PARAM_T param, CRITICAL_DATAS_T* value, BOOL isMin ); /*********************************************************************//** @@ -240,10 +266,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 @@ -392,7 +435,7 @@ U32 result = 1; // Validate parameter - if ( param <= NUM_OF_SYS_CONFIG_TREATMENT_PARAMS ) + if ( param < NUM_OF_SYS_CONFIG_TREATMENT_PARAMS ) { CRITICAL_DATAS_T data = getCriticalData( &sysConfigTreatmentParameters[ param ] ); @@ -422,6 +465,36 @@ /*********************************************************************//** * @brief + * The setSysConfigTreatmentParameterU32 function sets a given unsigned + * integer system configured treatment parameter. + * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid parameter ID is given. + * @details \b Inputs: none. + * @details \b Outputs: sysConfigTreatmentParameters[]. + * @param param ID of system configured treatment parameter to set. + * @param value unsigned integer value to set. + * @return TRUE if parameter updated successfully, FALSE otherwise. + *************************************************************************/ +BOOL setSysConfigTreatmentParameterU32( SYS_CONFIG_TREATMENT_PARAM_T param, U32 value ) +{ + BOOL result = FALSE; + + if ( param < NUM_OF_SYS_CONFIG_TREATMENT_PARAMS ) + { + CRITICAL_DATAS_T data = getCriticalData( &sysConfigTreatmentParameters[ param ] ); + + data.uInt = value; + result = setCriticalData( &sysConfigTreatmentParameters[ param ], data ); + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_PARAMS_INVALID_SET_U32_PARAM, (U32)param ) + } + + return result; +} + +/*********************************************************************//** + * @brief * The validateAndSetTreatmentParameters function validates received * treatment parameters. * @details \b Message \b Sent: MSG_ID_TD_RESP_TREATMENT_PARAMS_TO_VALIDATE @@ -460,7 +533,6 @@ // Extract treatment parameters from given payload to staging array so we can more easily work with them extractTreatmentParamsFromPayload( params ); - // Range check each treatment parameter paramsAreInRange = checkTreatmentParamsInRange( &rejReasons[0] ); @@ -497,15 +569,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 ); @@ -529,19 +601,17 @@ switch ( txType ) { - case TREATMENT_MODALITY_HDF: + case TREATMENT_MODALITY_HDF_ONLINE_FLUID: result = FALSE; break; - case TREATMENT_MODALITY_HD: + case TREATMENT_MODALITY_HD_SALINE_FLUID: 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: +// case TREATMENT_MODALITY_ISOLATED_UF: // TODO uncomment in phase 3 when ISO UF is a standalone modality // 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: @@ -554,7 +624,6 @@ // result = TRUE; // break; // -// // Treatment parameters applicable for isolated UF // default: // result = FALSE; // break; @@ -692,6 +761,7 @@ F32 hepDR = stagedParams[ TREATMENT_PARAM_HEPARIN_DELIVERY_RATE ].sFlt; F32 hepBV = stagedParams[ TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ].sFlt; F32 txVol = hepBV + ( hepDR * ( hepST / (F32)SEC_PER_MIN ) ) + SYRINGE_PUMP_PRIME_VOLUME_ML + SYRINGE_PUMP_FILL_VOLUME_OFFSET_ML; + TREATMENT_TYPE_T modality = (TREATMENT_TYPE_T)stagedParams[ TREATMENT_PARAM_TREATMENT_MODALITY ].uInt; // Variable to validate acid conversion factor consistency U32 acidType = stagedParams[ TREATMENT_PARAM_ACID_CONCENTRATE ].uInt; @@ -722,6 +792,13 @@ result = FALSE; } + // Verify Rx entered modality with the treatment initiation modality + if ( modality != getModality() ) + { + reasons[ TREATMENT_PARAM_TREATMENT_MODALITY ] = REQUEST_REJECT_REASON_MODALITY_MISMATCH; + result = FALSE; + } + return result; } @@ -819,17 +896,340 @@ /*********************************************************************//** * @brief - * The resetTreatmentParameters function resets the Treatment Parameters +<<<<<<< HEAD + * 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 + * @details \b Outputs: none + * @return TRUE if post-treatment isolated UF entry applies, FALSE otherwise. + *************************************************************************/ +static BOOL isPostTreatmentIsolatedUFRequest( void ) +{ + BOOL state = FALSE; + + state = ( TREATMENT_END_STATE == getTreatmentState() ) ? TRUE : FALSE; + + return state; +} + +/*********************************************************************//** + * @brief + * The validateIsolatedUFDuration function validates isolated UF duration + * before UI confirmation. + * @details \b Inputs: durationMin + * @details \b Outputs: none + * @param durationMin Isolated UF duration in minutes. + * @return Rejection reason code, or REQUEST_REJECT_REASON_NONE if valid. + *************************************************************************/ +static REQUEST_REJECT_REASON_CODE_T validateIsolatedUFDuration( U32 durationMin ) +{ + REQUEST_REJECT_REASON_CODE_T reason = REQUEST_REJECT_REASON_NONE; + TREATMENT_STATE_T treatmentState = getTreatmentState(); + U32 prescribedDurationMin = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); + U32 maxTreatmentDurationMin = getU32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_TREATMENT_DURATION ); + U32 cumulativeIsoUFTimeMin = getCumulativeIsolatedUFTimeMin(); + U32 totalDurationMin = prescribedDurationMin + cumulativeIsoUFTimeMin + durationMin; + + if ( MODE_TREA != getCurrentOperationMode() ) + { + reason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; + } + else if ( ( TREATMENT_DIALYSIS_STATE != treatmentState ) && ( TREATMENT_ISO_UF_STATE != treatmentState ) && + ( FALSE == isPostTreatmentIsolatedUFRequest() ) ) + { + reason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + } + else if ( durationMin < MIN_ISOLATED_UF_DURATION_MIN ) + { + reason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; + } + else if ( durationMin > maxTreatmentDurationMin ) + { + reason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; + } + else if ( totalDurationMin > maxTreatmentDurationMin ) + { + reason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; + } + + return reason; +} + +/*********************************************************************//** + * @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 = validateIsolatedUFDuration( durationMin ); + F32 ufRateMlMin = calculateIsolatedUFRateMlMin( durationMin, volumeGoalMl ); + F32 maxVolumeMl = (F32)calculateIsolatedUFMaxVolumeMl( durationMin ); + + if ( REQUEST_REJECT_REASON_NONE == reason ) + { + if ( volumeGoalMl < MIN_ISOLATED_UF_VOLUME_ML ) + { + reason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; + } + else if ( volumeGoalMl > maxVolumeMl ) + { + 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 maxRateVolumeMl = (F32)durationMin * MAX_UF_RATE_ML_MIN; + F32 cumulativeIsoUFVolumeMl = getCumulativeIsolatedUFVolumeMl(); + F32 prescriptionUFVolumeMl = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) * (F32)ML_PER_LITER; + F32 maxSessionVolumeMl = (F32)MAX_UF_VOLUME_ML - cumulativeIsoUFVolumeMl - prescriptionUFVolumeMl; + F32 maxVolumeMl = MIN( maxRateVolumeMl, maxSessionVolumeMl ); + + if ( maxVolumeMl < 0.0F ) + { + maxVolumeMl = 0.0F; + } + + 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: none + * @param message Duration change request message from UI. + * @return TRUE if the duration is valid, 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 = 0U; + + 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 = validateIsolatedUFDuration( durationMin ); + + if ( REQUEST_REJECT_REASON_NONE == reason ) + { + accepted = TRUE; + } + } + else + { + reason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + 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 settings change request containing both duration and volume goal. + * @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 = 0U; + F32 volumeGoalMl = 0.0F; + + 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; + } + } + else + { + reason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + 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 ) + { + if ( TREATMENT_ISO_UF_STATE != getTreatmentState() ) + { + 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(); + } + } + } + else + { + reason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + 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 initTreatmentParameters function initializes the Treatment Parameters * session flags and parameter values. - * @details Inputs: none - * @details Outputs: validTreatParamsReceived, treatParamsConfirmed, - * treatParamsRejected cleared and parameters reset. + * @details \b Inputs: none. + * @details \b Outputs: validTreatParamsReceived, treatParamsConfirmed, + * validTreatmentDurationReceived, validatedTreatmentDuration_min, + * validatedUFVolumeGoalL, validatedUFRateLhr, + * validUFVolumeReceived, validatedHeparinDeliveryDuration_min, + * treatment parameters reset. * @return none *************************************************************************/ -void resetTreatmentParameters( void ) +void initTreatmentParameters( void ) { - validTreatParamsReceived = FALSE; - treatParamsConfirmed = FALSE; + validTreatParamsReceived = FALSE; + treatParamsConfirmed = FALSE; + validTreatmentDurationReceived = FALSE; + validatedTreatmentDuration_min = 0U; + validatedUFVolumeGoalL = 0.0F; + validatedUFRateLhr = 0.0F; + validUFVolumeReceived = FALSE; + validatedHeparinDeliveryDuration_min = 0U; resetAllTreatmentParameters(); } @@ -955,18 +1355,30 @@ * The isTreatmentParamInRange function determines whether a given treatment * parameter is in range. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given param is invalid - * @details \b Inputs: treatParamsRanges[], hdInstitutionalRecord - * @details \b Outputs: none + * @details \b Inputs: TREAT_PARAMS_PROPERTIES[]. + * @details \b Outputs: none. * @param param ID of parameter to check range for * @param value value of parameter to check range for * @return TRUE if given treatment parameter is in range, FALSE if not *************************************************************************/ BOOL isTreatmentParamInRange( TREATMENT_PARAM_T param, CRITICAL_DATAS_T value ) { - BOOL result = TRUE; // TODO FALSE; + BOOL result = FALSE; if ( param < NUM_OF_TREATMENT_PARAMS ) { + if ( CRITICAL_DATA_TYPE_U32 == TREAT_PARAMS_PROPERTIES[ param ].dataType ) + { + result = ( ( value.uInt >= TREAT_PARAMS_PROPERTIES[ param ].min.uInt ) && + ( value.uInt <= TREAT_PARAMS_PROPERTIES[ param ].max.uInt ) ? TRUE : FALSE ); + } + else + { + result = ( ( value.sFlt >= TREAT_PARAMS_PROPERTIES[ param ].min.sFlt ) && + ( value.sFlt <= TREAT_PARAMS_PROPERTIES[ param ].max.sFlt ) ? TRUE : FALSE ); + } + } + // switch( param ) // { // case TREATMENT_PARAM_TREATMENT_MODALITY: @@ -1096,7 +1508,7 @@ // } //#endif // } - } + else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_PARAMS_INVALID_PARAM, (U32)param ) @@ -1405,6 +1817,981 @@ /************************************************************************* + * In-treatment treatment parameter edit function + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The validateAndSetTreatmentDuration function handles the UI treatment + * duration validation request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_DURATION_VALIDATE_RESPONSE including acceptance status, + * rejection reason, requested duration, UF volume goal (L), + * calculated UF rate (L/hr) and heparin delivery duration (minutes). + * @details \b Inputs: none. + * @details \b Outputs: validTreatmentDurationReceived, validatedTreatmentDuration_min, + * validatedUFRateLhr, validatedHeparinDeliveryDuration_min. + * @param message set message from UI which includes the requested treatment duration in minutes. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL validateAndSetTreatmentDuration( MESSAGE_T *message ) +{ + BOOL result = FALSE; + TREATMENT_DURATION_VALIDATE_REQUEST_PAYLOAD_T request; + TREATMENT_DURATION_VALIDATE_RESPONSE_PAYLOAD_T response; + + F32 presUFVolumeMl = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) * (F32)ML_PER_LITER; + F32 elapsedTreatmentTimeMin = (F32)getActualTreatmentTimeSecs() / (F32)SEC_PER_MIN; + F32 measuredUFVolumeMl = getUltrafiltrationVolumeDrawn() * (F32)ML_PER_LITER; + F32 remainingUFVolumeMl = presUFVolumeMl - measuredUFVolumeMl; + + U32 minDuration = getU32TreatmentParamLowerRangeLimit( TREATMENT_PARAM_TREATMENT_DURATION ); + U32 maxDuration = getU32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_TREATMENT_DURATION ); + + U32 currentTreatmentDuration_min = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); + U32 currentHeparinDuration_min = getTreatmentParameterU32( TREATMENT_PARAM_HEPARIN_DELIVERY_DURATION ); + U32 treatmentHeparinDiff_min = 0U; + + response.accepted = FALSE; + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.duration = 0U; + response.ufVolumeGoal = 0.0F; + response.ufRate = 0.0F; + response.heparinDuration = 0U; + + // Clear previously validated values. + validTreatmentDurationReceived = FALSE; + validatedTreatmentDuration_min = 0U; + validatedUFRateLhr = 0.0F; + validatedHeparinDeliveryDuration_min = 0U; + + if ( message->hdr.payloadLen == sizeof( TREATMENT_DURATION_VALIDATE_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( TREATMENT_DURATION_VALIDATE_REQUEST_PAYLOAD_T ) ); + + response.duration = request.duration; + + if ( ( request.duration >= minDuration ) && ( request.duration <= maxDuration ) ) + { + if ( (F32)request.duration > elapsedTreatmentTimeMin ) + { + if ( remainingUFVolumeMl < 0.0F ) + { + remainingUFVolumeMl = 0.0F; + } + + F32 remainingTreatmentTimeMin = (F32)request.duration - elapsedTreatmentTimeMin; + + // Calculate new UF rate (mL/min). + F32 newUfRateMlMin = remainingUFVolumeMl / remainingTreatmentTimeMin; + + // Convert UF values for UI response. + response.ufVolumeGoal = presUFVolumeMl / (F32)ML_PER_LITER; + response.ufRate = ( newUfRateMlMin * (F32)MIN_PER_HOUR ) / (F32)ML_PER_LITER; + + if ( newUfRateMlMin <= MAX_UF_RATE_ML_MIN ) + { + // Adjust Heparin delivery duration to maintain the previous difference from treatment duration. + if ( currentTreatmentDuration_min > currentHeparinDuration_min ) + { + treatmentHeparinDiff_min = currentTreatmentDuration_min - currentHeparinDuration_min; + } + + if ( response.duration > treatmentHeparinDiff_min ) + { + validatedHeparinDeliveryDuration_min = response.duration - treatmentHeparinDiff_min; + } + else + { + validatedHeparinDeliveryDuration_min = 0U; + } + + response.heparinDuration = validatedHeparinDeliveryDuration_min; + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + + // Store validated values for confirmation step. + validTreatmentDurationReceived = TRUE; + validatedTreatmentDuration_min = response.duration; + validatedUFRateLhr = response.ufRate; + validatedHeparinDeliveryDuration_min = response.heparinDuration; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_UF_RATE_OUT_OF_RANGE; + } + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_CURRENT; + } + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; + } + } + + result = sendMessage( MSG_ID_TD_DURATION_VALIDATE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( TREATMENT_DURATION_VALIDATE_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The validateAndSetBolusVolume function handles the UI fluid bolus volume + * change request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_BOLUS_VOLUME_CHANGE_RESPONSE including acceptance status + * and rejection reason. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param message set message from UI which includes the requested bolus volume. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL validateAndSetBolusVolume( MESSAGE_T *message ) +{ + BOOL result = FALSE; + BOLUS_VOLUME_CHANGE_REQUEST_PAYLOAD_T request; + UI_RESPONSE_PAYLOAD_T response; + U32 minBolusVolume_mL = getU32TreatmentParamLowerRangeLimit( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + U32 maxBolusVolume_mL = getU32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + + response.accepted = FALSE; + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + + if ( message->hdr.payloadLen == sizeof( BOLUS_VOLUME_CHANGE_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( BOLUS_VOLUME_CHANGE_REQUEST_PAYLOAD_T ) ); + + if ( ( request.bolusVolume >= minBolusVolume_mL ) && + ( request.bolusVolume <= maxBolusVolume_mL ) ) + { + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + + setTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME, request.bolusVolume ); + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + } + + result = sendMessage( MSG_ID_TD_BOLUS_VOLUME_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The vitalsAdjustmentHandleChangeRequest function handles the UI vitals + * settings change request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_VITALS_ADJUSTMENT_RESPONSE + * including acceptance status and per-parameter rejection reasons. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param message set message from UI which includes the requested vitals + * alarm limits and measurement interval. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL vitalsAdjustmentHandleChangeRequest( MESSAGE_T *message ) +{ + BOOL result = FALSE; + BOOL accepted = FALSE; + VITALS_ADJUSTMENT_REQUEST_PAYLOAD_T request; + VITALS_ADJUSTMENT_RESPONSE_PAYLOAD_T response; + + if ( message->hdr.payloadLen == sizeof( VITALS_ADJUSTMENT_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( VITALS_ADJUSTMENT_REQUEST_PAYLOAD_T ) ); + + response.systolicBpLowAlarmLimitRejectionReason = REQUEST_REJECT_REASON_NONE; + response.systolicBpHighAlarmLimitRejectionReason = REQUEST_REJECT_REASON_NONE; + response.heartRateLowAlarmLimitRejectionReason = REQUEST_REJECT_REASON_NONE; + response.heartRateHighAlarmLimitRejectionReason = REQUEST_REJECT_REASON_NONE; + response.vitalsMeasurementIntervalRejectionReason = REQUEST_REJECT_REASON_NONE; + + if ( FALSE == isVitalsAlarmLimitValueValid( request.systolicBpLowAlarmLimit, + TREATMENT_PARAM_SYSTOLIC_BP_LOW_ALARM_LIMIT ) ) + { + response.systolicBpLowAlarmLimitRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + if ( FALSE == isVitalsAlarmLimitValueValid( request.systolicBpHighAlarmLimit, + TREATMENT_PARAM_SYSTOLIC_BP_HIGH_ALARM_LIMIT ) ) + { + response.systolicBpHighAlarmLimitRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + if ( FALSE == isVitalsAlarmLimitValueValid( request.heartRateLowAlarmLimit, + TREATMENT_PARAM_HEART_RATE_LOW_ALARM_LIMIT ) ) + { + response.heartRateLowAlarmLimitRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + if ( FALSE == isVitalsAlarmLimitValueValid( request.heartRateHighAlarmLimit, + TREATMENT_PARAM_HEART_RATE_HIGH_ALARM_LIMIT ) ) + { + response.heartRateHighAlarmLimitRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + if ( FALSE == isVitalsMeasurementIntervalValid( request.vitalsMeasurementInterval_min, + TREATMENT_PARAM_BP_MEAS_INTERVAL ) ) + { + response.vitalsMeasurementIntervalRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + accepted = ( REQUEST_REJECT_REASON_NONE == response.systolicBpLowAlarmLimitRejectionReason ) && + ( REQUEST_REJECT_REASON_NONE == response.systolicBpHighAlarmLimitRejectionReason ) && + ( REQUEST_REJECT_REASON_NONE == response.heartRateLowAlarmLimitRejectionReason ) && + ( REQUEST_REJECT_REASON_NONE == response.heartRateHighAlarmLimitRejectionReason ) && + ( REQUEST_REJECT_REASON_NONE == response.vitalsMeasurementIntervalRejectionReason ); + + if ( TRUE == accepted ) + { + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_SYSTOLIC_BP_LOW_ALARM_LIMIT, request.systolicBpLowAlarmLimit ); + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_SYSTOLIC_BP_HIGH_ALARM_LIMIT, request.systolicBpHighAlarmLimit ); + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_HEART_RATE_LOW_ALARM_LIMIT, request.heartRateLowAlarmLimit ); + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_HEART_RATE_HIGH_ALARM_LIMIT, request.heartRateHighAlarmLimit ); + setTreatmentParameterU32( TREATMENT_PARAM_BP_MEAS_INTERVAL, request.vitalsMeasurementInterval_min ); + } + } + else + { + response.systolicBpLowAlarmLimitRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.systolicBpHighAlarmLimitRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.heartRateLowAlarmLimitRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.heartRateHighAlarmLimitRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.vitalsMeasurementIntervalRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + response.accepted = accepted; + + result = sendMessage( MSG_ID_TD_VITALS_ADJUSTMENT_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( VITALS_ADJUSTMENT_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The isVitalsAlarmLimitValueValid function validates a vitals alarm + * limit value against the configured range for the specified parameter. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param value vitals alarm limit value to validate. + * @param param vitals alarm limit treatment parameter. + * @return TRUE if value is valid, FALSE otherwise. + *************************************************************************/ +static BOOL isVitalsAlarmLimitValueValid( U32 value, SYS_CONFIG_TREATMENT_PARAM_T param ) +{ + BOOL limitValid; + + limitValid = ( ( value >= getU32SysConfigTreatmentParamLowerRangeLimit( param ) ) && + ( value <= getU32SysConfigTreatmentParamUpperRangeLimit( param ) ) ) ? TRUE : FALSE; + + return limitValid; +} + +/*********************************************************************//** + * @brief + * The isVitalsMeasurementIntervalValid function validates the vitals + * measurement interval value against the configured range. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param value vitals measurement interval value (minutes). + * @param param treatment parameter associated with the interval. + * @return TRUE if value is valid, FALSE otherwise. + *************************************************************************/ +static BOOL isVitalsMeasurementIntervalValid( U32 value, TREATMENT_PARAM_T param ) +{ + BOOL intervalValid; + + intervalValid = ( ( value >= getU32TreatmentParamLowerRangeLimit( param ) ) && + ( value <= getU32TreatmentParamUpperRangeLimit( param ) ) ) ? TRUE : FALSE; + + return intervalValid; +} + +/*********************************************************************//** + * @brief + * The pressureLimitHandleChangeRequest function handles the UI pressure + * limit change request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_PRESSURE_LIMITS_CHANGE_RESPONSE including acceptance status + * and per-parameter rejection reasons. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param message set message from UI which includes the requested pressure limit windows. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL pressureLimitHandleChangeRequest( MESSAGE_T *message ) +{ + BOOL result = FALSE; + BOOL accepted = FALSE; + + PRESSURE_LIMIT_CHANGE_REQUEST_T request; + PRESSURE_LIMITS_CHANGE_RESPONSE_PAYLOAD_T response; + + memset( &request, 0, sizeof( request ) ); + memset( &response, 0, sizeof( response ) ); + + response.arterialPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.venousPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.venousAsymmetricPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.tmpPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + + if ( message->hdr.payloadLen == sizeof( PRESSURE_LIMIT_CHANGE_REQUEST_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( PRESSURE_LIMIT_CHANGE_REQUEST_T ) ); + + response.arterialPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_NONE; + response.venousPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_NONE; + response.venousAsymmetricPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_NONE; + response.tmpPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_NONE; + + if ( FALSE == isArterialPressureLimitWindowValid( request.arterialPressureLimitWindowMMHG ) ) + { + response.arterialPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + if ( FALSE == isVenousPressureLimitWindowValid( request.venousHighPressureLimitWindowMMHG ) ) + { + response.venousPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + if ( FALSE == isVenousAsymmetricPressureLimitWindowValid( request.venousLowPressureLimitWindowMMHG ) ) + { + response.venousAsymmetricPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + if ( FALSE == isTmpPressureLimitWindowValid( request.tmpPressureLimitWindowMMHG ) ) + { + response.tmpPressureLimitWindowRejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + // Accept request only when all pressure limit values are valid. + accepted = ( REQUEST_REJECT_REASON_NONE == response.arterialPressureLimitWindowRejectionReason ) && + ( REQUEST_REJECT_REASON_NONE == response.venousPressureLimitWindowRejectionReason ) && + ( REQUEST_REJECT_REASON_NONE == response.venousAsymmetricPressureLimitWindowRejectionReason ) && + ( REQUEST_REJECT_REASON_NONE == response.tmpPressureLimitWindowRejectionReason ); + + if ( TRUE == accepted ) + { + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW, request.arterialPressureLimitWindowMMHG ); + + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_VEN_PRES_HIGH_LIMIT_WINDOW, request.venousHighPressureLimitWindowMMHG ); + + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_VEN_PRES_LOW_LIMIT_WINDOW, request.venousLowPressureLimitWindowMMHG ); + + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_TMP_PRES_LIMIT_WINDOW, request.tmpPressureLimitWindowMMHG ); + + updatePressureLimitWindows(); + } + } + + response.accepted = accepted; + + result = sendMessage( MSG_ID_TD_PRESSURE_LIMITS_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( PRESSURE_LIMITS_CHANGE_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The isPressureLimitValueValid function checks whether a pressure limit + * value is within the specified range. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param value pressure limit value to validate. + * @param minValue minimum allowable pressure limit value. + * @param maxValue maximum allowable pressure limit value. + * @return TRUE if value is valid, FALSE otherwise. + *************************************************************************/ +static BOOL isPressureLimitValueValid( U32 value, U32 minValue, U32 maxValue ) +{ + return ( ( value >= minValue ) && ( value <= maxValue ) ); +} + +/*********************************************************************//** + * @brief + * The isArterialPressureLimitWindowValid function validates the arterial + * pressure limit window value. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param value arterial pressure limit window value (mmHg). + * @return TRUE if the value is within valid range, FALSE otherwise. + *************************************************************************/ +static BOOL isArterialPressureLimitWindowValid( U32 value ) +{ + BOOL pressureValid; + + pressureValid = isPressureLimitValueValid( + value, + getU32SysConfigTreatmentParamLowerRangeLimit( TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW ), + getU32SysConfigTreatmentParamUpperRangeLimit( TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW ) ); + + return pressureValid; +} + +/*********************************************************************//** + * @brief + * The isVenousPressureLimitWindowValid function validates the venous + * pressure limit window value. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param value venous pressure limit window value (mmHg). + * @return TRUE if the value is within valid range, FALSE otherwise. + *************************************************************************/ +static BOOL isVenousPressureLimitWindowValid( U32 value ) +{ + BOOL pressureValid; + + pressureValid = isPressureLimitValueValid( + value, + getU32SysConfigTreatmentParamLowerRangeLimit( TREATMENT_PARAM_VEN_PRES_HIGH_LIMIT_WINDOW ), + getU32SysConfigTreatmentParamUpperRangeLimit( TREATMENT_PARAM_VEN_PRES_LOW_LIMIT_WINDOW ) ); + + return pressureValid; +} + +/*********************************************************************//** + * @brief + * The isVenousAsymmetricPressureLimitWindowValid function validates the + * venous asymmetric pressure limit window value. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param value venous asymmetric pressure limit window value (mmHg). + * @return TRUE if the value is within valid range, FALSE otherwise. + *************************************************************************/ +static BOOL isVenousAsymmetricPressureLimitWindowValid( U32 value ) +{ + BOOL pressureValid; + + pressureValid = isPressureLimitValueValid( + value, + getU32SysConfigTreatmentParamLowerRangeLimit( TREATMENT_PARAM_VEN_PRES_HIGH_LIMIT_WINDOW ), + getU32SysConfigTreatmentParamUpperRangeLimit( TREATMENT_PARAM_VEN_PRES_LOW_LIMIT_WINDOW ) ); + + return pressureValid; +} + +/*********************************************************************//** + * @brief + * The isTmpPressureLimitWindowValid function validates the TMP pressure + * limit window value. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param value TMP pressure limit window value (mmHg). + * @return TRUE if the value is within valid range, FALSE otherwise. + *************************************************************************/ +static BOOL isTmpPressureLimitWindowValid( U32 value ) +{ + BOOL pressureValid; + + pressureValid = isPressureLimitValueValid( + value, + getU32SysConfigTreatmentParamLowerRangeLimit( TREATMENT_PARAM_TMP_PRES_LIMIT_WINDOW ), + getU32SysConfigTreatmentParamUpperRangeLimit( TREATMENT_PARAM_TMP_PRES_LIMIT_WINDOW ) ); + + return pressureValid; +} + +/*********************************************************************//** + * @brief + * The validateAndSetUFVolume function handles the UI ultrafiltration + * volume validation request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_TREATMENT_UF_VOLUME_VALIDATE_RESPONSE including acceptance status, + * rejection reason, UF volume goal (L), and UF rate (L/hr). + * @details \b Inputs: none. + * @details \b Outputs: validUFVolumeReceived, validatedUFVolumeGoalL, validatedUFRateLhr. + * @param message set message from UI which includes the requested UF volume in L. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL validateAndSetUFVolume( MESSAGE_T *message ) +{ + BOOL result = FALSE; + UF_VOLUME_VALIDATE_REQUEST_PAYLOAD_T request; + UF_VOLUME_RESPONSE_PAYLOAD_T response; + + F32 elapsedTreatmentTimeMin = (F32)getActualTreatmentTimeSecs() / (F32)SEC_PER_MIN; + F32 measuredUFVolumeMl = getUltrafiltrationVolumeDrawn() * (F32)ML_PER_LITER; + F32 treatmentDurationMin = (F32)getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); + F32 requestedUFVolumeMl = 0.0F; + F32 requestedUFVolumeL = 0.0F; + F32 remainingUFVolumeMl = 0.0F; + F32 remainingTreatmentTimeMin = 0.0F; + F32 newUfRateMlMin = 0.0F; + F32 minUFVolumeL = getF32TreatmentParamLowerRangeLimit( TREATMENT_PARAM_UF_VOLUME ); + F32 maxUFVolumeL = getF32TreatmentParamUpperRangeLimit( TREATMENT_PARAM_UF_VOLUME ); + + response.accepted = FALSE; + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + response.ufVolumeGoal = 0.0F; + response.ufRate = 0.0F; + + // Clear previously validated values. + validUFVolumeReceived = FALSE; + validatedUFVolumeGoalL = 0.0F; + validatedUFRateLhr = 0.0F; + + if ( message->hdr.payloadLen == sizeof( UF_VOLUME_VALIDATE_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( UF_VOLUME_VALIDATE_REQUEST_PAYLOAD_T ) ); + + // Convert requested UF volume from L to mL for firmware calculations. + requestedUFVolumeL = request.ufVolume; + requestedUFVolumeMl = requestedUFVolumeL * (F32)ML_PER_LITER; + + // Send UF volume to UI in L. + response.ufVolumeGoal = requestedUFVolumeL; + + if ( ( requestedUFVolumeL >= minUFVolumeL ) && ( requestedUFVolumeL <= maxUFVolumeL ) ) + { + if ( requestedUFVolumeMl > measuredUFVolumeMl ) + { + if ( treatmentDurationMin > elapsedTreatmentTimeMin ) + { + remainingUFVolumeMl = requestedUFVolumeMl - measuredUFVolumeMl; + remainingTreatmentTimeMin = treatmentDurationMin - elapsedTreatmentTimeMin; + + // Calculate new UF rate in mL/min. + newUfRateMlMin = remainingUFVolumeMl / remainingTreatmentTimeMin; + + response.ufRate = ( newUfRateMlMin * (F32)MIN_PER_HOUR ) / (F32)ML_PER_LITER; + + if ( newUfRateMlMin <= MAX_UF_RATE_ML_MIN ) + { + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + + // Store validated values for confirmation step. + validUFVolumeReceived = TRUE; + validatedUFVolumeGoalL = response.ufVolumeGoal; + validatedUFRateLhr = response.ufRate; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_UF_RATE_OUT_OF_RANGE; + } + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_CURRENT; + } + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; + } + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; + } + } + + result = sendMessage( MSG_ID_TD_TREATMENT_UF_VOLUME_VALIDATE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( UF_VOLUME_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The validateAndSetTreatmentSetPoints function handles a UI prescription + * settings change request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_TREATMENT_SET_POINTS_CHANGE_RESPONSE including acceptance status + * and rejection reason for each prescription setting. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param message set message from UI which includes the requested prescription setting values. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL validateAndSetTreatmentSetPoints( MESSAGE_T *message ) +{ + BOOL result = FALSE; + BOOL isValid = TRUE; + CRITICAL_DATAS_T requestedValue; + TREATMENT_PRESCRIPTION_SETTINGS_EDIT_REQUEST_T request; + TREATMENT_PRESCRIPTION_SETTINGS_EDIT_RESPONSE_T response; + + memset( &requestedValue, 0, sizeof( requestedValue ) ); + memset( &request, 0, sizeof( request ) ); + memset( &response, 0, sizeof( response ) ); + + if ( message->hdr.payloadLen == sizeof( request ) ) + { + memcpy( &request, message->payload, sizeof( TREATMENT_PRESCRIPTION_SETTINGS_EDIT_REQUEST_T ) ); + + requestedValue.uInt = request.treatmentModality; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_TREATMENT_MODALITY, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_TREATMENT_MODALITY ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.uInt = request.bloodFlowRate; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_BLOOD_FLOW, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_BLOOD_FLOW ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.uInt = request.dialysateFlowRate; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_DIALYSATE_FLOW, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_DIALYSATE_FLOW ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.sFlt = request.dialysateTemperature; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_DIALYSATE_TEMPERATURE, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_DIALYSATE_TEMPERATURE ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.uInt = request.hepatitis; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_HEPATITIS_B, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_HEPATITIS_STATUS ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.uInt = request.acidConcentrate; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_ACID_CONCENTRATE, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_ACID_CONCENTRATE ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.sFlt = request.acidConcentrateConversionFactor; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_ACID_CONCENTRATE_CONV_FACTOR, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_ACID_CONVERSION_FACTOR ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.uInt = request.bicarbonateConcentrate; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_DRY_BICARB_CART_SIZE, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_BICARB_CONCENTRATE ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.uInt = request.sodium; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_SODIUM, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_SODIUM ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + requestedValue.uInt = request.bicarbonate; + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_BICARBONATE, requestedValue ) ) + { + response.rejectionReason[ TREATMENT_SETPOINT_BICARBONATE ] = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + isValid = FALSE; + } + + if ( TRUE == isValid ) + { + setTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_MODALITY, request.treatmentModality ); + setTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW, request.bloodFlowRate ); + setTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW, request.dialysateFlowRate ); + setTreatmentParameterF32( TREATMENT_PARAM_DIALYSATE_TEMPERATURE, request.dialysateTemperature ); + setTreatmentParameterU32( TREATMENT_PARAM_HEPATITIS_B, request.hepatitis ); + setTreatmentParameterU32( TREATMENT_PARAM_ACID_CONCENTRATE, request.acidConcentrate ); + setTreatmentParameterF32( TREATMENT_PARAM_ACID_CONCENTRATE_CONV_FACTOR, request.acidConcentrateConversionFactor ); + setTreatmentParameterU32( TREATMENT_PARAM_DRY_BICARB_CART_SIZE, request.bicarbonateConcentrate ); + setTreatmentParameterU32( TREATMENT_PARAM_SODIUM, request.sodium ); + setTreatmentParameterU32( TREATMENT_PARAM_BICARBONATE, request.bicarbonate ); + + response.accepted = TRUE; + } + } + else + { + response.rejectionReason[ TREATMENT_SETPOINT_BLOOD_FLOW ] = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + result = sendMessage( MSG_ID_TD_TREATMENT_SET_POINTS_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( TREATMENT_PRESCRIPTION_SETTINGS_EDIT_RESPONSE_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The validateAndSetBloodFlowRate function handles a UI treatment set point + * blood flow rate change request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_RESPONSE + * including acceptance status and rejection reason. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param message set message from UI which includes the requested blood flow rate. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL validateAndSetBloodFlowRate( MESSAGE_T *message ) +{ + BOOL result = FALSE; + CRITICAL_DATAS_T requestedValue; + BLOOD_FLOW_RATE_CHANGE_REQUEST_PAYLOAD_T request; + UI_RESPONSE_PAYLOAD_T response; + + memset( &requestedValue, 0, sizeof( requestedValue ) ); + memset( &request, 0, sizeof( request ) ); + memset( &response, 0, sizeof( response ) ); + + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + + if ( message->hdr.payloadLen == sizeof( BLOOD_FLOW_RATE_CHANGE_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( BLOOD_FLOW_RATE_CHANGE_REQUEST_PAYLOAD_T ) ); + + requestedValue.uInt = request.flowRate; + + if ( TRUE == isTreatmentParamInRange( TREATMENT_PARAM_BLOOD_FLOW, requestedValue ) ) + { + setTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW, request.flowRate ); + setDialysisBloodPumpFlowRate( request.flowRate ); + + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + } + + result = sendMessage( MSG_ID_TD_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The validateAndSetDialysateFlowRate function handles a UI treatment set point + * dialysate flow rate change request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_TREATMENT_SET_POINT_DIALYSATE_FLOW_CHANGE_RESPONSE + * including acceptance status and rejection reason. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param message set message from UI which includes the requested dialysate flow rate. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL validateAndSetDialysateFlowRate( MESSAGE_T *message ) +{ + BOOL result = FALSE; + CRITICAL_DATAS_T requestedValue; + DIALYSATE_FLOW_RATE_CHANGE_REQUEST_PAYLOAD_T request; + UI_RESPONSE_PAYLOAD_T response; + + memset( &requestedValue, 0, sizeof( requestedValue ) ); + memset( &request, 0, sizeof( request ) ); + memset( &response, 0, sizeof( response ) ); + + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + + if ( message->hdr.payloadLen == sizeof( DIALYSATE_FLOW_RATE_CHANGE_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( DIALYSATE_FLOW_RATE_CHANGE_REQUEST_PAYLOAD_T ) ); + + requestedValue.uInt = request.flowRate; + + if ( TRUE == isTreatmentParamInRange( TREATMENT_PARAM_DIALYSATE_FLOW, requestedValue ) ) + { + setTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW, request.flowRate ); + + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + } + + result = sendMessage( MSG_ID_TD_TREATMENT_SET_POINT_DIALYSATE_FLOW_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The validateAndSetDialysateTemperature function handles a UI treatment + * set point dialysate temperature change request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_TREATMENT_SET_POINT_DIALYSATE_TEMP_CHANGE_RESPONSE + * including acceptance status and rejection reason. + * @details \b Inputs: none. + * @details \b Outputs: none. + * @param message set message from UI which includes the requested dialysate temperature. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL validateAndSetDialysateTemperature( MESSAGE_T *message ) +{ + BOOL result = FALSE; + CRITICAL_DATAS_T requestedValue; + DIALYSATE_TEMPERATURE_CHANGE_REQUEST_PAYLOAD_T request; + UI_RESPONSE_PAYLOAD_T response; + + memset( &requestedValue, 0, sizeof( requestedValue ) ); + memset( &request, 0, sizeof( request ) ); + memset( &response, 0, sizeof( response ) ); + + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + + if ( message->hdr.payloadLen == sizeof( DIALYSATE_TEMPERATURE_CHANGE_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( DIALYSATE_TEMPERATURE_CHANGE_REQUEST_PAYLOAD_T ) ); + + requestedValue.sFlt = request.temperature; + + if ( TRUE == isTreatmentParamInRange( TREATMENT_PARAM_DIALYSATE_TEMPERATURE, requestedValue ) ) + { + setTreatmentParameterF32( TREATMENT_PARAM_DIALYSATE_TEMPERATURE, request.temperature ); + + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + } + + result = sendMessage( MSG_ID_TD_TREATMENT_SET_POINT_DIALYSATE_TEMP_CHANGE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The signalUserConfirmationOfTreatmentDuration function handles the UI + * treatment duration confirmation request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_DURATION_CONFIRM_RESPONSE including acceptance status + * and rejection reason. + * @details \b Inputs: validTreatmentDurationReceived, validatedTreatmentDuration_min, + * validatedUFRateLhr, validatedHeparinDeliveryDuration_min. + * @details \b Outputs: validTreatmentDurationReceived, validatedTreatmentDuration_min, + * validatedUFRateLhr, validatedHeparinDeliveryDuration_min. + * @param message set message from UI which includes the confirmed treatment duration, + * UF rate and heparin delivery duration. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL signalUserConfirmationOfTreatmentDuration( MESSAGE_T *message ) +{ + BOOL result = FALSE; + TREATMENT_DURATION_CONFIRM_REQUEST_PAYLOAD_T request; + UI_RESPONSE_PAYLOAD_T response; + F32 presUFVolumeMl = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) * (F32)ML_PER_LITER; + F32 validatedUFRateMlMin = ( validatedUFRateLhr * (F32)ML_PER_LITER ) / (F32)MIN_PER_HOUR; + F32 roundedValidatedUFRateLhr = 0.0F; + + response.accepted = FALSE; + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + + if ( message->hdr.payloadLen == sizeof( TREATMENT_DURATION_CONFIRM_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( TREATMENT_DURATION_CONFIRM_REQUEST_PAYLOAD_T ) ); + + roundedValidatedUFRateLhr = roundf( validatedUFRateLhr * 100.0F ) / 100.0F; + + // Verify confirmed duration and UF rate match previously validated values. + if ( ( TRUE == validTreatmentDurationReceived ) && ( request.duration == validatedTreatmentDuration_min ) && + ( fabs( request.ufRate - roundedValidatedUFRateLhr ) < NEARLY_ZERO ) && + ( request.heparinDuration == validatedHeparinDeliveryDuration_min ) ) + { + setTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION, validatedTreatmentDuration_min ); + setTreatmentParameterU32( TREATMENT_PARAM_HEPARIN_DELIVERY_DURATION, validatedHeparinDeliveryDuration_min ); + updateTreatmentBroadcastData( validatedUFRateLhr ); + setDialysisDDParams( getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), presUFVolumeMl, validatedUFRateMlMin ); + + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + + // Clear stored validated values. + validTreatmentDurationReceived = FALSE; + validatedTreatmentDuration_min = 0U; + validatedUFRateLhr = 0.0F; + validatedHeparinDeliveryDuration_min = 0U; + } + else if ( FALSE == validTreatmentDurationReceived ) + { + response.rejectionReason = REQUEST_REJECT_REASON_CONFIRMATION_NOT_EXPECTED; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_CONFIRMATION_MISMATCH; + } + } + + result = sendMessage( MSG_ID_TD_DURATION_CONFIRM_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The signalUserConfirmationOfUFVolume function handles the UI + * ultrafiltration volume confirmation request during Treatment mode. + * @details \b Message \b Sent: MSG_ID_TD_ULTRAFILTRATION_CHANGE_CONFIRM_RESPONSE + * including acceptance status and rejection reason. + * @details \b Inputs: validUFVolumeReceived, validatedUFVolumeGoalL, validatedUFRateLhr. + * @details \b Outputs: validUFVolumeReceived, validatedUFVolumeGoalL, validatedUFRateLhr. + * @param message set message from UI which includes the confirmed UF volume and UF rate. + * @return TRUE if response message is sent successfully, FALSE otherwise. + *************************************************************************/ +BOOL signalUserConfirmationOfUFVolume( MESSAGE_T *message ) +{ + BOOL result = FALSE; + UF_VOLUME_CONFIRM_REQUEST_PAYLOAD_T request; + UI_RESPONSE_PAYLOAD_T response; + F32 validatedUFVolumeGoalMl = 0.0F; + F32 validatedUFRateMlMin = 0.0F; + F32 roundedValidatedUFRateLhr = 0.0F; + + response.accepted = FALSE; + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + + if ( message->hdr.payloadLen == sizeof( UF_VOLUME_CONFIRM_REQUEST_PAYLOAD_T ) ) + { + memcpy( &request, &message->payload[ 0 ], sizeof( UF_VOLUME_CONFIRM_REQUEST_PAYLOAD_T ) ); + + validatedUFVolumeGoalMl = validatedUFVolumeGoalL * (F32)ML_PER_LITER; + validatedUFRateMlMin = ( validatedUFRateLhr * (F32)ML_PER_LITER ) / (F32)MIN_PER_HOUR; + roundedValidatedUFRateLhr = roundf( validatedUFRateLhr * 100.0F ) / 100.0F; + + // Verify confirmed UF volume and UF rate match previously validated values. + if ( ( TRUE == validUFVolumeReceived ) && ( fabs( request.ufVolume - validatedUFVolumeGoalL ) < NEARLY_ZERO ) && + ( fabs( request.ufRate - roundedValidatedUFRateLhr ) < NEARLY_ZERO ) ) + { + setTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME, validatedUFVolumeGoalL ); + updateTreatmentBroadcastData( validatedUFRateLhr ); + setDialysisDDParams( getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), validatedUFVolumeGoalMl, validatedUFRateMlMin ); + + response.accepted = TRUE; + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + + // Clear stored validated values. + validUFVolumeReceived = FALSE; + validatedUFVolumeGoalL = 0.0F; + validatedUFRateLhr = 0.0F; + } + else if ( FALSE == validUFVolumeReceived ) + { + response.rejectionReason = REQUEST_REJECT_REASON_CONFIRMATION_NOT_EXPECTED; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_CONFIRMATION_MISMATCH; + } + } + + result = sendMessage( MSG_ID_TD_ULTRAFILTRATION_CHANGE_CONFIRM_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08 *)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + + +/************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/