/************************************************************************** * * Copyright (c) 2024-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 OperationModes.c * * @author (last) Sean * @date (last) 30-Jul-2024 * * @author (original) Sean * @date (original) 30-Jul-2024 * ***************************************************************************/ #include "gio.h" #include "Messaging.h" #include "ModeInitPOST.h" #include "ModeService.h" #include "ModeFault.h" #include "ModeStandby.h" #include "ModeTxParams.h" #include "ModePreTreat.h" #include "ModeTreatment.h" //#include "ModePostTreat.h" //#include "NVDataMgmt.h" #include "OperationModes.h" #include "TaskGeneral.h" #include "Timers.h" /** * @addtogroup TDOperationModes * @{ */ // ********** private definitions ********** #define BROADCAST_TD_OP_MODE_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< TD 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 TD_OP_MODE_T lastMode; ///< Last operation mode prior to current mode. static TD_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; /// 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 TD_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 TD_OP_MODE_T arbitrateModeRequest( void ); static void transitionToNewOperationMode( TD_OP_MODE_T newMode ); static void broadcastOperationMode( void ); /*********************************************************************//** * @brief * The initOperationModes function initializes the Operation Modes unit. * @details \b Inputs: none * @details \b Outputs: Operation Modes unit 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; opModePublishInterval.data = BROADCAST_TD_OP_MODE_INTERVAL; opModePublishInterval.ovData = BROADCAST_TD_OP_MODE_INTERVAL; opModePublishInterval.ovInitData = BROADCAST_TD_OP_MODE_INTERVAL; opModePublishInterval.override = OVERRIDE_RESET; 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 \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if current mode is invalid. * @details \b Inputs: none * @details \b Outputs: currentMode is set by state machine. * @return none *************************************************************************/ void execOperationModes( void ) { TD_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 #ifndef TEST_UI_ONLY // Verify mode transition is legal unless tester working with system TODO - restore check when all modes are implemented // if ( ( isTestingActivated() != TRUE ) && ( getTestConfigStatus( TEST_CONFIG_RECOVER_TREATMENT ) != TRUE ) ) { newMode = MODE_TRANSITION_TABLE[ currentMode ][ newMode ]; } #endif // Is requested new mode valid and legal at this time? if ( newMode >= MODE_NLEG ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_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 ) { // 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_TD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_STATE, currentMode ) currentMode = MODE_FAUL; currentSubMode = 0; break; } // End switch #ifndef _VECTORCAST_ // Send operation status event when appropriate if ( ( priorSubMode != currentSubMode ) || ( priorSubState != currentSubState ) || ( prior4thLevelState != current4thLevelState ) ) #endif { sendOperationStatusEvent(); SEND_EVENT_WITH_2_U32_DATA( TD_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 \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given new mode is invalid. * @details \b Inputs: none * @details \b Outputs: modeRequest[] * @return none *************************************************************************/ void requestNewOperationMode( TD_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_TD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_REQUESTED, newMode ) } } /*********************************************************************//** * @brief * The getCurrentOperationMode function gets the current operation mode. * @details \b Inputs: currentMode * @details \b Outputs: none * @return the current operation mode *************************************************************************/ TD_OP_MODE_T getCurrentOperationMode( void ) { return currentMode; } /*********************************************************************//** * @brief * The getPreviousOperationMode function gets the previous operation mode. * @details \b Inputs: lastMode * @details \b Outputs: none * @return the previous operation mode *************************************************************************/ TD_OP_MODE_T getPreviousOperationMode( void ) { return lastMode; } /*********************************************************************//** * @brief * The getCurrentSubMode function gets the current operation sub-mode. * @details \b Inputs: currentSubMode * @details \b 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 \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given alarm action is invalid. * @details \b Inputs: currentMode * @details \b 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_TD_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_TO_SIGNAL_ACTION, currentMode ) break; } } /*********************************************************************//** * @brief * The arbitrateModeRequest function arbitrates any pending mode transition * requests. * @note Thread protection prevents new mode requests from being made while * arbitration is in progress. * @details \b Inputs: modeRequest[] * @details \b Outputs: modeRequest[] is reset * @return the next operation mode (current mode if no requests pending) *************************************************************************/ static TD_OP_MODE_T arbitrateModeRequest( void ) { TD_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 = (TD_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 \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given new mode is invalid. * @details \b Inputs: none * @details \b Outputs: transition function called for new mode * @return none *************************************************************************/ static void transitionToNewOperationMode( TD_OP_MODE_T newMode ) { SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_OP_MODE_CHANGE, lastMode, 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_TD_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 \b Inputs: broadcastModeIntervalCtr * @details \b 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_TD_OP_MODE_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( OP_MODE_PAYLOAD_T ) ); } } /*********************************************************************//** * @brief * The getConfirmationRequestStatus function returns the status of a confirmation request * @details \b Inputs: confirmRequests[] * @details \b 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 \b Inputs: confirmRequests[] * @details \b 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 \b Inputs: confirmRequests[] * @details \b 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 \b Inputs: none * @details \b 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 \b Inputs: none * @details \b Outputs: current4thLevelState * @param subState the enumerated sub state. * @return none *************************************************************************/ void setCurrent4thLevelState( U32 state ) { current4thLevelState = state; } /*********************************************************************//** * @brief * The sendOperationStatusEvent function constructs and sends an TD operation * status event. * @details \b Message \b Sent: MSG_ID_TD_EVENT * @details \b Inputs: currentMode, currentSubMode, currentSubState, current4thLevelState * @details \b Outputs: 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( TD_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 \b Inputs: none * @details \b Outputs: modeRequest[] * @param message message from Dialin which includes the op mode to * transition to. * @return TRUE if request successful, FALSE if not *************************************************************************/ BOOL testSetOperationMode( MESSAGE_T *message ) { BOOL result = FALSE; // Verify message payload length is valid if ( sizeof( U32 ) == message->hdr.payloadLen ) { U32 newMode; memcpy( &newMode, message->payload, sizeof( U32 ) ); if ( (TD_OP_MODE_T)newMode < NUM_OF_MODES ) { TD_OP_MODE_T reqMode = (TD_OP_MODE_T)newMode; requestNewOperationMode( reqMode ); result = TRUE; } } return result; } /*********************************************************************//** * @brief * The testSetOpModePublishIntervalOverride function sets the override of the * operation mode publication interval. * @details \b Inputs: none * @details \b Outputs: opModePublishInterval * @param message override message from Dialin which includes the interval * (in ms) to set the op mode publish to. * @return TRUE if override set successful, FALSE if not *************************************************************************/ BOOL testSetOpModePublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &opModePublishInterval, TASK_GENERAL_INTERVAL ); return result; } /**@}*/