|
| 1 | +/// @page page_custom_id_layer Defining Custom Message ID Protocol Stack Layer |
| 2 | +/// @tableofcontents |
| 3 | +/// The @b COMMS library provides default @ref comms::protocol::MsgIdLayer protocol |
| 4 | +/// stack layer to manage message ID information in the protocol framing. |
| 5 | +/// However, it may be insufficient (or incorrect) for some particular use cases, such as |
| 6 | +/// using @b bitfield field to store both numeric message ID and some extra flags |
| 7 | +/// (like <a href="http://mqtt.org">MQTT</a> protocol does). |
| 8 | +/// The @ref page_prot_stack_tutorial_new_layers section of the |
| 9 | +/// @ref page_prot_stack_tutorial page explains how to define new (custom) |
| 10 | +/// protocol layer. |
| 11 | +/// |
| 12 | +/// @b NOTE, that @ref comms::protocol::MsgIdLayer class |
| 13 | +/// contains significant amount of compile time logic of choosing the best |
| 14 | +/// code based on provided options as well as available polymorphic interface |
| 15 | +/// messages being read and/or written. It is very impractical to try to |
| 16 | +/// implement something similar from scratch or copy-paste the existing |
| 17 | +/// definition and introduce required changes. |
| 18 | +/// |
| 19 | +/// Since @b v1.2 COMMS library provides an ability to extend the existing |
| 20 | +/// definition of @ref comms::protocol::MsgIdLayer and customize some bits |
| 21 | +/// and pieces. Let's implement the mentioned example of sharing the same byte |
| 22 | +/// for numeric ID and some flags. |
| 23 | +/// |
| 24 | +/// First of all let's define the @ref page_define_prot_interface, which |
| 25 | +/// holds the @b flags information as data member of every message object. |
| 26 | +/// @code |
| 27 | +/// namespace my_prot |
| 28 | +/// { |
| 29 | +/// |
| 30 | +/// // Enum used for numeric message IDs |
| 31 | +/// enum MsgId |
| 32 | +/// { |
| 33 | +/// MsgId_Message1, |
| 34 | +/// MsgId_Message2, |
| 35 | +/// ... |
| 36 | +/// }; |
| 37 | +/// |
| 38 | +/// // Base class for all the fields defining serialization endian |
| 39 | +/// using FieldBase = comms::field::Field<comms::option::BigEndian>; |
| 40 | +/// |
| 41 | +/// // Definition of the message flags |
| 42 | +/// class MessageFlags : public |
| 43 | +/// comms::field::BitmaskValue< |
| 44 | +/// FieldBase, |
| 45 | +/// comms::option::BitmaskReservedBits<0xf0> |
| 46 | +/// > |
| 47 | +/// { |
| 48 | +/// public: |
| 49 | +/// // Provides names and generates access functions for internal bits. |
| 50 | +/// COMMS_BITMASK_BITS_SEQ(bit0, bit1, bit2, bit3); |
| 51 | +/// }; |
| 52 | +/// |
| 53 | +/// // Definition of the extensible common message interface |
| 54 | +/// template <typename... TOptions> |
| 55 | +/// class Message : public |
| 56 | +/// comms::Message< |
| 57 | +/// TOptions..., |
| 58 | +/// comms::option::BigEndian, |
| 59 | +/// comms::option::MsgIdType<MsgId>, |
| 60 | +/// comms::option::ExtraTransportFields<std::tuple<MessageFlags> > |
| 61 | +/// > |
| 62 | +/// { |
| 63 | +/// public: |
| 64 | +/// // Allow access to extra transport fields. |
| 65 | +/// COMMS_MSG_TRANSPORT_FIELDS_ACCESS(flags); |
| 66 | +/// }; |
| 67 | +/// |
| 68 | +/// } // namespace my_prot |
| 69 | +/// @endcode |
| 70 | +/// Just to refresh the reader's memory: the usage of @ref COMMS_MSG_TRANSPORT_FIELDS_ACCESS() |
| 71 | +/// macro for the interface definition will generate @b transportField_flags() |
| 72 | +/// convenience member function to access the stored @b flags field, while |
| 73 | +/// usage of @ref COMMS_BITMASK_BITS_SEQ() in the flags field definition will |
| 74 | +/// genereate @b getBitValue_X() and @b setBitValue_X() convenience member |
| 75 | +/// functions to get / set values of the bits (where X is one of the defined |
| 76 | +/// names: @b bit0, @b bit1, @b bit2, and @b bit3). |
| 77 | +/// |
| 78 | +/// Now, let's define the @b bitfield field, that splits one byte in half to |
| 79 | +/// store numeric message ID (in lower 4 bits) as well as extra flags (in upper 4 bits) |
| 80 | +/// @code |
| 81 | +/// namespace my_prot |
| 82 | +/// { |
| 83 | +/// |
| 84 | +/// class IdAndFlagsField : public |
| 85 | +/// comms::field::Bitfield< |
| 86 | +/// FieldBase, |
| 87 | +/// std::tuple< |
| 88 | +/// comms::field::EnumValue<FieldBase, MsgId, comms::option::FixedBitLength<4> >, |
| 89 | +/// comms::field::IntValue<FieldBase, std::uint8_t, comms::option::FixedBitLength<4> > |
| 90 | +/// > |
| 91 | +/// > |
| 92 | +/// { |
| 93 | +/// public: |
| 94 | +/// // Allow access to internal member fields. |
| 95 | +/// COMMS_FIELD_MEMBERS_ACCESS(id, flags); |
| 96 | +/// }; |
| 97 | +/// |
| 98 | +/// } // my_prot |
| 99 | +/// @endcode |
| 100 | +/// Again, just to refresh the reader's memory: the usage of @ref COMMS_FIELD_MEMBERS_ACCESS() |
| 101 | +/// macro for the bitfield definition will generate @b field_X() |
| 102 | +/// convenience access member functions for the listed names. |
| 103 | +/// |
| 104 | +/// Now it's time to actually extend the provided definition of |
| 105 | +/// the @ref comms::protocol::MsgIdLayer and support usage of the defined |
| 106 | +/// earlier @b IdAndFlagsField field. |
| 107 | +/// @code |
| 108 | +/// namespace my_prot |
| 109 | +/// { |
| 110 | +/// |
| 111 | +/// template <typename TMessage, typename TAllMessages, typename TNextLayer, typename... TOptions> |
| 112 | +/// class MsgIdAndFlagsLayer : public |
| 113 | +/// comms::protocol::MsgIdLayer< |
| 114 | +/// IdAndFlagsField, // Used field that contains message ID |
| 115 | +/// TMessage, // Common interface class |
| 116 | +/// TAllMessages, // std::tuple of all the supported input messages |
| 117 | +/// TNextLayer, // Next layer in the protocol stack |
| 118 | +/// TOptions..., // Application specific extension options |
| 119 | +/// comms::option::ExtendingClass<MsgIdAndFlagsLayer<TMessage, TAllMessages, TNextLayer, TOptions...> > |
| 120 | +/// // Make the comms::protocol::MsgIdLayer aware of it being extended |
| 121 | +/// > |
| 122 | +/// { |
| 123 | +/// // Repeat definition of the base class |
| 124 | +/// using Base = comms::protocol::MsgIdLayer<...>; |
| 125 | +/// |
| 126 | +/// public: |
| 127 | +/// // Repeat types defined in the base class (not visible by default) |
| 128 | +/// using MsgIdType = typename Base::MsgIdType; |
| 129 | +/// using MsgIdParamType = typename Base::MsgIdParamType; |
| 130 | +/// using Field = typename Base::Field; // same as IdAndFlagsField |
| 131 | +/// |
| 132 | +/// // Retrieve message ID value from the given IdAndFlagsField field |
| 133 | +/// static MsgIdType getMsgIdFromField(const Field& field) |
| 134 | +/// { |
| 135 | +/// return field.field_id().value(); |
| 136 | +/// } |
| 137 | +/// |
| 138 | +/// // Set flags value for the message object before proceeding to the next layer read |
| 139 | +/// // The message object is passed by reference |
| 140 | +/// template <typename TMsg> |
| 141 | +/// static void beforeRead(const Field& field, TMsg& msg) |
| 142 | +/// { |
| 143 | +/// msg.transportField_flags().value() = field.field_flags().value(); |
| 144 | +/// } |
| 145 | +/// |
| 146 | +/// // Assemble the field's value before its write given message ID as well |
| 147 | +/// // as message object itself. |
| 148 | +/// template <typename TMsg> |
| 149 | +/// static void prepareFieldForWrite(MsgIdParamType id, const TMsg& msg, Field& field) |
| 150 | +/// { |
| 151 | +/// field.field_id().value() = id; |
| 152 | +/// field.field_flags().value() = msg.transportField_flags().value(); |
| 153 | +/// } |
| 154 | +/// }; |
| 155 | +/// |
| 156 | +/// } // namespace my_prot |
| 157 | +/// @endcode |
| 158 | +/// The @ref comms::protocol::MsgIdLayer doesn't have any virtual functions and |
| 159 | +/// as the result not able to provide any polymorphic behavior. In order to be |
| 160 | +/// able to extend its default functionality there is a need to use |
| 161 | +/// <a href="https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern">Curiously Recurring Template Pattern</a>. |
| 162 | +/// It is done by passing @ref comms::option::ExtendingClass extension option with |
| 163 | +/// the type of the layer class being defined to the @ref comms::protocol::MsgIdLayer. |
| 164 | +/// |
| 165 | +/// The extending class is expected to define the listed below functions. They do not |
| 166 | +/// necessarily need to be @b static, accessing inner private state of the layer object |
| 167 | +/// is also acceptable. |
| 168 | +/// @li @b getMsgIdFromField() - Member function that is invoked to retrieve the |
| 169 | +/// numeric message ID out of the provided field object. |
| 170 | +/// @li @b beforeRead() - Member function that is invoked after appropriate |
| 171 | +/// message object has been created but the read operation has @b NOT yet been |
| 172 | +/// forwarded to the next protocol layer. It gives the developer a chance to |
| 173 | +/// update some extra transport fields accessible via message interface class. |
| 174 | +/// @li @b prepareFieldForWrite() - Member function that is invoked to prepare |
| 175 | +/// the field value before its write (serialization). After the function returns, |
| 176 | +/// the @ref comms::protocol::MsgIdLayer will invoke @b write member function |
| 177 | +/// of the passed field in order to serialize it. |
| 178 | +/// |
| 179 | +/// The newly defined custom protocol stack layer can be used instead of |
| 180 | +/// @ref comms::protocol::MsgIdLayer when defining |
| 181 | +/// @ref page_prot_stack_tutorial "protocol stack" (framing) of the protocol. |
| 182 | +/// |
0 commit comments