/************************************************************************** * * 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) 24-Aug-2023 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ #include "gio.h" #include "HDCommon.h" #include "ModeInitPOST.h" #include "ModeService.h" #include "ModeFault.h" #include "ModeStandby.h" #include "ModeTreatmentParams.h" #include "ModePreTreat.h" #include "ModeTreatment.h" #include "ModePostTreat.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "RTC.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" /** * @addtogroup HDOperationModes * @{ */ // ********** private definitions ********** #define BROADCAST_HD_OP_MODE_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< HD operation mode broadcast interval (in task interval/sec). #define DATA_PUBLISH_COUNTER_START_COUNT 11 ///< Data publish counter start count. #define MAX_PENDING_CONFIRM_REQUESTS 4 ///< Maximum number of confirmation requests allowed at any given time. /// Structure for confirmation request. typedef struct { GENERIC_CONFIRM_ID_T requestID; ///< Request ID GENERIC_CONFIRM_COMMAND_T requestType; ///< Request Type U32 timeStamp; ///< Timestamp for request CONFIRMATION_REQUEST_STATUS_T status; ///< Request status (pending, accepted, rejected) } CONFIRMATION_REQUEST_T; // ********** private data ********** static volatile BOOL modeRequest[ NUM_OF_MODES - 1 ]; ///< Pending operation mode change requests. static HD_OP_MODE_T lastMode; ///< Last operation mode prior to current mode. static HD_OP_MODE_T currentMode; ///< Current operation mode. static U32 currentSubMode; ///< The currently active state of the active mode. static U32 broadcastModeIntervalCtr; ///< Interval counter used to determine when to broadcast operation mode. Initialize to 11 to stagger broadcast. static U32 currentSubState; ///< current sub state. static U32 current4thLevelState; ///< current 4th level state. /// Interval (in task intervals) at which to publish operation mode data to CAN bus. static OVERRIDE_U32_T opModePublishInterval = { BROADCAST_HD_OP_MODE_INTERVAL, BROADCAST_HD_OP_MODE_INTERVAL, BROADCAST_HD_OP_MODE_INTERVAL, 0 }; /// Local structure init for saving confirmation requests static CONFIRMATION_REQUEST_T confirmRequests[ MAX_PENDING_CONFIRM_REQUESTS ] = { GENERIC_CONFIRM_ID_NONE, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0, CONFIRMATION_REQUEST_STATUS_UNUSED, GENERIC_CONFIRM_ID_NONE, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0, CONFIRMATION_REQUEST_STATUS_UNUSED, GENERIC_CONFIRM_ID_NONE, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0, CONFIRMATION_REQUEST_STATUS_UNUSED, GENERIC_CONFIRM_ID_NONE, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0, CONFIRMATION_REQUEST_STATUS_UNUSED, }; /// This matrix determines legal transitions from one mode to another static const HD_OP_MODE_T MODE_TRANSITION_TABLE[ NUM_OF_MODES - 1 ][ NUM_OF_MODES - 1 ] = { // From to-> FAULT SERVICE INIT STANBY TRT.PARAMS PRE-TREAT TREATMENT POST_TREA /* FAUL */{ MODE_FAUL, MODE_SERV, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, }, /* SERV */{ MODE_NLEG, MODE_SERV, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, }, /* INIT */{ MODE_FAUL, MODE_NLEG, MODE_INIT, MODE_STAN, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, }, /* STAN */{ MODE_FAUL, MODE_SERV, MODE_INIT, MODE_STAN, MODE_TPAR, MODE_NLEG, MODE_NLEG, MODE_NLEG, }, /* TPAR */{ MODE_FAUL, MODE_NLEG, MODE_NLEG, MODE_STAN, MODE_TPAR, MODE_PRET, MODE_NLEG, MODE_NLEG, }, /* PRET */{ MODE_FAUL, MODE_NLEG, MODE_NLEG, MODE_STAN, MODE_NLEG, MODE_PRET, MODE_TREA, MODE_POST, }, /* TREA */{ MODE_FAUL, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_TREA, MODE_POST, }, /* POST */{ MODE_FAUL, MODE_NLEG, MODE_NLEG, MODE_STAN, MODE_NLEG, MODE_NLEG, MODE_NLEG, MODE_POST, }, }; // ********** private function prototypes ********** static HD_OP_MODE_T arbitrateModeRequest( void ); static void transitionToNewOperationMode( HD_OP_MODE_T newMode ); static void broadcastOperationMode( void ); /*********************************************************************//** * @brief * The initOperationModes function initializes the Operation Modes module. * @details Inputs: none * @details Outputs: Operation Modes module initialized. * @return none *************************************************************************/ void initOperationModes( void ) { U32 i; // Initialize mode requests to none pending for ( i = 0; i < ( NUM_OF_MODES - 1 ); i++ ) { modeRequest[ i ] = FALSE; } // Start in init mode lastMode = MODE_INIT; currentMode = MODE_INIT; currentSubMode = 0; currentSubState = NO_SUB_STATE; current4thLevelState = NO_SUB_STATE; broadcastModeIntervalCtr = DATA_PUBLISH_COUNTER_START_COUNT; transitionToNewOperationMode( MODE_INIT ); // Call initializers for the individual modes initFaultMode(); initServiceMode(); initInitAndPOSTMode(); initStandbyMode(); initTreatParamsMode(); initPreTreatmentMode(); initTreatmentMode(); initPostTreatmentMode(); } /*********************************************************************//** * @brief * The execOperationModes function executes the Operation Modes state machine. * @details Inputs: none * @details Outputs: currentMode is set by state machine. * @return none *************************************************************************/ void execOperationModes( void ) { HD_OP_MODE_T newMode; U32 priorSubMode = currentSubMode; U32 priorSubState = currentSubState; U32 prior4thLevelState = current4thLevelState; // Any new mode requests? newMode = arbitrateModeRequest(); // Will return current mode if no pending requests if ( getTestConfigStatus( TEST_CONFIG_RECOVER_TREATMENT ) != TRUE ) { newMode = MODE_TRANSITION_TABLE[ currentMode ][ newMode ]; } // Is requested new mode valid and legal at this time? if ( newMode >= MODE_NLEG ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_ILLEGAL_MODE_TRANSITION_REQUESTED, newMode ) newMode = currentMode; } // Has mode changed? if ( currentMode != newMode ) { lastMode = currentMode; // Handle transition to new mode transitionToNewOperationMode( newMode ); currentMode = newMode; if ( ( MODE_TREA == lastMode ) && ( currentMode != MODE_TREA ) ) { // If the last mode is treatment but the new mode is not treatment // it means the treatment is done. Get the elapsed time since the beginning of the treatment and convert it to hours to be written U32 txElapsedTimeMS = calcTimeSince( getTreatmentStartTimeStamp() ); F32 txElapsedTimeHrs = (F32)txElapsedTimeMS / ( (F32)( MIN_PER_HOUR * SEC_PER_MIN * MS_PER_SECOND ) ); // Write the treatment hours and set the service to be false so the treatment hours is not reset setTxTimeHours( txElapsedTimeHrs ); } sendOperationStatusEvent(); } // Mode specific processing to be done continuously switch ( currentMode ) { case MODE_FAUL: currentSubMode = execFaultMode(); break; case MODE_SERV: currentSubMode = execServiceMode(); break; case MODE_INIT: currentSubMode = execInitAndPOSTMode(); break; case MODE_STAN: currentSubMode = execStandbyMode(); break; case MODE_TPAR: currentSubMode = execTreatParamsMode(); break; case MODE_PRET: currentSubMode = execPreTreatmentMode(); break; case MODE_TREA: currentSubMode = execTreatmentMode(); break; case MODE_POST: currentSubMode = execPostTreatmentMode(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_STATE, currentMode ) currentMode = MODE_FAUL; currentSubMode = 0; break; } // End switch // Send operation status event when appropriate if ( ( priorSubMode != currentSubMode ) || ( priorSubState != currentSubState ) || ( prior4thLevelState != current4thLevelState ) ) { sendOperationStatusEvent(); SEND_EVENT_WITH_2_U32_DATA( HD_EVENT_SUB_MODE_CHANGE, priorSubMode, currentSubMode ) } // Broadcast current operation mode on interval broadcastOperationMode(); } /*********************************************************************//** * @brief * The requestNewOperationMode function requests transition to a new * operation mode. The request will be arbitrated when the state machine * is next executed. * @details Inputs: none * @details Outputs: modeRequest[] * @return none *************************************************************************/ void requestNewOperationMode( HD_OP_MODE_T newMode ) { // Validate requested mode if ( newMode < MODE_NLEG ) { // Make request modeRequest[ newMode ] = TRUE; } else { // Invalid mode requested SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_REQUESTED, newMode ) } } /*********************************************************************//** * @brief * The getCurrentOperationMode function gets the current operation mode. * @details Inputs: currentMode * @details Outputs: none * @return the current operation mode *************************************************************************/ HD_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 *************************************************************************/ HD_OP_MODE_T getPreviousOperationMode( void ) { return lastMode; } /*********************************************************************//** * @brief * The getCurrentSubMode function gets the current operation sub-mode. * @details Inputs: currentSubMode * @details Outputs: none * @return the current operation sub-mode *************************************************************************/ U32 getCurrentSubMode( void ) { return currentSubMode; } /*********************************************************************//** * @brief * The initiateAlarmAction function initiate an alarm or alarm recovery * action according to the current operation mode. * @details Inputs: currentMode * @details Outputs: given action forwarded to current operation mode for execution * @param action ID of action to be initiated * @return none *************************************************************************/ void initiateAlarmAction( ALARM_ACTION_T action ) { // Forward request to the current operation mode switch ( currentMode ) { case MODE_FAUL: signalAlarmActionToFaultMode( action ); break; case MODE_SERV: signalAlarmActionToServiceMode( action ); break; case MODE_INIT: signalAlarmActionToInitAndPOSTMode( action ); break; case MODE_STAN: signalAlarmActionToStandbyMode( action ); break; case MODE_TPAR: signalAlarmActionToTreatParamsMode( action ); break; case MODE_PRET: signalAlarmActionToPreTreatmentMode( action ); break; case MODE_TREA: signalAlarmActionToTreatmentMode( action ); break; case MODE_POST: signalAlarmActionToPostTreatmentMode( action ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_TO_SIGNAL_ACTION, currentMode ) break; } } /*********************************************************************//** * @brief * The arbitrateModeRequest function arbitrates any pending mode transition * requests. * @details Inputs: modeRequest[] * @details Outputs: modeRequest[] is reset * @return the next operation mode (current mode if no requests pending) *************************************************************************/ static HD_OP_MODE_T arbitrateModeRequest( void ) { HD_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 < MODE_NLEG; i++ ) { if ( modeRequest[ i ] != FALSE ) { reqMode = (HD_OP_MODE_T)i; break; } } // Clear all requests now that an arbitration winner is selected for ( i = 0; i < MODE_NLEG; i++ ) { modeRequest[ i ] = FALSE; } // Un-block requests _enable_IRQ(); return reqMode; } /*********************************************************************//** * @brief * The transitionToNewOperationMode function calls the transition to function * for a new operation mode that we are transitioning to. * @details Inputs: none * @details Outputs: transition function called for new mode * @return none *************************************************************************/ static void transitionToNewOperationMode( HD_OP_MODE_T newMode ) { // Setup for new operating mode switch ( newMode ) { case MODE_FAUL: currentSubMode = transitionToFaultMode(); break; case MODE_SERV: currentSubMode = transitionToServiceMode(); break; case MODE_INIT: currentSubMode = transitionToInitAndPOSTMode(); break; case MODE_STAN: currentSubMode = transitionToStandbyMode(); break; case MODE_TPAR: currentSubMode = transitionToTreatParamsMode(); break; case MODE_PRET: currentSubMode = transitionToPreTreatmentMode(); break; case MODE_TREA: currentSubMode = transitionToTreatmentMode(); break; case MODE_POST: currentSubMode = transitionToPostTreatmentMode(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_TO_TRANSITION_TO, newMode ) break; } } /*********************************************************************//** * @brief * The broadcastOperationMode function sends the current operation mode at * the prescribed interval. * @details Inputs: broadcastModeIntervalCtr * @details Outputs: HD operation mode broadcast message sent. * @return none *************************************************************************/ static void broadcastOperationMode( void ) { if ( ++broadcastModeIntervalCtr >= getU32OverrideValue( &opModePublishInterval ) ) { OP_MODE_PAYLOAD_T data; broadcastModeIntervalCtr = 0; data.opMode = (U32)currentMode; data.subMode = currentSubMode; broadcastData( MSG_ID_HD_OP_MODE_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( OP_MODE_PAYLOAD_T ) ); } } /*********************************************************************//** * @brief * The getConfirmationRequestStatus function returns the status of a confirmation request * @details Inputs: confirmRequests[] * @details Outputs: confirmRequests[] consumed/cleared if completed. * @param requestID ID of confirmation being requested * @return Status of given confirmation *************************************************************************/ CONFIRMATION_REQUEST_STATUS_T getConfirmationRequestStatus( GENERIC_CONFIRM_ID_T requestID ) { BOOL pending = FALSE; U32 pendingIndex = 0; CONFIRMATION_REQUEST_STATUS_T status = CONFIRMATION_REQUEST_STATUS_PENDING; U32 i; for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) { if ( confirmRequests[ i ].requestID == requestID ) { status = confirmRequests[ i ].status; if ( status != CONFIRMATION_REQUEST_STATUS_PENDING ) { // Send UI clear if ( CONFIRMATION_REQUEST_STATUS_TIMEOUT == status ) { sendConfirmationRequest( confirmRequests[ i ].requestID, GENERIC_CONFIRM_CMD_TIMEOUT_CLOSE, 0 ); } else { sendConfirmationRequest( confirmRequests[ i ].requestID, GENERIC_CONFIRM_CMD_ACCEPT_CLOSE, 0 ); } // Clear the confirmation request, it is done and consumed confirmRequests[ i ].requestID = GENERIC_CONFIRM_ID_NONE; confirmRequests[ i ].requestType = GENERIC_CONFIRM_CMD_REQUEST_OPEN; confirmRequests[ i ].timeStamp = 0; confirmRequests[ i ].status = CONFIRMATION_REQUEST_STATUS_UNUSED; } } else if ( CONFIRMATION_REQUEST_STATUS_PENDING == confirmRequests[ i ].status ) { if ( TRUE == pending ) { // Is this newer than other pending request? if ( confirmRequests[ i ].timeStamp > confirmRequests[ pendingIndex ].timeStamp ) { pendingIndex = i; } } else { pendingIndex = i; pending = TRUE; } } } if ( ( CONFIRMATION_REQUEST_STATUS_PENDING != status ) && ( TRUE == pending ) ) { // Last confirmation cleared, pending request must be resent to UI sendConfirmationRequest( confirmRequests[ pendingIndex ].requestID, confirmRequests[ pendingIndex ].requestType, 0 ); } return status; } /*********************************************************************//** * @brief * The setConfirmationRequestStatus function sets the status of a confirmation request * @details Inputs: confirmRequests[] * @details Outputs: confirmRequests[]. * @param requestID ID of confirmation being requested * @param status Status to set for given confirmation (open, close, reject) * @return none *************************************************************************/ void setConfirmationRequestStatus( GENERIC_CONFIRM_ID_T requestID, CONFIRMATION_REQUEST_STATUS_T status ) { U32 i; for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) { if ( confirmRequests[ i ].requestID == requestID ) { confirmRequests[ i ].status = status; break; } } } /*********************************************************************//** * @brief * The addConfirmationRequest function sends a confirmation request to UI * @details Inputs: confirmRequests[] * @details Outputs: confirmRequests[] new added. * @param requestID ID of confirmation being requested * @param requestType Type of confirmation being requested (open, close, reject) * @param rejectReason Reason for reject if type is reject * @return request ID - will be non-zero if added *************************************************************************/ GENERIC_CONFIRM_ID_T addConfirmationRequest( GENERIC_CONFIRM_ID_T requestID, GENERIC_CONFIRM_COMMAND_T requestType, U32 rejectReason ) { U32 i; BOOL confirmAlreadyPending = FALSE; GENERIC_CONFIRM_ID_T newID = GENERIC_CONFIRM_ID_NONE; // Check to make sure specified confirmation is not already pending for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) { if ( ( confirmRequests[ i ].requestID == requestID ) && ( confirmRequests[ i ].status != CONFIRMATION_REQUEST_STATUS_UNUSED ) ) { confirmAlreadyPending = TRUE; break; } } // If not already pending, add confirmation to list of pending confirmations and send to UI to be displayed if ( confirmAlreadyPending != TRUE ) { for ( i = 0; i < MAX_PENDING_CONFIRM_REQUESTS; i++ ) { if ( CONFIRMATION_REQUEST_STATUS_UNUSED == confirmRequests[ i ].status ) { // Save the confirmation request info confirmRequests[ i ].requestID = requestID; confirmRequests[ i ].requestType = requestType; confirmRequests[ i ].timeStamp = getMSTimerCount(); confirmRequests[ i ].status = CONFIRMATION_REQUEST_STATUS_PENDING; newID = requestID; sendConfirmationRequest( requestID, requestType, rejectReason ); break; } } } return newID; } /*********************************************************************//** * @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 setCurrent4thLevelState function sets the current 4th level state. * @details Inputs: state * @details Outputs: current4thLevelState * @param subState the enumerated sub state. * @return none *************************************************************************/ void setCurrent4thLevelState( U32 state ) { current4thLevelState = state; } /*********************************************************************//** * @brief * The sendOperationStatusEvent function constructs and sends an HD operation * status event. * @details Inputs: currentMode, currentSubMode, currentSubState, current4thLevelState * @details Outputs: dat1, dat2. * @param none * @return none *************************************************************************/ void sendOperationStatusEvent( void ) { 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 ) + ( (U08)current4thLevelState << SHIFT_24_BITS ) ); dat2.dataType = EVENT_DATA_TYPE_U32; dat2.data.uInt.data = 0; dat1.dataType = EVENT_DATA_TYPE_U32; dat1.data.uInt.data = opData; sendEvent( HD_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: none * @details Outputs: modeRequest[] * @param newMode ID of requested mode to transition to * @return TRUE if request successful, FALSE if not *************************************************************************/ BOOL testSetOperationMode( HD_OP_MODE_T newMode ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { HD_OP_MODE_T check = MODE_TRANSITION_TABLE[ currentMode ][ newMode ]; // Is mode transition legal? if ( check == newMode ) { requestNewOperationMode( newMode ); result = TRUE; } } return result; } /*********************************************************************//** * @brief * The testSetOpModePublishIntervalOverride function sets the override of the * operation mode publication interval. * @details Inputs: none * @details Outputs: opModePublishInterval * @param ms milliseconds between operation mode broadcasts * @return TRUE if override set successful, FALSE if not *************************************************************************/ BOOL testSetOpModePublishIntervalOverride( U32 ms ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = ms / TASK_GENERAL_INTERVAL; result = TRUE; opModePublishInterval.ovData = intvl; opModePublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetOpModePublishIntervalOverride function resets the override of the * operation mode data publication interval. * @details Inputs: none * @details Outputs: opModePublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetOpModePublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; opModePublishInterval.override = OVERRIDE_RESET; opModePublishInterval.ovData = opModePublishInterval.ovInitData; } return result; } /**@}*/