The COMMS library provides default comms::protocol::ChecksumLayer and comms::protocol::ChecksumPrefixLayer protocol stack layer to handle transport framing checksum. However, they may be insufficient (or incorrect) for some particular use cases. For example the protocol may support multiple checksum algorithms usage of which is determined at runtime. The Implementing New Layers section of the Protocol Stack Definition Tutorial page explains how to define new (custom) protocol layer.
However, since v3.2 COMMS library provides an ability to extend the existing definition of comms::protocol::ChecksumLayer as well as comms::protocol::ChecksumPrefixLayer and customize some bits and pieces. Let's implement the mentioned above example of supporting multiple checksum algorithms.
This tutorial page focuses on the customization of comms::protocol::ChecksumLayer, but all the listed customization points are also applicable to comms::protocol::ChecksumPrefixLayer.
For this example the protocol framing is defined to be
SIZE | ID | CHECKSUM_TYPE | PAYLOAD | CHECKSUM
First of all let's define the Common Interface Class, which holds the checksum type information as data member of every message object.
namespace my_prot
{
enum MsgId
{
MsgId_Message1,
MsgId_Message2,
...
};
using FieldBase = comms::field::Field<comms::option::def::BigEndian>;
Definition of the checksum type
enum ChecksumTypeVal : std::uint8_t
{
Sum8,
Crc16,
Crc32
};
class ChecksumType : public
FieldBase,
ChecksumTypeVal
>
{
};
template <typename... TOptions>
class Message : public
TOptions...,
comms::option::def::BigEndian,
comms::option::def::MsgIdType<MsgId>,
comms::option::def::ExtraTransportFields<
std::tuple<
ChecksumType
>
>
>
{
public:
COMMS_MSG_TRANSPORT_FIELDS_NAMES(checksumType);
};
}
Main interface class for all the messages.
Definition Message.h:80
Enumerator value field.
Definition EnumValue.h:73
Just to refresh the reader's memory: the usage of COMMS_MSG_TRANSPORT_FIELDS_NAMES() macro for the interface definition will generate transportField_checksumType() convenience member function to access the stored checksum type field.
When implementing the transport framing (protocol stack) the handling of CHECKSUM_TYPE
can be done using regular comms::protocol::TransportValueLayer (see Extra Transport Values).
Now it's time to actually extend the provided definition of the comms::protocol::ChecksumLayer and support usage of multiple checksum algorithms.
namespace my_prot
{
template <typename TNextLayer>
class MyChecksumLayer : public
ChecksumField,
comms::protocol::checksum::Crc_32,
TNextLayer,
comms::option::def::ExtendingClass<MyChecksumLayer<TNextLayer> >
>
{
public:
using Field = typename Base::Field;
template <typename TMsg, typename TIter>
static typename Field::ValueType calculateChecksum(const TMsg* msgPtr, TIter& iter, std::size_t len, bool& checksumValid)
{
if (msgPtr == nullptr) {
checksumValid = false;
return static_cast<typename Field::ValueType>(0);
}
checksumValid = true;
auto checksumType = msgPtr->transportField_checksumType().value();
if (checksumType == ChecksumTypeVal::Sum8) {
return Calc()(iter, len);
}
if (checksumType == ChecksumType::Crc16) {
return Calc()(iter, len);
}
if (checksumType == ChecksumType::Crc32) {
return Calc()(iter, len);
}
checksumValid = false;
return static_cast<typename Field::ValueType>(0);
}
template <typename TMsg, typename TIter>
static comms::ErrorStatus doReadField(
const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
{
if (msgPtr == nullptr) {
}
auto checksumType = msgPtr->transportField_checksumType().value();
if (checksumType == ChecksumType::Sum8) {
return readFieldInternal<std::uint8_t>(field, iter, len);
}
if (checksumType == ChecksumType::Crc16) {
return readFieldInternal<std::uint16_t>(field, iter, len);
}
if (checksumType == ChecksumType::Crc32) {
return readFieldInternal<std::uint32_t>(field, iter, len);
}
}
template <typename TMsg, typename TIter>
static comms::ErrorStatus doWriteField(
const TMsg* msgPtr,
const Field& field, TIter& iter, std::size_t len)
{
if (msgPtr == nullptr) {
}
auto checksumType = msgPtr->transportField_checksumType().value();
if (checksumType == ChecksumType::Sum8) {
return writeFieldInternal<std::uint8_t>(field, iter, len);
}
if (checksumType == ChecksumType::Crc16) {
return writeFieldInternal<std::uint16_t>(field, iter, len);
}
if (checksumType == ChecksumType::Crc32) {
return writeFieldInternal<std::uint32_t>(field, iter, len);
}
}
template <typename TMsg>
static std::size_t doFieldLength(const TMsg& msg)
{
auto checksumType = msg.transportField_checksumType().value();
if (checksumType == ChecksumType::Sum8) {
return sizeof(std::uint8_t);
}
if (checksumType == ChecksumType::Crc16) {
return sizeof(std::uint16_t);
}
if (checksumType == ChecksumType::Crc32) {
return sizeof(std::uint32_t);
}
return 0;
}
private:
template <typename TTmpType, typename TIter>
{
FieldTmp fieldTmp;
auto es = fieldTmp.
read(iter, len);
return es;
}
field = comms::field_cast<Field>(fieldTmp);
return es;
}
template <typename TTmpType, typename TIter>
static comms::ErrorStatus writeFieldInternal(
const Field& field, TIter& iter, std::size_t len)
{
auto fieldTmp = comms::field_cast<FieldTmp>(field);
return fieldTmp.write(iter, len);
}
};
}
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
Field that represent integral value.
Definition IntValue.h:72
ErrorStatus read(TIter &iter, std::size_t size)
Read field value from input data sequence.
Definition IntValue.h:305
Protocol layer that is responsible to calculate checksum on the data written by all the wrapped inter...
Definition ChecksumLayer.h:73
Summary of all bytes checksum calculator.
Definition BasicSum.h:32
Crc< std::uint32_t, 0x04c11db7, 0xffffffff, 0xffffffff, true, true > Crc_32
Alias to Crc checksum calculator for standard CRC-32.
Definition Crc.h:399
Crc< std::uint16_t, 0x8005, 0, 0, true, true > Crc_16
Alias to Crc checksum calculator for standard CRC-16.
Definition Crc.h:389
ErrorStatus
Error statuses reported by the Communication module.
Definition ErrorStatus.h:17
@ Success
Used to indicate successful outcome of the operation.
The comms::protocol::ChecksumLayer doesn't have any virtual functions and as the result not able to provide any polymorphic behavior. In order to be able to extend its default functionality there is a need to use Curiously Recurring Template Pattern. It is done by passing comms::option::def::ExtendingClass extension option with the type of the layer class being defined to the comms::protocol::ChecksumLayer.
The extending class can customize the default behavior by overriding the listed below functions. They do not necessarily need to be static, accessing inner private state of the layer object is also acceptable.
The newly defined custom protocol stack layer can be used instead of comms::protocol::ChecksumLayer when defining protocol stack (framing) of the protocol. For example:
namespace my_prot
{
template <typename TMessage, typename TAllMessages>
struct Frame1 : public
MyChecksumLayer<
comms::protocol::MsgSizeLayer<
common::field::IntValue<FieldBase, std::uint16_t>,
comms::protocol::MsgIdLayer<
comms::feild::EnumValue<FieldBase, MsgId>,
TMessage,
TAllMessages,
comms::field::TransportValueLayer<
ChecksumType,
Message::TransportFieldIdx_checksumType,
comms::protocol::MsgDataLayer<>
>
>
>
>
{
COMMS_PROTOCOL_LAYERS_ACCESS_OUTER(checksum, size, size, checksumType, payload);
};
}