Index: firmware/App/Modes/Rinseback.c =================================================================== diff -u -r49dba1e95bb3763b4c150e7a80b84a65264a7ca8 -rb03a3ed91ebe15cfb419e34018f8fe600e744b4b --- firmware/App/Modes/Rinseback.c (.../Rinseback.c) (revision 49dba1e95bb3763b4c150e7a80b84a65264a7ca8) +++ firmware/App/Modes/Rinseback.c (.../Rinseback.c) (revision b03a3ed91ebe15cfb419e34018f8fe600e744b4b) @@ -1,29 +1,29 @@ /************************************************************************** * -* Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * -* @file Rinseback.c +* @file Rinseback.c * -* @author (last) Sean Nash -* @date (last) 14-Jan-2021 +* @author (last) Sean Nash +* @date (last) 12-Nov-2021 * -* @author (original) Sean -* @date (original) 14-Jan-2021 +* @author (original) Sean Nash +* @date (original) 20-Jan-2021 * ***************************************************************************/ #include "AirTrap.h" #include "BloodFlow.h" -#include "Buttons.h" #include "DGInterface.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "ModeTreatment.h" #include "ModeTreatmentParams.h" #include "OperationModes.h" +#include "PresOccl.h" #include "Rinseback.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" @@ -37,20 +37,44 @@ // ********** private definitions ********** -#define TARGET_RINSEBACK_VOLUME_ML 300.0 ///< Target rinseback volume to deliver back to the patient (in mL). -#define RINSEBACK_FLOW_RATE_ADJ_ML_MIN 25.0 ///< Adjustment amount (in mL/min) to apply when user requests increase/decrease in flow rate. -#define MIN_RINSEBACK_FLOW_RATE_ML_MIN 50.0 ///< Minimum rinseback flow rate (in mL/min). -#define MAX_RINSEBACK_FLOW_RATE_ML_MIN 150.0 ///< Maximum rinseback flow rate (in mL/min). +#define TUBING_RINSEBACK_VOLUME_ML 80.0 ///< Target rinseback volume to deliver back to the patient (in mL). +#define MAX_TOTAL_ADDITIONAL_RINSEBACK_VOLUME_ML 300.0 ///< Maximum total additional rinseback volume allowed : all additionals (in mL). +#define MAX_RINSEBACK_VOLUME_ERROR_ML 60.0 ///< Maximum error in total additional rinseback volume (20% of total). +#define TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML 10.0 ///< Target rinseback volume for an additional volume request (in mL). +#define RINSEBACK_FLOW_RATE_ADJ_ML_MIN 25 ///< Adjustment amount (in mL/min) to apply when user requests increase/decrease in flow rate. +#define MIN_RINSEBACK_FLOW_RATE_ML_MIN 50 ///< Minimum rinseback flow rate (in mL/min). +#define MAX_RINSEBACK_FLOW_RATE_ML_MIN 150 ///< Maximum rinseback flow rate (in mL/min). -static const F32 RINSEBACK_FLOW_INTEGRATOR = 1.0 / (F32)( SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); +/// Interval at which rinseback progress is to be published to UI. +#define RINSEBACK_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) +/// Maximum time allowed for rinseback operation until full volume is delivered. Timer is reset whenever BP is running. +static const U32 MAX_RINSEBACK_TIME = ( 5 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); +/// Maximum time allowed after rinseback operation completed. Timer is reset whenever BP is running (additional). +static const U32 MAX_RINSEBACK_DONE_TIME = ( 15 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); +/// Warning time after rinseback operation completed. Timer is reset whenever BP is running (additional). +static const U32 RINSEBACK_DONE_WARNING_TIME = ( 13 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); +/// Maximum time allowed for each additional rinseback volume delivery. +static const U32 MAX_RINSEBACK_ADDITIONAL_TIME = ( 20 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ); +/// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. +static const F32 RINSEBACK_FLOW_INTEGRATOR = 1.0 / (F32)( SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); // ********** private data ********** static RINSEBACK_STATE_T rinsebackState; ///< Current state of the rinseback sub-mode. +static U32 rinsebackRate_mL_min; ///< Rinseback rate to use/adjust for this current rinseback only. +static U32 rinsebackTimerCtr; ///< Timer counter for time spent in rinseback sub-mode. -static F32 cumulativeRinsebackVolume_mL; ///< Total cumulative rinseback volume (in mL). +static F32 rinsebackTargetVolume_mL = 300.0; ///< Calculated target rinseback volume (based on selected dialyzer and fixed tubing volume). +static OVERRIDE_F32_T cumulativeRinsebackVolume_mL = { 0.0, 0.0, 0.0, 0 }; ///< Total cumulative rinseback volume (in mL) from measured blood flow rate. +static F32 expectedRinsebackVolume_mL = 0.0; ///< Total cumulative rinseback volume (in mL) expected based on target blood flow rate. +static F32 targetRinsebackVolumePlusAdditional_mL; ///< Target rinseback volume w/ additional volume(s) added (in mL). +static F32 additionalRinsebackVolume_mL; ///< Total volume (in mL) delivered so far for additional volume request. +static F32 totalAdditionalRinsebackVolume_mL; ///< Total accumulated volume (in mL) delivered so far for all additional volumes combined. +static U32 rinsebackAdditionalTimerCtr; ///< Timer counter for duration of an additional rinseback delivery. +static U32 rinsebackPublishTimerCtr; ///< Timer counter for determining interval for rinseback status to be published. +/// Interval (in task intervals) at which to publish rinseback data to CAN bus. +static OVERRIDE_U32_T rinsebackPublishInterval = { RINSEBACK_DATA_PUBLISH_INTERVAL, RINSEBACK_DATA_PUBLISH_INTERVAL, RINSEBACK_DATA_PUBLISH_INTERVAL, 0 }; -static BOOL rinsebackStopRequested; ///< Flag indicates alarm requesting to stop rinseback. static BOOL startRinsebackRequested; ///< Flag indicates user requesting rinseback start (confirming saline bag move to arterial line end). static BOOL incrRinsebackFlowRateRequested; ///< Flag indicates user requesting rinseback flow rate be increased. static BOOL decrRinsebackFlowRateRequested; ///< Flag indicates user requesting rinseback flow rate be decreased. @@ -66,6 +90,11 @@ static void resetRinsebackFlags( void ); +static void setupForRinsebackDelivery( U32 rate ); +static void setupForRinsebackStopOrPause( void ); + +static F32 getRinsebackVolume( void ); + static RINSEBACK_STATE_T handleRinsebackStopInitState( void ); static RINSEBACK_STATE_T handleRinsebackRunState( void ); static RINSEBACK_STATE_T handleRinsebackPausedState( void ); @@ -83,6 +112,9 @@ static BOOL handleBackToTreatmentUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleEndTreatmentUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static void publishRinsebackData( void ); +static U32 getPublishRinsebackInterval( void ); + /*********************************************************************//** * @brief * The initRinseback function initializes the Rinseback sub-mode module. @@ -93,7 +125,16 @@ void initRinseback( void ) { rinsebackState = RINSEBACK_STOP_INIT_STATE; - cumulativeRinsebackVolume_mL = 0.0; + rinsebackRate_mL_min = getTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); + rinsebackTargetVolume_mL = TUBING_RINSEBACK_VOLUME_ML + (F32)getDialyzerBloodVolume(); + targetRinsebackVolumePlusAdditional_mL = rinsebackTargetVolume_mL; + rinsebackTimerCtr = 0; + cumulativeRinsebackVolume_mL.data = 0.0; + expectedRinsebackVolume_mL = 0.0; + additionalRinsebackVolume_mL = 0.0; + totalAdditionalRinsebackVolume_mL = 0.0; + rinsebackAdditionalTimerCtr = 0; + rinsebackPublishTimerCtr = 0; resetRinsebackFlags(); } @@ -106,7 +147,6 @@ *************************************************************************/ static void resetRinsebackFlags( void ) { - rinsebackStopRequested = FALSE; startRinsebackRequested = FALSE; incrRinsebackFlowRateRequested = FALSE; decrRinsebackFlowRateRequested = FALSE; @@ -124,7 +164,7 @@ * The transitionToRinseback function prepares for transition to Rinseback * sub-mode. * @details Inputs: none - * @details Outputs: none + * @details Outputs: Rinseback sub-mode setup to begin * @return none *************************************************************************/ void transitionToRinseback( void ) @@ -139,13 +179,14 @@ // Set valves to safe state setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); - setValvePosition( VBA, VALVE_POSITION_B_OPEN ); - setValvePosition( VBV, VALVE_POSITION_B_OPEN ); + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); // Ensure all pumps except DPi stopped signalBloodPumpHardStop(); signalDialOutPumpHardStop(); - // TODO - stop Heparin pump + stopSyringePump(); + endAirTrapControl(); // Re-circulate dialysate side of dialyzer w/ heating to maintain temperature setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); @@ -154,23 +195,97 @@ /*********************************************************************//** * @brief - * The execRinseback function executes the Rinseback sub-mode state machine. + * The setupForRinsebackDelivery function sets actuators appropriately + * for rinseback states where delivery is in progress. * @details Inputs: none + * @details Outputs: arterial and venous lines opened, blood pump started, + * and air trap levelin control is started. + * @param rate rinseback rate to use for blood pump + * @return none + *************************************************************************/ +static void setupForRinsebackDelivery( U32 rate ) +{ + // Open VBA and VBV valves to allow flow from saline bag and to patient venous line + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); // draw from saline back instead of patient + setValvePosition( VBV, VALVE_POSITION_B_OPEN ); // return to patient + // Start blood pump at rinseback flow rate + setBloodPumpTargetFlowRate( rate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + // Start air trap leveling control + startAirTrapControl(); +} + +/*********************************************************************//** + * @brief + * The setupForRinsebackStopOrPause function sets actuators appropriately + * for rinseback states where delivery is paused or stopped. + * @details Inputs: none + * @details Outputs: Blood pump stopped, arterial and venous lines closed, + * and air trap leveling control is stopped. + * @return none + *************************************************************************/ +static void setupForRinsebackStopOrPause( void ) +{ + // Stop blood pump + signalBloodPumpHardStop(); + // Close arterial and venous lines + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); + // Stop air trap leveling control + endAirTrapControl(); +} + +/*********************************************************************//** + * @brief + * The getRinsebackVolume function gets the calculated blood prime volume + * delivered. + * @details Inputs: cumulativeRinsebackVolume_mL * @details Outputs: none + * @return the current rinseback volume delivered (in mL). + *************************************************************************/ +static F32 getRinsebackVolume( void ) +{ + F32 result = cumulativeRinsebackVolume_mL.data; + + if ( OVERRIDE_KEY == cumulativeRinsebackVolume_mL.override ) + { + result = cumulativeRinsebackVolume_mL.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getCurrentRinsebackState function returns the current state of the + * rinseback sub-mode. + * @details Inputs: rinsebackState + * @details Outputs: none + * @return rinsebackState + *************************************************************************/ +RINSEBACK_STATE_T getCurrentRinsebackState( void ) +{ + return rinsebackState; +} + +/*********************************************************************//** + * @brief + * The execRinseback function executes the Rinseback sub-mode state machine. + * @details Inputs: rinsebackState + * @details Outputs: rinsebackState, rinsebackTimerCtr, flags * @return none *************************************************************************/ void execRinseback( void ) { + rinsebackTimerCtr++; + switch ( rinsebackState ) { case RINSEBACK_STOP_INIT_STATE: rinsebackState = handleRinsebackStopInitState(); - //setBloodIsPrimed( FALSE ); TODO - call in handler when transition to RB run break; case RINSEBACK_RUN_STATE: rinsebackState = handleRinsebackRunState(); - //setRinsebackIsCompleted( TRUE ); TODO - call in handler when rinseback is done break; case RINSEBACK_PAUSED_STATE: @@ -186,61 +301,137 @@ break; default: - // TODO - s/w fault + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_RINSEBACK_INVALID_STATE, rinsebackState ); break; } // Rinseback flags should be handled by now - reset in case not handled by current state resetRinsebackFlags(); + + // Publish rinseback progress while in rinseback sub-mode + publishRinsebackData(); } /*********************************************************************//** * @brief * The handleRinsebackStopInitState function handles the stopped initial * rinseback state operations. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: flags + * @details Outputs: flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackStopInitState( void ) { RINSEBACK_STATE_T result = RINSEBACK_STOP_INIT_STATE; + // Has user requested rinseback start? + if ( TRUE == startRinsebackRequested ) + { + setupForRinsebackDelivery( rinsebackRate_mL_min ); + // From moment we start rinseback, we consider blood side of dialyzer no longer fully primed + setBloodIsPrimed( FALSE ); + result = RINSEBACK_RUN_STATE; + sendTreatmentLogEventData( MID_TREATMENT_RINSE_BACK_EVENT, 0.0, 0.0 ); + } + // Has user requested to return to treatment? + else if ( TRUE == backToTreatmentRequested ) + { + signalGoToTreatmentStopped(); + } + // Has user requested to end the treatment? + else if ( TRUE == endTreatmentRequested ) + { + signalEndTreatment(); + } + // Has rinseback operation exceeded max time? + else if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) + { + signalGoToTreatmentStopped(); + activateAlarmNoData( ALARM_ID_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); + } + return result; } /*********************************************************************//** * @brief * The handleRinsebackRunState function handles the rinseback run state * operations. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: flags + * @details Outputs: cumulativeRinsebackVolume_mL, flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackRunState( void ) { RINSEBACK_STATE_T result = RINSEBACK_RUN_STATE; - // TODO - update rinseback volume - cumulativeRinsebackVolume_mL += ( getMeasuredBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); + // Reset rinseback t/o + rinsebackTimerCtr = 0; + // Update rinseback volume delivered so far + cumulativeRinsebackVolume_mL.data += ( getMeasuredBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); + expectedRinsebackVolume_mL += ( (F32)getTargetBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); + + // Has user requested to end rinseback? + if ( TRUE == endRinsebackRequested ) + { + setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); + result = RINSEBACK_STOP_STATE; + } // Has rinseback completed? - if ( cumulativeRinsebackVolume_mL <= TARGET_RINSEBACK_VOLUME_ML ) + else if ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) { setRinsebackIsCompleted( TRUE ); + setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } + // Check for empty saline bag + if ( TRUE == isSalineBagEmpty() ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); + setupForRinsebackStopOrPause(); + result = RINSEBACK_PAUSED_STATE; + } // Has user or alarm requested rinseback pause? - else if ( ( TRUE == rinsebackStopRequested ) || ( TRUE == isAnyAlarmActive() ) ) + else if ( ( TRUE == pauseRinsebackRequested ) || ( TRUE == doesAlarmStatusIndicateStop() ) ) { + setupForRinsebackStopOrPause(); result = RINSEBACK_PAUSED_STATE; } +#ifndef DISABLE_PUMP_FLOW_CHECKS + // Is rinseback taking too long? + else if ( fabs( expectedRinsebackVolume_mL - getRinsebackVolume() ) > MAX_RINSEBACK_VOLUME_ERROR_ML ) + { + setRinsebackIsCompleted( TRUE ); + setupForRinsebackStopOrPause(); + activateAlarmNoData( ALARM_ID_RINSEBACK_VOLUME_CHECK_FAILURE ); + result = RINSEBACK_STOP_STATE; + } +#endif // Otherwise, continue rinseback else { // Has user requested rate change? - if ( ( TRUE == incrRinsebackFlowRateRequested ) || ( TRUE == incrRinsebackFlowRateRequested ) ) + if ( ( TRUE == incrRinsebackFlowRateRequested ) || ( TRUE == decrRinsebackFlowRateRequested ) ) { - // TODO - update rate if within limits + U32 rbRate = rinsebackRate_mL_min; + + if ( TRUE == incrRinsebackFlowRateRequested ) + { + rbRate += RINSEBACK_FLOW_RATE_ADJ_ML_MIN; + } + else + { + rbRate -= RINSEBACK_FLOW_RATE_ADJ_ML_MIN; + } + incrRinsebackFlowRateRequested = FALSE; + decrRinsebackFlowRateRequested = FALSE; + if ( ( rbRate >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) && ( rbRate <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) ) + { + rinsebackRate_mL_min = rbRate; + setBloodPumpTargetFlowRate( rinsebackRate_mL_min, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + } } } @@ -249,68 +440,163 @@ /*********************************************************************//** * @brief - * The handleRinsebackStopInitState function handles the rinseback paused + * The handleRinsebackPausedState function handles the rinseback paused * state operations. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: flags + * @details Outputs: flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackPausedState( void ) { RINSEBACK_STATE_T result = RINSEBACK_PAUSED_STATE; + // Has rinseback operation exceeded max time? + if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) + { + signalGoToTreatmentStopped(); + activateAlarmNoData( ALARM_ID_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); + } + else if ( TRUE == resumeRinsebackRequested ) + { + setupForRinsebackDelivery( rinsebackRate_mL_min ); + result = RINSEBACK_RUN_STATE; + } + // Has user requested to end rinseback? + else if ( TRUE == endRinsebackRequested ) + { + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); + result = RINSEBACK_STOP_STATE; + } + return result; } /*********************************************************************//** * @brief * The handleRinsebackStoppedState function handles the stopped rinseback * state operations. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: flags + * @details Outputs: flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackStoppedState( void ) { RINSEBACK_STATE_T result = RINSEBACK_STOP_STATE; + // Have we been in this stopped state for too long w/o having delivered full blood volume back to patient? + if ( ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) && ( getRinsebackVolume() < rinsebackTargetVolume_mL ) ) + { + signalGoToTreatmentStopped(); + activateAlarmNoData( ALARM_ID_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); + } + // Have we been in this stopped state for too long despite having delivered full blood volume back to patient? + else if ( ( rinsebackTimerCtr > MAX_RINSEBACK_DONE_TIME ) && ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) ) + { + signalGoToTreatmentStopped(); + activateAlarmNoData( ALARM_ID_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); + clearAlarm( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_WARNING ); + } + // Have we been in this stopped state for too long despite having delivered full blood volume back to patient? + else if ( ( RINSEBACK_DONE_WARNING_TIME == rinsebackTimerCtr ) && ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) ) + { + activateAlarmNoData( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_WARNING ); + } + else if ( TRUE == recircRequested ) + { + signalRinsebackToRecirc(); + } + else if ( TRUE == additionalRinsebackRequested ) + { + additionalRinsebackRequested = FALSE; + // deliver additional rinseback volume only if max volume not reached and max time not reached + if ( ( rinsebackTimerCtr < MAX_RINSEBACK_TIME ) || ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) ) + { + rinsebackAdditionalTimerCtr = 0; + additionalRinsebackVolume_mL = 0.0; + targetRinsebackVolumePlusAdditional_mL += TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML; + setupForRinsebackDelivery( MIN_RINSEBACK_FLOW_RATE_ML_MIN ); + result = RINSEBACK_RUN_ADDITIONAL_STATE; + } + } + else if ( TRUE == backToTreatmentRequested ) + { + signalGoToTreatmentStopped(); + } + else if ( TRUE == endTreatmentRequested ) + { + signalEndTreatment(); + } + return result; } /*********************************************************************//** * @brief * The handleRinsebackRunAdditionalState function handles the rinseback additional * state operations. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: flags + * @details Outputs: flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackRunAdditionalState( void ) { RINSEBACK_STATE_T result = RINSEBACK_RUN_ADDITIONAL_STATE; + F32 rinsebackVolumeSinceLast = ( getMeasuredBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); + // Reset rinseback t/o + rinsebackTimerCtr = 0; + + // update additional rinseback volume delivered so far + additionalRinsebackVolume_mL += rinsebackVolumeSinceLast; + totalAdditionalRinsebackVolume_mL += rinsebackVolumeSinceLast; + cumulativeRinsebackVolume_mL.data += rinsebackVolumeSinceLast; + rinsebackAdditionalTimerCtr++; + + // Check for empty saline bag + if ( TRUE == isSalineBagEmpty() ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); + result = RINSEBACK_STOP_STATE; + } + // Has additional rinseback completed + else if ( additionalRinsebackVolume_mL >= TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) + { + result = RINSEBACK_STOP_STATE; + } + // Has additional rinseback timed out? + else if ( rinsebackAdditionalTimerCtr >= MAX_RINSEBACK_ADDITIONAL_TIME ) + { + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); + result = RINSEBACK_STOP_STATE; + } + // Has alarm requested stop? + else if ( TRUE == doesAlarmStatusIndicateStop() ) + { + targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); + result = RINSEBACK_STOP_STATE; + } + + // Setup for rinseback stop state if additional rinseback completed or ending. + if ( RINSEBACK_STOP_STATE == result ) + { + setupForRinsebackStopOrPause(); + // If this additional rinseback volume has brought us to "full" rinseback volume, set flag + if ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) + { + setRinsebackIsCompleted( TRUE ); + } + } + return result; } /*********************************************************************//** * @brief - * The signalStopRinseback function signals the rinseback sub-mode - * to stop per an active alarm. - * @details Inputs: none - * @details Outputs: rinsebackStopRequested - * @return none - *************************************************************************/ -void signalStopRinseback( void ) -{ - rinsebackStopRequested = TRUE; -} - -/*********************************************************************//** - * @brief * The signalRinsebackUserAction function signals a rinseback user action * has been requested. The request is handled and responded to. * @details Inputs: none - * @details Outputs: none + * @details Outputs: action handled, request responded to * @param action User action requested * @return none *************************************************************************/ @@ -357,11 +643,11 @@ break; case REQUESTED_USER_ACTION_RINSEBACK_END_TREATMENT: - accepted = handleBackToTreatmentUserAction( &rejReason ); + accepted = handleEndTreatmentUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_BACK_TO_TREATMENT: - accepted = handleEndTreatmentUserAction( &rejReason ); + accepted = handleBackToTreatmentUserAction( &rejReason ); break; default: @@ -383,8 +669,8 @@ * The handleStartRinsebackUserAction function handles a start rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: rinseback started if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ @@ -410,22 +696,22 @@ * The handleIncrRinsebackUserAction function handles an increase rinseback * flow rate user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: rinseback rate increased if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleIncrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; - F32 rbRate = (F32)getTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); - if ( ( rbRate + RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) + if ( ( rinsebackRate_mL_min + RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) { if ( RINSEBACK_RUN_STATE == rinsebackState ) { result = TRUE; incrRinsebackFlowRateRequested = TRUE; + rinsebackPublishTimerCtr = getPublishRinsebackInterval(); } else { @@ -445,22 +731,22 @@ * The handleDecrRinsebackUserAction function handles a decrease rinseback * flow rte user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: rinseback decreased if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleDecrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; - F32 rbRate = (F32)getTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); - if ( ( rbRate + RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) + if ( ( rinsebackRate_mL_min - RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) { if ( RINSEBACK_RUN_STATE == rinsebackState ) { result = TRUE; decrRinsebackFlowRateRequested = TRUE; + rinsebackPublishTimerCtr = getPublishRinsebackInterval(); } else { @@ -480,8 +766,8 @@ * The handlePauseRinsebackUserAction function handles a pause rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: rinseback paused if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ @@ -507,8 +793,8 @@ * The handleResumeRinsebackUserAction function handles a resume rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: rinseback resumed if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ @@ -534,8 +820,8 @@ * The handleEndRinsebackUserAction function handles an end rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: rinseback ended if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ @@ -561,8 +847,8 @@ * The handleAdditionalRinsebackUserAction function handles an additional rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: additional rinseback volume started if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ @@ -572,8 +858,15 @@ if ( RINSEBACK_STOP_STATE == rinsebackState ) { - result = TRUE; - additionalRinsebackRequested = TRUE; + if ( ( totalAdditionalRinsebackVolume_mL + TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) <= MAX_TOTAL_ADDITIONAL_RINSEBACK_VOLUME_ML ) + { + result = TRUE; + additionalRinsebackRequested = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ADDL_RINSEBACK_MAX_VOLUME_REACHED; + } } else { @@ -588,24 +881,32 @@ * The handleToRecircUserAction function handles a go to re-circulate * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: transition to re-circulate if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleToRecircUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; - if ( RINSEBACK_STOP_STATE == rinsebackState ) + if ( TRUE == isTreatmentCompleted() ) { - result = TRUE; - recircRequested = TRUE; + *rejReason = REQUEST_REJECT_REASON_TREATMENT_IS_COMPLETED; } - else + else if ( RINSEBACK_STOP_STATE != rinsebackState ) { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } + else if ( getRinsebackVolume() < rinsebackTargetVolume_mL ) + { + *rejReason = REQUEST_REJECT_REASON_RINSEBACK_NOT_COMPLETED; + } + else + { + result = TRUE; + recircRequested = TRUE; + } return result; } @@ -615,24 +916,28 @@ * The handleBackToTreatmentUserAction function handles a back to treatment * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: return to treatment if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleBackToTreatmentUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; - if ( ( RINSEBACK_STOP_INIT_STATE == rinsebackState ) || ( RINSEBACK_STOP_STATE == rinsebackState ) ) + if ( TRUE == isTreatmentCompleted() ) { - result = TRUE; - backToTreatmentRequested = TRUE; + *rejReason = REQUEST_REJECT_REASON_TREATMENT_IS_COMPLETED; } - else + else if ( ( RINSEBACK_STOP_INIT_STATE != rinsebackState ) && ( RINSEBACK_STOP_STATE != rinsebackState ) ) { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } + else + { + result = TRUE; + backToTreatmentRequested = TRUE; + } return result; } @@ -642,39 +947,191 @@ * The handleEndTreatmentUserAction function handles an end treatment * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. - * @details Inputs: none - * @details Outputs: none + * @details Inputs: rinsebackState + * @details Outputs: end treatment if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleEndTreatmentUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; - if ( ( RINSEBACK_STOP_INIT_STATE == rinsebackState ) || ( RINSEBACK_STOP_STATE == rinsebackState ) ) + if ( ( RINSEBACK_STOP_INIT_STATE != rinsebackState ) && ( RINSEBACK_STOP_STATE != rinsebackState ) ) { - result = TRUE; - endTreatmentRequested = TRUE; + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } else { - *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + result = TRUE; + endTreatmentRequested = TRUE; } return result; } /*********************************************************************//** * @brief - * The getCurrentRinsebackState function returns the current state of the - * rinseback sub-mode. - * @details Inputs: rinsebackState + * The publishRinsebackData function publishes rinseback progress to UI + * at 1 Hz interval. + * @details Inputs: rinsebackPublishTimerCtr, rinsebackTimerCtr, rinsebackState, + * cumulativeRinsebackVolume_mL, rinsebackRate_mL_min + * @details Outputs: rinseback data published + * @return none + *************************************************************************/ +static void publishRinsebackData( void ) +{ + if ( ++rinsebackPublishTimerCtr >= getPublishRinsebackInterval() ) + { + RINSEBACK_DATA_PAYLOAD_T data; + U32 timeout = MAX_RINSEBACK_TIME / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + U32 countdown = ( getRinsebackVolume() >= rinsebackTargetVolume_mL ? MAX_RINSEBACK_DONE_TIME : MAX_RINSEBACK_TIME ); + + countdown -= rinsebackTimerCtr; + // Handle countdown past zero + if ( countdown > MAX_RINSEBACK_DONE_TIME ) + { + countdown = 0; + } + // Scale to seconds + countdown /= ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + + data.targetRinsebackVolumeMl = rinsebackTargetVolume_mL; + rinsebackPublishTimerCtr = 0; + // If we have completed rinseback, timeout is no longer in force - indicate by zeroing timeout. Also include any additionals to target. + if ( ( rinsebackState > RINSEBACK_PAUSED_STATE ) && ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) ) + { + data.targetRinsebackVolumeMl = targetRinsebackVolumePlusAdditional_mL; + timeout = MAX_RINSEBACK_DONE_TIME / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + } + data.deliveredRinsebackVolumeMl = getRinsebackVolume(); + data.rinsebackFlowRateMlMin = rinsebackRate_mL_min; + if ( RINSEBACK_RUN_ADDITIONAL_STATE == rinsebackState ) + { + data.rinsebackFlowRateMlMin = MIN_RINSEBACK_FLOW_RATE_ML_MIN; + } + data.timeout = timeout; + data.countdown = countdown; + data.unused = 0.0; // TODO - remove when UI no longer looks for this field + broadcastData( MSG_ID_HD_RINSEBACK_PROGRESS, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( RINSEBACK_DATA_PAYLOAD_T ) ); + } +} + +/*********************************************************************//** + * @brief + * The getPublishRinsebackInterval function gets the rinseback data + * publication interval. + * @details Inputs: rinsebackPublishInterval * @details Outputs: none - * @return rinsebackState + * @return the current rinseback publication interval (in task intervals). *************************************************************************/ -RINSEBACK_STATE_T getCurrentRinsebackState( void ) +static U32 getPublishRinsebackInterval( void ) { - return rinsebackState; + U32 result = rinsebackPublishInterval.data; + + if ( OVERRIDE_KEY == rinsebackPublishInterval.override ) + { + result = rinsebackPublishInterval.ovData; + } + + return result; } + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testSetRinsebackVolumeOverride function overrides the calculated + * rinseback volume delivered. + * @details Inputs: none + * @details Outputs: cumulativeRinsebackVolume_mL + * @param vol override calculated rinseback volume (in mL) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetRinsebackVolumeOverride( F32 vol ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + cumulativeRinsebackVolume_mL.ovData = vol; + cumulativeRinsebackVolume_mL.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetRinsebackVolumeOverride function resets the override of the + * calculated rinseback volume. + * @details Inputs: none + * @details Outputs: cumulativeRinsebackVolume_mL + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetRinsebackVolumeOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + cumulativeRinsebackVolume_mL.override = OVERRIDE_RESET; + cumulativeRinsebackVolume_mL.ovData = cumulativeRinsebackVolume_mL.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetRinsebackPublishIntervalOverride function sets the override of the + * rinseback data publication interval. + * @details Inputs: none + * @details Outputs: rinsebackPublishInterval + * @param ms milliseconds between rinseback broadcasts + * @return TRUE if override set successful, FALSE if not + *************************************************************************/ +BOOL testSetRinsebackPublishIntervalOverride( U32 ms ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = ms / TASK_GENERAL_INTERVAL; + + result = TRUE; + rinsebackPublishInterval.ovData = intvl; + rinsebackPublishInterval.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetRinsebackPublishIntervalOverride function resets the override of the + * rinseback data publication interval. + * @details Inputs: none + * @details Outputs: rinsebackPublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetRinsebackPublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + rinsebackPublishInterval.override = OVERRIDE_RESET; + rinsebackPublishInterval.ovData = rinsebackPublishInterval.ovInitData; + } + + return result; +} + /**@}*/