/************************************************************************** * * 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 DGInterface.c * * @author (last) Sean Nash * @date (last) 24-Sep-2020 * * @author (original) Sean * @date (original) 08-Apr-2020 * ***************************************************************************/ #include "DialInFlow.h" #include "Dialysis.h" #include "DGDefs.h" #include "DGInterface.h" #include "ModeInitPOST.h" #include "ModeTreatment.h" #include "ModeTreatmentParams.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "Timers.h" /** * @addtogroup DGInterface * @{ */ // ********** private definitions ********** #define START_DG_CMD TRUE ///< Parameter for DG start/stop command function. True = start. #define STOP_DG_CMD FALSE ///< Parameter for DG start/stop command function. False = stop. #define RESERVOIR_SETTLE_TIME_MS 5000 ///< Time (in ms) allotted for reservoir to settle (after fill, before drain). #define SIZE_OF_LARGE_LOAD_CELL_AVG 32 ///< Large load cell moving average has 32 samples. #define DIALYSATE_TEMP_PERSISTENCE_PERIOD ( 3 * MS_PER_SECOND ) ///< Persistence period for dialysate temperature alarm. #define DIALYSATE_TEMP_RECOVERY_TOLERANCE_C 2.0 ///< Dialysate temperature recovery tolerance in degree C. #define DIALYSATE_TEMP_TOLERANCE_C 4.0 ///< Dialysate temperature tolerance in degree C. #define DIALYSATE_TEMP_HIGH_LIMIT_C 42.0 ///< Dialysate high temperature limit in degree C. #define DIALYSATE_TEMP_LOW_LIMIT_C 33.0 ///< Dialysate low temperature limit in degree C. #define MAX_RESERVOIR_VOLUME_ML 1950.0 ///< Maximum reservoir volume. Switch reservoirs if active reservoir exceeds this volume. #define SIZE_OF_LARGE_LOAD_CELL_AVG 32 ///< Large load cell moving average has 32 samples. /// States of the treatment reservoir management state machine. typedef enum TreatmentReservoirMgmt_States { TREATMENT_RESERVOIR_MGMT_START_STATE = 0, ///< If DG not already in re-circ mode, try to get it there. TREATMENT_RESERVOIR_MGMT_FLUSH_DG_LINES_STATE, ///< In DG re-circ, wait for lines to flush - then start draining inactive reservoir. TREATMENT_RESERVOIR_MGMT_DRAIN_RESERVOIR_STATE, ///< Wait for drain to complete. TREATMENT_RESERVOIR_MGMT_WAIT_TO_FILL_STATE, ///< Wait to fill inactive reservoir (if appropriate) - then start filling inactive reservoir. TREATMENT_RESERVOIR_MGMT_FILL_RESERVOIR_STATE, ///< Wait for fill to complete. TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE, ///< Wait a bit for filled reservoir to settle before getting baseline weight (volume). TREATMENT_RESERVOIR_MGMT_WAIT_FOR_RES_SWITCH_STATE, ///< Wait for active reservoir to be consumed and switch cmd given - then back to flush DG lines state. TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE, ///< Wait for inactive reservoir to settle before getting final weight (volume) before starting to drain. NUM_OF_TREATMENT_RESERVOIR_MGMT_STATES ///< Number of treatment reservoir mgmt. states. } TREATMENT_RESERVOIR_MGMT_STATE_T; // ********** private data ********** // DG status static DG_OP_MODE_T dgCurrentOpMode = DG_MODE_INIT; ///< Current DG operation mode. static U32 dgSubMode = 0; ///< Current state (sub-mode) of current DG operation mode. static BOOL dgStartCommandSent = FALSE; ///< Flag indicates command to start DG has been sent. static BOOL dgStarted = FALSE; ///< Flag indicates whether we have commanded the DG to start or stop. static BOOL dgTrimmerHeaterOn = FALSE; ///< Flag indicates whether we have commanded the DG to start or stop the trimmer heater. // State machine states /// Current state of treatment mode reservoir management. static TREATMENT_RESERVOIR_MGMT_STATE_T currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_START_STATE; static U32 resMgmtTimer = 0; ///< Used for keeping state time. // DG sensor data static F32 dgPressures[ NUM_OF_DG_PRESSURE_SENSORS ]; ///< Latest pressures reported by the DG. static F32 dgDialysateTemp = 0.0; ///< Dialysate temperature reported by the DG. static F32 dgRedundantDialysateTemp = 0.0; ///< Redundant dialysate temperature reported by the DG. static F32 dgPrimaryTempSet = 0.0; ///< Primary heater target temperature commanded. static F32 dgPrimaryTemp = 0.0; ///< Latest RO water temperature reported by the DG. static F32 dgTrimmerTempSet = 0.0; ///< Trimmer heater target temperature commanded. static F32 dgTrimmerTemp = 0.0; ///< Latest dialysate temperature reported by the DG. /// Measured weight from load cells. static OVERRIDE_F32_T loadCellWeightInGrams[ NUM_OF_LOAD_CELLS ]; /// Filtered (32 sample) weight of reservoirs. static F32 lgFilteredReservoirWeightInGrams[ NUM_OF_DG_RESERVOIRS ]; // Load cell filtering data /// Holds load cell samples for large load cell moving average. static F32 lgLoadCellReadings[ NUM_OF_DG_RESERVOIRS ][ SIZE_OF_LARGE_LOAD_CELL_AVG ]; static U32 lgLoadCellReadingsIdx = 0; ///< Index for next sample in large load cell rolling average sample array. static F32 lgLoadCellReadingsTotal[ NUM_OF_DG_RESERVOIRS ]; ///< Rolling total - used to calc large load cell moving average. // DG pumps data static F32 dgROPumpFlowRateMlMin = 0.0; ///< Latest RO water flow rate reported by the DG. static U32 dgROPumpPressureSetPtPSI = 0; ///< Latest RO pump target pressure reported by the DG. static U32 dgDrainPumpSpeedSetPtRPM = 0; ///< Latest Drain pump target speed reported by the DG. // Reservoir data static DG_RESERVOIR_ID_T dgActiveReservoir = DG_RESERVOIR_2; ///< Latest active reservoir reported by the DG. static DG_RESERVOIR_ID_T dgActiveReservoirSet = DG_RESERVOIR_2; ///< Active reservoir commanded. static U32 dgReservoirFillVolumeTarget = 0; ///< Latest fill-to volume reported by the DG. static U32 dgReservoirFillVolumeTargetSet = 0; ///< Fill-to volume commanded. static U32 dgReservoirDrainVolumeTarget = 0; ///< Latest drain-to volume reported by the DG. static U32 dgReservoirDrainVolumeTargetSet = 0; ///< Drain-to volume commanded. static U32 resUseTimer = 0; ///< Used to track time pumping from active reservoir (for volume used calculation). static F32 resUseVolumeMl = 0.0; ///< Accumulated volume used from active reservoir. static DG_DISINFECT_UI_STATES_T disinfectsStatus; ///< DG disinfects status. // DG command response static DG_CMD_RESPONSE_T dgCmdResp[ NUM_OF_DG_COMMANDS ]; ///< Keep the latest DG command response for each command. // ********** private function prototypes ********** static void checkDGRestart( void ); /*********************************************************************//** * @brief * The initDGInterface function initializes the DGInterface module. * @details Inputs: none * @details Outputs: DGInterface module initialized. * @return none *************************************************************************/ void initDGInterface( void ) { U32 i, j; dgStarted = FALSE; dgTrimmerHeaterOn = FALSE; dgPrimaryTempSet = 0.0; dgTrimmerTempSet = 0.0; dgActiveReservoirSet = DG_RESERVOIR_2; dgReservoirFillVolumeTargetSet = 0; dgReservoirDrainVolumeTargetSet = 0; // initialize load cell weights for ( i = 0; i < NUM_OF_LOAD_CELLS; i++ ) { loadCellWeightInGrams[ i ].data = 0.0; loadCellWeightInGrams[ i ].ovInitData = 0.0; loadCellWeightInGrams[ i ].ovData = 0.0; loadCellWeightInGrams[ i ].override = 0; } // initialize reservoirs weights for ( i = 0; i < NUM_OF_DG_RESERVOIRS; i++ ) { lgFilteredReservoirWeightInGrams[ i ] = 0.0; for ( j = 0; j < SIZE_OF_LARGE_LOAD_CELL_AVG; j++ ) { lgLoadCellReadings[ i ][ j ] = 0.0; } } // initialize DG command response for ( i = 0; i < NUM_OF_DG_COMMANDS; i++ ) { dgCmdResp[ i ].commandID = DG_CMD_NONE; dgCmdResp[ i ].rejected = TRUE; dgCmdResp[ i ].rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; } lgLoadCellReadingsIdx = 0; lgLoadCellReadingsTotal[ DG_RESERVOIR_1 ] = 0.0; lgLoadCellReadingsTotal[ DG_RESERVOIR_2 ] = 0.0; initPersistentAlarm( ALARM_ID_DIALYSATE_TEMPERATURE_HIGH, DIALYSATE_TEMP_PERSISTENCE_PERIOD, DIALYSATE_TEMP_PERSISTENCE_PERIOD ); initPersistentAlarm( ALARM_ID_DIALYSATE_TEMPERATURE_LOW, DIALYSATE_TEMP_PERSISTENCE_PERIOD, DIALYSATE_TEMP_PERSISTENCE_PERIOD ); } /*********************************************************************//** * @brief * The execDGInterfaceMonitor function executes the DG Interface monitoring * function. Ensures DG is sending fresh data in a timely manner. * @details Inputs: TBD * @details Outputs: TBD * @return none *************************************************************************/ void execDGInterfaceMonitor( void ) { // TODO - make sure DG sensor/state data is coming in timely manner (e.g. load cells s/b every 100 ms) // Check to see if DG has restarted checkDGRestart(); } /*********************************************************************//** * @brief * The initTreatmentReservoirMgmt function initializes the treatment reservoir * management state machine. * @details Inputs: none * @details Outputs: treatment reservoir management state machine initialized. * @return none *************************************************************************/ void initTreatmentReservoirMgmt( void ) { currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_START_STATE; resMgmtTimer = 0; resUseTimer = getMSTimerCount(); resUseVolumeMl = 0.0; } /*********************************************************************//** * @brief * The dialysisResumed function initializes the reservoir re-use timer * when dialysis is started/resumed so that dialysate usage can be tracked. * @details Inputs: none * @details Outputs: resUseTimer * @return none *************************************************************************/ void dialysisResumed( void ) { resUseTimer = getMSTimerCount(); } /*********************************************************************//** * @brief * The execTreatmentReservoirMgmt function executes the state machine for the * reservoir management during treatment mode. * @details Inputs: none * @details Outputs: DG reservoirs (drains & fills) managed. * @return none *************************************************************************/ void execTreatmentReservoirMgmt( void ) { DG_OP_MODE_T dgOpMode = getDGOpMode(); U32 dgSubMode = getDGSubMode(); U32 msSinceLastVolumeCalc = calcTimeSince( resUseTimer ); F32 flowRateMlPerMs = (F32)getTargetDialInFlowRate() / (F32)( MS_PER_SECOND * SEC_PER_MIN ); // Calculate volume used from active reservoir - do not accumulate if saline bolus in progress if ( SALINE_BOLUS_STATE_IN_PROGRESS != getSalineBolusState() ) { resUseVolumeMl += ( flowRateMlPerMs * msSinceLastVolumeCalc ); } resUseTimer = getMSTimerCount(); // Alarm if active reservoir is full and inactive reservoir is not yet ready if ( getReservoirWeight( getDGActiveReservoir() ) > MAX_RESERVOIR_VOLUME_ML ) { if ( currentTrtResMgmtState < TREATMENT_RESERVOIR_MGMT_WAIT_FOR_RES_SWITCH_STATE ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_RESERVOIR_FULL_AND_DG_NOT_READY_TO_SWITCH, getReservoirWeight( getDGActiveReservoir() ), (F32)currentTrtResMgmtState ) } else { clearAlarmCondition( ALARM_ID_HD_RESERVOIR_FULL_AND_DG_NOT_READY_TO_SWITCH ); } } // Treatment reservoir mgmt. state machine switch ( currentTrtResMgmtState ) { case TREATMENT_RESERVOIR_MGMT_START_STATE: currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_FLUSH_DG_LINES_STATE; break; case TREATMENT_RESERVOIR_MGMT_FLUSH_DG_LINES_STATE: if ( DG_MODE_CIRC == dgOpMode ) { if ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == dgSubMode ) { cmdStartDGDrain( DRAIN_RESERVOIR_TO_VOLUME_ML, TRUE ); } } else if ( DG_MODE_DRAI == dgOpMode ) { currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_DRAIN_RESERVOIR_STATE; } else { // TODO - ??? } break; case TREATMENT_RESERVOIR_MGMT_DRAIN_RESERVOIR_STATE: if ( DG_MODE_CIRC == dgOpMode ) { currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_WAIT_TO_FILL_STATE; } break; case TREATMENT_RESERVOIR_MGMT_WAIT_TO_FILL_STATE: // Delay fill start if we have paused treatment? if ( getTreatmentState() == TREATMENT_DIALYSIS_STATE ) { if ( DG_MODE_CIRC == dgOpMode ) { if ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == dgSubMode ) { U32 fillToVolume = FILL_RESERVOIR_TO_VOLUME_ML; if ( getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ) <= SLOW_DIALYSATE_FLOW_ML_MIN ) { fillToVolume = FILL_RESERVOIR_TO_VOLUME_LOW_FLOW_ML; } cmdStartDGFill( fillToVolume ); } } else { // TODO - ??? } } if ( DG_MODE_FILL == dgOpMode ) { currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_FILL_RESERVOIR_STATE; } break; case TREATMENT_RESERVOIR_MGMT_FILL_RESERVOIR_STATE: if ( ( DG_MODE_CIRC == dgOpMode ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getDGSubMode() ) ) { resMgmtTimer = getMSTimerCount(); currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE; } break; case TREATMENT_RESERVOIR_MGMT_WAIT_FOR_FILL_SETTLE_STATE: if ( TRUE == didTimeout( resMgmtTimer, RESERVOIR_SETTLE_TIME_MS ) ) { currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_RES_SWITCH_STATE; } break; case TREATMENT_RESERVOIR_MGMT_WAIT_FOR_RES_SWITCH_STATE: // Reservoir switch during treatment should only occur in this state (i.e. when DG is ready). // Switch reservoirs when active reservoir is spent or full (i.e. we have pumped fill volume through dialyzer) and DG ready if ( ( resUseVolumeMl >= (F32)dgReservoirFillVolumeTargetSet ) || ( getReservoirWeight( getDGActiveReservoir() ) > MAX_RESERVOIR_VOLUME_ML ) ) { DG_RESERVOIR_ID_T inactiveRes = getDGInactiveReservoir(); // Signal dialysis sub-mode to capture baseline volume for next reservoir. setStartReservoirVolume( inactiveRes ); // Command DG to switch reservoirs cmdSetDGActiveReservoir( inactiveRes ); // Signal dialysis sub-mode to switch reservoirs signalReservoirsSwitched(); resUseVolumeMl = 0.0; // Wait for used reservoir to settle resMgmtTimer = getMSTimerCount(); currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE; } break; case TREATMENT_RESERVOIR_MGMT_WAIT_FOR_SWITCH_SETTLE_STATE: if ( TRUE == didTimeout( resMgmtTimer, RESERVOIR_SETTLE_TIME_MS ) ) { // Signal dialysis sub-mode to capture final volume of prior reservoir after settling. setFinalReservoirVolume(); // Reset to start state to restart drain, fill, switch process. currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_START_STATE; } break; default: // TODO - s/w fault currentTrtResMgmtState = TREATMENT_RESERVOIR_MGMT_START_STATE; break; } } /*********************************************************************//** * @brief * The getDGOpMode function gets the current DG operating mode. * @details Inputs: dgCurrentOpMode * @details Outputs: none * @return Current DG operating mode. *************************************************************************/ DG_OP_MODE_T getDGOpMode( void ) { return dgCurrentOpMode; } /*********************************************************************//** * @brief * The getDGSubMode function gets the current DG operating sub-mode. * @details Inputs: dgSubMode * @details Outputs: none * @return Current DG operating sub-mode. *************************************************************************/ U32 getDGSubMode( void ) { return dgSubMode; } /*********************************************************************//** * @brief * The getDGActiveReservoir function gets the current active reservoir. * @details Inputs: dgActiveReservoirSet * @details Outputs: none * @return Currently commanded active reservoir. *************************************************************************/ DG_RESERVOIR_ID_T getDGActiveReservoir( void ) { return dgActiveReservoirSet; } /*********************************************************************//** * @brief * The getDGInactiveReservoir function gets the currently inactive reservoir. * @details Inputs: dgActiveReservoirSet * @details Outputs: none * @return Currently commanded inactive reservoir. *************************************************************************/ DG_RESERVOIR_ID_T getDGInactiveReservoir( void ) { DG_RESERVOIR_ID_T inactiveRes = ( DG_RESERVOIR_2 == dgActiveReservoirSet ? DG_RESERVOIR_1 : DG_RESERVOIR_2 ); return inactiveRes; } /*********************************************************************//** * @brief * The hasDGCompletedReservoirSwitch function checks if DG has completed a * switch reservoir command. * @details Inputs: dgActiveReservoir, dgActiveReservoirSet * @details Outputs: none * @return TRUE if reported active reservoir is the same as set id, otherwise FALSE *************************************************************************/ BOOL hasDGCompletedReservoirSwitch( void ) { return ( dgActiveReservoir == dgActiveReservoirSet ); } /*********************************************************************//** * @brief * The getDGPressure function gets the latest pressure reported by the DG * for a given pressure sensor. * @details Inputs: dgPressures[] * @details Outputs: none * @param sensorID pressure sensor we are getting reading for * @return Latest pressure reading reported by DG for the given sensor. *************************************************************************/ F32 getDGPressure( DG_PRESSURE_SENSORS_T sensorID ) { F32 result = 0.0; if ( sensorID < NUM_OF_DG_PRESSURE_SENSORS ) { result = dgPressures[ sensorID ]; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_PRESSURE_ID, sensorID ) } return result; } /*********************************************************************//** * @brief * The getDGROPumpPressureSetPt function gets the latest RO pump * pressure set point reported by the DG. * @details Inputs: dgROPumpPressureSetPtPSI * @details Outputs: none * @return Latest RO pump pressure set point reported by DG. *************************************************************************/ U32 getDGROPumpPressureSetPt( void ) { U32 result = dgROPumpPressureSetPtPSI; return result; } /*********************************************************************//** * @brief * The getDGROPumpFlowRateMlMin function gets the latest RO pump flow * rate reported by the DG. * @details Inputs: dgROPumpFlowRateMlMin * @details Outputs: none * @return Latest RO pump flow rate reported by DG. *************************************************************************/ F32 getDGROPumpFlowRateMlMin( void ) { F32 result = dgROPumpFlowRateMlMin; return result; } /*********************************************************************//** * @brief * The getDGDrainPumpRPMSetPt function gets the latest drain pump RPM * set point reported by the DG. * @details Inputs: dgDrainPumpSpeedSetPtRPM * @details Outputs: none * @return Latest drain pump RPM set point reported by DG. *************************************************************************/ U32 getDGDrainPumpRPMSetPt( void ) { U32 result = dgDrainPumpSpeedSetPtRPM; return result; } /*********************************************************************//** * @brief * The getLoadCellWeight function gets the current load cell weight. * @details Inputs: loadCellWeightInGrams * @details Outputs: none * @param loadCellID ID of load cell to get * @return the current load cell weight in grams *************************************************************************/ F32 getLoadCellWeight( LOAD_CELL_ID_T loadCellID ) { F32 result = 0.0; if ( loadCellID < NUM_OF_LOAD_CELLS ) { if ( OVERRIDE_KEY == loadCellWeightInGrams[ loadCellID ].override ) { result = loadCellWeightInGrams[ loadCellID ].ovData; } else { result = loadCellWeightInGrams[ loadCellID ].data; } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_LOAD_CELL_ID, loadCellID ); } return result; } /*********************************************************************//** * @brief * The getReservoirWeightLargeFilter function gets the load cell weight * of the given reservoir after large (32 sample) filter applied. * @details Inputs: lgFilteredReservoirWeightInGrams[] * @details Outputs: none * @param resID ID of reservoir to get filtered weight for * @return the current filtered weight of the given reservoir in grams *************************************************************************/ F32 getReservoirWeightLargeFilter( DG_RESERVOIR_ID_T resID ) { F32 result = 0.0; if ( resID < NUM_OF_DG_RESERVOIRS ) { result = lgFilteredReservoirWeightInGrams[ resID ]; } return result; } /*********************************************************************//** * @brief * The getDialysateTemperature function gets the latest dialysate temperature. * @details Inputs: dgDialysateTemp * @details Outputs: none * @return the current dialysate temperature *************************************************************************/ F32 getDialysateTemperature( void ) { return dgDialysateTemp; } /*********************************************************************//** * @brief * The getDGDisinfectsStates function returns the DG disinfects readings. * @details Inputs: none * @details Outputs: disinfectsStatus * @return the current DG disinfects readings *************************************************************************/ DG_DISINFECT_UI_STATES_T getDGDisinfectsStates( void ) { return disinfectsStatus; } /*********************************************************************//** * @brief * The getReservoirWeight function gets the load cell weight of a given reservoir. * @details Inputs: loadCellWeightInGrams[] * @details Outputs: none * @param resID ID of reservoir to get weight for * @return the current weight of the given reservoir in grams *************************************************************************/ F32 getReservoirWeight( DG_RESERVOIR_ID_T resID ) { LOAD_CELL_ID_T lc = ( DG_RESERVOIR_1 == resID ? LOAD_CELL_RESERVOIR_1_PRIMARY : LOAD_CELL_RESERVOIR_2_PRIMARY ); F32 wt = getLoadCellWeight( lc ); return wt; } /*********************************************************************//** * @brief * The setDGOpMode function sets the latest DG operating mode reported by * the DG. * @details Inputs: none * @details Outputs: dgCurrentOpMode, dgSubMode * @param opMode operating mode reported by DG * @param subMode sub-mode (current state) of operating mode reported by DG * @return none *************************************************************************/ void setDGOpMode( U32 opMode, U32 subMode ) { if ( opMode < NUM_OF_DG_MODES ) { dgCurrentOpMode = (DG_OP_MODE_T)opMode; dgSubMode = subMode; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_OPERATING_MODE, opMode ); } } /*********************************************************************//** * @brief * The setDialysateTemperatureReadings function sets the latest dialysate * temperatures reported by the DG. * @details Inputs: none * @details Outputs: dgDialysateTemp, dgRedundantDialysateTemp * @param temp1 dialysate temperature reported by DG * @param temp2 redundant dialysate temperature reported by DG * @return none *************************************************************************/ void setDialysateTemperatureReadings( F32 temp1, F32 temp2 ) { dgDialysateTemp = temp1; dgRedundantDialysateTemp = temp2; } /*********************************************************************//** * @brief * The setDGDialysateTemperatures function sets the latest temperature data * reported by the DG. * @details Inputs: none * @details Outputs: dgPrimaryTemp, dgTrimmerTemp * @param primaryHtrTemp Primary heater temperature reported by DG * @param trimmerHtrTemp Trimmer heater temperature reported by DG * @return none *************************************************************************/ void setDGDialysateTemperatures( F32 primaryHtrTemp, F32 trimmerHtrTemp ) { dgPrimaryTemp = primaryHtrTemp; dgTrimmerTemp = trimmerHtrTemp; } /*********************************************************************//** * @brief * The setDGReservoirsData function sets the latest reservoir data * reported by the DG. * @details Inputs: none * @details Outputs: dgActiveReservoir, dgReservoirFillVolumeTarget, dgReservoirDrainVolumeTarget * @param resID ID of active reservoir * @param fillVol Reservoir fill to volume reported by DG * @param drainVol Reservoir drain to volume reported by DG * @return none *************************************************************************/ void setDGReservoirsData( DG_RESERVOIR_ID_T resID, U32 fillVol, U32 drainVol ) { if ( resID < NUM_OF_DG_RESERVOIRS ) { dgActiveReservoir = resID; dgReservoirFillVolumeTarget = fillVol; dgReservoirDrainVolumeTarget = drainVol; } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_RESERVOIR_ID, resID ); } } /*********************************************************************//** * @brief * The setDGPressures function sets the latest pressures reported by the DG. * @details Inputs: none * @details Outputs: dgPressures[] * @param roIn latest RO pump inlet pressure reported by DG * @param roOut latest RO pump outlet pressure reported by DG * @param drainIn latest drain pump inlet pressure reported by DG * @param drainOut latest drain pump outlet pressure reported by DG * @return none *************************************************************************/ void setDGPressures( F32 roIn, F32 roOut, F32 drainIn, F32 drainOut ) { dgPressures[ DG_PRESSURE_SENSOR_RO_PUMP_INLET ] = roIn; dgPressures[ DG_PRESSURE_SENSOR_RO_PUMP_OUTLET ] = roOut; dgPressures[ DG_PRESSURE_SENSOR_DRAIN_PUMP_INLET ] = drainIn; dgPressures[ DG_PRESSURE_SENSOR_DRAIN_PUMP_OUTLET ] = drainOut; } /*********************************************************************//** * @brief * The setDGROPumpData function sets the latest RO pump data reported by the DG. * @details Inputs: none * @details Outputs: dgROPumpPressureSetPtPSI, dgROPumpFlowRateMlMin * @param presSetPt latest RO pump pressure set point reported by DG * @param flowRate latest RO pump flow rate (LPM) reported by DG * @return none *************************************************************************/ void setDGROPumpData( U32 presSetPt, F32 flowRate ) { dgROPumpPressureSetPtPSI = presSetPt; dgROPumpFlowRateMlMin = flowRate * ML_PER_LITER; } /*********************************************************************//** * @brief * The setDGDrainPumpData function sets the latest drain pump data reported by the DG. * @details Inputs: none * @details Outputs: dgDrainPumpSpeedSetPtRPM * @param rpmSetPt latest drain pump RPM set point reported by DG * @return none *************************************************************************/ void setDGDrainPumpData( U32 rpmSetPt ) { dgDrainPumpSpeedSetPtRPM = rpmSetPt; } /*********************************************************************//** * @brief * The setNewLoadCellReadings function sets the latest DG reservoir load cell * readings sent by the DG (in g or mL). New readings are expected once * every 100 ms. * @details Inputs: none * @details Outputs: loadCellWeightInGrams[], smFilteredReservoirWeightInGrams[], * lgFilteredReservoirWeightInGrams[] * @param res1Primary New weight from primary load cell of reservoir 1 * @param res1Backup New weight from backup load cell of reservoir 1 * @param res2Primary New weight from primary load cell of reservoir 2 * @param res2Backup New weight from backup load cell of reservoir 2 * @return none *************************************************************************/ void setNewLoadCellReadings( F32 res1Primary, F32 res1Backup, F32 res2Primary, F32 res2Backup ) { DG_RESERVOIR_ID_T res; DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); BOOL inTreatment = ( MODE_TREA == getCurrentOperationMode() ? TRUE : FALSE ); loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_1_PRIMARY ].data = res1Primary; loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_1_BACKUP ].data = res1Backup; loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_2_PRIMARY ].data = res2Primary; loadCellWeightInGrams[ LOAD_CELL_RESERVOIR_2_BACKUP ].data = res2Backup; // Feed new weight samples into filters and update moving averages for ( res = DG_RESERVOIR_1; res < NUM_OF_DG_RESERVOIRS; res++ ) { F32 wt = ( res == DG_RESERVOIR_1 ? res1Primary : res2Primary ); lgLoadCellReadingsTotal[ res ] -= lgLoadCellReadings[ res ][ lgLoadCellReadingsIdx ]; lgLoadCellReadings[ res ][ lgLoadCellReadingsIdx ] = wt; lgLoadCellReadingsTotal[ res ] += wt; lgFilteredReservoirWeightInGrams[ res ] = lgLoadCellReadingsTotal[ res ] / (F32)SIZE_OF_LARGE_LOAD_CELL_AVG; } lgLoadCellReadingsIdx = INC_WRAP( lgLoadCellReadingsIdx, 0, SIZE_OF_LARGE_LOAD_CELL_AVG - 1 ); // Update Dialysis sub-mode with new reservoir volumes updateReservoirVolumes( res1Primary, res2Primary ); } /*********************************************************************//** * @brief * The setDGDisinfectsStates function sets the latest disinfects states * from DG. * @details Inputs: none * @details Outputs: disinfectsStatus * @param states latest DG disinfects state readings * @return none *************************************************************************/ void setDGDisinfectsStates( DG_DISINFECT_UI_STATES_T states ) { memcpy( &disinfectsStatus, &states, sizeof(DG_DISINFECT_UI_STATES_T) ); } /*********************************************************************//** * @brief * The cmdSetDGDialysateTargetTemps function sends a target dialysate * temperature command message to the DG. * @details Inputs: none * @details Outputs: dgPrimaryTempSet, dgTrimmerTempSet * @param primaryHtrTemp commanded target dialysate temperature for the primary heater * @param trimmerHtrTemp commanded target dialysate temperature for the trimmer heater * @return none *************************************************************************/ void cmdSetDGDialysateTargetTemps( F32 primaryHtrTemp, F32 trimmerHtrTemp ) { dgPrimaryTempSet = primaryHtrTemp; dgTrimmerTempSet = trimmerHtrTemp; sendDialysateTempTargetsToDG( dgPrimaryTempSet, dgTrimmerTempSet ); } /*********************************************************************//** * @brief * The cmdStartDG function sends a start command to the DG. DG will transition * from standby to recirculate mode and start producing warm, pure water. * @details Inputs: none * @details Outputs: start DG command sent * @return none *************************************************************************/ void cmdStartDG( void ) { dgStartCommandSent = TRUE; sendDGStartStopCommand( START_DG_CMD ); } /*********************************************************************//** * @brief * The cmdStopDG function sends a stop command to the DG. DG will transition * from recirculate mode to standby mode. Pumps and heater go off. * @details Inputs: none * @details Outputs: stop DG command sent * @return none *************************************************************************/ void cmdStopDG( void ) { dgStarted = FALSE; sendDGStartStopCommand( STOP_DG_CMD ); } /*********************************************************************//** * @brief * The cmdStartDGTrimmerHeater function sends a start trimmer heater command * to the DG. * @details Inputs: none * @details Outputs: start DG trimmer heater command sent * @return none *************************************************************************/ void cmdStartDGTrimmerHeater( void ) { dgTrimmerHeaterOn = TRUE; dgCmdResp[ DG_CMD_START_TRIMMER_HEATER ].commandID = DG_CMD_NONE; sendDGStartStopTrimmerHeaterCommand( START_DG_CMD, dgTrimmerTempSet ); } /*********************************************************************//** * @brief * The cmdStopDGTrimmerHeater function sends a stop trimmer heater command * to the DG. * @details Inputs: none * @details Outputs: stop DG trimmer heater command sent * @return none *************************************************************************/ void cmdStopDGTrimmerHeater( void ) { dgTrimmerHeaterOn = FALSE; dgCmdResp[ DG_CMD_STOP_TRIMMER_HEATER ].commandID = DG_CMD_NONE; sendDGStartStopTrimmerHeaterCommand( STOP_DG_CMD, 0 ); } /*********************************************************************//** * @brief * The cmdSetDGActiveReservoir function sends a set active reservoir command * message to the DG. * @details Inputs: none * @details Outputs: set active reservoir command sent to DG. * @param resID ID of reservoir to set as active (reservoir for HD to draw from) * @return none *************************************************************************/ void cmdSetDGActiveReservoir( DG_RESERVOIR_ID_T resID ) { if ( resID < NUM_OF_DG_RESERVOIRS ) { dgActiveReservoirSet = resID; dgCmdResp[ DG_CMD_SWITCH_RESERVOIR ].commandID = DG_CMD_NONE; sendDGSwitchReservoirCommand( (U32)resID ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_RESERVOIR_ID, (U32)resID ); } } /*********************************************************************//** * @brief * The cmdChangeDGValveSetting function sends a change valve setting command * message to the DG. * @details Inputs: none * @details Outputs: change valve setting command sent to DG. * @param valveSettingID ID of valve setting to change to * @return none *************************************************************************/ void cmdChangeDGValveSetting( DG_VALVE_SETTING_ID_T valveSettingID ) { if ( valveSettingID < NUM_OF_DG_VALVE_SETTTINGS ) { dgCmdResp[ DG_CMD_VALVE_SETTING ].commandID = DG_CMD_NONE; sendDGChangeValveSettingCommand( (U32)valveSettingID ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_VALVE_SETTING_ID, (U32)valveSettingID ); } } /*********************************************************************//** * @brief * The cmdStartDGFill function sends a fill command message to the DG. * @details Inputs: none * @details Outputs: fill command sent to DG. * @param fillToVolMl volume (in mL) to fill inactive reservoir to * @return none *************************************************************************/ void cmdStartDGFill( U32 fillToVolMl ) { dgCmdResp[ DG_CMD_START_FILL ].commandID = DG_CMD_NONE; dgReservoirFillVolumeTargetSet = fillToVolMl; sendDGFillCommand( DG_CMD_START, fillToVolMl ); } /*********************************************************************//** * @brief * The cmdStopDGFill function sends a fill command with stop parameter message to the DG. * @details Inputs: none * @details Outputs: fill command with stop parameter sent to DG. * @return none *************************************************************************/ void cmdStopDGFill( void ) { dgCmdResp[ DG_CMD_STOP_FILL ].commandID = DG_CMD_NONE; dgReservoirFillVolumeTargetSet = 0; sendDGFillCommand( DG_CMD_STOP, 0 ); } /*********************************************************************//** * @brief * The cmdStartDGDrain function sends a drain command message to the DG. * @details Inputs: none * @details Outputs: drain command sent to DG. * @param drainToVolMl volume (in mL) to drain inactive reservoir to * @param tareLoadCell flag to tell DG tare load cell or not * @return none *************************************************************************/ void cmdStartDGDrain( U32 drainToVolMl, BOOL tareLoadCell ) { DRAIN_RESERVOIR_CMD_PAYLOAD_T payload; dgCmdResp[ DG_CMD_START_DRAIN ].commandID = DG_CMD_NONE; payload.drainToVolumeML = drainToVolMl; payload.tareLoadCells = tareLoadCell; dgReservoirDrainVolumeTargetSet = drainToVolMl; sendDGDrainCommand( &payload ); } /*********************************************************************//** * @brief * The cmdDGSampleWater function sends a sample water command message to the DG. * @details Inputs: none * @details Outputs: sample water command sent to DG. * @return none *************************************************************************/ void cmdDGSampleWater( SAMPLE_WATER_CMD_T cmd ) { sendDGSampleWaterCommand( cmd ); } /*********************************************************************//** * @brief * The cmdStartDGFlush function sends a start flush command message to * the DG. * @details Inputs: none * @details Outputs: start flush mode command sent to DG. * @return none *************************************************************************/ void cmdStartDGFlush( void ) { BOOL start = TRUE; dgCmdResp[ DG_CMD_START_FLUSH ].commandID = DG_CMD_NONE; sendDGStartFlushModeCommand( start ); } /*********************************************************************//** * @brief * The cmdStopDGFlush function sends a stop flush command message to * the DG. * @details Inputs: none * @details Outputs: stop flush mode command sent to DG. * @return none *************************************************************************/ void cmdStopDGFlush( void ) { BOOL start = FALSE; dgCmdResp[ DG_CMD_STOP_FLUSH ].commandID = DG_CMD_NONE; sendDGStartFlushModeCommand( start ); } /*********************************************************************//** * @brief * The cmdStartDGHeatDisinfect function sends a start heat disinfect * command message to the DG. * @details Inputs: none * @details Outputs: start heat disinfect mode command sent to DG. * @return none *************************************************************************/ void cmdStartDGHeatDisinfect( void ) { BOOL start = TRUE; dgCmdResp[ DG_CMD_START_HEAT_DISINFECT ].commandID = DG_CMD_NONE; sendDGStartHeatDisinfectModeCommand( start ); } /*********************************************************************//** * @brief * The cmdStopDGHeatDisinfect function sends a stop heat disinfect * command message to the DG. * @details Inputs: none * @details Outputs: stop heat disinfect mode command sent to DG. * @return none *************************************************************************/ void cmdStopDGHeatDisinfect( void ) { BOOL start = FALSE; dgCmdResp[ DG_CMD_STOP_HEAT_DISINFECT ].commandID = DG_CMD_NONE; sendDGStartHeatDisinfectModeCommand( start ); } /*********************************************************************//** * @brief * The cmdStartDGChemicalDisinfect function sends a start chemical disinfect * command message to the DG. * @details Inputs: none * @details Outputs: start chemical disinfect mode command sent to DG. * @return none *************************************************************************/ void cmdStartDGChemicalDisinfect( void ) { BOOL start = TRUE; dgCmdResp[ DG_CMD_START_CHEM_DISINFECT ].commandID = DG_CMD_NONE; sendDGStartChemicalDisinfectModeCommand( start ); } /*********************************************************************//** * @brief * The cmdStopDGChemicalDisinfect function sends a stop chemical disinfect * command message to the DG. * @details Inputs: none * @details Outputs: stop chemical disinfect mode command sent to DG. * @return none *************************************************************************/ void cmdStopDGChemicalDisinfect( void ) { BOOL start = FALSE; dgCmdResp[ DG_CMD_STOP_CHEM_DISINFECT ].commandID = DG_CMD_NONE; sendDGStartChemicalDisinfectModeCommand( start ); } /*********************************************************************//** * @brief * The handleDGCommandResponse function processes the latest DG command response. * @details Inputs: none * @details Outputs: process command response from DG * @param dgCmdRespPtr pointer to DG command response data record * @return none *************************************************************************/ void handleDGCommandResponse( DG_CMD_RESPONSE_T *dgCmdRespPtr ) { if ( dgCmdRespPtr->commandID < NUM_OF_DG_COMMANDS ) { if ( DG_CMD_NONE != dgCmdRespPtr->commandID ) { memcpy( &dgCmdResp[ dgCmdRespPtr->commandID ], dgCmdRespPtr, sizeof( DG_CMD_RESPONSE_T ) ); } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_COMMAND_RESPONSE_ID, dgCmdRespPtr->commandID ); } if ( TRUE == dgCmdRespPtr->rejected ) { switch ( dgCmdRespPtr->rejectCode ) { case DG_CMD_REQUEST_REJECT_REASON_INVALID_PARAMETER: activateAlarmNoData( ALARM_ID_DG_COMMAND_INVALID_PARAMETER_FAULT ); break; case DG_CMD_REQUEST_REJECT_REASON_NONE: case DG_CMD_REQUEST_REJECT_REASON_INVALID_MODE: default: // Our state machines will detect and handle DG mode out of sync // TODO Consider a generic handler for all state machine break; } } } /*********************************************************************//** * @brief * The getDGCommandResponse function gets the latest command response from DG. * @details Inputs: dgCmdResp * @details Outputs: none * @param commandID id of specific interested command response * @param cmdRespPtr pointer to data record to copy DG command response to * @return TRUE if a specific command response has been received, otherwise FALSE *************************************************************************/ BOOL getDGCommandResponse( U32 commandID, DG_CMD_RESPONSE_T *cmdRespPtr ) { BOOL hasCommandResp = FALSE; if ( commandID == dgCmdResp[ commandID ].commandID ) { hasCommandResp = TRUE; memcpy( cmdRespPtr, &dgCmdResp[ commandID ], sizeof( DG_CMD_RESPONSE_T ) ); dgCmdResp[ commandID ].commandID = DG_CMD_NONE; } return hasCommandResp; } /*********************************************************************//** * @brief * The checkDialysateTemperature function checks the dialysate temperature * reported by DG and alarm if temperature is out of range. * @details Inputs: dgTrimmerTempSet, dgDialysateTemp, dgRedundantDialysateTemp * @details Outputs: alarm if dialysate temperature is out of accepted range * @return none *************************************************************************/ void checkDialysateTemperature( void ) { BOOL const dialysateHighTemp = ( ( ( dgDialysateTemp - dgTrimmerTempSet ) > DIALYSATE_TEMP_TOLERANCE_C ) || ( dgDialysateTemp > DIALYSATE_TEMP_HIGH_LIMIT_C ) ); BOOL const dialysateLowTemp = ( ( ( dgTrimmerTempSet - dgDialysateTemp ) > DIALYSATE_TEMP_TOLERANCE_C ) || ( dgDialysateTemp < DIALYSATE_TEMP_LOW_LIMIT_C ) ); BOOL const dialysateTempRecovered = fabs( dgDialysateTemp - dgTrimmerTempSet ) < DIALYSATE_TEMP_RECOVERY_TOLERANCE_C ? TRUE : FALSE; #ifndef DISABLE_DIALYSATE_TEMP_CHECK if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_DIALYSATE_TEMPERATURE_HIGH, dialysateHighTemp ) ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIALYSATE_TEMPERATURE_HIGH, dgTrimmerTempSet, dgDialysateTemp ); } if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_DIALYSATE_TEMPERATURE_LOW, dialysateLowTemp ) ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIALYSATE_TEMPERATURE_LOW, dgTrimmerTempSet, dgDialysateTemp ); } if ( TRUE == isPersistentAlarmConditionCleared( ALARM_ID_DIALYSATE_TEMPERATURE_HIGH, dialysateHighTemp ) ) { clearAlarmCondition( ALARM_ID_DIALYSATE_TEMPERATURE_HIGH ); } if ( TRUE == isPersistentAlarmConditionCleared( ALARM_ID_DIALYSATE_TEMPERATURE_LOW, dialysateTempRecovered ) ) { clearAlarmCondition( ALARM_ID_DIALYSATE_TEMPERATURE_LOW ); } #endif } /*********************************************************************//** * @brief * The checkDGRestart function checks to see if DG has restarted after started * by HD and triggers appropriate alarm. * @details Inputs: dgStarted * @details Outputs: triggers a fault alarm if DG restarted * @return none *************************************************************************/ static void checkDGRestart( void ) { if ( ( dgStartCommandSent == TRUE ) && ( DG_MODE_CIRC == dgCurrentOpMode ) ) { dgStartCommandSent = FALSE; dgStarted = TRUE; } if ( TRUE == dgStarted ) { if ( ( DG_MODE_FAUL != dgCurrentOpMode ) && ( DG_MODE_CIRC != dgCurrentOpMode ) && ( DG_MODE_FILL != dgCurrentOpMode ) && ( DG_MODE_DRAI != dgCurrentOpMode ) ) { activateAlarmNoData( ALARM_ID_DG_RESTARTED_FAULT ); } } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetDialOutLoadCellWeightOverride function overrides the value of the * load cell sensor with a given weight (in grams). * @details Inputs: loadCellWeightInGrams[] * @details Outputs: loadCellWeightInGrams[] * @param sensor ID of load cell sensor to override weight for * @param value override weight (in grams) for the given sensor * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetDialOutLoadCellWeightOverride( U32 sensor, F32 value ) { BOOL result = FALSE; if ( sensor < NUM_OF_LOAD_CELLS ) { if ( TRUE == isTestingActivated() ) { result = TRUE; loadCellWeightInGrams[ sensor ].ovData = value; loadCellWeightInGrams[ sensor ].override = OVERRIDE_KEY; } } return result; } /*********************************************************************//** * @brief * The testResetDialOutLoadCellWeightOverride function resets the override of the * load cell sensor. * @details Inputs: loadCellWeightInGrams[] * @details Outputs: loadCellWeightInGrams[] * @param sensor ID of load cell sensor to override weight for * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetDialOutLoadCellWeightOverride( U32 sensor ) { BOOL result = FALSE; if ( sensor < NUM_OF_LOAD_CELLS ) { if ( TRUE == isTestingActivated() ) { result = TRUE; loadCellWeightInGrams[ sensor ].override = OVERRIDE_RESET; loadCellWeightInGrams[ sensor ].ovData = loadCellWeightInGrams[ sensor ].ovInitData; } } return result; } /**@}*/