LCOV - code coverage report
Current view: top level - docan/include/docan/receiver - DoCanMessageReceiveProtocolHandler.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 94 94 100.0 %
Date: 2025-10-07 10:52:16 Functions: 23 23 100.0 %

          Line data    Source code
       1             : // Copyright 2024 Accenture.
       2             : 
       3             : #pragma once
       4             : 
       5             : #include <platform/estdint.h>
       6             : 
       7             : namespace docan
       8             : {
       9             : /**
      10             :  * Enumeration defining state of the message.
      11             :  */
      12             : enum class ReceiveState : uint8_t
      13             : {
      14             :     /// A transport message should be allocated.
      15             :     ALLOCATE,
      16             :     /// Waiting for receiving a frame or next try for message allocation.
      17             :     WAIT,
      18             :     /// A flow control frame should be sent.
      19             :     SEND,
      20             :     /// The received message should be processed.
      21             :     PROCESSING,
      22             :     /// The reception of the message has ended.
      23             :     DONE
      24             : };
      25             : 
      26             : /**
      27             :  * Enumeration defining timeouts to be set.
      28             :  */
      29             : enum class ReceiveTimeout : uint8_t
      30             : {
      31             :     /// No timeout is needed.
      32             :     NONE,
      33             :     /// Timeout for reception of a frame is needed.
      34             :     RX,
      35             :     /// Timeout until next allocation of message is needed.
      36             :     ALLOCATE
      37             : };
      38             : 
      39             : /**
      40             :  * Message state emitted by the receiver.
      41             :  */
      42             : enum class ReceiveMessage : uint8_t
      43             : {
      44             :     /// No message.
      45             :     NONE,
      46             :     /// An event has occurred in an unexpected state.
      47             :     ILLEGAL_STATE,
      48             :     /// The maximum number of allocation retries has exceeded.
      49             :     ALLOCATION_RETRY_COUNT_EXCEEDED,
      50             :     /// The timeout for frame reception has expired.
      51             :     RX_TIMEOUT_EXPIRED,
      52             :     /// A frame with an unexpected sequence number has been received.
      53             :     BAD_SEQUENCE_NUMBER,
      54             :     /// The processing of the message was not successful.
      55             :     PROCESSING_FAILED
      56             : };
      57             : 
      58             : /**
      59             :  * Helper class that holds the result of event handling. Most important
      60             :  * is the hasTransition() method that indicates whether a state transition
      61             :  * to a new state has been performed.
      62             :  */
      63             : class ReceiveResult
      64             : {
      65             : public:
      66             :     /**
      67             :      * constructor. No message and no parameter is set.
      68             :      * \param transition flag whether a transition has been performed
      69             :      */
      70             :     explicit ReceiveResult(bool transition);
      71             : 
      72             :     /**
      73             :      * Set a message without parameter.
      74             :      * \param message message to emit
      75             :      * \return reference to this object
      76             :      */
      77             :     ReceiveResult& setMessage(ReceiveMessage message);
      78             : 
      79             :     /**
      80             :      * Set a message with parameter.
      81             :      * \param message message to emit
      82             :      * \param param additional parameter value
      83             :      * \return reference to this object
      84             :      */
      85             :     ReceiveResult& setMessage(ReceiveMessage message, uint8_t param);
      86             : 
      87             :     /**
      88             :      * Test whether a transition has occurred. A state transition indicates that
      89             :      * timeouts should be reset.
      90             :      * \return true if a transition has occurred.
      91             :      */
      92        5597 :     bool hasTransition() const { return _transition; }
      93             : 
      94             :     /**
      95             :      * Get the message that is connected with the result.
      96             :      * \return message value
      97             :      */
      98        1899 :     ReceiveMessage getMessage() const { return _message; }
      99             : 
     100             :     /**
     101             :      * Get the parameter to a message.
     102             :      * \return parameter value
     103             :      */
     104           3 :     uint8_t getParam() const { return _param; }
     105             : 
     106             :     /**
     107             :      * Check result for equality. Results are equal if all fields are identical.
     108             :      * \param other result to compare with
     109             :      * \return true if result is equal to other
     110             :      */
     111             :     bool operator==(ReceiveResult const& other) const;
     112             : 
     113             : private:
     114             :     bool _transition;
     115             :     ReceiveMessage _message;
     116             :     uint8_t _param;
     117             : };
     118             : 
     119             : /**
     120             :  * Class encapsulating the protocol handling for reception of a single message.
     121             :  */
     122             : template<typename FrameIndexType>
     123             : class DoCanMessageReceiveProtocolHandler
     124             : {
     125             : public:
     126             :     /**
     127             :      * Constructor.
     128             :      * \param frameCount number of expected frames
     129             :      */
     130             :     explicit DoCanMessageReceiveProtocolHandler(FrameIndexType frameCount);
     131             : 
     132             :     /**
     133             :      * Get the current state of the message.
     134             :      * \return state
     135             :      */
     136        5700 :     ReceiveState getState() const { return _state; }
     137             : 
     138             :     /**
     139             :      * Get the timeout to be set for this state.
     140             :      * \return timeout
     141             :      */
     142        1961 :     ReceiveTimeout getTimeout() const { return _timeout; }
     143             : 
     144             :     /**
     145             :      * Get the index of the next expected frame.
     146             :      * \return frame index
     147             :      */
     148          12 :     FrameIndexType getFrameIndex() const { return _frameIndex; }
     149             : 
     150             :     /**
     151             :      * Get the number of expected frames for the message.
     152             :      * \return frame count
     153             :      */
     154          18 :     FrameIndexType getFrameCount() const { return _frameCount; }
     155             : 
     156             :     /**
     157             :      * Test whether the a wait flow control frame should be sent.
     158             :      * This flag is only valid if state is STATE_SEND.
     159             :      * \return true if the next flow control frame should be a wait frame
     160             :      */
     161          89 :     bool isFlowControlWait() const { return (_allocateRetryCount > 0U) && (_frameCount > 1U); }
     162             : 
     163             :     /**
     164             :      * Test whether message allocation is required. This "allocation" state
     165             :      * can be present in more than one state.
     166             :      * \return true if allocation of a message is required
     167             :      */
     168         131 :     bool isAllocating() const { return _isAllocating; }
     169             : 
     170             :     /**
     171             :      * Cancel the reception process and set to state done.
     172             :      * \param message message to emit
     173             :      * \return result indicating state transition (and given message)
     174             :      */
     175          13 :     ReceiveResult cancel(ReceiveMessage const message = ReceiveMessage::NONE)
     176             :     {
     177          13 :         return setDone(message);
     178             :     }
     179             : 
     180             :     /**
     181             :      * Handles a shutdown request during message reception.
     182             :      * \return result indicating state transition
     183             :      */
     184           7 :     ReceiveResult shutdown()
     185             :     {
     186           7 :         return (_state == ReceiveState::PROCESSING) ? ReceiveResult(false) : cancel();
     187             :     }
     188             : 
     189             :     /**
     190             :      * Called to indicate the result of a message allocation try.
     191             :      * \param success true if a message has been allocated successfully
     192             :      * \param maxRetryCount maximum number of retries
     193             :      * \return result indicating state transition
     194             :      */
     195             :     ReceiveResult allocated(bool success, uint8_t maxRetryCount);
     196             : 
     197             :     /**
     198             :      * Called to indicate that the CAN frame has been sent.
     199             :      * \param success true if the CAN frame has been sent successfully
     200             :      * \return result indicating state transition
     201             :      */
     202             :     ReceiveResult frameSent(bool success);
     203             : 
     204             :     /**
     205             :      * Called to indicate that a consecutive frame has been received.
     206             :      * \param frameIndex sequence number of the received frame
     207             :      * \param maxBlockSize maximum block size for message reception
     208             :      * \return result indicating state transition
     209             :      */
     210             :     ReceiveResult consecutiveFrameReceived(uint8_t frameIndex, uint8_t maxBlockSize);
     211             : 
     212             :     /**
     213             :      * Called to indicate the result of message processing.
     214             :      * \param success true if message processing was successful
     215             :      * \return result indicating state transition
     216             :      */
     217          31 :     ReceiveResult processed(bool const success)
     218             :     {
     219          31 :         return setDone(success ? ReceiveMessage::NONE : ReceiveMessage::PROCESSING_FAILED);
     220             :     }
     221             : 
     222             :     /**
     223             :      * Called to indicate that the current timeout has expired
     224             :      * \return result indicating state transition
     225             :      */
     226          23 :     ReceiveResult expired()
     227             :     {
     228          23 :         switch (_timeout)
     229             :         {
     230           9 :             case ReceiveTimeout::RX:
     231             :             {
     232           9 :                 return setDone(ReceiveMessage::RX_TIMEOUT_EXPIRED);
     233             :             }
     234          13 :             case ReceiveTimeout::ALLOCATE:
     235             :             {
     236          13 :                 return setState(ReceiveState::ALLOCATE);
     237             :             }
     238           1 :             default:
     239             :             {
     240           1 :                 return ReceiveResult(false);
     241             :             }
     242             :         }
     243             :     }
     244             : 
     245             : private:
     246             :     inline ReceiveResult
     247             :     setState(ReceiveState state, ReceiveTimeout timeout = ReceiveTimeout::NONE);
     248             :     inline ReceiveResult setDone(ReceiveMessage message = ReceiveMessage::NONE, uint8_t param = 0U);
     249             : 
     250             :     FrameIndexType _frameIndex;
     251             :     FrameIndexType const _frameCount;
     252             :     ReceiveState _state;
     253             :     ReceiveTimeout _timeout;
     254             :     uint8_t _blockFrameIndex;
     255             :     uint8_t _allocateRetryCount;
     256             :     bool _isAllocating;
     257             : };
     258             : 
     259             : /**
     260             :  * Inline implementation.
     261             :  */
     262             : 
     263             : template<typename FrameIndexType>
     264          76 : inline DoCanMessageReceiveProtocolHandler<FrameIndexType>::DoCanMessageReceiveProtocolHandler(
     265             :     FrameIndexType const frameCount)
     266          76 : : _frameIndex(1U)
     267          76 : , _frameCount(frameCount)
     268          76 : , _state(ReceiveState::ALLOCATE)
     269          76 : , _timeout(ReceiveTimeout::NONE)
     270          76 : , _blockFrameIndex(0U)
     271          76 : , _allocateRetryCount(0U)
     272          76 : , _isAllocating(true)
     273          76 : {}
     274             : 
     275             : template<typename FrameIndexType>
     276          84 : inline ReceiveResult DoCanMessageReceiveProtocolHandler<FrameIndexType>::allocated(
     277             :     bool const success, uint8_t const maxRetryCount)
     278             : {
     279          84 :     if (!_isAllocating)
     280             :     {
     281           1 :         return ReceiveResult(true).setMessage(
     282           1 :             ReceiveMessage::ILLEGAL_STATE, static_cast<uint8_t>(_state));
     283             :     }
     284             : 
     285          83 :     if (success)
     286             :     {
     287          56 :         _isAllocating       = false;
     288          56 :         _allocateRetryCount = 0U;
     289          56 :         return setState((_frameCount == 1U) ? ReceiveState::PROCESSING : ReceiveState::SEND);
     290             :     }
     291             : 
     292          27 :     if (_frameCount == 1U)
     293             :     {
     294          13 :         ++_allocateRetryCount;
     295          13 :         if (_allocateRetryCount > 1U)
     296             :         {
     297           2 :             return setDone(ReceiveMessage::ALLOCATION_RETRY_COUNT_EXCEEDED);
     298             :         }
     299             : 
     300          11 :         return setState(ReceiveState::WAIT, ReceiveTimeout::ALLOCATE);
     301             :     }
     302             : 
     303          14 :     ++_allocateRetryCount;
     304          14 :     if (_allocateRetryCount > maxRetryCount)
     305             :     {
     306           2 :         return setDone(ReceiveMessage::ALLOCATION_RETRY_COUNT_EXCEEDED);
     307             :     }
     308             : 
     309          12 :     return setState(ReceiveState::SEND);
     310             : }
     311             : 
     312             : template<typename FrameIndexType>
     313             : inline ReceiveResult
     314          50 : DoCanMessageReceiveProtocolHandler<FrameIndexType>::frameSent(bool const success)
     315             : {
     316          50 :     if (_state != ReceiveState::SEND)
     317             :     {
     318           1 :         return ReceiveResult(true).setMessage(
     319           1 :             ReceiveMessage::ILLEGAL_STATE, static_cast<uint8_t>(_state));
     320             :     }
     321          49 :     if (success)
     322             :     {
     323          46 :         return setState(
     324             :             ReceiveState::WAIT,
     325          92 :             isFlowControlWait() ? ReceiveTimeout::ALLOCATE : ReceiveTimeout::RX);
     326             :     }
     327           3 :     return ReceiveResult(false);
     328             : }
     329             : 
     330             : template<typename FrameIndexType>
     331        1768 : inline ReceiveResult DoCanMessageReceiveProtocolHandler<FrameIndexType>::consecutiveFrameReceived(
     332             :     uint8_t const frameIndex, uint8_t const maxBlockSize)
     333             : {
     334        1768 :     if ((_state != ReceiveState::WAIT) || (_timeout != ReceiveTimeout::RX))
     335             :     {
     336           2 :         return ReceiveResult(false);
     337             :     }
     338             : 
     339        1766 :     if ((_frameIndex & 0xFU) != frameIndex)
     340             :     {
     341           3 :         return setDone(ReceiveMessage::BAD_SEQUENCE_NUMBER, frameIndex);
     342             :     }
     343             : 
     344        1763 :     ++_frameIndex;
     345        1763 :     if (_frameIndex == _frameCount)
     346             :     {
     347          18 :         return setState(ReceiveState::PROCESSING);
     348             :     }
     349        1745 :     if (maxBlockSize > 0U)
     350             :     {
     351           6 :         ++_blockFrameIndex;
     352           6 :         if (_blockFrameIndex == maxBlockSize)
     353             :         {
     354           3 :             _blockFrameIndex = 0U;
     355           3 :             return setState(ReceiveState::SEND);
     356             :         }
     357             :     }
     358             :     // set state to wait for timeout as in the default case
     359        1742 :     return setState(ReceiveState::WAIT, ReceiveTimeout::RX);
     360             : }
     361             : 
     362             : template<typename FrameIndexType>
     363        1901 : inline ReceiveResult DoCanMessageReceiveProtocolHandler<FrameIndexType>::setState(
     364             :     ReceiveState const state, ReceiveTimeout const timeout)
     365             : {
     366        1901 :     _state   = state;
     367        1901 :     _timeout = timeout;
     368        1901 :     return ReceiveResult(true);
     369             : }
     370             : 
     371             : template<typename FrameIndexType>
     372          60 : inline ReceiveResult DoCanMessageReceiveProtocolHandler<FrameIndexType>::setDone(
     373             :     ReceiveMessage const message, uint8_t const param)
     374             : {
     375          60 :     _state        = ReceiveState::DONE;
     376          60 :     _timeout      = ReceiveTimeout::NONE;
     377          60 :     _isAllocating = false;
     378          60 :     return ReceiveResult(true).setMessage(message, param);
     379             : }
     380             : 
     381        3883 : inline ReceiveResult::ReceiveResult(bool const transition)
     382        3883 : : _transition(transition), _message(ReceiveMessage::NONE), _param()
     383        3883 : {}
     384             : 
     385           7 : inline ReceiveResult& ReceiveResult::setMessage(ReceiveMessage const message)
     386             : {
     387           7 :     _message = message;
     388           7 :     _param   = 0U;
     389           7 :     return *this;
     390             : }
     391             : 
     392          75 : inline ReceiveResult& ReceiveResult::setMessage(ReceiveMessage const message, uint8_t const param)
     393             : {
     394          75 :     _message = message;
     395          75 :     _param   = param;
     396          75 :     return *this;
     397             : }
     398             : 
     399          86 : inline bool ReceiveResult::operator==(ReceiveResult const& other) const
     400             : {
     401          85 :     return (_transition == other._transition) && (_message == other._message)
     402         171 :            && (_param == other._param);
     403             : }
     404             : 
     405             : } // namespace docan

Generated by: LCOV version 1.14