Index: firmware/App/Modes/ModeTreatment.c =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -30,6 +30,8 @@ #include "StateTxBloodPrime.h" #include "StateTxDialysis.h" #include "StateTxPaused.h" +#include "StateTxRecirc.h" +#include "StateTxRinseback.h" //#include "Switches.h" #include "TaskGeneral.h" #include "Timers.h" @@ -91,9 +93,13 @@ static BOOL resumeTreatmentAlarmResponseRequest; ///< Flag indicates user has requested treatment resume. static BOOL initiateRinsebackAlarmResponseRequest; ///< Flag indicates user has requested rinseback. static BOOL endTreatmentAlarmResponseRequest; ///< Flag indicates user has requested treatment end. +static BOOL initiateTempBreakALarmReponseRequest; ///< Flag indicates user has requested temporary break. static BOOL endTreatmentRequest; ///< Flag indicates user has requested end of treatment from rinseback workflow. static BOOL bloodPrimeToDialysisRequest; ///< Flag indicates blood prime has completed and time to start dialysis. static BOOL resumeBlockedByAlarm; ///< Flag indicates an alarm has blocked resume of this treatment. +static BOOL rinsebackToRecircRequest; ///< Flag indicates rinseback sub-mode has requested transition to Re-circulate. +static BOOL recirculateToBloodPrimeRequest; ///< Flag indicates re-circulate sub-mode has requested transition to Treatment Blood prime. +static BOOL recircAllowed; ///< Flag indicates re-circulate allowed only if user selects temp break option. static U32 treatmentStartTimeStamp; ///< Treatment start timestamp for logging purpose. static U32 treatmentEndTimeStamp; ///< Treatment end timestamp for logging purpose. @@ -131,6 +137,7 @@ treatmentCompleted = FALSE; rinsebackDone = FALSE; resumeBlockedByAlarm = FALSE; + recircAllowed = FALSE; treatmentTimePublishInterval.data = TREATMENT_TIME_DATA_PUB_INTERVAL; treatmentTimePublishInterval.ovData = TREATMENT_TIME_DATA_PUB_INTERVAL; @@ -185,8 +192,8 @@ initBloodPrime(); initDialysis(); initTreatmentPaused(); -// initRinseback(); -// initTreatmentRecirc(); + initRinseback(); + initTreatmentRecirc(); // initTreatmentEnd(); initFluidBolus(); @@ -266,6 +273,20 @@ /*********************************************************************//** * @brief + * The isRecircAllowed function determines whether the recirculation is + * allowed or not. Only allowed in temporary break option + * seen an alarm that prevents resumption. + * @details \b Inputs: recircAllowed + * @details \b Outputs: none + * @return isRecircAllowed + *************************************************************************/ +BOOL isRecircAllowed( void ) +{ + return recircAllowed; +} + +/*********************************************************************//** + * @brief * The getTreatmentTimeRemainingSecs function determines the number of seconds * remaining in the treatment. * @details \b Inputs: presTreatmentTimeSecs, treatmentTimeMS @@ -349,6 +370,35 @@ /*********************************************************************//** * @brief + * The setRinsebackIsCompleted function sets that flag indicating whether a + * rinseback has been completed. Call this function with TRUE when rinseback + * operation is completed. Call this function with FALSE at start of blood + * prime operation. + * @details Inputs: none + * @details Outputs: rinsebackDone + * @param flag TRUE if rinseback has been completed, FALSE if not + * @return none + *************************************************************************/ +void setRinsebackIsCompleted( BOOL flag ) +{ + rinsebackDone = flag; +} + +/*********************************************************************//** + * @brief + * The getRinsebackIsCompleted function determines whether a rinseback has been + * completed (indicating blood-side circuit should be primarily saline or online fluid). + * @details Inputs: rinsebackDone + * @details Outputs: none + * @return rinsebackDone + *************************************************************************/ +BOOL getRinsebackIsCompleted( void ) +{ + return rinsebackDone; +} + +/*********************************************************************//** + * @brief * The signalAlarmActionToTreatmentMode function executes the given alarm action * as appropriate while in Treatment Mode. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid alarm action given. @@ -381,6 +431,10 @@ // Nothing to be done here break; + case ALARM_ACTION_TEMPORARY_BREAK: + initiateTempBreakALarmReponseRequest = TRUE; + break; + default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_ALARM_ACTION, (U32)action ) break; @@ -415,6 +469,32 @@ /*********************************************************************//** * @brief + * The signalGoToBloodPrime function signals that user wants to return + * to treatment blood prime from recirculate workflow. + * @details Inputs: none + * @details Outputs: + * @return none + *************************************************************************/ +void signalGoToBloodPrime( void ) +{ + recirculateToBloodPrimeRequest = TRUE; +} + +/*********************************************************************//** + * @brief + * The signalRinsebackToRecirc function signals that user wants to continue + * on to re-circulate portion of rinseback workflow. + * @details Inputs: none + * @details Outputs: rinsebackToRecircRequest + * @return none + *************************************************************************/ +void signalRinsebackToRecirc( void ) +{ + rinsebackToRecircRequest = TRUE; +} + +/*********************************************************************//** + * @brief * The execTreatmentMode function executes the Treatment Mode state machine. * @details \b Alarm: ALARM_ID_TD_TREATMENT_PED_BY_USER if user presses * the button @@ -585,6 +665,9 @@ // Handle signals from blood prime sub-mode if ( TRUE == bloodPrimeToDialysisRequest ) { + F32 rinsebackVolume_L = getDeliveredRinsebackVolume() / (F32)ML_PER_LITER; + F32 addToUFVolume = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) + rinsebackVolume_L; + lastTreatmentTimeStamp = getMSTimerCount(); // Kick dialysis sub-mode off setDialysisBloodPumpFlowRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ) ); @@ -594,6 +677,15 @@ // { // sendTreatmentLogEventData( UF_START_RESUME_EVENT, 0.0, presUFRate ); // } + + // Add only if rinseback is completed + if ( TRUE == getRinsebackIsCompleted() ) + { + // Add rinseback volume to UF volume (fluid in circuit goes back to the patient when we resume treatment) + setTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME, addToUFVolume ); + setRinsebackIsCompleted( FALSE ); + } + transitionToDialysis(); // // To update partial blood pump occlusion baseline - start of treatment // signalBloodPumpPressureOcclBaseline(); @@ -686,18 +778,28 @@ } signalInitiatePressureStabilization( USE_NORMAL_STABILIZATION_PERIOD ); } - // If user requests rinseback, go to rinseback -// else if ( TRUE == initiateRinsebackAlarmResponseRequest ) -// { -// transitionToRinseback(); -// result = TREATMENT_RINSEBACK_STATE; -// } + // If user requests temp break, go to rinseback and then recirc + else if ( TRUE == initiateTempBreakALarmReponseRequest ) + { + initiateTempBreakALarmReponseRequest = FALSE; + recircAllowed = TRUE; + transitionToRinseback(); + result = TREATMENT_RINSEBACK_STATE; + } + // If user requests blood return & end treatemnt, go to rinseback only and end treat + else if ( TRUE == initiateRinsebackAlarmResponseRequest ) + { + initiateRinsebackAlarmResponseRequest = FALSE; + recircAllowed = FALSE; + transitionToRinseback(); + result = TREATMENT_RINSEBACK_STATE; + } // If user requests treatment end, end treatment -// else if ( TRUE == endTreatmentAlarmResponseRequest ) -// { -// sendLastTreatmentPeriodicData = TRUE; -// requestNewOperationMode( MODE_POST ); -// } + else if ( TRUE == endTreatmentAlarmResponseRequest ) + { + //sendLastTreatmentPeriodicData = TRUE; + requestNewOperationMode( MODE_POST ); + } // Otherwise execute state machine for treatment paused sub-mode else { @@ -723,89 +825,76 @@ * The handleTreatmentRinsebackState function executes the rinseback state of the * Treatment Mode state machine. * @details \b Inputs: endTreatmentAlarmResponseRequest, resumeTreatmentAlarmResponseRequest, - * rinsebackToRecircRequest, rinsebackTopedRequest, endTreatmentRequest + * rinsebackToRecircRequest * @details \b Outputs: treatment rinseback sub-mode executed, sendLastTreatmentPeriodicData * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentRinsebackState( void ) { TREATMENT_STATE_T result = TREATMENT_RINSEBACK_STATE; -// // If user requests treatment end, end treatment -// if ( TRUE == endTreatmentAlarmResponseRequest ) -// { -// sendLastTreatmentPeriodicData = TRUE; -// requestNewOperationMode( MODE_POST ); -// } -// // Otherwise execute state machine for treatment rinseback sub-mode -// else -// { -// // If user requests resumption of treatment, resume rinseback -// if ( TRUE == resumeTreatmentAlarmResponseRequest ) -// { -// resumeTreatmentAlarmResponseRequest = FALSE; -// signalRinsebackAlarmResumeUserAction(); -// } -// // Execute treatment rinseback sub-mode -// execRinseback(); -// } -// -// // Handle signals from rinseback sub-mode -// if ( TRUE == rinsebackToRecircRequest ) -// { -// transitionToTreatmentRecirc(); -// result = TREATMENT_RECIRC_STATE; -// } -// else if ( TRUE == rinsebackTopedRequest ) -// { -// transitionToTreatment(); -// result = TREATMENT_PAUSED_STATE; -// } -// else if ( TRUE == endTreatmentRequest ) -// { -// requestNewOperationMode( MODE_POST ); -// } + // If user requests treatment end, end treatment + if ( TRUE == endTreatmentAlarmResponseRequest ) + { + //sendLastTreatmentPeriodicData = TRUE; + requestNewOperationMode( MODE_POST ); + } + else + { + // Forward alarm resume action to rinseback sub-mode as a rinseback specific resume + if ( TRUE == resumeTreatmentAlarmResponseRequest ) + { + resumeTreatmentAlarmResponseRequest = FALSE; + signalRinsebackAlarmResumeUserAction(); + } + // Execute treatment rinseback sub-mode + execRinseback(); + } + + // Handle signals from rinseback sub-mode + if ( TRUE == rinsebackToRecircRequest ) + { + transitionToTreatmentRecirc(); + result = TREATMENT_RECIRC_STATE; + } + return result; } /*********************************************************************//** * @brief * The handleTreatmentRecircState function executes the re-circulate state of the * Treatment Mode state machine. - * @details \b Inputs: endTreatmentAlarmResponseRequest, rinsebackTopedRequest, - * endTreatmentRequest + * @details \b Inputs: endTreatmentAlarmResponseRequest, recirculateToBloodPrimeRequest * @details \b Outputs: sendLastTreatmentPeriodicData * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentRecircState( void ) { TREATMENT_STATE_T result = TREATMENT_RECIRC_STATE; -// // If user requests treatment end, end treatment -// if ( TRUE == endTreatmentAlarmResponseRequest ) -// { -// sendLastTreatmentPeriodicData = TRUE; -// requestNewOperationMode( MODE_POST ); -// } -// else -// { -// // Execute treatment re-circ sub-mode -// execTreatmentRecirc(); -// } -// -// // Handle signals from treatment re-circ sub-mode -// if ( TRUE == rinsebackTopedRequest ) -// { -// Dialysis(); -// transitionToTreatment(); -// result = TREATMENT__STATE; -// } -// else if ( TRUE == endTreatmentRequest ) -// { -// requestNewOperationMode( MODE_POST ); -// } + // If user requests treatment end, end treatment + if ( TRUE == endTreatmentAlarmResponseRequest ) + { + //sendLastTreatmentPeriodicData = TRUE; + // Stop dialysate generation + cmdStopGenerateDialysate(); + requestNewOperationMode( MODE_POST ); + } + else + { + // Execute treatment re-circ sub-mode + execTreatmentRecirc(); + } + // Back to blood prime when user requests resume treatment + if ( TRUE == recirculateToBloodPrimeRequest ) + { + transitionToBloodPrime(); + result = TREATMENT_BLOOD_PRIME_STATE; + } + return result; } @@ -910,8 +999,8 @@ payload.dialysisState = getDialysisState(); payload.isoUFState = 0; payload.txPausedState = getCurrentTreatmentPausedState(); - payload.rinsebackState = 0; // getCurrentRinsebackState(); - payload.txRecircState = 0; // getCurrentTreatmentRecircState(); + payload.rinsebackState = getCurrentRinsebackState(); + payload.txRecircState = getCurrentTreatmentRecircState(); payload.txEndState = 0; // getCurrentTreatmentEndState(); payload.txFluidBolusState = getCurrentFluidBolusState(); payload.txHepState = 0; // @@ -1095,11 +1184,11 @@ *************************************************************************/ static void resetSignalFlags( void ) { -// rinsebackTopedRequest = FALSE; endTreatmentRequest = FALSE; -// rinsebackToRecircRequest = FALSE; + rinsebackToRecircRequest = FALSE; bloodPrimeToDialysisRequest = FALSE; // treatmentEndToRinsebackRequest = FALSE; + recirculateToBloodPrimeRequest = FALSE; } /*********************************************************************//** @@ -1114,6 +1203,7 @@ resumeTreatmentAlarmResponseRequest = FALSE; initiateRinsebackAlarmResponseRequest = FALSE; endTreatmentAlarmResponseRequest = FALSE; + initiateTempBreakALarmReponseRequest = FALSE; } /*********************************************************************//** Index: firmware/App/Modes/ModeTreatment.h =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Modes/ModeTreatment.h (.../ModeTreatment.h) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Modes/ModeTreatment.h (.../ModeTreatment.h) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -111,6 +111,8 @@ void signalAlarmActionToTreatmentMode( ALARM_ACTION_T action ); // Execute alarm action as appropriate for treatment mode void signalEndTreatment( void ); // Requesting transition to post-treatment mode void signalBloodPrimeToDialysis( void ); // Blood prime sub-mode is signaling to move on to dialysis sub-mode +void signalRinsebackToRecirc( void ); // Rinseback sub-mode is signaling to move on to re-circ sub-mode +void signalGoToBloodPrime( void ); // Re-circ sub-mode is signaling to return to treatment blood prime sub-mode void broadcastTreatmentTimeAndState( void ); // Broadcast the times and states of this treatment void updateTreatmentBroadcastData( F32 ufRateLhr ); // Update treatment broadcast data from current treatment parameters @@ -122,6 +124,9 @@ U32 getActualTreatmentTimeSecs( void ); // Determine the actual treatment duration in seconds void setBloodIsPrimed( BOOL flag ); // Set whether the blood-side circuit of the dialyzer has been primed with blood (F causes Tx start/resume to do blood prime first) BOOL getBloodIsPrimed( void ); // Determine whether the blood-side circuit of the dialyzer has been primed with blood +void setRinsebackIsCompleted( BOOL flag ); // Set whether a rinseback has been completed (TD blocks rinseback option) +BOOL getRinsebackIsCompleted( void ); // Determine whether a rinseback has been completed +BOOL isRecircAllowed( void ); // Determine whether the re-circulation is allowed or not. U32 getTreatmentStartTimeStamp( void ); // Get the treatment start time stamp U32 getTreatmentEndTimeStamp( void ); // Get the treatment end time stamp Index: firmware/App/Modes/OperationModes.c =================================================================== diff -u -r70f10c77f4f9a48d51030c00504d520fe25d0ba9 -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Modes/OperationModes.c (.../OperationModes.c) (revision 70f10c77f4f9a48d51030c00504d520fe25d0ba9) +++ firmware/App/Modes/OperationModes.c (.../OperationModes.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -41,6 +41,7 @@ #define BROADCAST_TD_OP_MODE_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< TD operation mode broadcast interval (in task interval/sec). #define DATA_PUBLISH_COUNTER_START_COUNT 11 ///< Data publish counter start count. #define MAX_PENDING_CONFIRM_REQUESTS 4 ///< Maximum number of confirmation requests allowed at any given time. +#define CONFIRMATION_DIALOG_TIMEOUT_MS ( 60U * MS_PER_SECOND ) ///< Timeout period for user confirmation dialogs (ms). /// Structure for confirmation request. typedef struct @@ -61,6 +62,8 @@ static U32 currentSubState; ///< current sub state. static U32 current4thLevelState; ///< current 4th level state. +static U32 pendingConfirmIndex; ///< Index of pending confirm requests + /// Interval (in task intervals) at which to publish operation mode data to CAN bus. static OVERRIDE_U32_T opModePublishInterval; /// Local structure init for saving confirmation requests @@ -116,6 +119,7 @@ opModePublishInterval.ovData = BROADCAST_TD_OP_MODE_INTERVAL; opModePublishInterval.ovInitData = BROADCAST_TD_OP_MODE_INTERVAL; opModePublishInterval.override = OVERRIDE_RESET; + pendingConfirmIndex = MAX_PENDING_CONFIRM_REQUESTS; transitionToNewOperationMode( MODE_INIT ); @@ -460,59 +464,60 @@ *************************************************************************/ CONFIRMATION_REQUEST_STATUS_T getConfirmationRequestStatus( GENERIC_CONFIRM_ID_T requestID ) { -// BOOL pending = FALSE; -// U32 pendingIndex = 0; + BOOL pending = FALSE; + U32 pendingIndex = 0; CONFIRMATION_REQUEST_STATUS_T status = CONFIRMATION_REQUEST_STATUS_PENDING; -// U32 i; + U32 i; -// for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) -// { -// if ( confirmRequests[ i ].requestID == requestID ) -// { -// status = confirmRequests[ i ].status; -// if ( status != CONFIRMATION_REQUEST_STATUS_PENDING ) -// { -// // Send UI clear -// if ( CONFIRMATION_REQUEST_STATUS_TIMEOUT == status ) -// { -// sendConfirmationRequest( confirmRequests[ i ].requestID, GENERIC_CONFIRM_CMD_TIMEOUT_CLOSE, 0 ); -// } -// else -// { -// sendConfirmationRequest( confirmRequests[ i ].requestID, GENERIC_CONFIRM_CMD_ACCEPT_CLOSE, 0 ); -// } -// -// // Clear the confirmation request, it is done and consumed -// confirmRequests[ i ].requestID = GENERIC_CONFIRM_ID_NONE; -// confirmRequests[ i ].requestType = GENERIC_CONFIRM_CMD_REQUEST_OPEN; -// confirmRequests[ i ].timeStamp = 0; -// confirmRequests[ i ].status = CONFIRMATION_REQUEST_STATUS_UNUSED; -// } -// } -// else if ( CONFIRMATION_REQUEST_STATUS_PENDING == confirmRequests[ i ].status ) -// { -// if ( TRUE == pending ) -// { -// // Is this newer than other pending request? -// if ( confirmRequests[ i ].timeStamp > confirmRequests[ pendingIndex ].timeStamp ) -// { -// pendingIndex = i; -// } -// } -// else -// { -// pendingIndex = i; -// pending = TRUE; -// } -// } -// } -// -// if ( ( CONFIRMATION_REQUEST_STATUS_PENDING != status ) && ( TRUE == pending ) ) -// { -// // Last confirmation cleared, pending request must be resent to UI -// sendConfirmationRequest( confirmRequests[ pendingIndex ].requestID, confirmRequests[ pendingIndex ].requestType, 0 ); -// } + for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) + { + if ( confirmRequests[ i ].requestID == requestID ) + { + status = confirmRequests[ i ].status; + if ( status != CONFIRMATION_REQUEST_STATUS_PENDING ) + { + // Send UI clear + if ( CONFIRMATION_REQUEST_STATUS_TIMEOUT == status ) + { + sendConfirmationRequest( confirmRequests[ i ].requestID, GENERIC_CONFIRM_CMD_TIMEOUT_CLOSE, 0 ); + } + else + { + sendConfirmationRequest( confirmRequests[ i ].requestID, GENERIC_CONFIRM_CMD_ACCEPT_CLOSE, 0 ); + } + // Clear the confirmation request, it is done and consumed + confirmRequests[ i ].requestID = GENERIC_CONFIRM_ID_NONE; + confirmRequests[ i ].requestType = GENERIC_CONFIRM_CMD_REQUEST_OPEN; + confirmRequests[ i ].timeStamp = 0; + confirmRequests[ i ].status = CONFIRMATION_REQUEST_STATUS_UNUSED; + pendingConfirmIndex = MAX_PENDING_CONFIRM_REQUESTS; + } + } + else if ( CONFIRMATION_REQUEST_STATUS_PENDING == confirmRequests[ i ].status ) + { + if ( TRUE == pending ) + { + // Is this newer than other pending request? + if ( confirmRequests[ i ].timeStamp > confirmRequests[ pendingIndex ].timeStamp ) + { + pendingIndex = i; + } + } + else + { + pendingIndex = i; + pending = TRUE; + } + } + } + + if ( ( CONFIRMATION_REQUEST_STATUS_PENDING != status ) && ( TRUE == pending ) ) + { + // Last confirmation cleared, pending request must be resent to UI + sendConfirmationRequest( confirmRequests[ pendingIndex ].requestID, confirmRequests[ pendingIndex ].requestType, 0 ); + } + return status; } @@ -551,40 +556,41 @@ *************************************************************************/ GENERIC_CONFIRM_ID_T addConfirmationRequest( GENERIC_CONFIRM_ID_T requestID, GENERIC_CONFIRM_COMMAND_T requestType, U32 rejectReason ) { -// U32 i; -// BOOL confirmAlreadyPending = FALSE; + U32 i; + BOOL confirmAlreadyPending = FALSE; GENERIC_CONFIRM_ID_T newID = GENERIC_CONFIRM_ID_NONE; -// // Check to make sure specified confirmation is not already pending -// for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) -// { -// if ( ( confirmRequests[ i ].requestID == requestID ) && -// ( confirmRequests[ i ].status != CONFIRMATION_REQUEST_STATUS_UNUSED ) ) -// { -// confirmAlreadyPending = TRUE; -// break; -// } -// } -// -// // If not already pending, add confirmation to list of pending confirmations and send to UI to be displayed -// if ( confirmAlreadyPending != TRUE ) -// { -// for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) -// { -// if ( CONFIRMATION_REQUEST_STATUS_UNUSED == confirmRequests[ i ].status ) -// { -// // Save the confirmation request info -// confirmRequests[ i ].requestID = requestID; -// confirmRequests[ i ].requestType = requestType; -// confirmRequests[ i ].timeStamp = getMSTimerCount(); -// confirmRequests[ i ].status = CONFIRMATION_REQUEST_STATUS_PENDING; -// newID = requestID; -// sendConfirmationRequest( requestID, requestType, rejectReason ); -// break; -// } -// } -// } + // Check to make sure specified confirmation is not already pending + for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) + { + if ( ( confirmRequests[ i ].requestID == requestID ) && + ( confirmRequests[ i ].status != CONFIRMATION_REQUEST_STATUS_UNUSED ) ) + { + confirmAlreadyPending = TRUE; + break; + } + } + // If not already pending, add confirmation to list of pending confirmations and send to UI to be displayed + if ( confirmAlreadyPending != TRUE ) + { + for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) + { + if ( CONFIRMATION_REQUEST_STATUS_UNUSED == confirmRequests[ i ].status ) + { + // Save the confirmation request info + confirmRequests[ i ].requestID = requestID; + confirmRequests[ i ].requestType = requestType; + confirmRequests[ i ].timeStamp = getMSTimerCount(); + confirmRequests[ i ].status = CONFIRMATION_REQUEST_STATUS_PENDING; + newID = requestID; + pendingConfirmIndex = i; + sendConfirmationRequest( requestID, requestType, rejectReason ); + break; + } + } + } + return newID; } @@ -641,7 +647,48 @@ sendEvent( TD_EVENT_OPERATION_STATUS, dat1, dat2 ); } +/*********************************************************************//** + * @brief + * The handleConfirmationDialogTimeout function checks if a pending + * confirmation dialog has exceeded the timeout period and triggers + * a timeout if so. + * @details \b Inputs: confirmRequests[], pendingConfirmIndex + * @details \b Outputs: confirmRequests[], pendingConfirmIndex + * @return none + *************************************************************************/ +void handleConfirmationDialogTimeout( void ) +{ + if ( ( pendingConfirmIndex < MAX_PENDING_CONFIRM_REQUESTS ) && + ( CONFIRMATION_REQUEST_STATUS_PENDING == confirmRequests[ pendingConfirmIndex ].status ) && + ( TRUE == didTimeout( confirmRequests[ pendingConfirmIndex ].timeStamp, CONFIRMATION_DIALOG_TIMEOUT_MS ) ) ) + { + confirmRequests[ pendingConfirmIndex ].status = CONFIRMATION_REQUEST_STATUS_TIMEOUT; + pendingConfirmIndex = MAX_PENDING_CONFIRM_REQUESTS; + } +} +/*********************************************************************//** + * @brief + * The getPendingConfirmationID function returns the ID of the currently + * pending confirmation dialog + * @details \b Inputs: pendingConfirmIndex, confirmRequests[] + * @details \b Outputs: none + * @return ID of the currently pending confirmation dialog, or + * GENERIC_CONFIRM_ID_NONE if no confirmation dialog is pending. + *************************************************************************/ +GENERIC_CONFIRM_ID_T getPendingConfirmationID( void ) +{ + GENERIC_CONFIRM_ID_T result = GENERIC_CONFIRM_ID_NONE; + + if ( pendingConfirmIndex < MAX_PENDING_CONFIRM_REQUESTS ) + { + result = confirmRequests[ pendingConfirmIndex ].requestID; + } + + return result; +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ Index: firmware/App/Modes/OperationModes.h =================================================================== diff -u -r395522dffef1348e176564925656012f529c1910 -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Modes/OperationModes.h (.../OperationModes.h) (revision 395522dffef1348e176564925656012f529c1910) +++ firmware/App/Modes/OperationModes.h (.../OperationModes.h) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -40,6 +40,18 @@ U32 subMode; ///< Current sub-mode of current operating mode } OP_MODE_PAYLOAD_T; +/// Payload structure for generic confirmation request +typedef struct +{ + U32 requestID; ///< Generic request ID. + U32 requestType; ///< Generic request type. + U32 rejectReason; ///< Reject reason. + F32 genericPayload1; ///< Generic payload 1. + F32 genericPayload2; ///< Generic payload 2. + F32 genericPayload3; ///< Generic payload 3. + F32 genericPayload4; ///< Generic payload 4. +} GENERIC_CONFIRMATION_REQUEST_T; + // ********** public function prototypes ********** void initOperationModes( void ); // Initialize this unit @@ -61,6 +73,9 @@ void sendOperationStatusEvent( void ); // Constructs and sends operation status event +void handleConfirmationDialogTimeout( void ); // Handle timeout for confirmation dialog +GENERIC_CONFIRM_ID_T getPendingConfirmationID( void ); // Get the pending confirmation id + BOOL testSetOperationMode( MESSAGE_T *message ); // Force transition to a given mode (if allowed) BOOL testSetOpModePublishIntervalOverride( MESSAGE_T *message ); // Override op mode data publish interval Index: firmware/App/Modes/StateTxBloodPrime.c =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Modes/StateTxBloodPrime.c (.../StateTxBloodPrime.c) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Modes/StateTxBloodPrime.c (.../StateTxBloodPrime.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -156,6 +156,7 @@ { // Set user alarm recovery actions allowed in this state setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_TEMPORARY_BREAK, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); Index: firmware/App/Modes/StateTxDialysis.c =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Modes/StateTxDialysis.c (.../StateTxDialysis.c) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Modes/StateTxDialysis.c (.../StateTxDialysis.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -117,6 +117,7 @@ // Set user alarm recovery actions allowed in this sub-mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_TEMPORARY_BREAK, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); Index: firmware/App/Modes/StateTxPaused.c =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Modes/StateTxPaused.c (.../StateTxPaused.c) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Modes/StateTxPaused.c (.../StateTxPaused.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -102,14 +102,16 @@ setCurrent4thLevelState( NO_SUB_STATE ); // Set user alarm recovery actions allowed in this sub-mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); - if ( FALSE ) // TODO ( TRUE == getRinsebackCompleted() ) + if ( TRUE == getRinsebackIsCompleted() ) { // block rinseback action if already done setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_TEMPORARY_BREAK, FALSE ); } else { setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_TEMPORARY_BREAK, TRUE ); } setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); Index: firmware/App/Modes/StateTxRecirc.c =================================================================== diff -u --- firmware/App/Modes/StateTxRecirc.c (revision 0) +++ firmware/App/Modes/StateTxRecirc.c (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -0,0 +1,584 @@ +/************************************************************************** +* +* Copyright (c) 2025-2026 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 StateTxRecirc.c +* +* @author (last) Praneeth Bunne +* @date (last) 29-Jun-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 29-Jun-2026 +* +***************************************************************************/ + +#include "AirTrap.h" +#include "BloodFlow.h" +#include "DDInterface.h" +#include "Messaging.h" +#include "ModeTreatment.h" +#include "OperationModes.h" +#include "StateTxRecirc.h" +#include "SyringePump.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "Valves.h" + +/** + * @addtogroup StateTxRecirc + * @{ + */ + +// ********** private definitions ********** + +/// Target flow rate for re-circulation of saline on blood-side circuit. +#define RECIRC_BP_FLOW_RATE_ML_MIN 100 + +/// Re-circulation status broadcast interval. +#define RECIRC_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) + +/// Conversion from minutes to task ticks +#define MIN_TO_TICKS( min ) ( min * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) + +/// Recirc warning before 10 mins from timeout +#define RECIRC_WARNING_LEAD_TIME_MIN 10U + +// ********** private data ********** + +static TREATMENT_RECIRC_STATE_T currentRecircState; ///< Current state of the treatment re-circulate sub-mode. + +static U32 recircTimerCtr; ///< Timer counter for time spent in re-circ sub-mode. +static U32 recircPublishTimerCtr; ///< Timer counter for next status broadcast. +static U32 recircWarningTicks; ///< Timeout Warning threshold (in ticks). +static U32 recircTimeoutTicks; ///< Timeout threshold (in ticks). + +static BOOL startRecircRequested; ///< Flag indicates user requesting to start re-circulating. +static BOOL reconnectRequested; ///< Flag indicates user requesting to stop re-circulating and prepare for patient reconnect. +static BOOL backToTreatmentRequested; ///< Flag indicates user confirmed reconnected, requesting to return to treatment. +static BOOL resumeRecircRequested; ///< Flag indicates user requesting resumption of re-circulation from stopped state. +static BOOL recircWarningAlarmFired; ///< Flag indicates warning alarm fired. +static BOOL recircTimeoutAlarmFired; ///< Flag indicates timeout alarm fired. + +// ********** private function prototypes ********** + +static void resetTreatmentRecircFlags( void ); +static void setupForRecirculationState( void ); +static void setupForRecirculationStopState( void ); +static void publishTreatmentRecircData( void ); +static void handleRecircTimeouts( void ); + +static TREATMENT_RECIRC_STATE_T handleRecircDisconnectPatientState( void ); +static TREATMENT_RECIRC_STATE_T handleRecircRecircState( void ); +static TREATMENT_RECIRC_STATE_T handleRecircStoppedState( void ); +static TREATMENT_RECIRC_STATE_T handleRecircReconnectPatientState( void ); + +static BOOL handleRecircConfirmDisconnectUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleRecircResumeUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleRecircReconnectUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleRecircConfirmReconnectUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); + +/*********************************************************************//** + * @brief + * The initTreatmentRecirc function initializes the Treatment Re-circulate + * sub-mode module. + * @details Inputs: none + * @details Outputs: Treatment Re-circulate sub-mode module initialized. + * @return none + *************************************************************************/ +void initTreatmentRecirc( void ) +{ + currentRecircState = TREATMENT_RECIRC_DISCONNECT_PATIENT_STATE; + recircTimerCtr = 0; + recircPublishTimerCtr = RECIRC_DATA_PUBLISH_INTERVAL - 9; + recircWarningAlarmFired = FALSE; + recircTimeoutAlarmFired = FALSE; + + // TODO: Replace getSysConfigTreatmentParameterU32DefaultValue() with getNVRecord2Driver() when implemented + recircWarningTicks = MIN_TO_TICKS( ( getSysConfigTreatmentParameterU32DefaultValue( TREATMENT_PARAM_TEMPORARY_BREAK_RECIRC_TIMEOUT ) ) + - ( RECIRC_WARNING_LEAD_TIME_MIN ) ); + recircTimeoutTicks = MIN_TO_TICKS( getSysConfigTreatmentParameterU32DefaultValue( TREATMENT_PARAM_TEMPORARY_BREAK_RECIRC_TIMEOUT ) ); + + resetTreatmentRecircFlags(); +} + +/*********************************************************************//** + * @brief + * The resetTreatmentRecircFlags function resets all re-circ request flags. + * @details Inputs: none + * @details Outputs: all request flags set to FALSE. + * @return none + *************************************************************************/ +static void resetTreatmentRecircFlags( void ) +{ + startRecircRequested = FALSE; + reconnectRequested = FALSE; + backToTreatmentRequested = FALSE; + resumeRecircRequested = FALSE; +} + +/*********************************************************************//** + * @brief + * The transitionToTreatmentRecirc function prepares for transition to + * Treatment Re-circulate sub-mode. + * @details Inputs: none + * @details Outputs: none + * @return none + *************************************************************************/ +void transitionToTreatmentRecirc( void ) +{ + initTreatmentRecirc(); + setCurrentSubState( (U32)currentRecircState ); + + // Set alarm actions + setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE); + setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE); + setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE); + + // Set actuators for disconnect patient state + setupForRecirculationStopState(); + + // Bypass dialyzer + cmdBypassDialyzer( TRUE ); + + // Stop syringe pump and air trap control + stopSyringePump(); + endAirTrapControl(); +} + +/*********************************************************************//** + * @brief + * The setupForRecirculationState function configures actuators for the + * active re-circulation (Recirc) state. + * @details Inputs: none + * @details Outputs: none + * @return none + *************************************************************************/ +static void setupForRecirculationState( void ) +{ + // Open arterail and venous lines + setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); + setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); + + // Start blood pump at re-circulation flow rate + setBloodPumpTargetFlowRate( RECIRC_BP_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); +} + +/*********************************************************************//** + * @brief + * The setupForRecirculationStopState function configures actuators for + * Disconnect Patient, Stopped, Reconnect Patient states. + * @details Inputs: none + * @details Outputs: none + * @return none + *************************************************************************/ +static void setupForRecirculationStopState( void ) +{ + // Stop blood pump + signalBloodPumpHardStop(); + + // Close arterail and venous lines + setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); + setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); +} + +/*********************************************************************//** + * @brief + * The handleRecircTimeouts function checks total time in re-circulate + * sub-mode and fires warning alarm and timeout alarm at 20 min and 30 min respectively. + * Only fires each alarm once per re-circulate session. + * @details \b Alarms: ALARM_ID_TD_RECIRC_TIMEOUT_WARNING if 10 mins left from timeout, + * ALARM_ID_TD_RECIRC_TIMEOUT if recirculation timeout + * @details Inputs: recircTimerCtr, recircWarningTicks, recircTimeoutTicks, recircWarningAlarmFired + * @details Outputs: recircWarningAlarmFired, recircTimeoutAlarmFired + * @return none + *************************************************************************/ +static void handleRecircTimeouts( void ) +{ + if ( ( recircTimerCtr >= recircWarningTicks ) && ( FALSE == recircWarningAlarmFired ) ) + { + recircWarningAlarmFired = TRUE; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_RECIRC_TIMEOUT_WARNING, (U32)recircTimerCtr ); + } + + if ( ( recircTimerCtr >= recircTimeoutTicks ) && ( FALSE == recircTimeoutAlarmFired ) ) + { + recircTimeoutAlarmFired = TRUE; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_RECIRC_TIMEOUT, (U32)recircTimerCtr ); + } +} + +/*********************************************************************//** + * @brief + * The execTreatmentRecirc function executes the Treatment Re-circulate + * sub-mode state machine. + * @details Inputs: currentRecircState + * @details Outputs: currentRecircState, recircTimerCtr + * @return none + *************************************************************************/ +void execTreatmentRecirc( void ) +{ + TREATMENT_RECIRC_STATE_T priorState = currentRecircState; + + // Count time in this sub-mode + recircTimerCtr++; + + // Check and fire timeout alarms (applies to all sub-states) + handleRecircTimeouts(); + + switch( currentRecircState ) + { + case TREATMENT_RECIRC_DISCONNECT_PATIENT_STATE: + currentRecircState = handleRecircDisconnectPatientState(); + break; + + case TREATMENT_RECIRC_RECIRC_STATE: + currentRecircState = handleRecircRecircState(); + break; + + case TREATMENT_RECIRC_STOPPED_STATE: + currentRecircState = handleRecircStoppedState(); + break; + + case TREATMENT_RECIRC_RECONNECT_PATIENT_STATE: + currentRecircState = handleRecircReconnectPatientState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TREATMENT_RECIRC_INVALID_STATE, (U32)currentRecircState ); + break; + } + + if ( priorState != currentRecircState ) + { + setCurrentSubState( (U32)currentRecircState ); + SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, (U32)priorState, (U32)currentRecircState ); + } + + // Re-circulate flags should be handled by now - reset in case not handled by current state + resetTreatmentRecircFlags(); + + // Broadcast recirc status + publishTreatmentRecircData(); +} + +/*********************************************************************//** + * @brief + * The handleRecircDisconnectPatientState function handles the Disconnect + * Patient state of Re-circ sub mode. Waits for user to confirm patient + * has been disconnected before starting re-circulation. + * @details Inputs: startRecircRequested + * @details Outputs: none + * @return next Treatment re-circulate state + *************************************************************************/ +static TREATMENT_RECIRC_STATE_T handleRecircDisconnectPatientState( void ) +{ + TREATMENT_RECIRC_STATE_T state = TREATMENT_RECIRC_DISCONNECT_PATIENT_STATE; + + // User requested to start re-circ? + if ( TRUE == startRecircRequested ) + { + setupForRecirculationState(); + state = TREATMENT_RECIRC_RECIRC_STATE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleRecircRecircState function handles the active Re-circulate + * state of Re-circ sub mode. + * @details Inputs: reconnectRequested + * @details Outputs: none + * @return next Treatment re-circulate state + *************************************************************************/ +static TREATMENT_RECIRC_STATE_T handleRecircRecircState( void ) +{ + TREATMENT_RECIRC_STATE_T state = TREATMENT_RECIRC_RECIRC_STATE; + + // Alarm indicates stop? + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForRecirculationStopState(); + state = TREATMENT_RECIRC_STOPPED_STATE; + } + // User requested resume treatment? + else if ( TRUE == reconnectRequested ) + { + setupForRecirculationStopState(); + state = TREATMENT_RECIRC_RECONNECT_PATIENT_STATE; + } + else + { + // Continue recirculation. + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleRecircStoppedState function handles the Stopped state of + * Re-circ sub mode. + * @details Inputs: resumeRecircRequested + * @details Outputs: none + * @return next Treatment re-circulate state + *************************************************************************/ +static TREATMENT_RECIRC_STATE_T handleRecircStoppedState( void ) +{ + TREATMENT_RECIRC_STATE_T state = TREATMENT_RECIRC_STOPPED_STATE; + + // User requested resume re-circ? + if ( TRUE == resumeRecircRequested ) + { + setupForRecirculationState(); + state = TREATMENT_RECIRC_RECIRC_STATE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleRecircReconnectPatientState function handles the Reconnect + * Patient state of Re-circ sub mode. + * @details Inputs: backToTreatmentRequested + * @details Outputs: none + * @return next Treatment re-circulate state + *************************************************************************/ +static TREATMENT_RECIRC_STATE_T handleRecircReconnectPatientState( void ) +{ + TREATMENT_RECIRC_STATE_T state = TREATMENT_RECIRC_RECONNECT_PATIENT_STATE; + + // User requested resume treatment? + if ( TRUE == backToTreatmentRequested ) + { + signalGoToBloodPrime(); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The signalTreatmentRecircUserAction function signals a re-circ user action + * has been requested. The request is handled and responded to. + * @details Inputs: none + * @details Outputs: none + * @param message UI message which has requested user action +* @return TRUE if request is accepted, FALSE if rejected. + *************************************************************************/ +BOOL signalTreatmentRecircUserAction( MESSAGE_T *message ) +{ + BOOL result = FALSE; + U32 cmd = 0; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; + UI_RESPONSE_PAYLOAD_T response; + + if ( sizeof(U32) == message->hdr.payloadLen ) + { + memcpy( &cmd, message->payload, sizeof(U32) ); + + if ( FALSE == isAnyAlarmActive() ) + { + switch ( cmd ) + { + case REQUESTED_USER_ACTION_TX_RECIRC_CONFIRM_DISCONNECT: + result = handleRecircConfirmDisconnectUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_TX_RECIRC_RECONNECT: + result = handleRecircReconnectUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_TX_RECIRC_RESUME_RC: + result = handleRecircResumeUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_TX_RECIRC_CONFIRM_RECONNECT: + result = handleRecircConfirmReconnectUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_TX_RECIRC_END_TREATMENT: + // Send message to UI to get user confirmation to end treatment - action initiated only upon receipt of user confirmation from UI + addConfirmationRequest( GENERIC_CONFIRM_ID_TREATMENT_END, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0U ); + result = TRUE; + break; + + default: + rejReason = REQUEST_REJECT_REASON_INVALID_COMMAND; + break; + } + } + else + { + rejReason = REQUEST_REJECT_REASON_ALARM_IS_ACTIVE; + } + } + else + { + rejReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + // Respond to user action request + response.accepted = result; + response.rejectionReason = rejReason; + sendMessage( MSG_ID_TD_RECIRCULATE_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * The handleRecircConfirmDisconnectUserAction function handles a confirm + * patient disconnect user action request. It is assumed that the calling + * function will set the reject reason parameter to None beforehand. + * @details Inputs: currentRecircState + * @details Outputs: startRecircRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleRecircConfirmDisconnectUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( TREATMENT_RECIRC_DISCONNECT_PATIENT_STATE == currentRecircState ) + { + startRecircRequested = TRUE; + result = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleRecircResumeUserAction function handles a re-circulate resume + * user action request. It is assumed that the calling function will set + * the reject reason parameter to None beforehand. + * @details Inputs: currentRecircState + * @details Outputs: resumeRecircRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleRecircResumeUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( TREATMENT_RECIRC_STOPPED_STATE == currentRecircState ) + { + resumeRecircRequested = TRUE; + result = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleRecircReconnectUserAction function handles a re-circulate re-connect + * user action request. It is assumed that the calling function will set + * the reject reason parameter to None beforehand. + * @details Inputs: currentRecircState + * @details Outputs: reconnectRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleRecircReconnectUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( TREATMENT_RECIRC_RECIRC_STATE == currentRecircState ) + { + reconnectRequested = TRUE; + result = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleRecicConfirmReconnectUserAction function handles a re-circulate + * back to treatment user action request. It is assumed that the calling + * function will set the reject reason parameter to None beforehand. + * @details Inputs: currentRecircState + * @details Outputs: backToTreatmentRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleRecircConfirmReconnectUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( TREATMENT_RECIRC_RECONNECT_PATIENT_STATE != currentRecircState ) + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + else if ( FALSE == isRecircAllowed() ) + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + else + { + backToTreatmentRequested = TRUE; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getCurrentTreatmentRecircState function returns the current state + * of the treatment re-circulate sub-mode. + * @details Inputs: currentRecircState + * @details Outputs: none + * @return treatmentRecircState + *************************************************************************/ +TREATMENT_RECIRC_STATE_T getCurrentTreatmentRecircState( void ) +{ + return currentRecircState; +} + +/*********************************************************************//** + * @brief + * The publishTreatmentRecircData function broadcasts re-circulate countdown + * data to the UI at 1 Hz. + * @details Inputs: recircPublishTimerCtr, recircTimerCtr + * @details Outputs: none + * @return none + *************************************************************************/ +static void publishTreatmentRecircData( void ) +{ + if ( ++recircPublishTimerCtr >= RECIRC_DATA_PUBLISH_INTERVAL ) + { + TREATMENT_RECIRC_PAYLOAD_T data; + U32 ticksPerSec = ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + U32 elapsedSecs = ( recircTimerCtr / ticksPerSec ); + U32 timeoutSecs = ( recircTimeoutTicks / ticksPerSec ); + + data.countdown = ( recircTimerCtr < recircTimeoutTicks ) ? ( timeoutSecs - elapsedSecs ) : 0U; + + recircPublishTimerCtr = 0; + + broadcastData( MSG_ID_TD_RECIRCULATE_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( TREATMENT_RECIRC_PAYLOAD_T ) ); + } +} + +/**@}*/ Index: firmware/App/Modes/StateTxRecirc.h =================================================================== diff -u --- firmware/App/Modes/StateTxRecirc.h (revision 0) +++ firmware/App/Modes/StateTxRecirc.h (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -0,0 +1,51 @@ +/************************************************************************** +* +* Copyright (c) 2025-2026 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 StateTxRecirc.h +* +* @author (last) Praneeth Bunne +* @date (last) 29-Jun-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 29-Jun-2026 +* +***************************************************************************/ + +#ifndef __STATE_TX_RECIRC_H__ +#define __STATE_TX_RECIRC_H__ + +#include "TDCommon.h" +#include "TDDefs.h" + +/** + * @defgroup StateTxRecirc StateTxRecirc + * @brief Treatment Recirc state unit. + * + * @addtogroup StateTxRecirc + * @{ + */ + +// ********** public definitions ****************** + +/// Payload record structure for treatment recirculate mode data broadcast message +typedef struct +{ + U32 countdown; ///< Recirculation timeout countdown (in seconds) +} TREATMENT_RECIRC_PAYLOAD_T; + +// ********** public function prototypes ********** + +void initTreatmentRecirc( void ); +void transitionToTreatmentRecirc( void ); +void execTreatmentRecirc( void ); + +BOOL signalTreatmentRecircUserAction( MESSAGE_T *message ); +TREATMENT_RECIRC_STATE_T getCurrentTreatmentRecircState( void ); + +/**@}*/ + +#endif Index: firmware/App/Modes/StateTxRinseback.c =================================================================== diff -u --- firmware/App/Modes/StateTxRinseback.c (revision 0) +++ firmware/App/Modes/StateTxRinseback.c (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -0,0 +1,969 @@ +/************************************************************************** +* +* Copyright (c) 2025-2026 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 StateTxRinseback.c +* +* @author (last) Praneeth Bunne +* @date (last) 15-Jun-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 15-Jun-2026 +* +***************************************************************************/ + +#include "AirTrap.h" +#include "BloodFlow.h" +#include "DDInterface.h" +#include "Messaging.h" +#include "ModeStandby.h" +#include "ModeTreatment.h" +#include "OperationModes.h" +#include "Pressures.h" +#include "StateTxPaused.h" +#include "StateTxRinseback.h" +#include "SyringePump.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "TxParams.h" +#include "Valves.h" + +/** + * @addtogroup StateTxRinseback + * @{ + */ + +// ********** private definitions ********** + +#define TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML 50.0F ///< Target rinseback volume for an additional volume request (in mL). +#define RINSEBACK_FLOW_RATE_ADJ_ML_MIN 10U ///< Adjustment amount (in mL/min) to apply when user requests increase/decrease +#define DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN 200U ///< Default rinseback flow rate (in mL/min). +#define MIN_RINSEBACK_FLOW_RATE_ML_MIN 100U ///< Minimum rinseback flow rate (in mL/min). +#define MAX_RINSEBACK_FLOW_RATE_ML_MIN 500U ///< Maximum rinseback flow rate (in mL/min). + +/// 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. +#define MAX_RINSEBACK_TIME ( 5 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) +/// Maximum time allowed for each additional rinseback volume delivery. +#define MAX_RINSEBACK_ADDITIONAL_TIME ( 25 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) +/// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. +#define RINSEBACK_FLOW_INTEGRATOR 1.0F / (F32)( SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) + +// ********** private data ********** + +static RINSEBACK_STATE_T currentRinsebackState; ///< Current state of the rinseback sub-mode. +static FLUID_TYPE_T fluidType; ///< Fluid Type to rinseback +static U32 rinsebackTimerCtr; ///< Timer counter for time spent in rinseback sub-mode. +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. +static U32 rinsebackTargetVolume_mL; ///< Calculated target rinseback volume (based on selected dialyzer and fixed tubing volume). +static OVERRIDE_F32_T cummulativeRinsebackVolume_mL = { 0.0, 0.0, 0.0, 0 }; ///< Total cumulative rinseback volume (in mL) from measured 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. + +/// 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 startRinsebackRequested; ///< Flag indicating user requesting rinseback start (confirming saline bag move to arterial line end). +static BOOL incrRinsebackFlowRateRequested; ///< Flag indicating user requesting rinseback flow rate be increased. +static BOOL decrRinsebackFlowRateRequested; ///< Flag indicating user requesting rinseback flow rate be decreased. +static BOOL pauseRinsebackRequested; ///< Flag indicating user requesting rinseback pause. +static BOOL resumeRinsebackRequested; ///< Flag indicating user requesting rinseback resume. +static BOOL additionalRinsebackRequested; ///< Flag indicating user requesting a little more rinseback volume. +static BOOL recircRequested; ///< Flag indicating user requesting to begin re-circulation. + +// ********** private function prototypes ********** + +static void resetRinsebackFlags( void ); + +static void setupForRinsebackDelivery( void ); +static void setupForRinsebackStopOrPause( void ); + +static RINSEBACK_STATE_T handleRinsebackStopInitState( void ); +static RINSEBACK_STATE_T handleRinsebackRunState( void ); +static RINSEBACK_STATE_T handleRinsebackPausedState( void ); +static RINSEBACK_STATE_T handleRinsebackStoppedState( void ); +static RINSEBACK_STATE_T handleRinsebackAdditionalState( void ); + +static BOOL handleStartRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleIncrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleDecrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handlePauseRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleResumeRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleAdditionalRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); +static BOOL handleRecircUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); + +static void publishRinsebackData( void ); +static U32 getPublishRinsebackInterval( void ); + +/*********************************************************************//** + * @brief + * The initRinseback function initializes the Rinseback sub-mode module. + * @details Inputs: none + * @details Outputs: Rinseback sub-mode module initialized. + * @return none + *************************************************************************/ +void initRinseback( void ) +{ + currentRinsebackState = RINSEBACK_STOP_INIT_STATE; + fluidType = FLUID_TYPE_SALINE; //getFluidType(); + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE, (U32)DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN ); // Set to default rate before rinseback start + rinsebackTargetVolume_mL = getTreatmentParameterU32( TREATMENT_PARAM_PRIME_RINSEBACK_VOLUME ); + targetRinsebackVolumePlusAdditional_mL = rinsebackTargetVolume_mL; + rinsebackTimerCtr = 0; + cummulativeRinsebackVolume_mL.data = 0.0F; + additionalRinsebackVolume_mL = 0.0F; + rinsebackAdditionalTimerCtr = 0; + rinsebackPublishTimerCtr = 0; + resetRinsebackFlags(); +} + +/*********************************************************************//** + * @brief + * The resetRinsebackFlags function resets the Rinseback request flags. + * @details Inputs: none + * @details Outputs: Rinseback request flags reset to FALSE. + * @return none + *************************************************************************/ +static void resetRinsebackFlags( void ) +{ + startRinsebackRequested = FALSE; + incrRinsebackFlowRateRequested = FALSE; + decrRinsebackFlowRateRequested = FALSE; + pauseRinsebackRequested = FALSE; + resumeRinsebackRequested = FALSE; + additionalRinsebackRequested = FALSE; + recircRequested = FALSE; +} + +/*********************************************************************//** + * @brief + * The transitionToRinseback function prepares for transition to Rinseback + * sub-mode. + * @details Inputs: none + * @details Outputs: Rinseback sub-mode setup to begin + * @return none + *************************************************************************/ +void transitionToRinseback( void ) +{ + initRinseback(); + setCurrentSubState( (U32)currentRinsebackState ); + + // Set user alarm recovery actions allowed in this sub-mode + setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_TEMPORARY_BREAK, FALSE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); + + // Set actuators + setupForRinsebackStopOrPause(); + + // Bypass dialyzer + cmdBypassDialyzer( TRUE ); + + // Stop syringe pump + stopSyringePump(); +} + +/*********************************************************************//** + * @brief + * The setupForRinsebackDelivery function sets actuators appropriately + * for rinseback states where delivery is in progress. + * @details Inputs: none + * @details Outputs: none + * @param rate rinseback rate to use for blood pump and HDF pump + * @return none + *************************************************************************/ +static void setupForRinsebackDelivery( void ) +{ + // Open arterial & venous valves to return blood. + setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); + setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); + // Select the rinseback fluid source based fluid medium, which is based on modality. + if ( FLUID_TYPE_SALINE == fluidType ) + { + setBloodPumpTargetFlowRate( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + } + else + { + cmdSubstitutionRate( (F32)getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) ); + } + + // 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: none + * @return none + *************************************************************************/ +static void setupForRinsebackStopOrPause( void ) +{ + // Stop pumps + signalBloodPumpHardStop(); + cmdSubstitutionRate( 0.0F ); + // Switch arterial & venous valves to safe state + setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); + setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); + // Stop air trap leveling control + endAirTrapControl(); +} + +/*********************************************************************//** + * @brief + * The getDeliveredRinsebackVolume function gets the calculated rinseback volume + * delivered. + * @details Inputs: cumulativeRinsebackVolume_mL + * @details Outputs: none + * @return the current rinseback volume delivered (in mL). + *************************************************************************/ +F32 getDeliveredRinsebackVolume( void ) +{ + F32 result = cummulativeRinsebackVolume_mL.data; + + if ( OVERRIDE_KEY == cummulativeRinsebackVolume_mL.override ) + { + result = cummulativeRinsebackVolume_mL.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getCurrentRinsebackState function returns the current state of the + * rinseback sub-mode. + * @details Inputs: currentRinsebackState + * @details Outputs: none + * @return currentRinsebackState + *************************************************************************/ +RINSEBACK_STATE_T getCurrentRinsebackState( void ) +{ + return currentRinsebackState; +} + +/*********************************************************************//** + * @brief + * The execRinseback function executes the Rinseback sub-mode state machine. + * @details Inputs: currentRinsebackState + * @details Outputs: currentRinsebackState, rinsebackTimerCtr + * @return none + *************************************************************************/ +void execRinseback( void ) +{ + RINSEBACK_STATE_T priorSubState = currentRinsebackState; + + rinsebackTimerCtr++; + + switch ( currentRinsebackState ) + { + case RINSEBACK_STOP_INIT_STATE: + currentRinsebackState = handleRinsebackStopInitState(); + break; + + case RINSEBACK_RUN_STATE: + currentRinsebackState = handleRinsebackRunState(); + break; + + case RINSEBACK_PAUSED_STATE: + currentRinsebackState = handleRinsebackPausedState(); + break; + + case RINSEBACK_STOP_STATE: + currentRinsebackState = handleRinsebackStoppedState(); + break; + + case RINSEBACK_RUN_ADDITIONAL_STATE: + currentRinsebackState = handleRinsebackAdditionalState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_RINSEBACK_STATE, currentRinsebackState ); + break; + } + + if ( priorSubState != currentRinsebackState ) + { + setCurrentSubState( (U32)currentRinsebackState ); + SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, (U32)priorSubState, (U32)currentRinsebackState ); + } + + // 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 \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback + * timeout + * @details Inputs: startRinsebackRequested + * @details Outputs: none + * @return next rinseback state + *************************************************************************/ +static RINSEBACK_STATE_T handleRinsebackStopInitState( void ) +{ + RINSEBACK_STATE_T state = RINSEBACK_STOP_INIT_STATE; + + // Have we been in this state for too long? + if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM, (U32)rinsebackTimerCtr, (U32)MAX_RINSEBACK_TIME ); + } + // User requested rinseback start + else if ( TRUE == startRinsebackRequested ) + { + setBloodIsPrimed( FALSE ); + setupForRinsebackDelivery(); + state = RINSEBACK_RUN_STATE; + } + else + { + // No action required. + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleRinsebackRunState function handles the rinseback run state + * operations. + * @details \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback + * timeout, ALARM_ID_TD_EMPTY_SALINE_BAG if saline bag is empty or + * saline line is clamped + * @details Inputs: rinsebackTargetVolume_mL, pauseRinsebackRequestd, + * incrRinsebackFlowRateRequested, decrRinsebackFlowRateRequested + * @details Outputs: rinsebackTimerCtr, cumulativeRinsebackVolume_mL, + * rinsebackRate_mL_min, incrRinsebackFlowRateRequested, decrRinsebackFlowRateRequested + * @return next rinseback state + *************************************************************************/ +static RINSEBACK_STATE_T handleRinsebackRunState( void ) +{ + RINSEBACK_STATE_T state = RINSEBACK_RUN_STATE; + + // Reset rinseback timeout + rinsebackTimerCtr = 0; + + // Update rinseback volume delivered so far + cummulativeRinsebackVolume_mL.data += ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) * RINSEBACK_FLOW_INTEGRATOR ); + + // Check for empty saline bag if we are using saline to rinseback. + if ( ( FLUID_TYPE_SALINE == fluidType ) && ( TRUE == isSalineBagEmpty() ) ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_EMPTY_SALINE_BAG, getFilteredArterialPressure() ); + setupForRinsebackStopOrPause(); + state = RINSEBACK_PAUSED_STATE; + } + // Rinseback complete ? + else if ( getDeliveredRinsebackVolume() >= rinsebackTargetVolume_mL ) + { + setRinsebackIsCompleted( TRUE ); + setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getDeliveredRinsebackVolume(); + state = RINSEBACK_STOP_STATE; + } + // Has user or alarm requested stop ? + else if ( ( TRUE == pauseRinsebackRequested ) || TRUE == doesAlarmStatusIndicateStop() ) + { + setupForRinsebackStopOrPause(); + state = RINSEBACK_PAUSED_STATE; + } + // Otherwise, continue + else + { + if ( ( TRUE == incrRinsebackFlowRateRequested ) || ( TRUE == decrRinsebackFlowRateRequested ) ) + { + U32 newRate = getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); + + if ( TRUE == incrRinsebackFlowRateRequested ) + { + newRate += RINSEBACK_FLOW_RATE_ADJ_ML_MIN; + } + else + { + newRate -= RINSEBACK_FLOW_RATE_ADJ_ML_MIN; + } + + incrRinsebackFlowRateRequested = FALSE; + decrRinsebackFlowRateRequested = FALSE; + + if ( ( newRate >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) && ( newRate <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) ) + { + // Set the rinseabck flow rate with the new rate + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE, newRate ); + setupForRinsebackDelivery(); + } + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleRinsebackPausedState function handles the rinseback paused + * state operations. + * @details \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback + * timeout + * @details Inputs: rinsebackTimerCtr, resumeRinsebackRequested + * @details Outputs: targetRinsebackVolumePlusAdditional_mL + * @return next rinseback state + *************************************************************************/ +static RINSEBACK_STATE_T handleRinsebackPausedState( void ) +{ + RINSEBACK_STATE_T state = RINSEBACK_PAUSED_STATE; + + // Have we been in this state for too long? + if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM, (U32)rinsebackTimerCtr, (U32)MAX_RINSEBACK_TIME ); + } + // User requested to resume + else if ( TRUE == resumeRinsebackRequested ) + { + setupForRinsebackDelivery(); + state = RINSEBACK_RUN_STATE; + } + else + { + // No action required + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleRinsebackStoppedState function handles the stopped rinseback + * state operations. + * @details \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback + * timeout + * @details Inputs: rinsebackTimerCtr, rinsebackTargetVolume_mL, recircRequested, + * additionalRinsebackRequested + * @details Outputs: additionalRinsebackRequested, + * rinsebackAdditionalTimerCtr, additionalRinsebackVolume_mL + * @return next rinseback state + *************************************************************************/ +static RINSEBACK_STATE_T handleRinsebackStoppedState( void ) +{ + RINSEBACK_STATE_T state = 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 ) && ( getDeliveredRinsebackVolume() < rinsebackTargetVolume_mL ) ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM, (U32)rinsebackTimerCtr, (U32)MAX_RINSEBACK_TIME ); + } + // User requested to recirculate + else if ( TRUE == recircRequested ) + { + if ( TRUE == isRecircAllowed() ) + { + signalRinsebackToRecirc(); + } + } + else if ( TRUE == additionalRinsebackRequested ) + { + additionalRinsebackRequested = FALSE; + + if ( ( rinsebackTimerCtr < MAX_RINSEBACK_TIME ) || ( getDeliveredRinsebackVolume() >= rinsebackTargetVolume_mL ) ) + { + rinsebackAdditionalTimerCtr = 0; + additionalRinsebackVolume_mL = 0.0F; + targetRinsebackVolumePlusAdditional_mL += TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML; + setSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE, DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN ); + setupForRinsebackDelivery(); + state = RINSEBACK_RUN_ADDITIONAL_STATE; + } + } + else + { + // No action required. + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleRinsebackRunAdditionalState function handles the rinseback additional + * state operations. + * @details \b Alarms: ALARM_ID_TD_EMPTY_SALINE_BAG if saline bag is empty or + * saline line is clamped. + * @details Inputs: additionalRinsebackVolume_mL, rinsebackAdditionalTimerCtr + * @details Outputs: rinsebackTimerCtr, additionalRinsebackVolume_mL, cumulativeRinsebackVolume_mL, + * rinsebackAdditionalTimerCtr, targetRinsebackVolumePlusAdditional_mL + * @return next rinseback state + *************************************************************************/ +static RINSEBACK_STATE_T handleRinsebackAdditionalState( void ) +{ + RINSEBACK_STATE_T state = RINSEBACK_RUN_ADDITIONAL_STATE; + F32 rinsebackVolumeSinceLast = ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) * RINSEBACK_FLOW_INTEGRATOR ); + + // Reset rinseback t/o + rinsebackTimerCtr = 0; + + // update additional rinseback volume delivered so far + additionalRinsebackVolume_mL += rinsebackVolumeSinceLast; + cummulativeRinsebackVolume_mL.data += rinsebackVolumeSinceLast; + rinsebackAdditionalTimerCtr++; + + // Check for empty saline bag if we are using saline to rinseback. + if ( ( FLUID_TYPE_SALINE == fluidType ) && ( TRUE == isSalineBagEmpty() ) ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_EMPTY_SALINE_BAG, getFilteredArterialPressure() ); + setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getDeliveredRinsebackVolume(); + state = RINSEBACK_STOP_STATE; + } + // Has additional volume been delivered? + else if ( additionalRinsebackVolume_mL >= TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) + { + setupForRinsebackStopOrPause(); + state = RINSEBACK_STOP_STATE; + } + // Has alarm requested stop or timed out? + else if ( ( TRUE == doesAlarmStatusIndicateStop() ) || ( rinsebackAdditionalTimerCtr >= MAX_RINSEBACK_ADDITIONAL_TIME ) ) + { + setupForRinsebackStopOrPause(); + targetRinsebackVolumePlusAdditional_mL = getDeliveredRinsebackVolume(); + state = RINSEBACK_STOP_STATE; + } + else + { + // No action required. + } + + return state; +} + +/*********************************************************************//** + * @brief + * The signalRinsebackAlarmResumeUserAction function signals the rinseback + * sub-mode to resume per user alarm action. + * @details Inputs: currentRinsebackState + * @details Outputs: resumeRinsebackRequested + * @return none + *************************************************************************/ +void signalRinsebackAlarmResumeUserAction( void ) +{ + if ( RINSEBACK_PAUSED_STATE == currentRinsebackState ) + { + resumeRinsebackRequested = TRUE; + } +} + +/*********************************************************************//** + * @brief + * The signalRinsebackUserAction function signals a rinseback user action + * has been requested. + * @details Inputs: none + * @details Outputs: action handled, request responded to + * @param message UI message which has requested user action +* @return TRUE if request is accepted, FALSE if rejected. + *************************************************************************/ +BOOL signalRinsebackUserAction( MESSAGE_T *message ) +{ + BOOL result = FALSE; + U32 cmd = 0; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; + UI_RESPONSE_PAYLOAD_T response; + + + if ( sizeof(U32) == message->hdr.payloadLen ) + { + memcpy( &cmd, message->payload, sizeof(U32) ); + + // Reject user action requests if any alarm is currently active. User must clear alarm first. + if ( FALSE == isAnyAlarmActive() ) + { + switch ( cmd ) + { + case REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_START: + result = handleStartRinsebackUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_RINSEBACK_INCREASE_RATE: + result = handleIncrRinsebackUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_RINSEBACK_DECREASE_RATE: + result = handleDecrRinsebackUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_RINSEBACK_PAUSE: + result = handlePauseRinsebackUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_RINSEBACK_RESUME: + result = handleResumeRinsebackUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_RINSEBACK_ADDITIONAL: + result = handleAdditionalRinsebackUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_RECIRCULATE: + result = handleRecircUserAction( &rejReason ); + break; + + case REQUESTED_USER_ACTION_RINSEBACK_END_TREATMENT: + // Send message to UI to get user confirmation to end treatment - action initiated only upon receipt of user confirmation from UI + addConfirmationRequest( GENERIC_CONFIRM_ID_TREATMENT_END, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0U ); + result = TRUE; + break; + + default: + rejReason = REQUEST_REJECT_REASON_INVALID_COMMAND; + break; + } + } + else + { + rejReason = REQUEST_REJECT_REASON_ALARM_IS_ACTIVE; + } + } + else + { + rejReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + // Respond to user action request + response.accepted = result; + response.rejectionReason = rejReason; + sendMessage( MSG_ID_TD_RINSEBACK_CMD_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/*********************************************************************//** + * @brief + * 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: currentRinsebackState + * @details Outputs: startRinsebackRequested + * @param rejReason code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleStartRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( RINSEBACK_STOP_INIT_STATE == currentRinsebackState ) + { + result = TRUE; + startRinsebackRequested = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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: currentRinsebackState + * @details Outputs: incrRinsebackFlowRateRequested + * @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; + + if ( ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) + RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) + { + if ( RINSEBACK_RUN_STATE == currentRinsebackState ) + { + result = TRUE; + incrRinsebackFlowRateRequested = TRUE; + rinsebackPublishTimerCtr = getPublishRinsebackInterval(); + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + } + else + { + *rejReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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: currentRinsebackState + * @details Outputs: decrRinsebackFlowRateRequested + * @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; + + if ( ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) - RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) + { + if ( RINSEBACK_RUN_STATE == currentRinsebackState ) + { + result = TRUE; + decrRinsebackFlowRateRequested = TRUE; + rinsebackPublishTimerCtr = getPublishRinsebackInterval(); + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + } + else + { + *rejReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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: currentRinsebackState + * @details Outputs: pauseRinsebackRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handlePauseRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( RINSEBACK_RUN_STATE == currentRinsebackState ) + { + result = TRUE; + pauseRinsebackRequested = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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: currentRinsebackState + * @details Outputs: resumeRinsebackRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleResumeRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( RINSEBACK_PAUSED_STATE == currentRinsebackState ) + { + result = TRUE; + resumeRinsebackRequested = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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: currentRinsebackState + * @details Outputs: additionalRinsebackRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleAdditionalRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( RINSEBACK_STOP_STATE == currentRinsebackState ) + { + result = TRUE; + additionalRinsebackRequested = TRUE; + } + else + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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: currentRinsebackState + * @details Outputs:recircRequested + * @param rejReason Code indicating reason for rejection + * @return TRUE if user action accepted, FALSE if not + *************************************************************************/ +static BOOL handleRecircUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) +{ + BOOL result = FALSE; + + if ( TRUE == isTreatmentCompleted() ) + { + *rejReason = REQUEST_REJECT_REASON_TREATMENT_IS_COMPLETED; + } + else if ( currentRinsebackState != RINSEBACK_STOP_STATE ) + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + else if ( TRUE != isRecircAllowed() ) + { + *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; + } + else + { + result = TRUE; + recircRequested = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The publishRinsebackData function publishes rinseback progress to UI + * at 1 Hz interval. + * @details Inputs: rinsebackPublishTimerCtr, rinsebackTimerCtr, targetRinsebackVolumePlusAdditional_mL + * rinsebackRate_mL_min + * @details Outputs: none + * @return none + *************************************************************************/ +static void publishRinsebackData( void ) +{ + if ( ++rinsebackPublishTimerCtr >= getPublishRinsebackInterval() ) + { + RINSEBACK_DATA_PAYLOAD_T data; + BOOL rinsebackComplete = getRinsebackIsCompleted(); + + rinsebackPublishTimerCtr = 0; + + data.targetRinsebackVolumeMl = targetRinsebackVolumePlusAdditional_mL; + data.deliveredRinsebackVolumeMl = getDeliveredRinsebackVolume(); + data.rinsebackFlowRateMlMin = getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); + data.isCompleted = rinsebackComplete; + data.isRecircAllowed = isRecircAllowed(); + + // Report blood-sitting timeout countdown only while rinseback not completed and blood pump is stopped + if ( ( TRUE == rinsebackComplete ) || ( 0 == rinsebackTimerCtr ) ) + { + data.timeout = 0; + data.countdown = 0; + } + else + { + U32 countdown = ( rinsebackTimerCtr <= MAX_RINSEBACK_TIME ) ? ( MAX_RINSEBACK_TIME - rinsebackTimerCtr ) : 0; + + data.timeout = MAX_RINSEBACK_TIME / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + data.countdown = countdown / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + } + + broadcastData( MSG_ID_TD_RINSEBACK_PROGRESS, COMM_BUFFER_OUT_CAN_TD_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 the current rinseback publication interval (in task intervals). + *************************************************************************/ +static U32 getPublishRinsebackInterval( void ) +{ + U32 result = rinsebackPublishInterval.data; + + if ( OVERRIDE_KEY == rinsebackPublishInterval.override ) + { + result = rinsebackPublishInterval.ovData; + } + + return result; +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + +/*********************************************************************//** + * @brief + * The testRinsebackVolumeOverride function overrides or resets the cumulative + * rinseback volume delivered. + * @details \b Inputs: none + * @details \b Outputs: cummulativeRinsebackVolume_mL + * @param message Override message from Dialin which includes the rinseback + * volume (in mL) to override to, or reset command. + * @return TRUE if override or reset successful, FALSE if not + *************************************************************************/ +BOOL testRinsebackVolumeOverride( MESSAGE_T *message ) +{ + BOOL result = FALSE; + + result = f32Override( message, &cummulativeRinsebackVolume_mL ); + + return result; +} + +/*********************************************************************//** + * @brief + * The testRinsebackPublishIntervalOverride function overrides or resets the + * rinseback data publication interval. + * @details \b Inputs: message + * @details \b Outputs: rinsebackPublishInterval + * @param message Override message from Dialin which includes the interval + * (in ms) to override the rinseback broadcast interval to, or reset command. + * @return TRUE if override or reset successful, FALSE if not + *************************************************************************/ +BOOL testRinsebackPublishIntervalOverride( MESSAGE_T *message ) +{ + BOOL result = FALSE; + + result = u32BroadcastIntervalOverride( message, &rinsebackPublishInterval, TASK_GENERAL_INTERVAL ); + + return result; +} + +/**@}*/ Index: firmware/App/Modes/StateTxRinseback.h =================================================================== diff -u --- firmware/App/Modes/StateTxRinseback.h (revision 0) +++ firmware/App/Modes/StateTxRinseback.h (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -0,0 +1,67 @@ +/************************************************************************** +* +* Copyright (c) 2025-2026 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 StateTxRinseback.h +* +* @author (last) Praneeth Bunne +* @date (last) 15-Jun-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 15-Jun-2026 +* +***************************************************************************/ + +#ifndef __STATE_TX_RINSEBACK_H__ +#define __STATE_TX_RINSEBACK_H__ + +#include "TDCommon.h" +#include "TDDefs.h" + +/** + * @defgroup StateTxRinseback StateTxRinseback + * @brief Treatment Rinseback state unit. + * + * @addtogroup StateTxRinseback + * @{ + */ + +// ********** public definitions ****************** + +#pragma pack(push,1) + +/// Payload record structure for a rinseback data broadcast message. +typedef struct +{ + F32 targetRinsebackVolumeMl; ///< Target rinse back volume in mL. + F32 deliveredRinsebackVolumeMl; ///< Delivered rinse back volume in mL. + U32 rinsebackFlowRateMlMin; ///< Rinse back flow rate in mL/min. + U32 timeout; ///< Rinseback timeout duration (in seconds). + U32 countdown; ///< Countdown. + BOOL isCompleted; ///< Is completed. + BOOL isRecircAllowed; ///< Is re-circulation allowed from risneback. +} RINSEBACK_DATA_PAYLOAD_T; + +#pragma pack(pop) + +// ********** public function prototypes ********** + +void initRinseback( void ); +void transitionToRinseback( void ); +void execRinseback( void ); + +void signalRinsebackAlarmResumeUserAction( void ); // User Alarm action to resume +BOOL signalRinsebackUserAction( MESSAGE_T *message ); // User rinseback actions +RINSEBACK_STATE_T getCurrentRinsebackState( void ); + +F32 getDeliveredRinsebackVolume( void ); + +BOOL testRinsebackVolumeOverride( MESSAGE_T *message ); +BOOL testRinsebackPublishIntervalOverride( MESSAGE_T *message ); + +/**@}*/ + +#endif Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -196,6 +196,9 @@ SW_FAULT_ID_MODE_POST_TREATMENT_INVALID_STATE1 = 165, SW_FAULT_ID_MODE_POST_TX_AUTO_EJECT_INVALID_STATE = 166, SW_FAULT_ID_INVALID_FLUID_BOLUS_STATE = 167, + SW_FAULT_ID_INVALID_RINSEBACK_STATE = 168, + SW_FAULT_ID_TREATMENT_RECIRC_INVALID_STATE = 169, + SW_FAULT_ID_ALARM_MGMT_INVALID_CONFIRM_ID = 170, NUM_OF_SW_FAULT_IDS } SW_FAULT_ID_T; Index: firmware/App/Services/AlarmMgmtTD.c =================================================================== diff -u -r395522dffef1348e176564925656012f529c1910 -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Services/AlarmMgmtTD.c (.../AlarmMgmtTD.c) (revision 395522dffef1348e176564925656012f529c1910) +++ firmware/App/Services/AlarmMgmtTD.c (.../AlarmMgmtTD.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -157,6 +157,7 @@ alarmStatus.noBloodRecirc = FALSE; alarmStatus.noDialRecirc = FALSE; alarmStatus.ok = FALSE; + alarmStatus.noTempBreak = FALSE; alarmsBlockedTimer = 0; lastUserAlarmActionReceivedTime = 0; @@ -498,6 +499,13 @@ } break; + case ALARM_USER_ACTION_TEMPORARY_BREAK: + if ( ( alarmStatus.noTempBreak != TRUE ) && ( alarmStatus.noRinseback != TRUE ) ) + { + alarmActionIsValid = TRUE; + } + break; + default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_ALARM_USER_ACTION1, action ); break; @@ -544,7 +552,8 @@ break; case ALARM_USER_ACTION_RINSEBACK: - initiateAlarmAction( ALARM_ACTION_RINSEBACK ); + // Send message to UI to get user confirmation to rinseback (blood return & end treatment) - action initiated only upon receipt of user confirmation from UI + addConfirmationRequest( GENERIC_CONFIRM_ID_RINSEBACK, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0 ); break; case ALARM_USER_ACTION_END_TREATMENT: @@ -556,6 +565,11 @@ initiateAlarmAction( ALARM_ACTION_ACK ); break; + case ALARM_USER_ACTION_TEMPORARY_BREAK: + // Send message to UI to get user confirmation to temporary break - action initiated only upon receipt of user confirmation from UI + addConfirmationRequest( GENERIC_CONFIRM_ID_TEMPORARY_BREAK, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0 ); + break; + // This default cannot be reached in VectorCAST due to check above for alarmActionIsValid #ifndef _VECTORCAST_ default: @@ -925,24 +939,54 @@ /*********************************************************************//** * @brief * The monitorAlarms function monitors alarm audio current and also looks - * for and executes a user confirmation of user request to end treatment. + * for and executes a user confirmation of user request to shutdown, + * end treatment, temporary break, rinseback. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ static void monitorAlarms( void ) { + GENERIC_CONFIRM_ID_T confirmID = getPendingConfirmationID(); + execAlarmAudio(); - // Check for user confirmation of end treatment alarm response - if ( CONFIRMATION_REQUEST_STATUS_ACCEPTED == getConfirmationRequestStatus( GENERIC_CONFIRM_ID_TREATMENT_END ) ) + if ( ( GENERIC_CONFIRM_ID_NONE != confirmID ) && + ( CONFIRMATION_REQUEST_STATUS_ACCEPTED == getConfirmationRequestStatus( confirmID ) ) ) { - // To avoid raising repeated alarm before reaching end treatment - setVenousBubbleDetectionEnabled( H18_BBLD, FALSE ); - clearAllRecoverableAlarms( ALARM_USER_ACTION_END_TREATMENT ); - initiateAlarmAction( ALARM_ACTION_END_TREATMENT ); + switch ( confirmID ) + { + case GENERIC_CONFIRM_ID_SHUT_DOWN: + // TODO — handle shutdown confirmation + break; + + case GENERIC_CONFIRM_ID_TREATMENT_END: + setVenousBubbleDetectionEnabled( H18_BBLD, FALSE ); + clearAllRecoverableAlarms( ALARM_USER_ACTION_END_TREATMENT ); + initiateAlarmAction( ALARM_ACTION_END_TREATMENT ); + break; + + case GENERIC_CONFIRM_ID_RINSEBACK: + clearAllRecoverableAlarms( ALARM_USER_ACTION_RINSEBACK ); + initiateAlarmAction( ALARM_ACTION_RINSEBACK ); + break; + + case GENERIC_CONFIRM_ID_TEMPORARY_BREAK: + clearAllRecoverableAlarms( ALARM_USER_ACTION_TEMPORARY_BREAK ); + initiateAlarmAction( ALARM_ACTION_TEMPORARY_BREAK ); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, + SW_FAULT_ID_ALARM_MGMT_INVALID_CONFIRM_ID, + (U32)confirmID ); + break; + } } + // Timeout pending confirmation dialog if user does not respond within 1 minute + handleConfirmationDialogTimeout(); + // TODO - Check current vs. expected audio output } @@ -1150,6 +1194,7 @@ BOOL endTxOnlyAlarmActive = FALSE; BOOL usrAckReq = FALSE; BOOL noMinimize = TRUE; + BOOL noTempBreak = FALSE; TD_OP_MODE_T currentMode = getCurrentOperationMode(); ALARM_T propsTop = getAlarmProperties( alarmStatus.alarmTop ); ALARM_ID_T a; @@ -1158,10 +1203,12 @@ alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_RESUME ] = ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RESUME ] ? FALSE : TRUE ); alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_RINSEBACK ] = ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RINSEBACK ] ? FALSE : TRUE ); alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_END_TREATMENT ] = ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_END_TREATMENT ] ? FALSE : TRUE ); + alarmButtonBlockers[ ALARM_BUTTON_STATE_BLOCK_TEMP_BREAK ] = ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_TEMPORARY_BREAK ] ? FALSE : TRUE ); // Reset user alarm recovery actions allowed by active alarms flags alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RESUME ] = FALSE; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RINSEBACK ] = FALSE; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_END_TREATMENT ] = FALSE; + alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_TEMP_BREAK ] = FALSE; // Determine alarm flags for ( a = ALARM_ID_NO_ALARM; a < NUM_OF_ALARM_IDS; a++ ) @@ -1178,6 +1225,7 @@ alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RESUME ] |= props.alarmNoResume; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_RINSEBACK ] |= props.alarmNoRinseback; alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_END_TREATMENT ] |= props.alarmNoEndTreatment; + alarmButtonBlockers[ ALARM_BUTTON_TABLE_BLOCK_TEMP_BREAK ] |= props.alarmNoRinseback; if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_RESUME ] ) { noResume = ( TRUE == props.alarmNoResume ? TRUE : noResume ); @@ -1202,6 +1250,14 @@ { noEndTreatment = TRUE; } + if ( TRUE == alarmUserRecoveryActionEnabled[ ALARM_USER_ACTION_TEMPORARY_BREAK ] ) + { + noTempBreak = ( TRUE == props.alarmNoRinseback ? TRUE : noTempBreak ); + } + else + { + noTempBreak = TRUE; + } // if there are any active alarms that only allow end treatment, set flag so we can ensure we do not allow OK option. if ( ( TRUE == props.alarmNoResume ) && ( TRUE == props.alarmNoRinseback ) && @@ -1227,6 +1283,7 @@ noResume = TRUE; noRinseback = TRUE; noEndTreatment = TRUE; + noTempBreak = TRUE; } // if OK option enabled and there are any active alarms that only allow end treatment, block OK option and allow end treatment option (DEN-16594). @@ -1243,6 +1300,7 @@ noResume = TRUE; noRinseback = TRUE; noEndTreatment = TRUE; + noTempBreak = TRUE; } // If in Treatment-Stop state or Fault/Service/Standby Mode, allow user to minimize the alarm window @@ -1261,6 +1319,7 @@ alarmStatus.noEndTreatment = noEndTreatment; alarmStatus.ok = usrAckReq; alarmStatus.noMinimize = noMinimize; + alarmStatus.noTempBreak = noTempBreak; resumeBlockedByAlarmProperty = noResumePerAlarmPropertyOnly; } @@ -1399,6 +1458,7 @@ payload.alarmsFlags |= ( almStatus.noResume ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_RESUME) : 0 ); payload.alarmsFlags |= ( almStatus.noRinseback ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_RINSEBACK) : 0 ); payload.alarmsFlags |= ( almStatus.noEndTreatment ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_END_TREATMENT) : 0 ); + payload.alarmsFlags |= ( almStatus.noTempBreak ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_NO_TEMPORARY_BREAK) : 0 ); payload.alarmsFlags |= ( almStatus.ok ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_OK_BUTTON_ONLY) : 0 ); payload.alarmsFlags |= ( almStatus.alarmsSilenced ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_ALARMS_SILENCED) : 0 ); payload.alarmsFlags |= ( almStatus.lampOn ? BIT_BY_POS(ALARM_STATE_FLAG_BIT_POS_LAMP_ON) : 0 ); Index: firmware/App/Services/AlarmMgmtTD.h =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Services/AlarmMgmtTD.h (.../AlarmMgmtTD.h) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Services/AlarmMgmtTD.h (.../AlarmMgmtTD.h) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -47,6 +47,7 @@ ALARM_ACTION_RINSEBACK, ///< User selected rinseback from alarm recovery options ALARM_ACTION_END_TREATMENT, ///< User selected end treatment from alarm recovery options ALARM_ACTION_ACK, ///< User selected Ack from alarm recovery options + ALARM_ACTION_TEMPORARY_BREAK, ///< User selected Temp break from alarm recover options NUMBER_OF_ALARM_ACTIONS ///< Number of alarm actions } ALARM_ACTION_T; @@ -71,7 +72,7 @@ BOOL ok; ///< Display OK button instead of other options BOOL noMinimize; ///< Prevent user from minimizing the alarm window BOOL lampOn; ///< The alarm lamp is on - BOOL unused; ///< Flag not used - available + BOOL noTempBreak; ///< Temporary break may not be initiated at this time. } COMP_ALARM_STATUS_T; #pragma pack(pop) @@ -82,9 +83,11 @@ ALARM_BUTTON_TABLE_BLOCK_RESUME = 0, ///< Alarm table properties are blocking alarm resume button ALARM_BUTTON_TABLE_BLOCK_RINSEBACK, ///< Alarm table properties are blocking alarm rinseback button ALARM_BUTTON_TABLE_BLOCK_END_TREATMENT, ///< Alarm table properties are blocking alarm end treatment button + ALARM_BUTTON_TABLE_BLOCK_TEMP_BREAK, ///< Alarm table properties are blocking alarm temp break button ALARM_BUTTON_STATE_BLOCK_RESUME, ///< State properties are blocking alarm resume button ALARM_BUTTON_STATE_BLOCK_RINSEBACK, ///< State properties are blocking alarm rinseback button ALARM_BUTTON_STATE_BLOCK_END_TREATMENT, ///< State properties are blocking alarm end treatment button + ALARM_BUTTON_STATE_BLOCK_TEMP_BREAK, ///< State properties are blocking alarm temp break button NUM_OF_ALARM_BUTTON_BLOCKERS ///< Number of alarm actions } ALARM_BUTTON_BLOCKER_T; Index: firmware/App/Services/Messaging.c =================================================================== diff -u -r5cc57a6d8eb3721811a2d6ace3176b7074c52fe8 -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Services/Messaging.c (.../Messaging.c) (revision 5cc57a6d8eb3721811a2d6ace3176b7074c52fe8) +++ firmware/App/Services/Messaging.c (.../Messaging.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -40,6 +40,8 @@ #include "StateTxBloodPrime.h" #include "StateTxDialysis.h" #include "StateTxPaused.h" +#include "StateTxRinseback.h" +#include "StateTxRecirc.h" #include "Switches.h" #include "SyringePump.h" #include "SystemCommTD.h" @@ -135,6 +137,9 @@ { MSG_ID_UI_ADJUST_DISPOSABLES_REMOVAL_CONFIRM_REQUEST, &handleAutoEjectRequest }, { MSG_ID_FFU_SIGNAL_TD_UPDATE_AVAILABLE, &handleUpdateAvailable }, { MSG_ID_UI_FLUID_BOLUS_REQUEST, &handleFluidBolusRequest }, + { MSG_ID_UI_RINSEBACK_CMD_REQUEST, &signalRinsebackUserAction }, + { MSG_ID_UI_RECIRCULATE_REQUEST, &signalTreatmentRecircUserAction }, + { MSG_ID_UI_GENERIC_CONFIRMATION_RESULT_RESPONSE, &handleUIConfirmation }, { MSG_ID_TD_SOFTWARE_RESET_REQUEST, &testTDSoftwareResetRequest }, { MSG_ID_TD_BUBBLE_OVERRIDE_REQUEST, &testBubbleDetectOverride }, { MSG_ID_TD_BUBBLE_PUBLISH_INTERVAL_OVERRIDE_REQUEST, &testBubblesDataPublishIntervalOverride }, @@ -217,6 +222,8 @@ { MSG_ID_TD_SYRINGE_PUMP_ADC_READ_COUNTER_OVERRIDE_REQUEST, &testSyringePumpADCReadCounterOverride }, { MSG_ID_TD_HEPARIN_BOLUS_TARGET_RATE_OVERRIDE_REQUEST, &testHeparinBolusTargetRateOverride }, { MSG_ID_TD_SYRINGE_PUMP_FORCE_SENSOR_CALIBRATION_REQUEST, &testCalibrateForceSensor }, + { MSG_ID_TD_RINSEBACK_VOLUME_OVERRIDE, &testRinsebackVolumeOverride }, + { MSG_ID_TD_RINSEBACK_PUBLISH_INTERVAL_OVERRIDE_REQUEST, &testRinsebackPublishIntervalOverride }, }; /// Number of entries in the message handling function lookup table. @@ -737,6 +744,64 @@ /*********************************************************************//** * @brief + * The sendConfirmationRequest function constructs and transmits a + * confirmation request message to the UI. + * @details \b Inputs: none + * @details \b Outputs: none + * @param requestID ID of the confirmation being requested + * @param requestType Type of confirmation being requested + * @param rejectReason Reason for reject if type is reject, 0 otherwise + * @return none + *************************************************************************/ +void sendConfirmationRequest( GENERIC_CONFIRM_ID_T requestID, GENERIC_CONFIRM_COMMAND_T requestType, U32 rejectReason ) +{ + GENERIC_CONFIRMATION_REQUEST_T payload; + + payload.requestID = (U32)requestID; + payload.requestType = (U32)requestType; + payload.rejectReason = rejectReason; + payload.genericPayload1 = 0.0F; + payload.genericPayload2 = 0.0F; + payload.genericPayload3 = 0.0F; + payload.genericPayload4 = 0.0F; + + sendMessage( MSG_ID_TD_UI_GENERIC_CONFIRMATION_REQUEST, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)&payload, sizeof( GENERIC_CONFIRMATION_REQUEST_T ) ); +} + +/*********************************************************************//** + * @brief + * The handleUIConfirmationResponse function handles a UI response for a + * confirmation request. + * @details \b Inputs: none + * @details \b Outputs: none + * @param message pointer to the received confirmation response message + * @return TRUE if message handled successfully, FALSE if not + *************************************************************************/ +BOOL handleUIConfirmation( MESSAGE_T *message ) +{ + BOOL result = FALSE; + + if ( ( 2U * sizeof(U32) ) == message->hdr.payloadLen ) + { + U32 requestID; + U32 status; + + memcpy( &requestID, message->payload, sizeof(U32) ); + memcpy( &status, message->payload + sizeof(U32), sizeof(U32) ); + + if ( ( CONFIRMATION_REQUEST_STATUS_ACCEPTED == ( CONFIRMATION_REQUEST_STATUS_T)status ) || + ( CONFIRMATION_REQUEST_STATUS_REJECTED == ( CONFIRMATION_REQUEST_STATUS_T)status ) ) + { + setConfirmationRequestStatus( ( GENERIC_CONFIRM_ID_T )requestID, ( CONFIRMATION_REQUEST_STATUS_T)status ); + result = TRUE; + } + } + + return result; +} + +/*********************************************************************//** + * @brief * The testTDSoftwareResetRequest function handles a request to reset the * TD firmware processor. * @note If reset is successful, this function will not return. Index: firmware/App/Services/Messaging.h =================================================================== diff -u -r9c833ef5623ce842267e284d958820ac0dc3a7fc -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Services/Messaging.h (.../Messaging.h) (revision 9c833ef5623ce842267e284d958820ac0dc3a7fc) +++ firmware/App/Services/Messaging.h (.../Messaging.h) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -139,6 +139,9 @@ BOOL testGetTestConfiguration( MESSAGE_T *message ); BOOL testResetAllTestConfigurations( MESSAGE_T *message ); +void sendConfirmationRequest( GENERIC_CONFIRM_ID_T requestID, GENERIC_CONFIRM_COMMAND_T requestType, U32 rejectReason ); +BOOL handleUIConfirmation( MESSAGE_T *message ); + /**@}*/ #endif Index: firmware/App/Services/TxParams.c =================================================================== diff -u -r5cc57a6d8eb3721811a2d6ace3176b7074c52fe8 -r1275f9e92eb1ab0cd1b321c0c8eb940524e22117 --- firmware/App/Services/TxParams.c (.../TxParams.c) (revision 5cc57a6d8eb3721811a2d6ace3176b7074c52fe8) +++ firmware/App/Services/TxParams.c (.../TxParams.c) (revision 1275f9e92eb1ab0cd1b321c0c8eb940524e22117) @@ -143,13 +143,14 @@ { CRITICAL_DATA_TYPE_U32, {.uInt=50}, {.uInt=100}, {.uInt=50} }, // TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW { CRITICAL_DATA_TYPE_U32, {.uInt=20}, {.uInt=35}, {.uInt=35} }, // TREATMENT_PARAM_VEN_PRES_LIMIT_ASYMMETRIC { 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_RATEs + { 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 { CRITICAL_DATA_TYPE_U32, {.uInt=100}, {.uInt=260}, {.uInt=200} }, // TREATMENT_PARAM_SYSTOLIC_BP_HIGH_ALARM_LIMIT { CRITICAL_DATA_TYPE_U32, {.uInt=40}, {.uInt=140}, {.uInt=40} }, // TREATMENT_PARAM_HEART_RATE_LOW_ALARM_LIMIT { CRITICAL_DATA_TYPE_U32, {.uInt=80}, {.uInt=180}, {.uInt=120} }, // TREATMENT_PARAM_HEART_RATE_HIGH_ALARM_LIMIT { CRITICAL_DATA_TYPE_U32, {.uInt=1}, {.uInt=5}, {.uInt=1} }, // TREATMENT_PARAM_PRES_ALARM_PERSISTENCE + { CRITICAL_DATA_TYPE_U32, {.uInt=30}, {.uInt=120}, {.uInt=30} }, // TREATMENT_PARAM_TEMPORARY_BREAK_RECIRC_TIMEOUT }; /// Dialyzer volumes (blood and dialysate) table in mL for each type of dialyzer.