commschamp / cc.ublox.commsdsl

CommsDSL schema-based definition of UBLOX protocol
https://commschamp.github.io
43 stars 10 forks source link

VALSET example #11

Closed andreamancuso closed 2 months ago

andreamancuso commented 2 months ago

Hi @arobenko

Are there any examples on how to construct a VALSET programmatically?

This is what I came up with so far

using OutCfgValset = cc_ublox::message::CfgValset<OutMessage>;

OutCfgValset msg;
auto& layers = msg.field_layers();
auto& cfgData = msg.field_cfgdata();

cc_ublox::message::CfgValsetFields<>::Cfgdata a;

cc_ublox::field::CfgValPairSimple b;

b.field_key().setValue(0xa2);
b.field_val().setValue(1);

cfgData.setValue(b);

layers.setBitValue_ram(true);

sendMessage(msg);

The compiler doesn't quite like that.

Thank you in advance!

andreamancuso commented 2 months ago

Meanwhile I sent hand-crafted VALSETs and managed to enable NAV-POSLLH and NAV-PVT on an M10 EVK. I can parse both without any issues!

arobenko commented 2 months ago

Short Answer

The tutorial4 explains how to access the <variant>'s fields. To assign the cfgdata field as needed should probably look like this:

OutCfgValset msg;
auto& cfgData = msg.field_cfgdata();

using KeyType = OutCfgValset::Field_cfgdata::Field_key::ValueType;
cfgData.field_key().setValue(KeyType::CFG_MSGOUT_UBX_NAV_POSLLH_UART1);
cfgData.field_val().initField_cfgValL().setValue(1); // select (initialize) proper variant member and then set the value

Longer Answer

In the spirit of the commsdsl the definition of the CfgValset message should have referenced CfgValPair instead of CfgValPairSimple. However, such definition was too heavy for some compilers to handle, especially with development computers with less than 16GB of memory. Another less boilerplate (not enforcing the length of the val field) code, although much heavier on compiler and probably runtime could look like this (not really tested, might not even compile):

#include "comms/util/cast.h"
...
cc_ublox::field::CfgValPair<> pairField;
auto& posllhMem = pairField.initField_msgoutUbxNavPosllhUart1();
posllhMem.field_val().setValue(1);

OutCfgValset msg;
msg.field_cfgdata() = comms::field_cast<OutCfgValset::Field_cfgdata>(pairField);

The cast function is documented here and explained in the tutorial21.

In General

To resolve a burden of using a very small subset of the protocol functionality, especially having a long list of <variant> members, (somehow) allowing selection of the custom list of the variant members during the code generation is on my future TODO list. If I come around to implement it, then maybe the definition of the protocol may change to include CfgValPair field instead of CfgValPairSimple.

andreamancuso commented 2 months ago

Thank you for your comprehensive answer. 👍

andreamancuso commented 2 months ago

This is likely due to me being thick - here's how I got it working

void Session::sendValSetWithSingleKeyValuePair(CfgValKeyId valKeyId, long valValue) {
    using CfgdataElement = cc_ublox::message::CfgValsetFields<>::CfgdataMembers::Element;

    OutCfgValset msg;
    auto& layers = msg.field_layers();
    auto& cfgData = msg.field_cfgdata().value();

    layers.setBitValue_ram(true);

    CfgdataElement valsetElement;
    valsetElement.field_key().setValue(valKeyId);
    valsetElement.field_val().initField_l().setValue(valValue);

    cfgData.push_back(valsetElement);

    sendMessage(msg);
};

I am very happy with the outcome and am looking forward to start adding more functionality.

arobenko commented 2 months ago

Looks good, however it is important to remember that "L" field is only 1 byte long. you should change the type of the "valValue" parameter to be something like std::uint8_t or even bool. In case you'll need longer values you'll have do overload the function where you'll initialize and use other variant members, like ....initField_u8()... for example.