/************************************************************************** * * Copyright (c) 2019-2024 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 ModeStandby.c * * @author (last) Dara Navaei * @date (last) 15-Jul-2024 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ #include "AirTrap.h" #include "AlarmLamp.h" #include "Battery.h" #include "BloodFlow.h" #include "Buttons.h" #include "Compatible.h" #include "DGInterface.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "Dialysis.h" #include "ModePostTreat.h" #include "ModePreTreat.h" #include "ModeStandby.h" #include "ModeTreatment.h" #include "ModeTreatmentParams.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "RTC.h" #include "Switches.h" #include "SyringePump.h" #include "SystemComm.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup HDStandbyMode * @{ */ // ********** private definitions ********** #define DISINFECTS_DATA_PUB_INTERVAL ( 1 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Disinfects data publish interval in counts. #define DISINFECTS_TIME_INTERVAL_S ( 3 * SECONDS_IN_A_DAY ) ///< HD/DG 3-day service interval in seconds. #define FLUSH_TIME_INTERVAL_S ( 2 * MIN_PER_HOUR * SEC_PER_MIN ) ///< Flush time interval in seconds. #define MAX_ALLOWED_RO_FILTER_TEMP_FOR_TX_C 44.0F ///< Maximum allowed temperature to start a treatment in C. /// DG cleaning mode status data structure typedef struct { BOOL startCleaningMode; ///< Flag to indicate to start cleaning mode. DG_COMMAND_T startRequestCmdID; ///< DG cleaning mode start request command ID. ALARM_ID_T alarmID; ///< The informative alarm to trigger for each cleaning mode. DG_OP_MODE_T dgOpMode; ///< DG operation mode. BOOL sampleRqstTrgrd; ///< Flag to indicate the sample alarm has been triggered (for RO perm and chem flush). GENERIC_CONFIRM_ID_T sampleRequestID; ///< Sample request pass/fail message status. GENERIC_CONFIRM_ID_T stopRequestCmdID; ///< Stop DG cleaning request command ID } DG_CLEANING_MODE_STATUS_T; // ********** private data ********** static HD_STANDBY_STATE_T currentStandbyState; ///< Current state (sub-mode) of standby mode. static BOOL treatStartReqReceived; ///< Flag indicates user has requested initiation of a treatment. static GENERIC_CONFIRM_ID_T disinfectCancelReqID; ///< ID of requested cancel disinfect mode. static BOOL homingInitiated; ///< Boolean flag to indicate homing is initiated. static BOOL flagNoDisinfectRequired = FALSE; ///< Flag indicates disinfect not required on next mode entry. static DG_CLEANING_MODE_STATUS_T currentDGCleaningMode; ///< Current DG cleaning mode status. /// Interval (in task intervals) at which to publish standby mode data to CAN bus. static OVERRIDE_U32_T standbyModePublishInterval = { DISINFECTS_DATA_PUB_INTERVAL, DISINFECTS_DATA_PUB_INTERVAL, DISINFECTS_DATA_PUB_INTERVAL, 0 }; #ifdef CARTRIDGE_TEST_BUILD // Variables and definitions used only for cartridge test build. #define CARTRIDGE_TEST_COMPLETE_TIME ( ( 10 * SEC_PER_MIN * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) static BOOL testCartridgesMode = FALSE; // Flag indicates we are testing cartridges. static U32 testCartridgesTimerCtr = 0; // Test timer counter. #endif // ********** private function prototypes ********** static HD_STANDBY_STATE_T handleStandbyModeStartState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForTreatmentState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDisinfectState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDGCleaningModeCmdResponseState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDGCleaningModeStartState( void ); static HD_STANDBY_STATE_T handleStandbyModeDGCleaningModeInProgressState( void ); static void handleDisinfectCancel( BOOL stop ); static void setRequestedCleaningMode( DG_OP_MODE_T opMode, BOOL isHeatDisPassiveCool ); static void clearCurrentCleaningModeStatus( void ); static void handleChemFlushSampleCollection( void ); static void handleROPermeateSampleCollection( void ); static BOOL isDGDisinfectValid( REQUEST_REJECT_REASON_CODE_T* rejReason ); static BOOL haveHDDGServicesBeenExpired( REQUEST_REJECT_REASON_CODE_T* rejReason ); /*********************************************************************//** * @brief * The initStandbyMode function initializes the Standby Mode module. * @details Inputs: none * @details Outputs: currentStandbyState, treatStartReqReceived, * disinfectCancelReqID, homingInitiated * @return none *************************************************************************/ void initStandbyMode( void ) { currentStandbyState = STANDBY_START_STATE; treatStartReqReceived = FALSE; disinfectCancelReqID = GENERIC_CONFIRM_ID_NONE; homingInitiated = FALSE; } /*********************************************************************//** * @brief * The transitionToStandbyMode function prepares for transition to standby mode. * @details Inputs: none * @details Outputs: Standby Mode module re-initialized, DG interface initialized, * blood & dialysate pumps shut off. * @return initial state *************************************************************************/ U32 transitionToStandbyMode( void ) { HD_OP_MODE_T previousOpMode = getPreviousOperationMode(); // Re-initialize when transitioning to standby mode initStandbyMode(); initDGInterface(); resetAirTrap(); resetBloodPumpRotorCount(); resetDialInPumpRotorCount(); resetPreLoadStatus(); setVenousBubbleDetectionEnabled( FALSE ); setCurrentSubState( NO_SUB_STATE ); clearNoRetriggerFlag(); clearCurrentCleaningModeStatus(); // Set user alarm recovery actions allowed in this mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, FALSE ); // Pumps should be off signalBloodPumpHardStop(); signalDialInPumpHardStop(); signalDialOutPumpHardStop(); stopSyringePump(); // Set valves to default positions setValveAirTrap( STATE_CLOSED ); setValvePosition( VDI, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VDO, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VBA, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VBV, VALVE_POSITION_A_INSERT_EJECT ); // If we just exited Post Treatment Mode, goto disinfect sub state. if ( ( MODE_POST == previousOpMode ) && ( flagNoDisinfectRequired != TRUE ) ) { doorClosedRequired( FALSE, FALSE ); // door no longer required to be closed in standby mode currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; } else { flagNoDisinfectRequired = FALSE; doorClosedRequired( TRUE, FALSE ); } syringeDetectionRequired( FALSE ); // Request DG service record and usage information from DG sendDGServiceRequestToDG(); sendDGUsageInfoRequestToDG(); return currentStandbyState; } /*********************************************************************//** * @brief * The execStandbyMode function executes the Standby Mode state machine. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return current state (sub-mode) *************************************************************************/ U32 execStandbyMode( void ) { BOOL stop = isStopButtonPressed(); handleDisinfectCancel( stop ); if ( TRUE == getVenousBubbleDetectionEnabled() ) { // if no active alarms anymore, restore to default valve states for standby mode if ( ( FALSE == doesAlarmStatusIndicateStop() ) && ( VALVE_POSITION_C_CLOSE == getValvePosition( VDI ) ) ) { setValvePosition( VDI, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VDO, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VBA, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VBV, VALVE_POSITION_A_INSERT_EJECT ); } } // State machine to get DG to prep a reservoir so we can start a treatment switch ( currentStandbyState ) { case STANDBY_START_STATE: currentStandbyState = handleStandbyModeStartState(); break; case STANDBY_WAIT_FOR_TREATMENT_STATE: currentStandbyState = handleStandbyModeWaitForTreatmentState(); break; case STANDBY_WAIT_FOR_DISINFECT_STATE: currentStandbyState = handleStandbyModeWaitForDisinfectState(); break; case STANDBY_WAIT_FOR_DG_CLEANING_MODE_CMD_RESPONSE_STATE: currentStandbyState = handleStandbyModeWaitForDGCleaningModeCmdResponseState(); break; case STANDBY_WAIT_FOR_DG_CLEANING_MODE_TO_START_STATE: currentStandbyState = handleStandbyModeWaitForDGCleaningModeStartState(); break; case STANDBY_CLEANING_MODE_IN_PROGRESS_STATE: currentStandbyState = handleStandbyModeDGCleaningModeInProgressState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_STANDBY_INVALID_STATE, currentStandbyState ); currentStandbyState = STANDBY_START_STATE; break; } #ifdef CARTRIDGE_TEST_BUILD if ( TRUE == testCartridgesMode ) { if ( ++testCartridgesTimerCtr >= CARTRIDGE_TEST_COMPLETE_TIME ) { stop = TRUE; // Stop cartridge test after 10 hrs } if ( TRUE == isAlarmActive( ALARM_ID_HD_FLUID_LEAK_DETECTED ) ) { stop = TRUE; // Stop cartridge test if a fluid leak is detected } } if ( TRUE == stop ) { if ( testCartridgesMode != TRUE ) { testSetBloodPumpTargetDutyCycle( 0.652F ); testSetDialInPumpTargetDutyCycle( 0.701F ); testSetDialOutPumpTargetDutyCycle( 0.566F ); setValvePosition( VDI, VALVE_POSITION_B_OPEN ); setValvePosition( VDO, VALVE_POSITION_B_OPEN ); setValvePosition( VBA, VALVE_POSITION_B_OPEN ); setValvePosition( VBV, VALVE_POSITION_B_OPEN ); setValveAirTrap( STATE_CLOSED ); testCartridgesMode = TRUE; } else { signalBloodPumpHardStop(); signalDialInPumpHardStop(); signalDialOutPumpHardStop(); setValvePosition( VDI, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VDO, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VBA, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VBV, VALVE_POSITION_A_INSERT_EJECT ); setValveAirTrap( STATE_OPEN ); testCartridgesMode = FALSE; testCartridgesTimerCtr = 0; } } #endif return currentStandbyState; } /*********************************************************************//** * @brief * The signalUserInitiateTreatment function handles user initiation of a * treatment. * @details Inputs: none * @details Outputs: treatStartReqReceived * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateTreatment( void ) { BOOL result = FALSE; DG_VERSIONS_T dgVersion = getDGVersion(); REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; // Verify HD is in standby mode waiting for treatment start request if ( ( MODE_STAN != getCurrentOperationMode() ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE != currentStandbyState ) || ( isPOSTPassed() != TRUE ) ) { rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; } // Verify DG is communicating with HD else if ( TRUE != isDGCommunicating() ) { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } // Verify DG software is compatible with HD software else if ( dgVersion.compatibilityRev != SW_COMPATIBILITY_REV ) { rejReason = REQUEST_REJECT_REASON_DG_INCOMPATIBLE; } // Verify DG is not busy else if ( ( DG_MODE_STAN != getDGOpMode() ) || ( DG_STANDBY_MODE_STATE_IDLE != getDGSubMode() ) ) // || ( isDGPOSTPassed() != TRUE ) ) // TODO - is DG sending status??? { rejReason = REQUEST_REJECT_REASON_DG_NOT_IN_STANDBY_IDLE_STATE; } // Verify HD battery has sufficient charge to hold up logic/sensors for at least 10 minutes else if ( FALSE == isBatteryCharged() ) { rejReason = REQUEST_REJECT_REASON_BATTERY_IS_NOT_CHARGED; } if ( getTestConfigStatus( TEST_CONFIG_SKIP_DISINFECT_AND_SERVICE_TX_BLOCKERS ) != TRUE ) { if ( getHeatDisinfectTemperatureSensorValue() > MAX_ALLOWED_RO_FILTER_TEMP_FOR_TX_C ) { rejReason = REQUEST_REJECT_REASON_DG_RO_FILTER_TEMPERATURE_OUT_OF_RANGE; } haveHDDGServicesBeenExpired( &rejReason ); isDGDisinfectValid( &rejReason ); } // If no reason to reject request to start treatment, set flag to initiate treatment workflow if ( REQUEST_REJECT_REASON_NONE == rejReason ) { result = TRUE; treatStartReqReceived = TRUE; } // Respond to request to start treatment sendInitiateTreatmentResponseMsg( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalNoDisinfectRequired function signals the standby mode module * that a disinfection will not be required on next entry to standby mode. * @details Inputs: none * @details Outputs: flagNoDisinfectRequired * @return none *************************************************************************/ void signalNoDisinfectRequired( void ) { flagNoDisinfectRequired = TRUE; } /*********************************************************************//** * @brief * The signalAlarmActionToStandbyMode function executes the given alarm action * as appropriate while in Standby Mode. * @details Inputs: none * @details Outputs: given alarm action executed * @param action ID of alarm action to execute * @return none *************************************************************************/ void signalAlarmActionToStandbyMode( ALARM_ACTION_T action ) { switch ( action ) { case ALARM_ACTION_STOP: if ( isAlarmActive( ALARM_ID_HD_VENOUS_BUBBLE_DETECTED ) != TRUE ) { // no stop action in standby mode } // if Dialin enabled bubble detection, stop on venous bubble detection alarm else { // Pumps should be off signalBloodPumpHardStop(); signalDialInPumpHardStop(); signalDialOutPumpHardStop(); stopSyringePump(); // Set valves to safe positions setValveAirTrap( STATE_CLOSED ); setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); } break; default: // do not handle other actions in standby mode break; } } /*********************************************************************//** * @brief * The signalUserInitiateFlushMode function handles user initiation of flush * mode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateFlushMode( void ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( MODE_STAN == getCurrentOperationMode() ) { if ( ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { setRequestedCleaningMode( DG_MODE_FLUS, FALSE ); result = TRUE; currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; rejReason = REQUEST_REJECT_REASON_NONE; } else { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } sendDisinfectConfirmResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalUserInitiateHeatDisinfectActiveCoolMode function handles user * initiation of heat disinfect with active cool mode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateHeatDisinfectActiveCoolMode( void ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( MODE_STAN == getCurrentOperationMode() ) { if ( ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { setRequestedCleaningMode( DG_MODE_HEAT, FALSE ); result = TRUE; currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; rejReason = REQUEST_REJECT_REASON_NONE; } else { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } sendDisinfectConfirmResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalUserInitiateChemicalDisinfectMode function handles user * initiation of chemical disinfect mode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateChemicalDisinfectMode( void ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( MODE_STAN == getCurrentOperationMode() ) { if ( ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { if ( TRUE == isChemDisinfectEnabledInInstitRecord() ) { setRequestedCleaningMode( DG_MODE_CHEM, FALSE ); result = TRUE; currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; rejReason = REQUEST_REJECT_REASON_NONE; } else { rejReason = REQUEST_REJECT_REASON_CHEM_DISINFECT_NOT_ENABLED_INST_CONFIG; } } else { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } sendDisinfectConfirmResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalUserInitiateChemicalDisinfectFlushMode function handles user * initiation of chemical disinfect flush mode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateChemicalDisinfectFlushMode( void ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( MODE_STAN == getCurrentOperationMode() ) { if ( ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { if ( TRUE == isChemDisinfectEnabledInInstitRecord() ) { setRequestedCleaningMode( DG_MODE_CHFL, FALSE ); result = TRUE; currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; rejReason = REQUEST_REJECT_REASON_NONE; } else { rejReason = REQUEST_REJECT_REASON_CHEM_DISINFECT_NOT_ENABLED_INST_CONFIG; } } else { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } sendDisinfectConfirmResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalUserInitiateROPermeateSampleMode function handles user * initiation of RO permeate sample mode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateROPermeateSampleMode( void ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( MODE_STAN == getCurrentOperationMode() ) { if ( ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { setRequestedCleaningMode( DG_MODE_ROPS, FALSE ); result = TRUE; currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; rejReason = REQUEST_REJECT_REASON_NONE; } else { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } sendDisinfectConfirmResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalUserInitiateHeatDisinfectPassiveCoolMode function handles user * initiation of passive cool heat disinfect mode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateHeatDisinfectPassiveCoolMode( void ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( MODE_STAN == getCurrentOperationMode() ) { if ( ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { setRequestedCleaningMode( DG_MODE_HEAT, TRUE ); result = TRUE; currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; rejReason = REQUEST_REJECT_REASON_NONE; } else { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } sendDisinfectConfirmResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalUserInitiateActiveCoolMode function handles user initiation of * active cool mode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateActiveCoolMode( void ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( MODE_STAN == getCurrentOperationMode() ) { if ( ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { setRequestedCleaningMode( DG_MODE_HCOL, FALSE ); result = TRUE; currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; rejReason = REQUEST_REJECT_REASON_NONE; } else { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } sendDisinfectConfirmResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalInitiateStandbyDisinfectSubmode function handles user * initiation of setting the disinfects submode. * @details Inputs: currentStandbyState * @details Outputs: currentStandbyState * @param cmd initiate (1) or cancel (0) * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalInitiateStandbyDisinfectSubmode( U32 cmd ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; if ( cmd == 0 ) { // Cancel Disinfect command if ( STANDBY_WAIT_FOR_DISINFECT_STATE == currentStandbyState ) { currentStandbyState = STANDBY_WAIT_FOR_TREATMENT_STATE; result = TRUE; rejReason = REQUEST_REJECT_REASON_NONE; } else { result = FALSE; rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; } } else { // Initiate Disinfect command if ( ( MODE_STAN == getCurrentOperationMode() ) && ( STANDBY_WAIT_FOR_TREATMENT_STATE == currentStandbyState ) ) { if ( TRUE == isDGCommunicating() ) { currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; result = TRUE; rejReason = REQUEST_REJECT_REASON_NONE; } else { result = FALSE; rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } } } handleSetHDStandbyDisinfectSubmodeResponse( result, rejReason ); return result; } /*********************************************************************//** * @brief * The signalROPermeateSampleDGReadyToDispense function set the signal * that DG is ready for more RO permeate dispensing * @details Inputs: none * @details Outputs: currentDGCleaningMode * @return none *************************************************************************/ void signalROPermeateSampleDGReadyToDispense( void ) { currentDGCleaningMode.sampleRqstTrgrd = TRUE; } // ********** private functions ********** /*********************************************************************//** * @brief * The handleStandbyModeStartState function handles the standby start state. * This state waits for the door to be closed and then initiates homing of * pumps and valves and transitions to the wait for treatment state. * @details Inputs: * @details Outputs: * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeStartState( void ) { HD_STANDBY_STATE_T state = STANDBY_START_STATE; // Wait for door to be closed so we can home actuators if ( STATE_CLOSED == getSwitchStatus( FRONT_DOOR ) ) { // If we haven't already initiated homing of actuators, initiate now if ( homingInitiated != TRUE ) { VALVE_T valve; // Home pumps and valves for ( valve = VDI; valve < NUM_OF_VALVES; ++valve ) { homeValve( valve, VALVE_NO_FORCE_HOME, VALVE_CARTRIDGE_MAY_BE_PRESENT ); } homeBloodPump(); homeDialInPump(); homeDialOutPump(); retractSyringePump(); homingInitiated = TRUE; } else { // If homing has been initiated, wait for syringe pump to home and the verify force sensor calibration #ifndef _RELEASE_ if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP ) ) { state = STANDBY_WAIT_FOR_TREATMENT_STATE; // Go to wait for treatment state after above check } else #endif { if ( ( TRUE == isSyringePumpHome() ) && ( TRUE == isSyringePumpStopped() ) ) { syringePumpVerifyForceSensorDACCalibration(); homingInitiated = FALSE; // reset for next time doorClosedRequired( FALSE, FALSE ); // door no longer required to be closed in standby mode state = STANDBY_WAIT_FOR_TREATMENT_STATE; // Go to wait for treatment state after above check } } } } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForTreatmentState function handles wait for * treatment state. * @details Inputs: treatStartReqReceived * @details Outputs: treatStartReqReceived * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForTreatmentState( void ) { HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_TREATMENT_STATE; DG_OP_MODE_T dgOperationMode = getDGOpMode(); switch ( dgOperationMode ) { case DG_MODE_FILL: // If DG is filling while we are in standby mode, abort the fill cmdStopDGFill(); break; case DG_MODE_GENE: // If DG is in idle generation state while we are in standby mode, transition DG to standby too cmdStopDG(); break; // If DG is any of the cleaning modes, set the HD standby to be in the corresponding cleaning mode case DG_MODE_FLUS: case DG_MODE_HEAT: case DG_MODE_CHEM: case DG_MODE_CHFL: case DG_MODE_HCOL: case DG_MODE_ROPS: setRequestedCleaningMode( dgOperationMode, FALSE ); state = STANDBY_WAIT_FOR_DG_CLEANING_MODE_TO_START_STATE; break; default: // Do nothing. There are other DG modes that HD standby does not need to act upon them. break; } // If DG is communicating and we don't yet have DG version info, request it if ( TRUE == isDGCommunicating() ) { DG_VERSIONS_T dgVersion = getDGVersion(); if ( 0 == dgVersion.compatibilityRev ) { sendFWVersionRequest(); } } // If treatment start is requested by user, initiate treatment workflow (transition to treatment params mode). if ( TRUE == treatStartReqReceived ) { PRESSURE_LIMIT_CHANGE_RESPONSE_T respRecord = { TRUE, REQUEST_REJECT_REASON_NONE, 0, 0, 0 }; // Initialize treatment modes before starting a new treatment initTreatParamsMode(); initPreTreatmentMode(); initTreatmentMode(); initPostTreatmentMode(); // Send UI default pressure settings since user is not asked to set them. respRecord.artPresLimitWindowmmHg = getTreatmentParameterS32DefaultValue( TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW ); respRecord.venPresLimitWindowmmHg = getTreatmentParameterS32DefaultValue( TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW ); respRecord.venPresLimitAsymmetricmmHg = getTreatmentParameterS32DefaultValue( TREATMENT_PARAM_VEN_PRES_LIMIT_ASYMMETRIC ); sendPressureLimitsChangeResponse( &respRecord ); // Start treatment workflow with treatment parameters mode requestNewOperationMode( MODE_TPAR ); treatStartReqReceived = FALSE; } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDisinfectState function handles wait for * disinfect state. * @details Inputs: flushStartReqReceived, heatDisinfectStartReqReceived, * chemDisinfectStartReqReceived * @details Outputs: none * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDisinfectState( void ) { HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DISINFECT_STATE; if ( TRUE == currentDGCleaningMode.startCleaningMode ) { switch ( currentDGCleaningMode.dgOpMode ) { case DG_MODE_FLUS: cmdStartDGFlush(); break; case DG_MODE_HEAT: if ( DG_CMD_START_HEAT_DISINFECT_ACTIVE_COOL == currentDGCleaningMode.startRequestCmdID ) { cmdStartDGHeatDisinfectActiveCool(); } else { cmdStartDGHeatDisinfectPassiveCool(); } break; case DG_MODE_CHEM: cmdStartDGChemicalDisinfect(); break; case DG_MODE_CHFL: cmdStartDGChemicalFlushDisinfect(); break; case DG_MODE_ROPS: cmdStartDGROPermeateSampleMode(); break; case DG_MODE_HCOL: cmdStartDGActiveCool(); break; } state = STANDBY_WAIT_FOR_DG_CLEANING_MODE_CMD_RESPONSE_STATE; } else { switch ( getDGOpMode() ) { case DG_MODE_FLUS: case DG_MODE_HEAT: case DG_MODE_CHEM: case DG_MODE_CHFL: case DG_MODE_ROPS: // This is for the situations that any of the disinfect modes are started using the proxy commands while the HD // is in this state of the Standby Mode. setRequestedCleaningMode( getDGOpMode(), FALSE ); state = STANDBY_WAIT_FOR_DG_CLEANING_MODE_TO_START_STATE; currentDGCleaningMode.startCleaningMode = FALSE; break; default: // Do nothing we are not in the cleaning mode break; } } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGCleaningModeCmdResponseState function handles * the standby mode wait for DG cleaning mode command response state. * @details Inputs: currentDGCleaningMode * @details Outputs: currentDGCleaningMode * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGCleaningModeCmdResponseState( void ) { DG_CMD_RESPONSE_T dgCmdResp; HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_CLEANING_MODE_CMD_RESPONSE_STATE; BOOL result = FALSE; if ( TRUE == getDGCommandResponse( currentDGCleaningMode.startRequestCmdID, &dgCmdResp ) ) { // Assume the command will be rejected and it goes back to wait for disinfect command state = STANDBY_WAIT_FOR_DISINFECT_STATE; currentDGCleaningMode.startCleaningMode = FALSE; if ( DG_CMD_REQUEST_REJECT_REASON_NONE == dgCmdResp.rejectCode ) { state = STANDBY_WAIT_FOR_DG_CLEANING_MODE_TO_START_STATE; result = TRUE; } sendDisinfectConfirmResponse( result, dgCmdResp.rejectCode ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGCleaningModeStartState function handles * the standby mode wait for DG cleaning mode start state. * @details Inputs: currentDGCleaningMode * @details Outputs: none * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGCleaningModeStartState( void ) { HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_CLEANING_MODE_TO_START_STATE; if ( currentDGCleaningMode.dgOpMode == getDGOpMode() ) { state = STANDBY_CLEANING_MODE_IN_PROGRESS_STATE; activateAlarmNoData( currentDGCleaningMode.alarmID ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeDGCleaningModeInProgressState function handles * the standby mode DG cleaning mode in progress state. * @details Inputs: currentDGCleaningMode * @details Outputs: none * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeDGCleaningModeInProgressState( void ) { HD_STANDBY_STATE_T state = STANDBY_CLEANING_MODE_IN_PROGRESS_STATE; if ( DG_MODE_CHFL == currentDGCleaningMode.dgOpMode ) { handleChemFlushSampleCollection(); } else if ( DG_MODE_ROPS == currentDGCleaningMode.dgOpMode ) { handleROPermeateSampleCollection(); } if ( getDGOpMode() != currentDGCleaningMode.dgOpMode ) { state = STANDBY_WAIT_FOR_TREATMENT_STATE; clearAlarm( currentDGCleaningMode.alarmID ); // In case alarm 306 has been triggered, and it was not cleared, clear it upon exiting the disinfection mode. clearAlarm( ALARM_ID_DG_TURN_OFF_INLET_WATER_VALVES ); clearCurrentCleaningModeStatus(); } return state; } /*********************************************************************//** * @brief * The handleDisinfectCancel function handles * DG disinfect cancel UI interaction. * @details Inputs: disinfectCancelReqID, currentDGCleaningMode, * currentStandbyState * @details Outputs: disinfectCancelReqID * @param stop button status * @return none *************************************************************************/ static void handleDisinfectCancel( BOOL stop ) { CONFIRMATION_REQUEST_STATUS_T confirm_status; if ( STANDBY_CLEANING_MODE_IN_PROGRESS_STATE == currentStandbyState ) { if ( ( TRUE == stop ) && ( GENERIC_CONFIRM_ID_NONE == disinfectCancelReqID ) ) { disinfectCancelReqID = addConfirmationRequest( currentDGCleaningMode.stopRequestCmdID, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0 ); } else if ( disinfectCancelReqID != GENERIC_CONFIRM_ID_NONE ) { // Get the confirmation request. It consumes the request if completed and responds to UI. confirm_status = getConfirmationRequestStatus( disinfectCancelReqID ); switch ( confirm_status ) { case CONFIRMATION_REQUEST_STATUS_ACCEPTED: // Clear request active status disinfectCancelReqID = GENERIC_CONFIRM_ID_NONE; switch ( currentDGCleaningMode.dgOpMode ) { case DG_MODE_FLUS: cmdStopDGFlush(); break; case DG_MODE_HEAT: cmdStopDGHeatDisinfect(); break; case DG_MODE_CHEM: cmdStopDGChemicalDisinfect(); break; case DG_MODE_CHFL: cmdStopDGChemFlushDisinfect(); break; case DG_MODE_HCOL: cmdStopDGActiveCool(); break; case DG_MODE_ROPS: cmdStopDGROPermeateSampleMode(); break; } break; case CONFIRMATION_REQUEST_STATUS_TIMEOUT: case CONFIRMATION_REQUEST_STATUS_REJECTED: // Clear request active status disinfectCancelReqID = GENERIC_CONFIRM_ID_NONE; break; case CONFIRMATION_REQUEST_STATUS_PENDING: case CONFIRMATION_REQUEST_STATUS_UNUSED: default: // Nothing to do break; } } } } /*********************************************************************//** * @brief * The setRequestedCleaningMode function sets the variables for the requested * DG cleaning mode. * @details Inputs: currentDGCleaningMode * @details Outputs: none * @param opMode DG operation mode that has been requested (i.e. heat disinfect) * @param isHeatDisPassiveCool boolean flag to indicate whether this is a passive * cool heat disinfect or not. * @return none *************************************************************************/ static void setRequestedCleaningMode( DG_OP_MODE_T opMode, BOOL isHeatDisPassiveCool ) { switch ( opMode ) { case DG_MODE_FLUS: currentDGCleaningMode.startCleaningMode = TRUE; currentDGCleaningMode.startRequestCmdID = DG_CMD_START_FLUSH; currentDGCleaningMode.dgOpMode = DG_MODE_FLUS; currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.alarmID = ALARM_ID_HD_DISINFECT_FLUSH; currentDGCleaningMode.stopRequestCmdID = GENERIC_CONFIRM_ID_DISINFECT_STOP_WATERFLUSH; break; case DG_MODE_HEAT: currentDGCleaningMode.startCleaningMode = TRUE; currentDGCleaningMode.startRequestCmdID = ( FALSE == isHeatDisPassiveCool ? DG_CMD_START_HEAT_DISINFECT_ACTIVE_COOL : DG_CMD_START_HEAT_DISINFECT_PASSIVE_COOL ); currentDGCleaningMode.dgOpMode = DG_MODE_HEAT; currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.alarmID = ALARM_ID_HD_DISINFECT_HEAT; currentDGCleaningMode.stopRequestCmdID = GENERIC_CONFIRM_ID_DISINFECT_STOP_HEAT; break; case DG_MODE_CHEM: currentDGCleaningMode.startCleaningMode = TRUE; currentDGCleaningMode.startRequestCmdID = DG_CMD_START_CHEM_DISINFECT; currentDGCleaningMode.dgOpMode = DG_MODE_CHEM; currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.alarmID = ALARM_ID_HD_DISINFECT_CHEM; currentDGCleaningMode.stopRequestCmdID = GENERIC_CONFIRM_ID_DISINFECT_STOP_CHEMICAL; break; case DG_MODE_CHFL: currentDGCleaningMode.startCleaningMode = TRUE; currentDGCleaningMode.startRequestCmdID = DG_CMD_START_CHEM_DISINFECT_FLUSH; currentDGCleaningMode.dgOpMode = DG_MODE_CHFL; currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.alarmID = ALARM_ID_HD_DISINFECT_CHEM_FLUSH; currentDGCleaningMode.stopRequestCmdID = GENERIC_CONFIRM_ID_DISINFECT_STOP_CHEMICAL_FLUSH; break; case DG_MODE_HCOL: currentDGCleaningMode.startCleaningMode = FALSE; currentDGCleaningMode.startRequestCmdID = DG_CMD_START_ACTIVE_COOL; currentDGCleaningMode.dgOpMode = DG_MODE_HCOL; currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.alarmID = ALARM_ID_HD_DISINFECT_HEAT_COOL; currentDGCleaningMode.stopRequestCmdID = GENERIC_CONFIRM_ID_DISINFECT_STOP_ACTIVE_COOL; break; case DG_MODE_ROPS: currentDGCleaningMode.startCleaningMode = TRUE; currentDGCleaningMode.startRequestCmdID = DG_CMD_START_RO_PERMEATE_SAMPLE; currentDGCleaningMode.dgOpMode = DG_MODE_ROPS; currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.alarmID = ALARM_ID_HD_RO_PERMEATE_SAMPLE; currentDGCleaningMode.stopRequestCmdID = GENERIC_CONFIRM_ID_DISINFECT_STOP_RO_PERMEATE_SAMPLE; break; } } /*********************************************************************//** * @brief * The clearCurrentCleaningModeStatus function clears the DG cleaning mode * variables. * @details Inputs: none * @details Outputs: currentDGCleaningMode * @return none *************************************************************************/ static void clearCurrentCleaningModeStatus( void ) { currentDGCleaningMode.startCleaningMode = FALSE; currentDGCleaningMode.alarmID = ALARM_ID_NO_ALARM; currentDGCleaningMode.startRequestCmdID = DG_CMD_NONE; currentDGCleaningMode.dgOpMode = DG_MODE_FAUL; currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.sampleRequestID = GENERIC_CONFIRM_ID_NONE; currentDGCleaningMode.stopRequestCmdID = GENERIC_CONFIRM_ID_NONE; } /*********************************************************************//** * @brief * The handleChemFlushSampleCollection function handles the chemical disinfect * flush mode's sample collection. In this state of the chemical disinfect * flush mode, user interaction is needed. * @details Inputs: currentDGCleaningMode * @details Outputs: currentDGCleaningMode * @return none *************************************************************************/ static void handleChemFlushSampleCollection( void ) { if ( ( FALSE == currentDGCleaningMode.sampleRqstTrgrd ) && ( TRUE == isAlarmActive( ALARM_ID_DG_CHEM_DISINFECT_FLUSH_FLUSH_SAMPLE ) ) ) { // Check if the flush sample alarm has been raised for the first time in the sample flush state currentDGCleaningMode.sampleRqstTrgrd = TRUE; } else if ( ( TRUE == currentDGCleaningMode.sampleRqstTrgrd ) && ( FALSE == isAlarmActive( ALARM_ID_DG_CHEM_DISINFECT_FLUSH_FLUSH_SAMPLE ) ) ) { // Sample flush alarm has been triggered and the user has cleared the alarm by pressing Ok. The user is collecting sample. // Send the notification to the UI to prompt the pass/fail screen so the user can choose whether the sampling after flush passed or failed currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.sampleRequestID = addConfirmationRequest( GENERIC_CONFIRM_ID_DISINFECT_CHEM_FLUSH_SAMPLE_PASS_FAIL, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0 ); } if ( currentDGCleaningMode.sampleRequestID != GENERIC_CONFIRM_ID_NONE ) { // There is a user confirm CONFIRMATION_REQUEST_STATUS_T status = getConfirmationRequestStatus( currentDGCleaningMode.sampleRequestID ); U32 sampleStatus = 0; switch( status ) { // If the request status is accepted send a 1 to DG case CONFIRMATION_REQUEST_STATUS_ACCEPTED: sampleStatus = (U32)CONFIRMATION_REQUEST_STATUS_ACCEPTED; currentDGCleaningMode.sampleRequestID = GENERIC_CONFIRM_ID_NONE; handleSendChemFlushPassFailToDG( sampleStatus ); break; // If the request timed out or rejected, it is 0 or failure to DG case CONFIRMATION_REQUEST_STATUS_TIMEOUT: case CONFIRMATION_REQUEST_STATUS_REJECTED: currentDGCleaningMode.sampleRequestID = GENERIC_CONFIRM_ID_NONE; handleSendChemFlushPassFailToDG( sampleStatus ); break; case CONFIRMATION_REQUEST_STATUS_PENDING: case CONFIRMATION_REQUEST_STATUS_UNUSED: default: // Nothing to do break; } } } /*********************************************************************//** * @brief * The handleROPermeateSampleCollection function handles the RO permeate * sample collection with the user * @details Inputs: currentDGCleaningMode * @details Outputs: currentDGCleaningMode * @return none *************************************************************************/ static void handleROPermeateSampleCollection( void ) { if ( ( FALSE == currentDGCleaningMode.sampleRqstTrgrd ) && ( TRUE == isAlarmActive( ALARM_ID_DG_RO_PERMEATE_SAMPLE_REMOVE_DIA_CAP ) ) ) { // Check if the flush sample alarm has been raised for the first time in the sample flush state currentDGCleaningMode.sampleRqstTrgrd = TRUE; } else if ( ( TRUE == currentDGCleaningMode.sampleRqstTrgrd ) && ( FALSE == isAlarmActive( ALARM_ID_DG_RO_PERMEATE_SAMPLE_REMOVE_DIA_CAP ) ) ) { // Sample flush alarm has been triggered and the user has cleared the alarm by pressing Ok. The user is collecting sample. // Send the notification to the UI to prompt the pass/fail screen so the user can choose whether the sampling after flush passed or failed currentDGCleaningMode.sampleRqstTrgrd = FALSE; currentDGCleaningMode.sampleRequestID = addConfirmationRequest( GENERIC_CONFIRM_ID_RO_PERMEATE_SAMPLE_STOP_OR_DISPENSE, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0 ); } if ( currentDGCleaningMode.sampleRequestID != GENERIC_CONFIRM_ID_NONE ) { // There is a user confirm CONFIRMATION_REQUEST_STATUS_T status = getConfirmationRequestStatus( currentDGCleaningMode.sampleRequestID ); U32 sampleStatus = 0; switch( status ) { // If the request status is accepted send a 1 to DG case CONFIRMATION_REQUEST_STATUS_ACCEPTED: sampleStatus = (U32)CONFIRMATION_REQUEST_STATUS_ACCEPTED; currentDGCleaningMode.sampleRequestID = GENERIC_CONFIRM_ID_NONE; sendRequestROPermeateSampleCollectionToDG( sampleStatus ); break; // If the request timed out or rejected, it is 0 or failure to DG case CONFIRMATION_REQUEST_STATUS_TIMEOUT: case CONFIRMATION_REQUEST_STATUS_REJECTED: currentDGCleaningMode.sampleRequestID = GENERIC_CONFIRM_ID_NONE; sendRequestROPermeateSampleCollectionToDG( sampleStatus ); break; case CONFIRMATION_REQUEST_STATUS_PENDING: case CONFIRMATION_REQUEST_STATUS_UNUSED: default: // Nothing to do break; } } } /*********************************************************************//** * @brief * The isDGDisinfectValid function checks whether the DG disinfects is * acceptable to start another treatment. * @details Inputs: none * @details Outputs: none * @param rejReason pointer to the provided reject reason buffer to be send to UI * @return TRUE if the disinfect is valid otherwise, FALSE ***********************************************************************/ static BOOL isDGDisinfectValid( REQUEST_REJECT_REASON_CODE_T* rejReason ) { DG_SERVICE_AND_USAGE_DATA_T data; BOOL status = FALSE; getHDVersionDGServiceAndUsageData( &data ); if ( TRUE == data.isDGUsageInfoAvailable ) { HD_USAGE_INFO_RECORD_T usageRecord; getNVRecord2Driver( GET_USAGE_RECORD, (U08*)&usageRecord, sizeof( HD_USAGE_INFO_RECORD_T ), 0, ALARM_ID_NO_ALARM ); U32 lastChemCompleteDateS = data.dgUsageInfo.lastChemDisCompleteDateEpoch; U32 lastChemStartDateS = data.dgUsageInfo.lastChemDisStartDateEpoch; U32 lastChemFlushCompleteDateS = data.dgUsageInfo.lastChemDisFlushCompleteDateEpoch; U32 lastHeatCompleteDateS = data.dgUsageInfo.lastHeatDisCompleteDateEpoch; U32 lastFlushCompleteDateS = data.dgUsageInfo.lastBasicFlushCompleteDateEpoch; U32 lastHeatCoolCompleteDateS = data.dgUsageInfo.lastHeatActiveCoolCompleteDateEpoch; U32 lastStartTxTimeDateS = usageRecord.txLastStartTimeEpoch; U32 currentRTCEpochTimeS = getRTCTimestamp(); // Last Treatment Start < Last Heat Disinfect Complete or Last Treatment Start < Last Chem Disinfect Complete so it means at least a heat disinfect // or a chemical disinfect has been done since the last treatment BOOL hasDisBeenDone = ( ( ( lastStartTxTimeDateS < lastChemCompleteDateS ) || ( lastStartTxTimeDateS < lastHeatCompleteDateS ) ) ? TRUE : FALSE ); // Last Heat Disinfect Complete < Current Time – Heat Disinfect Interval, so the heat disinfect that has been done has not been expired BOOL isHeatDisValid = ( ( currentRTCEpochTimeS - lastHeatCompleteDateS ) < DISINFECTS_TIME_INTERVAL_S ? TRUE : FALSE ); // Last Chem Flush Complete < Last Chem Disinfect Start, so after running a chemical disinfect, a chemical disinfect flush has been done BOOL isChemFlushComplete = ( lastChemFlushCompleteDateS > lastChemStartDateS ? TRUE : FALSE ); // If either of the basic flush, heat disinfect, or chemical disinfect flush have been done within the interval, it means the filters have been flushed BOOL isBasicFlushValid = ( ( currentRTCEpochTimeS - lastFlushCompleteDateS ) < FLUSH_TIME_INTERVAL_S ? TRUE : FALSE ); BOOL isChemFlushValid = ( ( currentRTCEpochTimeS - lastChemFlushCompleteDateS ) < FLUSH_TIME_INTERVAL_S ? TRUE : FALSE ); BOOL isHeatDisCoolFlushValid = ( ( currentRTCEpochTimeS - lastHeatCoolCompleteDateS ) < FLUSH_TIME_INTERVAL_S ? TRUE : FALSE ); BOOL isHeatDisFlushValid = ( ( currentRTCEpochTimeS - lastHeatCompleteDateS ) < FLUSH_TIME_INTERVAL_S ? TRUE : FALSE ); BOOL isFlushValid = ( ( ( TRUE == isBasicFlushValid ) || ( TRUE == isChemFlushValid ) || ( TRUE == isHeatDisCoolFlushValid ) || ( TRUE == isHeatDisFlushValid ) ) ? TRUE : FALSE ); if ( FALSE == isFlushValid ) { *rejReason = REQUEST_REJECT_REASON_DG_FILTER_FLUSH_HAS_BEEN_EXPIRED; } if ( ( FALSE == isHeatDisValid ) && ( FALSE == isChemFlushComplete ) ) { *rejReason = REQUEST_REJECT_REASON_DG_DISINFECT_HAS_BEEN_EXPIRED; } // If all of the above conditions are true, it means we can start a treatment if ( ( TRUE == hasDisBeenDone ) && ( TRUE == isHeatDisValid ) && ( TRUE == isChemFlushComplete ) && ( TRUE == isFlushValid ) ) { status = TRUE; } } return status; } /*********************************************************************//** * @brief * The haveHDDGServicesBeenExpired function checks whether the last DG/HD * service time is still within the interval or not. * @details Inputs: none * @details Outputs: none * @param rejReason pointer to the provided reject reason buffer to be send to UI * @return TRUE if the service for HD or DG has expired otherwise FALSE ***********************************************************************/ static BOOL haveHDDGServicesBeenExpired( REQUEST_REJECT_REASON_CODE_T* rejReason ) { DG_SERVICE_AND_USAGE_DATA_T dgData; HD_SERVICE_RECORD_T hdServiceRecord; BOOL status = FALSE; U32 currentRTCEpochTime = getRTCTimestamp(); getHDVersionDGServiceAndUsageData( &dgData ); getNVRecord2Driver( GET_SRV_RECORD, (U08*)&hdServiceRecord, sizeof( HD_SERVICE_RECORD_T ), 0, ALARM_ID_NO_ALARM ); if ( TRUE == dgData.isDGServiceRecordAvailable ) { U32 dgSrvcElapsedTimeS = currentRTCEpochTime - dgData.dgServiceRecord.lastServiceEpochDate; U32 hdSrvcElapsedTimeS = currentRTCEpochTime - hdServiceRecord.lastServiceEpochDate; BOOL hasDGSrvcBeenExpired = ( dgSrvcElapsedTimeS > dgData.dgServiceRecord.serviceIntervalSeconds ? TRUE : FALSE ); BOOL hasHDSrvcBeenExpied = ( hdSrvcElapsedTimeS > hdServiceRecord.serviceIntervalSeconds ? TRUE : FALSE ); if ( TRUE == hasDGSrvcBeenExpired ) { status = TRUE; *rejReason = REQUEST_REJECT_REASON_DG_SERVICE_IS_DUE; } if ( TRUE == hasHDSrvcBeenExpied ) { status = TRUE; *rejReason = REQUEST_REJECT_REASON_HD_SERVICE_IS_DUE; } } return status; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetStandbyModePublishIntervalOverride function sets the override of the * standby mode data publication interval. * @details Inputs: none * @details Outputs: standbyModePublishInterval * @param ms milliseconds between standby mode broadcasts * @return TRUE if override set successful, FALSE if not *************************************************************************/ BOOL testSetStandbyModePublishIntervalOverride( U32 ms ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = ms / TASK_GENERAL_INTERVAL; result = TRUE; standbyModePublishInterval.ovData = intvl; standbyModePublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetStandbyModePublishIntervalOverride function resets the override of the * standby mode data publication interval. * @details Inputs: none * @details Outputs: standbyModePublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetStandbyModePublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; standbyModePublishInterval.override = OVERRIDE_RESET; standbyModePublishInterval.ovData = standbyModePublishInterval.ovInitData; } return result; } /**@}*/