LCOV - code coverage report
Current view: top level - uds/include/uds/base - AbstractDiagJob.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 63 63 100.0 %
Date: 2025-10-07 10:52:16 Functions: 14 15 93.3 %

          Line data    Source code
       1             : // Copyright 2024 Accenture.
       2             : 
       3             : #pragma once
       4             : 
       5             : #include "uds/DiagReturnCode.h"
       6             : #include "uds/UdsConstants.h"
       7             : #include "uds/session/DiagSession.h"
       8             : 
       9             : #include <etl/uncopyable.h>
      10             : #include <util/estd/assert.h>
      11             : 
      12             : #include <platform/estdint.h>
      13             : 
      14             : namespace http
      15             : {
      16             : namespace html
      17             : {
      18             : class UdsController;
      19             : }
      20             : } // namespace http
      21             : 
      22             : namespace uds
      23             : {
      24             : class IncomingDiagConnection;
      25             : class IDiagAuthenticator;
      26             : class IDiagSessionManager;
      27             : class DiagSubSession;
      28             : class Service;
      29             : class DiagJobRoot;
      30             : 
      31             : /**
      32             :  * Common base class for diagnosis jobs
      33             :  *
      34             :  *
      35             :  *
      36             :  * \section Design
      37             :  * AbstractDiagJobs are elements of a tree-data-structure that represents
      38             :  * the ECUs complete diagnosis applications when fully configured. Each
      39             :  * AbstractDiagJob knows the <b>full</b> request it implements and which
      40             :  * part of the request needs to be provided by a parent node.
      41             :  *
      42             :  * \section Example
      43             :  * A class ReadVIN would implement the request 0x22,0xF1,0x90 requiring 1 prefix
      44             :  * byte, i.e. 0x22. This byte could be provided by a ReadDataByIdentifier class.
      45             :  */
      46             : class AbstractDiagJob : public ::etl::uncopyable
      47             : {
      48             : public:
      49             :     using DiagSessionMask = DiagSession::DiagSessionMask;
      50             : 
      51             :     static uint8_t const EMPTY_REQUEST = 0U;
      52             : 
      53             :     static uint8_t const EMPTY_RESPONSE = 0U;
      54             : 
      55             :     static uint8_t const VARIABLE_REQUEST_LENGTH = 0xFFU;
      56             : 
      57             :     static uint8_t const VARIABLE_RESPONSE_LENGTH = 0xFFU;
      58             : 
      59             :     /**
      60             :      * Error codes method invocations may return
      61             :      */
      62             :     enum ErrorCode
      63             :     {
      64             :         /** AbstractDiagJob has been successfully added */
      65             :         JOB_ADDED,
      66             :         /** An error occurred adding AbstractDiagJob */
      67             :         JOB_NOT_ADDED
      68             :     };
      69             : 
      70             :     /**
      71             :      * Possible results of sending a response
      72             :      */
      73             :     enum ResponseSendResult
      74             :     {
      75             :         /** the response has been successfully transmitted */
      76             :         RESPONSE_SENT,
      77             :         /** an error occurred sending the response */
      78             :         RESPONSE_SEND_FAILED
      79             :     };
      80             : 
      81             :     /**
      82             :      * Constructor
      83             :      * \param   implementedRequest  uint8_t array which contains the
      84             :      *          request implemented by this AbstractDiagJob.
      85             :      * \param   requestLength       Size of pImplementedRequest which is equal
      86             :      *          to the total length of the request implemented by this
      87             :      *          AbstractDiagJob.
      88             :      * \param   prefixLength        Number of bytes of pImplementedRequest that
      89             :      *          this AbstractDiagJob requires to be checked by a parent
      90             :      *          AbstractDiagJob.
      91             :      * \param   sessionMask Mask of DiagSessions in which this job is active.
      92             :      */
      93             : 
      94         521 :     AbstractDiagJob(
      95             :         uint8_t const* const implementedRequest,
      96             :         uint8_t const requestLength,
      97             :         uint8_t const prefixLength,
      98             :         DiagSessionMask const sessionMask = DiagSession::ALL_SESSIONS())
      99         521 :     : fpImplementedRequest(implementedRequest)
     100         521 :     , fpFirstChild(nullptr)
     101         521 :     , fpNextJob(nullptr)
     102         521 :     , fAllowedSessions(sessionMask)
     103         521 :     , fResponseLength(VARIABLE_RESPONSE_LENGTH)
     104         521 :     , fRequestLength(requestLength)
     105         521 :     , fPrefixLength(prefixLength)
     106         521 :     , fRequestPayloadLength(VARIABLE_REQUEST_LENGTH)
     107         521 :     , fDefaultDiagReturnCode(DiagReturnCode::ISO_GENERAL_REJECT)
     108         521 :     , fSuppressPositiveResponseBitEnabled(false)
     109             :     {
     110         521 :         if (requestLength > 0U)
     111             :         {
     112         319 :             estd_assert(requestLength > prefixLength);
     113             :         }
     114         520 :     }
     115             : 
     116             :     /**
     117             :      * Constructor
     118             :      * \param   implementedRequest  uint8_t array which contains the
     119             :      *          request implemented by this AbstractDiagJob
     120             :      * \param   requestLength       Size of pImplementedRequest which is equal
     121             :      *          to the total length of the request implemented by this
     122             :      *          AbstractDiagJob
     123             :      * \param   prefixLength        Number of bytes of pImplementedRequest that
     124             :      *          this AbstractDiagJob requires to be checked by a parent
     125             :      *          AbstractDiagJob
     126             :      * \param   requestPayloadLength    Number of bytes of the request payload,
     127             :      * (excluding prefixLength) pass VARIABLE_REQUEST_LENGTH if your diagnosis
     128             :      * job has variable length
     129             :      * \param   responseLength  Number of bytes required for your response, pass
     130             :      * VARIABLE_RESPONSE_LENGTH if the response has variable length
     131             :      * \param   sessionMask Mask of DiagSessions in which this job is active
     132             :      */
     133         337 :     AbstractDiagJob(
     134             :         uint8_t const* const implementedRequest,
     135             :         uint8_t const requestLength,
     136             :         uint8_t const prefixLength,
     137             :         uint8_t const requestPayloadLength,
     138             :         uint8_t const responseLength,
     139             :         DiagSessionMask const sessionMask = DiagSession::ALL_SESSIONS())
     140         337 :     : fpImplementedRequest(implementedRequest)
     141         337 :     , fpFirstChild(nullptr)
     142         337 :     , fpNextJob(nullptr)
     143         337 :     , fAllowedSessions(sessionMask)
     144         337 :     , fResponseLength(responseLength)
     145         337 :     , fRequestLength(requestLength)
     146         337 :     , fPrefixLength(prefixLength)
     147         337 :     , fRequestPayloadLength(requestPayloadLength)
     148         337 :     , fDefaultDiagReturnCode(DiagReturnCode::ISO_GENERAL_REJECT)
     149         337 :     , fSuppressPositiveResponseBitEnabled(false)
     150             :     {
     151         337 :         if (requestLength > 0U)
     152             :         {
     153         335 :             estd_assert(requestLength > prefixLength);
     154             :         }
     155         336 :     }
     156             : 
     157             : #ifdef UNIT_TEST
     158         892 :     virtual ~AbstractDiagJob() {}
     159             : #endif
     160             : 
     161             :     /**
     162             :      * Sets the default instance of IDiagSessionManager which will be used by
     163             :      * all instances of AbstractDiagJob
     164             :      * \param sessionManager
     165             :      */
     166             :     static void setDefaultDiagSessionManager(IDiagSessionManager& sessionManager);
     167             : 
     168             :     /**
     169             :      * Tells this AbstractDiagJob to execute a given request
     170             :      * \param   connection  IncomingDiagConnection that triggers the request
     171             :      * \param   request Array containing the request
     172             :      * \param   requestLength Length of request
     173             :      *
     174             :      * \section Behaviour
     175             :      * This method is the only public interface to give a incoming
     176             :      * request to this AbstractDiagJob. It first calls verify and in case
     177             :      * verify returns a  DiagReturnCode::Type other than NOT_RESPONSIBLE, it is
     178             :      * assumed that the request belongs to this job,
     179             :      * process will be called and after that execute will return.
     180             :      *
     181             :      * \see     verify()
     182             :      * \see     process()
     183             :      */
     184             :     DiagReturnCode::Type
     185             :     execute(IncomingDiagConnection& connection, uint8_t const request[], uint16_t requestLength);
     186             : 
     187             :     /**
     188             :      * Adds an AbstractDiagJob as child node
     189             :      * \param   job AbstractDiagJob to add as child node
     190             :      * \return
     191             :      *          - JOB_ADDED: job has been added as a child node
     192             :      *          - JOB_NOT_ADDED: job could not be added as child node
     193             :      */
     194             :     ErrorCode addAbstractDiagJob(AbstractDiagJob& job);
     195             : 
     196             :     /**
     197             :      * Removes an AbstractDiagJob from to tree structure
     198             :      * \param   job AbstractDiagJob to remove
     199             :      */
     200             :     void removeAbstractDiagJob(AbstractDiagJob& job);
     201             : 
     202             :     /**
     203             :      * Callback that gets invoked when a response on a IncomingDiagConnection
     204             :      * has been sent
     205             :      * \param   connection  IncomingDiagConnection on which the response has
     206             :      * been sent
     207             :      * \param   result  ResponseSendResult indicating status of transmission
     208             :      *
     209             :      * \section Default
     210             :      * The default implementation terminates the connection. So if more
     211             :      * responses are to be sent, this method must be overwritten.
     212             :      */
     213             :     virtual void responseSent(IncomingDiagConnection& connection, ResponseSendResult result);
     214             : 
     215             :     static IDiagAuthenticator const& getDefaultDiagAuthenticator();
     216             : 
     217             :     uint32_t getRequestId() const;
     218             : 
     219             : protected:
     220             :     friend class AsyncDiagHelper;
     221             :     friend class AbstractAsyncDiagJob;
     222             : 
     223             :     friend bool operator==(AbstractDiagJob const&, AbstractDiagJob const&);
     224             : 
     225             :     /**
     226             :      * Checks is a AbstractDiagJob requires a certain prefix and thus is
     227             :      * child of the calling AbstractDiagJob
     228             :      * \param   pPrefix Array containing a provided prefix
     229             :      * \param   length  Length of pPrefix
     230             :      * \return
     231             :      *          - true: This job is a child of pPrefix
     232             :      *          - false: This job is not a child of pPrefix
     233             :      */
     234             :     bool isChild(uint8_t const prefix[], uint16_t length) const;
     235             : 
     236             :     bool isFamily(uint8_t const prefix[], uint16_t length) const;
     237             : 
     238             :     /**
     239             :      * Constructor that creates a copy of an given AbstractDiagJob
     240             :      * \param   pJob
     241             :      *
     242             :      * This constructor is provided to give the possibility to create
     243             :      * wrapper classes of an AbstractDiagJob.
     244             :      *
     245             :      * \see AsyncDiagJob
     246             :      */
     247          36 :     explicit AbstractDiagJob(AbstractDiagJob const* const pJob)
     248          36 :     : fpImplementedRequest(pJob->fpImplementedRequest)
     249          36 :     , fpFirstChild(nullptr)
     250          36 :     , fpNextJob(nullptr)
     251          36 :     , fAllowedSessions(pJob->fAllowedSessions)
     252          36 :     , fResponseLength(pJob->fResponseLength)
     253          36 :     , fRequestLength(pJob->fRequestLength)
     254          36 :     , fPrefixLength(pJob->fPrefixLength)
     255          36 :     , fRequestPayloadLength(pJob->fRequestPayloadLength)
     256          36 :     , fDefaultDiagReturnCode(DiagReturnCode::ISO_GENERAL_REJECT)
     257          36 :     , fSuppressPositiveResponseBitEnabled(false)
     258          36 :     {}
     259             : 
     260             :     /**
     261             :      * Verifies a given request with respect to the responsibility of this job
     262             :      * \param   request Array containing the request to verify
     263             :      * \param   requestLength   Length of request
     264             :      * \return  Result of verification
     265             :      *          - NOT_RESPONSIBLE: This job is not responsible for handling the
     266             :      * request. The next job will be asked.
     267             :      *          - OK: This job wants to handle the request. The number of bytes
     268             :      * specified by fRequestLength will be added to the IncomingDiagConnection
     269             :      * and thus be part of a positive response. process() will be called afterwards.
     270             :      *          - Any other DiagReturnCode::Type will result in a negative response
     271             :      * being sent using the DiagReturnCode.
     272             :      *
     273             :      * \note
     274             :      * This method must be implemented by every inheriting class.
     275             :      *
     276             :      * \see     process()
     277             :      * \see     IncomingDiagConnection::addIdentifier()
     278             :      */
     279             :     virtual DiagReturnCode::Type verify(uint8_t const request[], uint16_t requestLength) = 0;
     280             : 
     281             :     /**
     282             :      * Processes a given request after it has been successfully verified.
     283             :      * \param   connection  IncomingDiagConnection from which the request has been
     284             :      * received.
     285             :      * \param   request Array containing the request to verify
     286             :      * \param   requestLength   Length of request
     287             :      * \return  Result of verification
     288             :      *          - OK: This job wants to handle the request and will send a
     289             :      * response later (either positive or negative).
     290             :      * and thus be part of a positive response. process() will be called afterwards.
     291             :      *          - Any other DiagReturnCode::Type will result in a negative response
     292             :      * being sent using the DiagReturnCode.
     293             :      *
     294             :      * \note
     295             :      * Any leaf node must overwrite this method. The default implementation
     296             :      * just traverses the diagnosis tree.
     297             :      *
     298             :      * \see     verify()
     299             :      * \see     IncomingDiagConnection
     300             :      */
     301             :     virtual DiagReturnCode::Type process(
     302             :         IncomingDiagConnection& connection, uint8_t const* const request, uint16_t requestLength);
     303             : 
     304             :     /**
     305             :      * Compares two byte arrays.
     306             :      * \param   data1   First array to compare
     307             :      * \param   data2   Second array to compare
     308             :      * \param   length  Number of bytes to compare
     309             :      * \return
     310             :      *          - true: Both arrays are equal
     311             :      *          - false: Arrays are not equal
     312             :      *
     313             :      * It is assumed that both data1 and data2 have at least length bytes.
     314             :      */
     315             :     static bool compare(uint8_t const data1[], uint8_t const data2[], uint16_t length);
     316             : 
     317             :     /**
     318             :      * \return  Pointer to next job
     319             :      *          - nullptr: No next AbstractDiagJob exists
     320             :      *          - else: Pointer to next AbstractDiagJob
     321             :      */
     322             :     AbstractDiagJob* getNextJob() const;
     323             :     /**
     324             :      * Get pointer to next job for a given abstract diag job
     325             :      * \param job Reference to job to get next pointer for
     326             :      * \return Pointer to next job
     327             :      *          - nullptr: No next AbstractDiagJob exists
     328             :      *          - else: pointer To next AbstractDiagJob
     329             :      */
     330             :     static AbstractDiagJob* getNextJob(AbstractDiagJob& job);
     331             : 
     332             :     /**
     333             :      * Get pointer to static reference of
     334             :      * the first child instance of root
     335             :      * \return Pointer to child job
     336             :      */
     337             :     static DiagJobRoot* getDiagJobRoot();
     338             : 
     339             :     /**
     340             :      * Sets pointer to next AbstractDiagJob
     341             :      * \param   pJob    Pointer to AbstractDiagJob that will be a sibling
     342             :      */
     343             :     void setNextJob(AbstractDiagJob* pJob);
     344             : 
     345             :     /**
     346             :      * \return  Active session
     347             :      */
     348             :     DiagSession const& getSession() const;
     349             : 
     350             :     DiagReturnCode::Type getDefaultDiagReturnCode() const;
     351             : 
     352             :     void setDefaultDiagReturnCode(DiagReturnCode::Type returnCode);
     353             : 
     354             :     DiagSession::DiagSessionMask const getAllowedSessions() const;
     355             : 
     356             :     uint8_t const* getImplementedRequest() const;
     357             : 
     358             :     uint8_t getRequestLength() const;
     359             : 
     360             :     virtual IDiagSessionManager& getDiagSessionManager();
     361             : 
     362             :     virtual IDiagSessionManager const& getDiagSessionManager() const;
     363             : 
     364             :     virtual IDiagAuthenticator const& getDiagAuthenticator() const;
     365             : 
     366             :     /**
     367             :      * Enables suppressPositiveResponse. Call this from constructor if
     368             :      * this job supports the suppressPositiveResponse bit.
     369             :      */
     370          82 :     inline void enableSuppressPositiveResponse(bool const set = true)
     371             :     {
     372          82 :         fSuppressPositiveResponseBitEnabled = set;
     373          82 :     }
     374             : 
     375           1 :     inline void setEnableSuppressPositiveResponse(AbstractDiagJob& job) const
     376             :     {
     377           1 :         job.fSuppressPositiveResponseBitEnabled = fSuppressPositiveResponseBitEnabled;
     378           1 :     }
     379             : 
     380             :     /**
     381             :      * Reset a variable holding an incoming connection and return the previous value.
     382             :      * \param connection Reference to variable storing pointer to connection
     383             :      * \return Pointer to previous connection
     384             :      */
     385             :     static IncomingDiagConnection* getAndResetConnection(IncomingDiagConnection*& connection);
     386             : 
     387             : private:
     388             :     friend class Service;
     389             :     friend class ServiceWithAuthentication;
     390             :     friend class DiagJobRoot;
     391             :     friend class ::http::html::UdsController;
     392             : 
     393             :     /** Mask with suppress positive response bit set */
     394             :     static uint8_t const SUPPRESS_POSITIVE_RESPONSE_MASK = static_cast<uint8_t>(0x80U);
     395             : 
     396             :     static IDiagSessionManager* sfpSessionManager;
     397             : 
     398             :     static DiagJobRoot* sfpDiagJobRoot;
     399             : 
     400             :     void acceptJob(
     401             :         IncomingDiagConnection& connection, uint8_t const request[], uint16_t const requestLength);
     402             :     /**
     403             :      * Does suppress positive response bit handling
     404             :      */
     405             :     void checkSuppressPositiveResponseBit(
     406             :         IncomingDiagConnection& connection, uint8_t const request[]) const;
     407             : 
     408             :     /** Array containing the request implemented by this job */
     409             :     uint8_t const* const fpImplementedRequest;
     410             :     /** Pointer to first child. A child has fpImplementedRequest as its prefix */
     411             :     AbstractDiagJob* fpFirstChild;
     412             :     /** Pointer to next job that has the same prefix */
     413             :     AbstractDiagJob* fpNextJob;
     414             :     /** Mask with bits set for session in which this job may be executed */
     415             :     DiagSession::DiagSessionMask const fAllowedSessions;
     416             :     /** Required length of response */
     417             :     uint8_t const fResponseLength;
     418             :     /** Length of fpImplementedRequest */
     419             :     uint8_t const fRequestLength;
     420             :     /** Number of bytes of fpImplementedRequest that need to be implemented by prior job in the tree
     421             :      */
     422             :     uint8_t const fPrefixLength;
     423             :     /** Length of the total request (fpImplementedRequest + parameters) */
     424             :     uint8_t const fRequestPayloadLength;
     425             :     /** Default DiagReturnCode::Type for a non leaf node */
     426             :     DiagReturnCode::Type fDefaultDiagReturnCode;
     427             :     /** Indication if positive response bit handling is enabled */
     428             :     bool fSuppressPositiveResponseBitEnabled;
     429             : };
     430             : 
     431             : /**
     432             :  * Compares two instances of AbstractDiagJob.
     433             :  * \param   x   First AbstractDiagJob to compare
     434             :  * \param   y   Second AbstractDiagJob to compare
     435             :  * \return
     436             :  *          - true: Jobs implement the same request and have the same prefix
     437             :  *          - false: Otherwise
     438             :  */
     439             : bool operator==(AbstractDiagJob const& x, AbstractDiagJob const& y);
     440             : 
     441        1219 : inline AbstractDiagJob* AbstractDiagJob::getNextJob() const { return fpNextJob; }
     442             : 
     443          15 : inline AbstractDiagJob* AbstractDiagJob::getNextJob(AbstractDiagJob& job) { return job.fpNextJob; }
     444             : 
     445           9 : inline DiagReturnCode::Type AbstractDiagJob::getDefaultDiagReturnCode() const
     446             : {
     447           9 :     return fDefaultDiagReturnCode;
     448             : }
     449             : 
     450         233 : inline void AbstractDiagJob::setDefaultDiagReturnCode(DiagReturnCode::Type const returnCode)
     451             : {
     452         233 :     fDefaultDiagReturnCode = returnCode;
     453         233 : }
     454             : 
     455           8 : inline DiagSession::DiagSessionMask const AbstractDiagJob::getAllowedSessions() const
     456             : {
     457           8 :     return fAllowedSessions;
     458             : }
     459             : 
     460          50 : inline uint8_t const* AbstractDiagJob::getImplementedRequest() const
     461             : {
     462          50 :     return fpImplementedRequest;
     463             : }
     464             : 
     465         161 : inline uint8_t AbstractDiagJob::getRequestLength() const { return fRequestLength; }
     466             : 
     467             : inline IncomingDiagConnection*
     468           3 : AbstractDiagJob::getAndResetConnection(IncomingDiagConnection*& connection)
     469             : {
     470           3 :     IncomingDiagConnection* const prevConnection = connection;
     471           3 :     connection                                   = nullptr;
     472           3 :     return prevConnection;
     473             : }
     474             : 
     475             : } // namespace uds

Generated by: LCOV version 1.14