/************************************************************************** * * Copyright (c) 2019-2023 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 OperationModes.c * * @author (last) Michael Garthwaite * @date (last) 08-Aug-2023 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ #include "gio.h" #include "MessageSupport.h" #include "ModeChemicalDisinfect.h" #include "ModeChemicalDisinfectFlush.h" #include "ModeDrain.h" #include "ModeFault.h" #include "ModeFill.h" #include "ModeFlush.h" #include "ModeGenIdle.h" #include "ModeHeatDisinfect.h" #include "ModeHeatDisinfectActiveCool.h" #include "ModeInitPOST.h" #include "ModeROPermeateSample.h" #include "ModeService.h" #include "ModeSolo.h" #include "ModeStandby.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "ROPump.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" /** * @addtogroup DGOperationModes * @{ */ // ********** private definitions ********** #define BROADCAST_DG_OP_MODE_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< interval (ms/task time) at which the operation mode is published on the CAN bus. // The data publish counter offset is a lower number to make sure DG is sending its status to the HD from the beginning and alarm 140 is not raised #define DATA_PUBLISH_COUNTER_START_COUNT 2 ///< Data publish counter start count. // ********** private data ********** static volatile BOOL modeRequest[ NUM_OF_DG_MODES - 1 ]; ///< Array of mode request flags. static DG_OP_MODE_T lastMode; ///< Last operation mode prior to current mode. static DG_OP_MODE_T currentMode; ///< The currently active mode. static U32 currentSubMode; ///< The currently active state of the active mode. /// DG operation mode data publish interval. static OVERRIDE_U32_T dgOpModePublishInterval = { BROADCAST_DG_OP_MODE_INTERVAL, BROADCAST_DG_OP_MODE_INTERVAL, 0, 0 }; static U32 dataPublishCounter; ///< Interval counter used to determine when to broadcast operation mode. Initialize to 11 to stagger broadcast. static U32 currentSubState; ///< The currently active sub state. /// This matrix determines legal transitions from one mode to another. static const DG_OP_MODE_T MODE_TRANSITION_TABLE[ NUM_OF_DG_MODES - 1 ][ NUM_OF_DG_MODES - 1 ] = { // from to-> FAULT SERVICE INIT STANBY STBY-SOLO GEN-IDLE FILL DRAIN FLUSH HEAT DIS CHEM DIS CHEM_FLUSH HEAT_COOL RO_PER_SAMPLE /* FAUL */{ DG_MODE_FAUL, DG_MODE_SERV, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG }, /* SERV */{ DG_MODE_FAUL, DG_MODE_SERV, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_ROPS }, /* INIT */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_INIT, DG_MODE_STAN, DG_MODE_SOLO, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG }, /* STAN */{ DG_MODE_FAUL, DG_MODE_SERV, DG_MODE_INIT, DG_MODE_STAN, DG_MODE_SOLO, DG_MODE_GENE, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_FLUS, DG_MODE_HEAT, DG_MODE_CHEM, DG_MODE_CHFL, DG_MODE_HCOL, DG_MODE_ROPS }, /* SOLO */{ DG_MODE_FAUL, DG_MODE_SERV, DG_MODE_INIT, DG_MODE_STAN, DG_MODE_SOLO, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_FLUS, DG_MODE_HEAT, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_HCOL, DG_MODE_ROPS }, /* GENE */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_NLEG, DG_MODE_GENE, DG_MODE_FILL, DG_MODE_DRAI, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG }, /* FILL */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_NLEG, DG_MODE_GENE, DG_MODE_FILL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG }, /* DRAI */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_NLEG, DG_MODE_GENE, DG_MODE_NLEG, DG_MODE_DRAI, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG }, /* FLUS */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_SOLO, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_FLUS, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG }, /* HEAT */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_SOLO, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_HEAT, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_HCOL, DG_MODE_NLEG }, /* CHEM */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_CHEM, DG_MODE_CHFL, DG_MODE_NLEG, DG_MODE_NLEG }, /* CHFL */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_CHFL, DG_MODE_NLEG, DG_MODE_NLEG }, /* HCOL */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_HCOL, DG_MODE_NLEG }, /* ROPS */{ DG_MODE_FAUL, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_STAN, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_NLEG, DG_MODE_ROPS }, }; // ********** private function prototypes ********** static DG_OP_MODE_T arbitrateModeRequest( void ); static void transitionToNewOperationMode( DG_OP_MODE_T newMode ); static U32 getDGOpModePublishInterval( void ); static void broadcastOperationMode( void ); static void sendOperationStatusEvent( void ); /*********************************************************************//** * @brief * The initOperationModes function initializes the operation modes module. * @details Inputs: none * @details Outputs: modeRequest, lastMode, currentMode, currentSubMode, * dataPublishCounter * @return none *************************************************************************/ void initOperationModes( void ) { U32 i; // initialize mode requests to none pending for ( i = 0; i < ( NUM_OF_DG_MODES - 1 ); i++ ) { modeRequest[ i ] = FALSE; } // start in init mode lastMode = DG_MODE_INIT; currentMode = DG_MODE_INIT; currentSubMode = 0; currentSubState = NO_SUB_STATE; dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; transitionToNewOperationMode( DG_MODE_INIT ); // call initializers for the individual modes initFaultMode(); initServiceMode(); initInitAndPOSTMode(); initStandbyMode(); initSoloMode(); initGenIdleMode(); initFillMode(); initDrainMode(); initFlushMode(); initHeatDisinfectMode(); initChemicalDisinfectMode(); initHeatDisinfectActiveCoolMode(); initChemicalDisinfectFlushMode(); initROPermeateSampleMode(); } /*********************************************************************//** * @brief * The execOperationModes function executes the operation modes state machine. * @details Inputs: currentMode, currentSubMode * @details Outputs: currentMode, currentSubMode * @return none *************************************************************************/ void execOperationModes( void ) { DG_OP_MODE_T newMode; U32 priorSubMode = currentSubMode; U32 priorSubState = currentSubState; // any new mode requests? newMode = arbitrateModeRequest(); // will return current mode if no pending requests if ( getTestConfigStatus( TEST_CONFIG_RECOVER_TREATMENT ) != TRUE ) { // If the test configuration is not enabled check for the legality of the transition request newMode = MODE_TRANSITION_TABLE[ currentMode ][ newMode ]; } // is requested new mode valid and legal at this time? if ( newMode >= DG_MODE_NLEG ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_ILLEGAL_MODE_TRANSITION_REQUESTED, (U32)newMode ) newMode = currentMode; } // has mode changed? if ( currentMode != newMode ) { // handle transition to new mode lastMode = currentMode; transitionToNewOperationMode( newMode ); currentMode = newMode; sendOperationStatusEvent(); } // mode specific processing to be done continuously switch ( currentMode ) { case DG_MODE_FAUL: currentSubMode = execFaultMode(); break; case DG_MODE_SERV: currentSubMode = execServiceMode(); break; case DG_MODE_INIT: currentSubMode = execInitAndPOSTMode(); break; case DG_MODE_STAN: currentSubMode = execStandbyMode(); break; case DG_MODE_SOLO: currentSubMode = execSoloMode(); break; case DG_MODE_GENE: currentSubMode = execGenIdleMode(); break; case DG_MODE_FILL: currentSubMode = execFillMode(); break; case DG_MODE_DRAI: currentSubMode = execDrainMode(); break; case DG_MODE_FLUS: currentSubMode = execFlushMode(); break; case DG_MODE_HEAT: currentSubMode = execHeatDisinfectMode(); break; case DG_MODE_CHEM: currentSubMode = execChemicalDisinfectMode(); break; case DG_MODE_HCOL: currentSubMode = execHeatDisinfectActiveCoolMode(); break; case DG_MODE_CHFL: currentSubMode = execChemicalDisinfectFlushMode(); break; case DG_MODE_ROPS: currentSubMode = execROPermeateSampleMode(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_STATE, (U32)currentMode ) currentMode = DG_MODE_FAUL; currentSubMode = 0; break; } // Send operation status event when appropriate if ( ( priorSubMode != currentSubMode ) || ( priorSubState != currentSubState ) ) { sendOperationStatusEvent(); SEND_EVENT_WITH_2_U32_DATA( DG_EVENT_SUB_MODE_CHANGE, priorSubMode, currentSubMode ) } // publish op mode on interval broadcastOperationMode(); } /*********************************************************************//** * @brief * The requestNewOperationMode function requests a new operation mode. * @details Inputs: none * @details Outputs: makes the requested mode "pending" * @param newMode requested mode * @return none *************************************************************************/ void requestNewOperationMode( DG_OP_MODE_T newMode ) { // validate requested mode if ( newMode < DG_MODE_NLEG ) { // make request modeRequest[ newMode ] = TRUE; } else { // invalid mode requested SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_REQUESTED, (U32)newMode ) } } /*********************************************************************//** * @brief * The getCurrentOperationMode function returns the current operation mode. * @details Inputs: none * @details Outputs: none * @return current mode *************************************************************************/ DG_OP_MODE_T getCurrentOperationMode( void ) { return currentMode; } /*********************************************************************//** * @brief * The getPreviousOperationMode function gets the previous operation mode. * @details Inputs: lastMode * @details Outputs: none * @return the previous operation mode *************************************************************************/ DG_OP_MODE_T getPreviousOperationMode( void ) { return lastMode; } /*********************************************************************//** * @brief * The arbitrateModeRequest function selects highest priority mode request * and clear all requests. * @details Inputs: none * @details Outputs: Arbitrated mode requests * @return highest priority requested mode *************************************************************************/ static DG_OP_MODE_T arbitrateModeRequest( void ) { DG_OP_MODE_T reqMode = currentMode; U32 i; // block additional requests until after mode arbitration _disable_IRQ(); // select highest priority mode request -or- current mode if no requests pending for ( i = 0; i < DG_MODE_NLEG; i++ ) { if ( modeRequest[i] != FALSE ) { reqMode = (DG_OP_MODE_T)i; break; } } // clear all requests now that an arbitration winner is selected for ( i = 0; i < DG_MODE_NLEG; i++ ) { modeRequest[i] = FALSE; } // un-block requests _enable_IRQ(); return reqMode; } /*********************************************************************//** * @brief * The transitionToNewOperationMode function undergo the process of transition * to new operation mode. * @details Inputs: currentSubMode * @details Outputs: Transition to new mode * @param newMode new op mode to transition to * @return none *************************************************************************/ static void transitionToNewOperationMode( DG_OP_MODE_T newMode ) { U32 priorSubMode = currentSubMode; SEND_EVENT_WITH_2_U32_DATA( DG_EVENT_OP_MODE_CHANGE, lastMode, newMode ) // setup for new operating mode switch ( newMode ) { case DG_MODE_FAUL: currentSubMode = transitionToFaultMode(); break; case DG_MODE_SERV: currentSubMode = transitionToServiceMode(); break; case DG_MODE_INIT: currentSubMode = transitionToInitAndPOSTMode(); break; case DG_MODE_STAN: currentSubMode = transitionToStandbyMode(); break; case DG_MODE_SOLO: currentSubMode = transitionToSoloMode(); break; case DG_MODE_GENE: currentSubMode = transitionToGenIdleMode(); break; case DG_MODE_FILL: currentSubMode = transitionToFillMode(); break; case DG_MODE_DRAI: currentSubMode = transitionToDrainMode(); break; case DG_MODE_FLUS: currentSubMode = transitionToFlushMode(); break; case DG_MODE_HEAT: currentSubMode = transitionToHeatDisinfectMode(); break; case DG_MODE_CHEM: currentSubMode = transitionToChemicalDisinfectMode(); break; case DG_MODE_HCOL: currentSubMode = transitionToHeatDisinfectActiveCoolMode(); break; case DG_MODE_CHFL: currentSubMode = transitionToChemicalDisinfectFlushMode(); break; case DG_MODE_ROPS: currentSubMode = transitionToROPermeateSampleMode(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_TO_TRANSITION_TO, (U32)newMode ) break; } // Send sub-mode change event when appropriate if ( priorSubMode != currentSubMode ) { SEND_EVENT_WITH_2_U32_DATA( DG_EVENT_SUB_MODE_CHANGE, priorSubMode, currentSubMode ) } } /*********************************************************************//** * @brief * The broadcastOperationMode function broadcasts the current operation mode at * the prescribed interval. * @details Inputs: broadcastModeIntervalCtr * @details Outputs: DG operation mode broadcast message sent * @return none *************************************************************************/ static void broadcastOperationMode( void ) { if ( ++dataPublishCounter >= getDGOpModePublishInterval() ) { OP_MODES_DATA_T data; data.currentMode = (U32)currentMode; data.currentSubMode = currentSubMode; broadcastData( MSG_ID_DG_OP_MODE_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( OP_MODES_DATA_T ) ); dataPublishCounter = 0; } } /*********************************************************************//** * @brief * The getDGOpModePublishInterval function gets the current DG operation mode * data publish interval. * @details Inputs: dgOpModePublishInterval * @details Outputs: DG operation mode broadcast message sent * @return none *************************************************************************/ static U32 getDGOpModePublishInterval( void ) { U32 result = dgOpModePublishInterval.data; if ( OVERRIDE_KEY == dgOpModePublishInterval.override ) { result = dgOpModePublishInterval.ovData; } return result; } /*********************************************************************//** * @brief * The setCurrentSubState function sets the current subState. * @details Inputs: subState * @details Outputs: currentSubState * @param subState the enumerated sub state. * @return none *************************************************************************/ void setCurrentSubState( U32 subState ) { currentSubState = subState; } /*********************************************************************//** * @brief * The sendOperationStatusEvent function constructs and sends an DG operation * status event. * @details Inputs: currentMode, currentSubMode, currentSubState * @details Outputs: dat1, dat2. * @param none * @return none *************************************************************************/ static void sendOperationStatusEvent() { EVENT_DATA_T dat1; EVENT_DATA_T dat2; U32 opData = ( (U08)currentMode + ( (U08)currentSubMode << SHIFT_8_BITS_FOR_BYTE_SHIFT ) + ( (U08)currentSubState << SHIFT_16_BITS_FOR_WORD_SHIFT ) ); dat2.dataType = EVENT_DATA_TYPE_U32; dat2.data.uInt.data = 0; dat1.dataType = EVENT_DATA_TYPE_U32; dat1.data.uInt.data = opData; sendEvent( DG_EVENT_OPERATION_STATUS, dat1, dat2 ); } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetOperationMode function will transition to a given operation * mode if the transition is legal. * @details Inputs: MODE_TRANSITION_TABLE[][] * @details Outputs: modeRequest[], result * @param newMode ID of requested mode to transition to * @return TRUE if request successful, FALSE if not *************************************************************************/ BOOL testSetOperationMode( DG_OP_MODE_T newMode ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { DG_OP_MODE_T check = MODE_TRANSITION_TABLE[ currentMode ][ newMode ]; // Is mode transition legal? if ( check == newMode ) { requestNewOperationMode( newMode ); result = TRUE; } } return result; } /*********************************************************************//** * @brief * The testSetDGOpModePublishIntervalOverride function overrides the * DG operation mode publish interval. * @details Inputs: none * @details Outputs: dgOpModePublishInterval * @param value override DG operation mode publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetDGOpModePublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_GENERAL_INTERVAL; result = TRUE; dgOpModePublishInterval.ovData = intvl; dgOpModePublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetDGOpModePublishIntervalOverride function resets the * override of the DG operation mode publish interval. * @details Inputs: none * @details Outputs: dgOpModePublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetDGOpModePublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; dgOpModePublishInterval.override = OVERRIDE_RESET; dgOpModePublishInterval.ovData = dgOpModePublishInterval.ovInitData; } return result; } /**@}*/