/************************************************************************** * * Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file ModeStandby.c * * @author (last) Dara Navaei * @date (last) 24-Oct-2022 * * @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 "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 "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 ( 2 * SECONDS_IN_A_DAY ) ///< HD/DG 2-day service interval in seconds. #define FLUSH_TIME_INTERVAL_S ( 30 * SEC_PER_MIN * MS_PER_SECOND ) ///< Flush time interval in seconds. // ********** 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 BOOL flushStartReqReceived; ///< Flag indicates user has requested initiation of flush mode. static BOOL heatDisinfectStartReqReceived; ///< Flag indicates user has requested initiation of heat disinfect mode. static BOOL chemDisinfectStartReqReceived; ///< Flag indicates user has requested initiation of chemical disinfect mode. static GENERIC_CONFIRM_ID_T disinfectCancelReqID; ///< ID of requested cancel disinfect mode. static DG_DISINFECT_STATE_T dgDisinfectState; ///< DG disinfect state to be boadcast to UI. static U32 dataPublishCounter; ///< Disinfects data publish counter. static BOOL homingInitiated; ///< Boolean flag to indicate homing is initiated. /// 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 }; static const U32 SERVICE_TIME_INTERVAL_S = (U32)( 365 * 0.5 * SECONDS_IN_A_DAY ); ///< HD/DG 6-month service interval in seconds. // ********** private function prototypes ********** static void handleDisinfectCancel( BOOL stop ); 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 handleStandbyModeWaitForDGFlushCmdResponseState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDGFlushToStartState( void ); static HD_STANDBY_STATE_T handleStandbyModeDGFlushInProgressState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDGHeatDisinfectCmdResponseState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDGHeatDisinfectStartState( void ); static HD_STANDBY_STATE_T handleStandbyModeDGHeatDisinfectInProgressState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDGChemDisinfectCmdResponseState( void ); static HD_STANDBY_STATE_T handleStandbyModeWaitForDGChemDisinfectStartState( void ); static HD_STANDBY_STATE_T handleStandbyModeDGChemDisininfectInProgressState( void ); static BOOL isDGDisinfectValid( void ); static BOOL haveHDDGServicesBeenExpired( REQUEST_REJECT_REASON_CODE_T* rejReason ); static void publishDisinfectData( void ); /*********************************************************************//** * @brief * The initStandbyMode function initializes the Standby Mode module. * @details Inputs: none * @details Outputs: currentStandbyState, treatStartReqReceived, * flushStartReqReceived, dataPublishCounter, heatDisinfectStartReqReceived, * chemDisinfectStartReqReceived, dgDisinfectState, homingInitiated * @return none *************************************************************************/ void initStandbyMode( void ) { currentStandbyState = STANDBY_START_STATE; treatStartReqReceived = FALSE; flushStartReqReceived = FALSE; dataPublishCounter = 0; heatDisinfectStartReqReceived = FALSE; chemDisinfectStartReqReceived = FALSE; disinfectCancelReqID = GENERIC_CONFIRM_ID_NONE; dgDisinfectState = DG_DISINFECT_NOT_RUNNING_STATE; 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(); doorClosedRequired( FALSE, FALSE ); syringeDetectionRequired( FALSE ); // 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 ) { currentStandbyState = STANDBY_WAIT_FOR_DISINFECT_STATE; } // 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 ); #ifndef RUN_WITHOUT_DG // 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_FLUSH_CMD_RESPONSE_STATE: currentStandbyState = handleStandbyModeWaitForDGFlushCmdResponseState(); break; case STANDBY_WAIT_FOR_DG_FLUSH_TO_START_STATE: currentStandbyState = handleStandbyModeWaitForDGFlushToStartState(); break; case STANDBY_DG_FLUSH_IN_PROGRESS_STATE: currentStandbyState = handleStandbyModeDGFlushInProgressState(); break; case STANDBY_WAIT_FOR_DG_HEAT_DISINFECT_CMD_RESPONSE_STATE: currentStandbyState = handleStandbyModeWaitForDGHeatDisinfectCmdResponseState(); break; case STANDBY_WAIT_FOR_DG_HEAT_DISINFECT_TO_START_STATE: currentStandbyState = handleStandbyModeWaitForDGHeatDisinfectStartState(); break; case STANDBY_DG_HEAT_DISINFECT_IN_PROGRESS_STATE: currentStandbyState = handleStandbyModeDGHeatDisinfectInProgressState(); break; case STANDBY_WAIT_FOR_DG_CHEM_DISINFECT_CMD_RESPONSE_STATE: currentStandbyState = handleStandbyModeWaitForDGChemDisinfectCmdResponseState(); break; case STANDBY_WAIT_FOR_DG_CHEM_DISINFECT_TO_START_STATE: currentStandbyState = handleStandbyModeWaitForDGChemDisinfectStartState(); break; case STANDBY_DG_CHEM_DISINFECT_IN_PROGRESS_STATE: currentStandbyState = handleStandbyModeDGChemDisininfectInProgressState(); 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; } #else // State machine to get DG to prep a reservoir so we can start a treatment switch ( currentStandbyState ) { case STANDBY_START_STATE: currentStandbyState = STANDBY_WAIT_FOR_TREATMENT_STATE; // Temporary test code - TODO - remove later homeBloodPump(); homeDialInPump(); homeDialOutPump(); break; case STANDBY_WAIT_FOR_TREATMENT_STATE: if ( TRUE == treatStartReqReceived ) { requestNewOperationMode( MODE_TPAR ); treatStartReqReceived = FALSE; } 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; } #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; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; if ( ( MODE_STAN != getCurrentOperationMode() ) || ( STANDBY_WAIT_FOR_TREATMENT_STATE != currentStandbyState ) ) { rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; } if ( TRUE != isDGCommunicating() ) { rejReason = REQUEST_REJECT_REASON_DG_COMM_LOST; } if ( ( DG_MODE_STAN != getDGOpMode() ) || ( DG_STANDBY_MODE_STATE_IDLE != getDGSubMode() ) ) { rejReason = REQUEST_REJECT_REASON_DG_NOT_IN_STANDBY_IDLE_STATE; } if ( FALSE == isBatteryCharged() ) { rejReason = REQUEST_REJECT_REASON_BATTERY_IS_NOT_CHARGED; } #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SERVICE_AND_DISINFECT_CHECK ) != SW_CONFIG_ENABLE_VALUE ) #endif { // This function checks both HD and DG service records and if any of them has failed, it fills the provided reject reason buffer haveHDDGServicesBeenExpired( &rejReason ); if ( FALSE == isDGDisinfectValid() ) { rejReason = REQUEST_REJECT_REASON_DG_DISINFECT_HAS_BEEN_EXPIRED; } } if ( REQUEST_REJECT_REASON_NONE == rejReason ) { result = TRUE; treatStartReqReceived = TRUE; } sendInitiateTreatmentResponseMsg( result, rejReason ); return result; } /*********************************************************************//** * @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 ) { // Alarm actions not handled in Standby mode } /*********************************************************************//** * @brief * The signalUserInitiateFlushMode function handles user initiation of flush * mode. * @details Inputs: currentStandbyState * @details Outputs: flushStartReqReceived * @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() ) { flushStartReqReceived = 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 signalUserInitiateFlushMode function handles user initiation of heat * disinfect mode. * @details Inputs: currentStandbyState * @details Outputs: heatDisinfectStartReqReceived * @return TRUE if signal accepted, FALSE if not *************************************************************************/ BOOL signalUserInitiateHeatDisinfectMode( 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() ) { heatDisinfectStartReqReceived = 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 signalUserInitiateChemicalDisinfectMode function handles user * initiation of chemical disinfect mode. * @details Inputs: currentStandbyState * @details Outputs: chemDisinfectStartReqReceived * @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() ) { chemDisinfectStartReqReceived = 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 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; } // ********** private functions ********** /*********************************************************************//** * @brief * The handleDisinfectCancel function handles * DG disinfect cancel UI interaction. * @details Inputs: none * @details Outputs: none * @param stop button status * @return none *************************************************************************/ static void handleDisinfectCancel( BOOL stop ) { CONFIRMATION_REQUEST_STATUS_T confirm_status; GENERIC_CONFIRM_ID_T confirm_id; if ( ( STANDBY_DG_FLUSH_IN_PROGRESS_STATE == currentStandbyState ) || ( STANDBY_DG_HEAT_DISINFECT_IN_PROGRESS_STATE == currentStandbyState ) || ( STANDBY_DG_CHEM_DISINFECT_IN_PROGRESS_STATE == currentStandbyState ) ) { if ( ( TRUE == stop ) && ( GENERIC_CONFIRM_ID_NONE == disinfectCancelReqID ) ) { if ( STANDBY_DG_FLUSH_IN_PROGRESS_STATE == currentStandbyState ) { confirm_id = GENERIC_CONFIRM_ID_DISINFECT_STOP_WATERFLUSH; } else if ( STANDBY_DG_HEAT_DISINFECT_IN_PROGRESS_STATE == currentStandbyState ) { confirm_id = GENERIC_CONFIRM_ID_DISINFECT_STOP_HEAT; } else if ( STANDBY_DG_CHEM_DISINFECT_IN_PROGRESS_STATE == currentStandbyState ) { confirm_id = GENERIC_CONFIRM_ID_DISINFECT_STOP_CHEMICAL; } // Send message to UI to indicate user request to cancel disinfect disinfectCancelReqID = addConfirmationRequest( confirm_id, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0 ); } else if ( GENERIC_CONFIRM_ID_NONE != disinfectCancelReqID ) { // 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 ( currentStandbyState ) { case STANDBY_DG_FLUSH_IN_PROGRESS_STATE: cmdStopDGFlush(); break; case STANDBY_DG_HEAT_DISINFECT_IN_PROGRESS_STATE: cmdStopDGHeatDisinfect(); break; case STANDBY_DG_CHEM_DISINFECT_IN_PROGRESS_STATE: cmdStopDGChemicalDisinfect(); break; default: // UI Confirm already closed. Nothing to do. 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 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 ); } homeBloodPump(); homeDialInPump(); homeDialOutPump(); retractSyringePump(); homingInitiated = TRUE; } } else { // Trigger door open alarm to prompt user to close the door activateAlarmNoData( ALARM_ID_CARTRIDGE_DOOR_OPENED ); } // If homing has been initiated, wait for syringe pump to home and the verify force sensor calibration if ( ( TRUE == homingInitiated ) && ( TRUE == isSyringePumpHome() ) ) { syringePumpVerifyForceSensorDACCalibration(); homingInitiated = FALSE; // reset for next time state = STANDBY_WAIT_FOR_TREATMENT_STATE; // Go to wait for treatment state after above check } return state; } // Verify calibration /*********************************************************************//** * @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(); // If DG is filling while we are in standby mode, abort the fill if ( DG_MODE_FILL == dgOperationMode ) { cmdStopDGFill(); } // If DG is in idle generation state while we are in standby mode, transition DG to standby too if ( DG_MODE_GENE == dgOperationMode ) { cmdStopDG(); } // If treatment start is requested by user, initiate treatment workflow (transition to treatment params mode). if ( TRUE == treatStartReqReceived ) { BOOL startTreatment = TRUE; if ( TRUE == startTreatment ) { // Initialize treatment modes before starting a new treatment initTreatParamsMode(); initPreTreatmentMode(); initTreatmentMode(); initPostTreatmentMode(); // 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 == flushStartReqReceived ) { cmdStartDGFlush(); state = STANDBY_WAIT_FOR_DG_FLUSH_CMD_RESPONSE_STATE; } else if ( TRUE == heatDisinfectStartReqReceived ) { cmdStartDGHeatDisinfect(); state = STANDBY_WAIT_FOR_DG_HEAT_DISINFECT_CMD_RESPONSE_STATE; } else if ( TRUE == chemDisinfectStartReqReceived ) { cmdStartDGChemicalDisinfect(); state = STANDBY_WAIT_FOR_DG_CHEM_DISINFECT_CMD_RESPONSE_STATE; } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGFlushCmdResponseState function handles DG flush * command response state. * @details Inputs: none * @details Outputs: flushStartReqReceived, dgDisinfectState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGFlushCmdResponseState( void ) { DG_CMD_RESPONSE_T dgCmdResp; HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_FLUSH_CMD_RESPONSE_STATE; BOOL result = FALSE; // Check if DG has responded to the start DG flush command if ( TRUE == getDGCommandResponse( DG_CMD_START_FLUSH, &dgCmdResp ) ) { // Assume the command will be rejected and it goes back to wait for disinfect command state = STANDBY_WAIT_FOR_DISINFECT_STATE; flushStartReqReceived = FALSE; if ( DG_CMD_REQUEST_REJECT_REASON_NONE == dgCmdResp.rejectCode ) { // If the command has not been rejected, transition to the next state dgDisinfectState = DG_DISINFECT_FLUSH_STATE; state = STANDBY_WAIT_FOR_DG_FLUSH_TO_START_STATE; result = TRUE; } sendDisinfectConfirmResponse( result, dgCmdResp.rejectCode ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGFlushToStartState function handles DG flush * wait to start state. * @details Inputs: none * @details Outputs: dgDisinfectState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGFlushToStartState( void ) { HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_FLUSH_TO_START_STATE; if ( DG_MODE_FLUS == getDGOpMode() ) { dgDisinfectState = DG_DISINFECT_NOT_RUNNING_STATE; state = STANDBY_DG_FLUSH_IN_PROGRESS_STATE; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DISINFECT_FLUSH, 0 ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeDGFlushInProgressState function handles DG flush * in progress state. * @details Inputs: none * @details Outputs: dgDisinfectState, currentStandbyState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeDGFlushInProgressState( void ) { HD_STANDBY_STATE_T state = STANDBY_DG_FLUSH_IN_PROGRESS_STATE; if ( getDGOpMode() != DG_MODE_FLUS ) { dgDisinfectState = DG_DISINFECT_NOT_RUNNING_STATE; state = STANDBY_WAIT_FOR_TREATMENT_STATE; clearAlarm( ALARM_ID_HD_DISINFECT_FLUSH ); } publishDisinfectData(); return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGHeatDisinfectCmdResponseState function handles * DG heat disinfect command response state. * @details Inputs: none * @details Outputs: heatDisinfectStartReqReceived, dgDisinfectState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGHeatDisinfectCmdResponseState( void ) { DG_CMD_RESPONSE_T dgCmdResp; HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_HEAT_DISINFECT_CMD_RESPONSE_STATE; BOOL result = FALSE; if ( TRUE == getDGCommandResponse( DG_CMD_START_HEAT_DISINFECT, &dgCmdResp ) ) { state = STANDBY_WAIT_FOR_DISINFECT_STATE; heatDisinfectStartReqReceived = FALSE; if ( DG_CMD_REQUEST_REJECT_REASON_NONE == dgCmdResp.rejectCode ) { dgDisinfectState = DG_DISINFECT_HEAT_STATE; state = STANDBY_WAIT_FOR_DG_HEAT_DISINFECT_TO_START_STATE; result = TRUE; } sendDisinfectConfirmResponse( result, dgCmdResp.rejectCode ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGHeatDisinfectStartState function handles * DG heat disinfect wait for start state. * @details Inputs: none * @details Outputs: dgDisinfectState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGHeatDisinfectStartState( void ) { HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_HEAT_DISINFECT_TO_START_STATE; if ( DG_MODE_HEAT == getDGOpMode() ) { dgDisinfectState = DG_DISINFECT_NOT_RUNNING_STATE; state = STANDBY_DG_HEAT_DISINFECT_IN_PROGRESS_STATE; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DISINFECT_HEAT, 0 ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeDGHeatDisinfectInProgressState function handles DG * heat disinfect in progress state. * @details Inputs: none * @details Outputs: dgDisinfectState, currentStandbyState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeDGHeatDisinfectInProgressState( void ) { HD_STANDBY_STATE_T state = STANDBY_DG_HEAT_DISINFECT_IN_PROGRESS_STATE; if ( getDGOpMode() != DG_MODE_HEAT ) { dgDisinfectState = DG_DISINFECT_NOT_RUNNING_STATE; state = STANDBY_WAIT_FOR_TREATMENT_STATE; clearAlarm( ALARM_ID_HD_DISINFECT_HEAT ); } publishDisinfectData(); return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGChemDisinfectCmdResponseState function handles * DG chemical disinfect wait for command response state. * @details Inputs: none * @details Outputs: chemDisinfectStartReqReceived, dgDisinfectState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGChemDisinfectCmdResponseState( void ) { DG_CMD_RESPONSE_T dgCmdResp; HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_CHEM_DISINFECT_CMD_RESPONSE_STATE; BOOL result = FALSE; if ( TRUE == getDGCommandResponse( DG_CMD_START_CHEM_DISINFECT, &dgCmdResp ) ) { state = STANDBY_WAIT_FOR_DISINFECT_STATE; chemDisinfectStartReqReceived = FALSE; if ( DG_CMD_REQUEST_REJECT_REASON_NONE == dgCmdResp.rejectCode ) { dgDisinfectState = DG_DISINFECT_CHEM_STATE; state = STANDBY_WAIT_FOR_DG_CHEM_DISINFECT_TO_START_STATE; result = TRUE; } sendDisinfectConfirmResponse( result, dgCmdResp.rejectCode ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeWaitForDGChemDisinfectStartState function handles * DG chemical disinfect wait for start state. * @details Inputs: none * @details Outputs: dgDisinfectState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeWaitForDGChemDisinfectStartState( void ) { HD_STANDBY_STATE_T state = STANDBY_WAIT_FOR_DG_CHEM_DISINFECT_TO_START_STATE; if ( DG_MODE_CHEM == getDGOpMode() ) { dgDisinfectState = DG_DISINFECT_NOT_RUNNING_STATE; state = STANDBY_DG_CHEM_DISINFECT_IN_PROGRESS_STATE; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_DISINFECT_CHEM, 0 ); } return state; } /*********************************************************************//** * @brief * The handleStandbyModeDGChemDisininfectInProgressState function handles * DG chemical disinfect in progress state. * @details Inputs: none * @details Outputs: dgDisinfectState, currentStandbyState * @return next state of the standby mode state machine *************************************************************************/ static HD_STANDBY_STATE_T handleStandbyModeDGChemDisininfectInProgressState( void ) { HD_STANDBY_STATE_T state = STANDBY_DG_CHEM_DISINFECT_IN_PROGRESS_STATE; if ( getDGOpMode() != DG_MODE_CHEM ) { dgDisinfectState = DG_DISINFECT_NOT_RUNNING_STATE; state = STANDBY_WAIT_FOR_TREATMENT_STATE; clearAlarm( ALARM_ID_HD_DISINFECT_CHEM ); } publishDisinfectData(); return state; } /*********************************************************************//** * @brief * The isDGDisinfectValid function checks whether the DG disinfects is * acceptable to start another treatment. * @details Inputs: none * @details Outputs: none * @return TRUE if the disinfect is valid otherwise, FALSE ***********************************************************************/ static BOOL isDGDisinfectValid( void ) { DG_SERVICE_AND_USAGE_DATA_T data; BOOL status = FALSE; getHDVersionDGServiceAndUsageData( &data ); if ( TRUE == data.isDGUsageInfoAviable ) { HD_USAGE_INFO_RECORD_T usageRecord; getNVRecord2Driver( GET_USAGE_RECORD, (U08*)&usageRecord, sizeof( HD_USAGE_INFO_RECORD_T ), 0, ALARM_ID_NO_ALARM ); U32 lastChemCompleteDate = data.dgUsageInfo.lastChemDisCompleteDateEpoch; U32 lastChemFlushCompleteDate = data.dgUsageInfo.lastChemDisFlushCompleteDateEpoch; U32 lastHeatCompleteDate = data.dgUsageInfo.lastHeatDisCompleteDateEpoch; U32 lastFlushCompleteDate = data.dgUsageInfo.lastBasicFlushCompleteDateEpoch; U32 lastStartTxTimeDate = usageRecord.txLastStartTimeEpoch; // Last Treatment Start > Last Heat Disinfect Complete AND Last Treatment Start > Last Chem Disinfect Complete BOOL hasDisBeenDone = ( ( ( lastStartTxTimeDate > lastChemCompleteDate ) && ( lastStartTxTimeDate > lastHeatCompleteDate ) ) ? FALSE : TRUE ); // Last Chem Disinfect Complete < Current Time – Chem Disinfect Interval BOOL isChemDisValid = ( lastChemCompleteDate < ( getRTCTimestamp() - DISINFECTS_TIME_INTERVAL_S ) ? TRUE : FALSE ); // Last Heat Disinfect Complete < Current Time – Heat Disinfect Interval BOOL isHeatDisValid = ( lastHeatCompleteDate < ( getRTCTimestamp() - DISINFECTS_TIME_INTERVAL_S ) ? TRUE : FALSE ); // Last Chem Flush Complete < Last Chem Disinfect Start BOOL isChemFlushComplete = ( lastChemFlushCompleteDate < lastChemCompleteDate ? FALSE : TRUE ); // Last Basic Flush Complete < Current Time – Flush Interval AND Last Heat Disinfect Complete < Current Time – Flush Interval // AND Last Chem Flush Complete < Current Time – Flush Interval BOOL isBasicFlushValid = ( lastFlushCompleteDate < ( getRTCTimestamp() - FLUSH_TIME_INTERVAL_S ) ? TRUE : FALSE ); BOOL isHeatDisFlushValid = ( lastHeatCompleteDate < ( getRTCTimestamp() - FLUSH_TIME_INTERVAL_S ) ? TRUE : FALSE ); BOOL isChemFlushValid = ( lastChemFlushCompleteDate < ( getRTCTimestamp() - FLUSH_TIME_INTERVAL_S ) ? TRUE : FALSE ); BOOL isFlushValid = ( isBasicFlushValid | isHeatDisFlushValid | isChemFlushValid ); if ( ( TRUE == hasDisBeenDone ) && ( TRUE == isChemDisValid ) && ( 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 time is still valid otherwise, FALSE ***********************************************************************/ static BOOL haveHDDGServicesBeenExpired( REQUEST_REJECT_REASON_CODE_T* rejReason ) { BOOL status = TRUE; DG_SERVICE_AND_USAGE_DATA_T dgData; HD_SERVICE_RECORD_T hdServiceRecord; getHDVersionDGServiceAndUsageData( &dgData ); getNVRecord2Driver( GET_SRV_RECORD, (U08*)&hdServiceRecord, sizeof( HD_SERVICE_RECORD_T ), 0, ALARM_ID_NO_ALARM ); if ( TRUE == dgData.isDGServiceRecordAvailable ) { U32 dgSrvcElapsedTimeS = getRTCTimestamp() - dgData.dgServiceRecord.lastServiceEpochDate; BOOL hasDGSrvcBeenExpired = ( dgSrvcElapsedTimeS > SERVICE_TIME_INTERVAL_S ? TRUE : FALSE ); U32 hdSrvcElapsedTimeS = getRTCTimestamp() - hdServiceRecord.lastServiceEpochDate; BOOL hasHDSrvcBeenExpied = ( hdSrvcElapsedTimeS > SERVICE_TIME_INTERVAL_S ? TRUE : FALSE ); if ( TRUE == hasDGSrvcBeenExpired ) { status = FALSE; *rejReason = REQUEST_REJECT_REASON_DG_SERVICE_IS_DUE; } if ( TRUE == hasHDSrvcBeenExpied ) { status = FALSE; *rejReason = REQUEST_REJECT_REASON_HD_SERVICE_IS_DUE; } } return status; } /*********************************************************************//** * @brief * The publishDisinfectData function publishes disinfects data at * the set interval. * @details Inputs: dataPublishCounter * @details Outputs: dataPublishCounter * @return: none *************************************************************************/ static void publishDisinfectData( void ) { if ( ++dataPublishCounter > DISINFECTS_DATA_PUB_INTERVAL ) { DG_DISINFECT_UI_STATES_T state = getDGDisinfectsStates(); DISINFECTS_DATA_T data; switch( currentStandbyState ) { case STANDBY_DG_FLUSH_IN_PROGRESS_STATE: data.disinfectDGFlushState = state.flushUIState; break; case STANDBY_DG_HEAT_DISINFECT_IN_PROGRESS_STATE: data.disinfectDGHeatState = state.heatDisinfectUIState; break; case STANDBY_DG_CHEM_DISINFECT_IN_PROGRESS_STATE: data.disinfectDGChemState = state.chemDisinfectUIState; break; } data.disinfectSubModeHDState = (U32)dgDisinfectState; broadcastData( MSG_ID_HD_DISINFECT_STANDBY_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( DISINFECTS_DATA_T ) ); dataPublishCounter = 0; } } /************************************************************************* * 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; } /**@}*/