Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -rfb4b4a4f9d16742bfb76f62970a43c97e1ac14f0 -r3477dd649f67442ad4bdea810ce5a251c1b0a2bf --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision fb4b4a4f9d16742bfb76f62970a43c97e1ac14f0) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision 3477dd649f67442ad4bdea810ce5a251c1b0a2bf) @@ -1,21 +1,23 @@ -/**********************************************************************//** - * - * Copyright (c) 2020 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 Dialysis.c - * - * @date 15-Jan-2020 - * @author S. Nash - * - * @brief State machine for the dialysis sub-mode of treatment mode. - * - **************************************************************************/ +/************************************************************************** +* +* Copyright (c) 2019-2020 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 Dialysis.c +* +* @author (last) Sean Nash +* @date (last) 24-Jun-2020 +* +* @author (original) Sean +* @date (original) 15-Jan-2020 +* +***************************************************************************/ #include "BloodFlow.h" #include "Buttons.h" +#include "DGInterface.h" #include "Dialysis.h" #include "DialInFlow.h" #include "DialOutFlow.h" @@ -32,8 +34,8 @@ // ********** private definitions ********** #define MAX_UF_RATE_ML_PER_MIN 45.83 ///< Maximum ultrafiltration rate in mL/min (2750 mL/hr). -#define MAX_UF_ACCURACY_ERROR_ML 25.0 ///< Maximum ultrafiltration accuracy error in mL over the entire treatment. -#define UF_ACCURACY_CHECK_INTERVAL ((1 * SEC_PER_MIN * MS_PER_SECOND) / TASK_GENERAL_INTERVAL) ///< Ultrafiltration rate accuracy check interval count +#define MAX_UF_ACCURACY_ERROR_ML 250.0 ///< Maximum ultrafiltration accuracy error in mL over the entire treatment. +#define UF_ACCURACY_CHECK_INTERVAL ((1 * SEC_PER_MIN * MS_PER_SECOND) / TASK_GENERAL_INTERVAL) ///< Ultrafiltration rate accuracy check interval count // ********** private data ********** @@ -42,7 +44,8 @@ static F32 refUFVolume; ///< Current reference volume for ultrafiltration (Where should we be w/r/t ultrafiltration). static F32 measUFVolume; ///< Current total measured volume for ultrafiltration (Where are we w/r/t ultrafiltration). -static F32 resStartVolume; ///< Reservoir start volume for ultrafiltration (i.e. where did we start with current reservoir). +static F32 resStartVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir start volume for ultrafiltration (i.e. where did we start with each reservoir). +static F32 resFinalVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir final volume for ultrafiltration (i.e. where did we end after switch with each reservoir). static F32 measUFVolumeFromPriorReservoirs; ///< Current total ultrafiltration volume from previous reservoirs in current treatment. static U32 uFTimeMS; ///< Current elapsed ultrafiltration time (in ms). Used for calculating UF reference volume. @@ -87,7 +90,10 @@ refUFVolume = 0.0; measUFVolume = 0.0; - resStartVolume = 0.0; +// resStartVolume[ DG_RESERVOIR_1 ] = 0.0; +// resStartVolume[ DG_RESERVOIR_2 ] = 0.0; + resFinalVolume[ DG_RESERVOIR_1 ] = 0.0; + resFinalVolume[ DG_RESERVOIR_2 ] = 0.0; measUFVolumeFromPriorReservoirs = 0.0; uFTimeMS = 0; @@ -158,14 +164,11 @@ *************************************************************************/ void startDialysis( void ) { - U32 tempDPORate = (setDialysateFlowRate * 6) / 10; // TODO - temporary fudge factor - remove later - lastUFTimeStamp = getMSTimerCount(); setDialOutUFVolumes( refUFVolume, measUFVolume ); - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - // setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // TODO - restore later - setDialOutPumpTargetRate( tempDPORate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // TODO - test code - remove later + setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // TODO - Heparin pump } @@ -239,22 +242,41 @@ BOOL pauseUF( void ) { BOOL result = FALSE; + REQUEST_REJECT_REASON_CODE_T rejectReason = REQUEST_REJECT_REASON_NONE; TREATMENT_STATE_T trtState = getTreatmentState(); - OP_MODE currMode = getCurrentOperationMode(); + HD_OP_MODE_T currMode = getCurrentOperationMode(); if ( ( MODE_TREA == currMode ) && ( TREATMENT_DIALYSIS_STATE == trtState ) && ( DIALYSIS_UF_STATE == currentDialysisState ) && ( UF_RUNNING_STATE == currentUFState ) ) { + result = TRUE; // set outlet pump to dialysate rate - result = setDialOutPumpTargetRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - - if ( TRUE == result ) + setDialOutPumpTargetRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + // go to UF paused state + currentUFState = UF_PAUSED_STATE; + } + else + { + if ( MODE_TREA != currMode ) { - // go to UF paused state - currentUFState = UF_PAUSED_STATE; + rejectReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } + else if ( TREATMENT_DIALYSIS_STATE != trtState ) + { + rejectReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + } + else if ( DIALYSIS_UF_STATE != currentDialysisState ) + { + rejectReason = REQUEST_REJECT_REASON_SALINE_BOLUS_IN_PROGRESS; + } + else + { + rejectReason = REQUEST_REJECT_REASON_UF_NOT_IN_PROGESS; + } } + // TODO - send response w/ reason code if rejected + return result; } @@ -269,24 +291,43 @@ BOOL resumeUF( void ) { BOOL result = FALSE; + REQUEST_REJECT_REASON_CODE_T rejectReason = REQUEST_REJECT_REASON_NONE; TREATMENT_STATE_T trtState = getTreatmentState(); - OP_MODE currMode = getCurrentOperationMode(); + HD_OP_MODE_T currMode = getCurrentOperationMode(); if ( ( MODE_TREA == currMode ) && ( TREATMENT_DIALYSIS_STATE == trtState ) && ( DIALYSIS_UF_STATE == currentDialysisState ) && ( UF_PAUSED_STATE == currentUFState ) ) { + result = TRUE; // set outlet pump to dialysate rate + set UF rate - result = setDialOutPumpTargetRate( setDialysateFlowRate + FLOAT_TO_INT_WITH_ROUND( setUFRate ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - - if ( TRUE == result ) + setDialOutPumpTargetRate( setDialysateFlowRate + FLOAT_TO_INT_WITH_ROUND( setUFRate ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + // restart UF time accumulation for reference volume calculation + lastUFTimeStamp = getMSTimerCount(); + // go to UF paused state + currentUFState = UF_RUNNING_STATE; + } + else + { + if ( MODE_TREA != currMode ) { - // restart UF time accumulation for reference volume calculation - lastUFTimeStamp = getMSTimerCount(); - // go to UF paused state - currentUFState = UF_RUNNING_STATE; + rejectReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } + else if ( TREATMENT_DIALYSIS_STATE != trtState ) + { + rejectReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + } + else if ( DIALYSIS_UF_STATE != currentDialysisState ) + { + rejectReason = REQUEST_REJECT_REASON_SALINE_BOLUS_IN_PROGRESS; + } + else + { + rejectReason = REQUEST_REJECT_REASON_UF_NOT_PAUSED; + } } + // TODO - send response w/ reason code if rejected + return result; } @@ -307,20 +348,19 @@ switch ( currentDialysisState ) { case DIALYSIS_START_STATE: - resStartVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); // always start dialysis w/ reservoir 1 currentDialysisState = DIALYSIS_UF_STATE; break; case DIALYSIS_UF_STATE: currentDialysisState = handleDialysisUltrafiltrationState(); break; - case DIALYSIS_SOLUTION_INFUSION_STATE: + case DIALYSIS_SALINE_BOLUS_STATE: currentDialysisState = handleDialysisSolutionInfusionState(); break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_STATE, currentDialysisState ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_STATE, currentDialysisState ) break; } } @@ -361,7 +401,7 @@ break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_UF_STATE, currentUFState ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_UF_STATE, currentUFState ) break; } @@ -379,7 +419,7 @@ *************************************************************************/ static DIALYSIS_STATE_T handleDialysisSolutionInfusionState( void ) { - DIALYSIS_STATE_T result = DIALYSIS_SOLUTION_INFUSION_STATE; + DIALYSIS_STATE_T result = DIALYSIS_SALINE_BOLUS_STATE; // TODO - @@ -460,6 +500,9 @@ uFTimeMS += msSinceLast; lastUFTimeStamp = newTime; + // update UF ref volume in UF running state only + refUFVolume += ( ( (F32)msSinceLast / MS_PER_SECOND ) / SEC_PER_MIN ) * setUFRate; + // calculate UF volumes and provide to dialysate outlet pump controller updateUFVolumes(); @@ -536,7 +579,9 @@ // check UF max rate if ( uFMeasRate > MAX_UF_RATE_ML_PER_MIN ) { +#ifndef DISABLE_UF_ALARMS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_UF_RATE_TOO_HIGH_ERROR, uFMeasRate ); +#endif } // reset for next check lastUFVolumeChecked = measUFVolume; @@ -546,7 +591,9 @@ // check total UF volume error if ( ( FABS( refUFVolume - measUFVolume ) ) >= (F32)MAX_UF_ACCURACY_ERROR_ML ) { +#ifndef DISABLE_UF_ALARMS SET_ALARM_WITH_2_F32_DATA( ALARM_ID_UF_VOLUME_ACCURACY_ERROR, refUFVolume, measUFVolume ); +#endif } } @@ -563,14 +610,108 @@ *************************************************************************/ static void updateUFVolumes( void ) { - F32 latestResVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); // TODO - just res 1 for now - add reservoir switching, mgmt later. + DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); + F32 latestResVolume; + // get volume of active reservoir + if ( DG_RESERVOIR_1 == activeRes ) + { + latestResVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); + } + else + { + latestResVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_2_PRIMARY ); + } + // calculate UF volumes and provide to dialysate outlet pump controller - refUFVolume = ( ( (F32)uFTimeMS / MS_PER_SECOND ) / SEC_PER_MIN ) * setUFRate; - measUFVolume = measUFVolumeFromPriorReservoirs + ( latestResVolume - resStartVolume ); + measUFVolume = measUFVolumeFromPriorReservoirs + ( latestResVolume - resStartVolume[ activeRes ] ); + resFinalVolume[ activeRes ] = latestResVolume; setDialOutUFVolumes( refUFVolume, measUFVolume ); } -/**@}*/ +/*********************************************************************//** + * @brief + * The setStartReservoirVolume function updates the baseline volume of the \n + * next reservoir to be drawn from before it is switched to (i.e. while it \n + * is the inactive reservoir) in order to get a more stable volume. + * @details + * Inputs : active reservoir, load cell reading from inactive reservoir + * Outputs : resStartVolume[] + * @return none + *************************************************************************/ +void setStartReservoirVolume( void ) +{ + DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); + DG_RESERVOIR_ID_T inactiveRes; + F32 resVolume; + // get volume of inactive reservoir + if ( DG_RESERVOIR_2 == activeRes ) + { + inactiveRes = DG_RESERVOIR_1; + resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); + } + else + { + inactiveRes = DG_RESERVOIR_2; + resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_2_PRIMARY ); + } + // set starting baseline volume for next reservoir before we switch to it + resStartVolume[ inactiveRes ] = resVolume; +} + +/*********************************************************************//** + * @brief + * The signalReservoirsSwitched function informs this module that the \n + * reservoirs have been switched. The UF volume from prior reservoirs is \n + * tentatively added to with a load cell reading of the inactive reservoir. + * @details + * Inputs : resFinalVolume[], resStartVolume[] + * Outputs : measUFVolumeFromPriorReservoirs + * @return none + *************************************************************************/ +void signalReservoirsSwitched( void ) +{ + DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); + DG_RESERVOIR_ID_T inactiveRes = ( activeRes == DG_RESERVOIR_1 ? DG_RESERVOIR_2 : DG_RESERVOIR_1 ); + + // update UF volume from prior reservoirs per tentative res volume for last reservoir + measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); +} + +/*********************************************************************//** + * @brief + * The setFinalReservoirVolume function updates the UF volume from prior reservoirs \n + * with a more stable final reservoir volume of the prior reservoir after it's \n + * had a moment to settle. + * @details + * Inputs : active reservoir, load cell reading from inactive reservoir + * Outputs : measUFVolumeFromPriorReservoirs, resFinalVolume[], resStartVolume[] + * @return none + *************************************************************************/ +void setFinalReservoirVolume( void ) +{ + DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); + DG_RESERVOIR_ID_T inactiveRes; + F32 resVolume; + + // get volume of inactive reservoir + if ( DG_RESERVOIR_2 == activeRes ) + { + inactiveRes = DG_RESERVOIR_1; + resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); + } + else + { + inactiveRes = DG_RESERVOIR_2; + resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_2_PRIMARY ); + } + + // update UF volume from prior reservoirs per final res volume for last reservoir a bit after we've switched and reservoir has settled + measUFVolumeFromPriorReservoirs -= ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); + resFinalVolume[ inactiveRes ] = resVolume; + measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); +} + +/**@}*/