Index: firmware/App/Services/FluidBolus.c =================================================================== diff -u -r6f876554db45a19590eaf2122ef47e33f7a7d69b -r1abc0349c736a70fb56db6895947abfbba0eee22 --- firmware/App/Services/FluidBolus.c (.../FluidBolus.c) (revision 6f876554db45a19590eaf2122ef47e33f7a7d69b) +++ firmware/App/Services/FluidBolus.c (.../FluidBolus.c) (revision 1abc0349c736a70fb56db6895947abfbba0eee22) @@ -15,12 +15,17 @@ * ***************************************************************************/ +#include "AlarmMgmt.h" #include "AlarmMgmtTD.h" #include "BloodFlow.h" #include "DDInterface.h" #include "FluidBolus.h" #include "Messaging.h" #include "Pressures.h" +#include "ModeTreatment.h" +#include "StateTxBloodPrime.h" +#include "StateTxDialysis.h" +#include "StateTxPaused.h" #include "TaskGeneral.h" #include "Timers.h" #include "TxParams.h" @@ -33,6 +38,8 @@ // ********** private definitions ********** +#define NUM_OF_FLUID_BOLUS_PERMITTED_ALARMS 6U ///< Number of permitted alarms for fluid bolus from paused state. + static const U32 FLUID_BOLUS_DATA_PUB_INTERVAL = ( MS_PER_SECOND / TASK_GENERAL_INTERVAL); ///< Saline bolus data broadcast interval (ms/task time) count. @@ -43,20 +50,25 @@ static U32 fluidBolusBroadCastTimerCtr; ///< Broadcast Timer counter static BOOL fluidBolusStartRequested; ///< Flag indicates a fluid bolus start has been requested by user. static BOOL fluidBolusAbortRequested; ///< Flag indicates a fluid bolus abort has been requested by user. +static BOOL pubBolusPermitted; ///< Flag indicates a bolus is permitted or not to UI (used to broadcast). +static U32 targetBloodFlowMLPM; ///< Blood pump flow rate (mL/min) to use for current bolus delivery. static F32 totalFluidVolumeDelivered_mL; ///< Volume (mL) in total of fluid delivered so far (cumulative for all boluses including current one). static F32 bolusFluidVolumeDelivered_mL; ///< Volume (mL) of current bolus delivered so far (calculated from measured blood flow rate). static U32 bolusVolumeLastUpdateTimeStamp; ///< Time stamp for last bolus volume update. +// TODO: replace TRUE with getDialysateGoodToDeliverStatus() when we are ready +BOOL isDialysateGoodToDeliver = TRUE; ///< Flag indicating dialysate is good to deliver to the patient. + ///< Permitted alarm list-bolus allowed from paused state -static const ALARM_ID_T FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[] = +static const ALARM_ID_T FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[ NUM_OF_FLUID_BOLUS_PERMITTED_ALARMS ] = { ALARM_ID_TD_TREATMENT_STOPPED_BY_USER, ALARM_ID_TD_ARTERIAL_PRESSURE_HIGH, ALARM_ID_TD_ARTERIAL_PRESSURE_LOW, ALARM_ID_TD_VENOUS_PRESSURE_HIGH, ALARM_ID_TD_TMP_PRESSURE_HIGH, - ALARM_ID_TD_TMP_PRESSURE_LOW, + ALARM_ID_TD_TMP_PRESSURE_LOW }; // ********** private function prototypes ********** @@ -76,21 +88,23 @@ * and therefore should only be called when a new treatment is due to begin. * @details \b Inputs: none * @details \b Outputs: currentFluidBolusState, currentFluidBolusMedium, - * fluidBolusBroadCastTimerCtr, totalFluidVolumeDelivered_mL, + * fluidBolusBroadCastTimerCtr, targetBloodFlowMLPM, totalFluidVolumeDelivered_mL, * bolusFluidVolumeDelivered_mL, fluidBolusStartRequested, - * fluidBolusAbortRequested, bolusVolumeLastUpdateTimeStamp + * fluidBolusAbortRequested, bolusVolumeLastUpdateTimeStamp, pubBolusPermitted * @return none *************************************************************************/ void initFluidBolus( void ) { currentFluidBolusState = FLUID_BOLUS_IDLE_STATE; currentFluidBolusMedium = getFluidBolusMedium(); - fluidBolusBroadCastTimerCtr = 0; + fluidBolusBroadCastTimerCtr = FLUID_BOLUS_DATA_PUB_INTERVAL - 10; // setup to stagger publish from other broadcasters + targetBloodFlowMLPM = 0; totalFluidVolumeDelivered_mL = 0.0F; bolusFluidVolumeDelivered_mL = 0.0F; fluidBolusStartRequested = FALSE; fluidBolusAbortRequested = FALSE; bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); + pubBolusPermitted = TRUE; } /*********************************************************************//** @@ -133,6 +147,9 @@ SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, priorState, currentFluidBolusState ); } + // Publish fluid bolus data at set interval (whether we are delivering one or not) + publishFluidBolusData(); + return currentFluidBolusState; } @@ -161,18 +178,6 @@ /*********************************************************************//** * @brief - * The getFluidBolusState function gets the current fluid bolus state. - * @details \b Inputs: currentFluidBolusState - * @details \b Outputs: none - * @return currentFluidBolusState - *************************************************************************/ -FLUID_BOLUS_STATE_T getFluidBolusState( void ) -{ - return currentFluidBolusState; -} - -/*********************************************************************//** - * @brief * The isFluidBolusActive function determines whether a fluid bolus is * currently being delivered. * @details \b Inputs: currentFluidBolusState @@ -199,32 +204,20 @@ /*********************************************************************//** * @brief - * The getCurrentFluidBolusVolumeDelivered function gets the volume - * delivered for the currently active bolus. - * @details \b Inputs: bolusFluidVolumeDelivered_mL - * @details \b Outputs: none - * @return current bolus volume delivered in mL. - *************************************************************************/ -F32 getCurrentFluidBolusVolumeDelivered( void ) -{ - return bolusFluidVolumeDelivered_mL; -} - -/*********************************************************************//** - * @brief * The signalStartFluidBolus function is called by a parent treatment state * to initiate a fluid bolus. * @details \b Inputs: currentFluidBolusState - * @details \b Outputs: fluidBolusStartRequested - * @return TRUE if state is IDLE, FALSE otherwise. + * @details \b Outputs: fluidBolusStartRequested, targetBloodFlowMLPM + * @return none *************************************************************************/ -BOOL signalStartFluidBolus( void ) +BOOL signalStartFluidBolus( U32 flowRate ) { BOOL result = FALSE; if ( ( FLUID_BOLUS_IDLE_STATE == currentFluidBolusState ) && ( FALSE == fluidBolusStartRequested ) ) { fluidBolusStartRequested = TRUE; + targetBloodFlowMLPM = flowRate; result = TRUE; } @@ -249,10 +242,25 @@ /*********************************************************************//** * @brief + * The setBolusPermitted function sets the fluid bolus permitted flag, + * which is broadcast to the UI to enable or disable the fluid bolus + * button on screen. + * @details \b Inputs: none + * @details \b Outputs: pubBolusPermitted + * @param permitted TRUE if a fluid bolus is currently permitted, FALSE otherwise. + * @return none + *************************************************************************/ +void setBolusPermitted( BOOL permitted ) +{ + pubBolusPermitted = permitted; +} + +/*********************************************************************//** + * @brief * The publishFluidBolusData function publishes the fluid bolus data * at the set time interval. * @details \b Inputs: fluidBolusBroadCastTimerCtr, currentFluidBolusState, - * bolusFluidVolumeDelivered_mL, totalFluidVolumeDelivered_mL + * bolusFluidVolumeDelivered_mL, totalFluidVolumeDelivered_mL, pubBolusPermitted * @details \b Outputs: fluidBolusBroadCastTimerCtr * @return none *************************************************************************/ @@ -266,6 +274,7 @@ data.bolFluidVolumeMl = bolusFluidVolumeDelivered_mL; data.cumFluidVolumeMl = totalFluidVolumeDelivered_mL; data.fluidBolusState = currentFluidBolusState; + data.bolusPermitted = pubBolusPermitted; broadcastData( MSG_ID_TD_FLUID_BOLUS_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( FLUID_BOLUS_DATA_PAYLOAD_T ) ); @@ -290,7 +299,7 @@ { fluidBolusStartRequested = FALSE; // Stop blood pump - setBloodPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + signalBloodPumpHardStop(); // Stop substitution pump cmdSubstitutionRate( 0.0F ); bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); @@ -325,23 +334,23 @@ bolusFluidVolumeDelivered_mL = 0.0; bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); + // Set arterial & venous valves setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); if ( FLUID_BOLUS_MEDIUM_SALINE == currentFluidBolusMedium ) { - setBloodPumpTargetFlowRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setBloodPumpTargetFlowRate( targetBloodFlowMLPM, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); state = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; } - else if ( TRUE == isDialysateGoodToDeliver() ) + else if ( TRUE == isDialysateGoodToDeliver ) { // set D92 flow rate - cmdSubstitutionRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ) ); + cmdSubstitutionRate( (F32)targetBloodFlowMLPM ); state = FLUID_BOLUS_SUBSITUTE_IN_PROGRESS_STATE; } else { - // No actuator action required. state = FLUID_BOLUS_IDLE_STATE; } } @@ -356,13 +365,13 @@ * volume from measured blood flow, fires an alarm if the saline bag is empty, * and completes the bolus when the target volume is reached or an abort is requested. * @details \b Inputs: bolusFluidVolumeDelivered_mL, fluidBolusAbortRequested - * @details \b Outputs: fluidBolusAbortRequested, + * @details \b Outputs: fluidBolusAbortRequested * @return next fluid bolus state *************************************************************************/ static FLUID_BOLUS_STATE_T handleFluidBolusSalineInProgressState( void ) { - FLUID_BOLUS_STATE_T state = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; - F32 bolusTargetVolume = (F32)getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + FLUID_BOLUS_STATE_T state = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; + F32 bolusTargetVolume = (F32)getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); updateFluidBolusVolumeDelivered(); @@ -373,11 +382,15 @@ state = FLUID_BOLUS_IDLE_STATE; } // Determine if bolus is complete or stopped by user - else if ( ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) ) + if ( ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) ) { fluidBolusAbortRequested = FALSE; state = FLUID_BOLUS_IDLE_STATE; } + else + { + // No action required + } // complete or abort or alarm if ( state != FLUID_BOLUS_SALINE_IN_PROGRESS_STATE ) @@ -410,10 +423,9 @@ updateFluidBolusVolumeDelivered(); // Check for dialysate, target volume delivered or abort from user - if ( ( FALSE == isDialysateGoodToDeliver() ) || ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) ) + if ( ( FALSE == isDialysateGoodToDeliver ) || ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) ) { fluidBolusAbortRequested = FALSE; - // TODO: d92 to restore or turn off if modality is hd. cmdSubstitutionRate( 0.0F ); completeBolusToCumulative(); state = FLUID_BOLUS_IDLE_STATE; @@ -426,26 +438,15 @@ * @brief * The updateFluidBolusVolumeDelivered function integrates the volume * delivered for the active bolus. - * @details \b Inputs: currentFluidBolusMedium, bolusVolumeLastUpdateTimeStamp + * @details \b Inputs: bolusVolumeLastUpdateTimeStamp, targetBloodFlowMLPM * @details \b Outputs: bolusFluidVolumeDelivered_mL, bolusVolumeLastUpdateTimeStamp * @return none *************************************************************************/ static void updateFluidBolusVolumeDelivered( void ) { F32 timeSinceLastVolumeUpdateMin = (F32)calcTimeSince( bolusVolumeLastUpdateTimeStamp ) / (F32)( MS_PER_SECOND * SEC_PER_MIN ); - F32 rate_mL_min; - if ( FLUID_BOLUS_MEDIUM_SALINE == currentFluidBolusMedium ) - { - rate_mL_min = getMeasuredBloodFlowRate(); - } - else - { - //TODO - rate_mL_min = (F32)getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ); - } - - bolusFluidVolumeDelivered_mL += rate_mL_min * timeSinceLastVolumeUpdateMin; + bolusFluidVolumeDelivered_mL += (F32)targetBloodFlowMLPM * timeSinceLastVolumeUpdateMin; bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); } @@ -466,37 +467,159 @@ /*********************************************************************//** * @brief - * The areAllActiveAlarmsPermittedForBolus function checks whether all - * currently active alarms are from permitted list. - * @details \b Inputs: FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[] + * The isBolusAllowedByActiveAlarms function checks whether all + * currently active alarms permit a fluid bolus from the paused state. + * For saline medium, a non-permitted alarm blocks the bolus only if it is + * TD source. For substitute medium, any non-permitted alarm blocks the bolus. + * @details \b Inputs: FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[], currentFluidBolusMedium * @details \b Outputs: none - * @return TRUE if all active alarms are from the permitted list and at - * least one permitted alarm is active. FALSE otherwise. + * @return TRUE if all active alarms permit the bolus, FALSE otherwise. *************************************************************************/ -BOOL areAllActiveAlarmsPermittedForBolus( void ) +BOOL isBolusAllowedByActiveAlarms( void ) { - U32 i; - U32 permittedCount = 0U; - U32 totalCount = getActiveAlarmCount(); - BOOL result = FALSE; + U32 alarm; + U32 permittedIndex; + BOOL permitted = FALSE; + BOOL result = TRUE; - if ( 0U == totalCount ) + for ( alarm = 0; alarm < NUM_OF_ALARM_IDS; alarm++ ) { - return FALSE; + if ( TRUE == isAlarmActive( alarm ) ) + { + for ( permittedIndex = 0; permittedIndex < NUM_OF_FLUID_BOLUS_PERMITTED_ALARMS; permittedIndex++ ) + { + if ( FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[ permittedIndex ] == alarm ) + { + permitted = TRUE; + break; + } + } + + if ( FALSE == permitted ) + { + if ( FLUID_BOLUS_MEDIUM_SALINE == currentFluidBolusMedium ) + { + if ( ALM_SRC_TD == getAlarmSource( alarm ) ) + { + result = FALSE; + break; + } + } + else + { + result = FALSE; + break; + } + } + } } - for ( i = 0U; i < 6; i++ ) + return result; +} + +/*********************************************************************//** +* @brief +* The handleFluidBolusRequest function handles the UI fluid bolus request. +* @details \b Message \b Sent: MSG_ID_TD_FLUID_BOLUS_RESPONSE +* @details \b Inputs: none +* @details \b Outputs: none +* @return TRUE if request is accepted, FALSE if rejected. +*************************************************************************/ +BOOL handleFluidBolusRequest( MESSAGE_T *message ) +{ + BOOL result = FALSE; + U32 cmd = 0; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; + FLUID_BOLUS_RESPONSE_PAYLOAD_T response; + + if ( sizeof(U32) == message->hdr.payloadLen ) { - if ( TRUE == isAlarmActive( FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[ i ] ) ) + memcpy( &cmd, message->payload, sizeof(U32) ); + + if ( FLUID_BOLUS_CMD_START == cmd ) { - permittedCount++; + if ( TRUE == isFluidBolusActive() ) + { + // TD Fluid Bolus Additional Bolus Prevention + rejReason = REQUEST_REJECT_REASON_FLUID_BOLUS_IN_PROGRESS; + } + else if ( TREATMENT_PAUSED_STATE == getTreatmentState() ) + { + if ( ( TRUE == isBolusAllowedByActiveAlarms() ) ) + { + result = signalPauseTreatFluidBolusRequest(); + rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; + } + else + { + rejReason = REQUEST_REJECT_REASON_ALARM_IS_ACTIVE; + } + } + else + { + // Route to whichever calling state is currently active + switch ( getTreatmentState() ) + { + case TREATMENT_BLOOD_PRIME_STATE: + result = signalBloodPrimeFluidBolusRequest(); + rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; + break; + + case TREATMENT_DIALYSIS_STATE: + result = signalDialysisFluidBolusRequest(); + rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; + break; + +// case TREATMENT_HDF_STATE: +// result = signalHdfFluidBolusRequest(); +// rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; +// break; +// +// case TREATMENT_ISO_UF_STATE: +// result = signalIsoUFFluidBolusRequest(); +// rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; +// break; +// +// case TREATMENT_END_STATE: +// result = signalTreatmentEndFluidBolusRequest(); +// rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; +// break; + + default: + rejReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + break; + } + } } + else if ( FLUID_BOLUS_CMD_STOP == cmd ) + { + if ( TRUE == isFluidBolusActive() ) + { + signalAbortFluidBolus(); + result = TRUE; + rejReason = REQUEST_REJECT_REASON_NONE; + } + else + { + rejReason = REQUEST_REJECT_REASON_FLUID_BOLUS_NOT_IN_PROGRESS; + } + } + else + { + rejReason = REQUEST_REJECT_REASON_INVALID_COMMAND; + } } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_INVALID_PAYLOAD_LENGTH, (U32)message->hdr.payloadLen ); + } - result = ( permittedCount == totalCount ) ? TRUE: FALSE; + response.accepted = result; + response.rejectionReason = rejReason; + response.targetVolumeMl = getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + sendMessage( MSG_ID_TD_FLUID_BOLUS_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)&response, sizeof( FLUID_BOLUS_RESPONSE_PAYLOAD_T ) ); return result; } - /**@}*/