Deserializing non-polimorphic structure from network packet - c++

I'm developing network protocol. I have a lot of non-polimorphic structures. I need to pass input structure to packet with its id(unique number of structure) and serialize it to byte array to send.
And deserialize it the same: receive byte array, get structure id, and deserialize bytes as this structure.
How?
Example with comments:
// Internal structure, cannot be edited
struct FirstProcedureParams {
std::string some_string;
int some_integer;
};
// Also cannot be edited
struct SecondProcedureParams {
std::vector<int> some_vector;
std::map<int, int> some_map;
};
// My class to serialize and deserialize packet data to send to client or server.
class CallProcedurePacket : public BasePacket {
public:
void Serialize(uint8_t** bytes, size_t* length) override;
void Deserialize(const uint8_t* bytes, size_t length) override;
public:
int procedure_id; // for example, 1 - FirstProcedureParams, 2 - SecondProcedureParams.
// there is must be parameters for concrete procedure id.
};
void CallProcedurePacket::Serialize(uint8_t** bytes, size_t* length) {
// Some stuff..
writer.Write(procedure_id);
// How can I write dynamic parameters structure
// If it is not polymorphic and it hasn't method like Serialize?
// In this example only 2 structures: FirstProcedureParams and SecondProcedureParams.
// But in real code I have more than 15 structures.
}
void CallProcedurePacket::Deserialize(const uint8_t* bytes, size_t length) {
// Some stuff..
reader.Read(procedure_id);
// I need to get FirstProcedureParams if procedure_id equal 1
// or SecondProcedureParams if procedure_id equal 2.
}
// -----------------------------
// Example of using CallProcedurePacket:
void SomeSendFunction() {
FirstProcedureParams params;
params.some_string = "some string";
params.some_integer = 789;
CallProcedurePacket call_procedure_packet;
call_procedure_packet.procedure_id = 1;
call_procedure_packet.params = params; // what type should be `params` field? how can I write FirstProcedureParams to it?
SendPacket(call_procedure_packet);
}
void SomeSendFunction2() {
SecondProcedureParams params;
params.some_vector = { 1, 2, 3 };
params.some_map = {
{ 1, 5 },
{ 7, 9 }
};
CallProcedurePacket call_procedure_packet;
call_procedure_packet.procedure_id = 2;
call_procedure_packet.params = params; // params field should accept SecondProcedureParams too.
SendPacket(call_procedure_packet);
}
// And reading..
void SomeReceiveFunction() {
CallProcedurePacket call_procedure_packet = ReceivePacket();
if (call_procedure_packet.procedure_id == 1) {
FirstProcedureParams params;
// somehow get the params.
procedure(params.some_string, params.some_integer);
}
else if (call_procedure_packet.procedure_id == 2) {
SecondProcedureParams params;
// some usage of params.
}
}

Related

How to code a SEQUENCE OF inside a SEQUENCE OF (asn1 to c)

I'm working with vanetza to make cpms messages, I have built the .c and .cpp files from the asn1, so the structures of the message have the structure of the asn1. I have a structure (PerceptionDataContainer) which is a SEQUENCE OF PerceptionData, the Sequence of is a special type of list. Each PerceptionData have a perceivedobjects structure which is a SEQUENCE OF PerceivedObject struct, I have made this code to add information to these struct;
vanetza::asn1::Cpm cpm;
auto object = asn1::allocate<PerceivedObject_t>(); //this fill the struct PerceivedObject with basic info
auto perception = asn1::allocate<PerceptionData_t>();
PerceivedObject *arrayobjeto[2];
cpm->cpm.cpmParameters.perceptionData = asn1::allocate<CpmParameters::CpmParameters__perceptionData>();
cpm->header.protocolVersion=2;
cpm->header.messageID=ItsPduHeader__messageID_cpm;
cpm->header.stationID=1;
cpm->cpm.cpmParameters.managementContainer.stationType=2;
for(int i=0; i<2;i++) {
arrayobjeto[i]=asn1::allocate<PerceivedObject>();
arrayobjeto[i]->objectID= i+1;
arrayobjeto[i]->timeOfMeasurement = TimeOfMeasurement_oneMilliSecond;
arrayobjeto[i]->xDistance.value = DistanceValue_oneMeter;
arrayobjeto[i]->xDistance.confidence = DistanceConfidence_oneMeter;
arrayobjeto[i]->yDistance.value = DistanceValue_oneMeter;
arrayobjeto[i]->yDistance.confidence = DistanceConfidence_oneMeter;
arrayobjeto[i]->xSpeed.value = SpeedValueExtended_oneCentimeterPerSec;
arrayobjeto[i]->xSpeed.confidence = SpeedConfidence_equalOrWithinOneMeterPerSec;
arrayobjeto[i]->ySpeed.value = SpeedValueExtended_oneCentimeterPerSec;
arrayobjeto[i]->ySpeed.confidence = SpeedConfidence_equalOrWithinOneMeterPerSec;
}
perception->containerId=1;
perception->containerData.present=PerceptionData__containerData_PR_PerceivedObjectContainer;
perception->containerData.choice.PerceivedObjectContainer.numberOfPerceivedObjects=1;
for(int i=0; i<2;i++) {
EXPECT_EQ(0, ASN_SEQUENCE_ADD(&perception->containerData.choice.PerceivedObjectContainer.perceivedObjects,arrayobjeto[i]));
}
EXPECT_EQ(0, ASN_SEQUENCE_ADD(&cpm->cpm.cpmParameters.perceptionData->list, perception));
EXPECT_EQ(1, cpm->cpm.cpmParameters.perceptionData->list.count);
EXPECT_EQ(2, cpm->cpm.cpmParameters.perceptionData->list.array[0]->containerData.choice.PerceivedObjectContainer.perceivedObjects.list.count);
EXPECT_EQ(perception, cpm->cpm.cpmParameters.perceptionData->list.array[0]);
EXPECT_EQ(arrayobjeto[0], cpm->cpm.cpmParameters.perceptionData->list.array[0]->containerData.choice.PerceivedObjectContainer.perceivedObjects.list.array[0]);
EXPECT_TRUE(cpm.validate());
EXPECT_FALSE(cpm.encode().empty());
ByteBuffer buffer = cpm.encode();
std::cout << "tamaƱo: " << buffer.size() << "\n";
for (const auto byte:buffer){
printf("%02x ",byte);
}
ASSERT_TRUE(cpm.decode(buffer));
std::cout << cpm.size();
The definitions of the structs are these:
typedef struct CpmParameters {
CpmManagementContainer_t managementContainer;
struct OriginatingStationData *stationDataContainer; /* OPTIONAL */
struct CpmParameters__perceptionData {
A_SEQUENCE_OF(struct PerceptionData) list;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} *perceptionData;
....
typedef struct PerceptionData {
CpmContainerId_t containerId;
struct PerceptionData__containerData {
PerceptionData__containerData_PR present;
union PerceptionData__containerData_u {
SensorInformationContainer_t SensorInformationContainer;
PerceivedObjectContainer_t PerceivedObjectContainer;
FreeSpaceAddendumContainer_t FreeSpaceAddendumContainer;
} choice;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} containerData;
....
typedef struct PerceivedObjectContainer {
NumberOfPerceivedObjects_t numberOfPerceivedObjects; /* DEFAULT 0 */
struct PerceivedObjectContainer__perceivedObjects {
A_SEQUENCE_OF(struct PerceivedObject) list;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} perceivedObjects;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} PerceivedObjectContainer_t;
I have reached a good encode but it can not be decode correctly and it seems I have loaded the data into the structs not perfectly, anyone knows how to do it correctly?

using a bytes field as proxy for arbitrary messages

Hello nano developers,
I'd like to realize the following proto:
message container {
enum MessageType {
TYPE_UNKNOWN = 0;
evt_resultStatus = 1;
}
required MessageType mt = 1;
optional bytes cmd_evt_transfer = 2;
}
message evt_resultStatus {
required int32 operationMode = 1;
}
...
The dots denote, there are more messages with (multiple) primitive containing datatypes to come. The enum will grow likewise, just wanted to keep it short.
The container gets generated as:
typedef struct _container {
container_MessageType mt;
pb_callback_t cmd_evt_transfer;
} container;
evt_resultStatus is:
typedef struct _evt_resultStatus {
int32_t operationMode;
} evt_resultStatus;
The field cmd_evt_transfer should act as a proxy of subsequent messages like evt_resultStatus holding primitive datatypes.
evt_resultStatus shall be encoded into bytes and be placed into the cmd_evt_transfer field.
Then the container shall get encoded and the encoding result will be used for subsequent transfers.
The background why to do so, is to shorten the proto definition and avoid the oneof thing. Unfortunately syntax version 3 is not fully supported, so we can not make use of any fields.
The first question is: will this approach be possible?
What I've got so far is the encoding including the callback which seems to behave fine. But on the other side, decoding somehow skips the callback. I've read issues here, that this happened also when using oneof and bytes fields.
Can someone please clarify on how to proceed with this?
Sample code so far I got:
bool encode_msg_test(pb_byte_t* buffer, int32_t sval, size_t* sz, char* err) {
evt_resultStatus rs = evt_resultStatus_init_zero;
rs.operationMode = sval;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
/*encode container*/
container msg = container_init_zero;
msg.mt = container_MessageType_evt_resultStatus;
msg.cmd_evt_transfer.arg = &rs;
msg.cmd_evt_transfer.funcs.encode = encode_cb;
if(! pb_encode(&stream, container_fields, &msg)) {
const char* local_err = PB_GET_ERROR(&stream);
sprintf(err, "pb_encode error: %s", local_err);
return false;
}
*sz = stream.bytes_written;
return true;
}
bool encode_cb(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
evt_resultStatus* rs = (evt_resultStatus*)(*arg);
//with the below in place a stream full error rises
// if (! pb_encode_tag_for_field(stream, field)) {
// return false;
// }
if(! pb_encode(stream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
//buffer holds previously encoded data
bool decode_msg_test(pb_byte_t* buffer, int32_t* sval, size_t msg_len, char* err) {
container msg = container_init_zero;
evt_resultStatus res = evt_resultStatus_init_zero;
msg.cmd_evt_transfer.arg = &res;
msg.cmd_evt_transfer.funcs.decode = decode_cb;
pb_istream_t stream = pb_istream_from_buffer(buffer, msg_len);
if(! pb_decode(&stream, container_fields, &msg)) {
const char* local_err = PB_GET_ERROR(&stream);
sprintf(err, "pb_encode error: %s", local_err);
return false;
}
*sval = res.operationMode;
return true;
}
bool decode_cb(pb_istream_t *istream, const pb_field_t *field, void **arg) {
evt_resultStatus * rs = (evt_resultStatus*)(*arg);
if(! pb_decode(istream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
I feel, I don't have a proper understanding of the encoding / decoding process.
Is it correct to assume:
the first call of pb_encode (in encode_msg_test) takes care of the mt field
the second call of pb_encode (in encode_cb) handles the cmd_evt_transfer field
If I do:
bool encode_cb(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
evt_resultStatus* rs = (evt_resultStatus*)(*arg);
if (! pb_encode_tag_for_field(stream, field)) {
return false;
}
if(! pb_encode(stream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
then I get a stream full error on the call of pb_encode.
Why is that?
Yes, the approach is reasonable. Nanopb callbacks do not care what the actual data read or written by the callback is.
As for why your decode callback is not working, you'll need to post the code you are using for decoding.
(As an aside, Any type does work in nanopb and is covered by this test case. But the type_url included in all Any messages makes them have a quite large overhead.)

Point to a struct member?

I have the following typedefined struct:
typedef struct
{
uint8_t u8Byte1; // This byte takes the needed values sometimes
uint8_t u8Byte2; // Not used
uint8_t u8Byte3; // This byte takes the needed values the other times
uint8_t u8Byte4; // Not used
} tstrMapMetadata;
And I have a thread that fills (with data from a sensor) this struct and uses one of its values:
while(true)
{
tstrMapMetadata * pstrMapMetadata = something();
if(pstrMapMetadata->u8Byte1 == SOMETHING) //<---- this line
{
//Do something special
}
}
But now I have a condition (constant during the thread) in which I want the comparison of the marked line done with u8Byte3 instead of u8Byte1.
Of course I could
if(((condition) ? pstrMapMetadata->u8Byte1 : pstrMapMetadata->u8Byte3) == SOMETHING) //<---- this line
But the I would be doing the same comparison all the time.
Is there a way to point to one of the members (are they called like this?) of the struct before its declaration?
Something like (this code of course doesn't work but gives an idea of what I'm looking for):
uint8_t * coolPointer;
if(condition)
{
coolPointer = (someOperation)tstrMapMetadata(u8Byte1);
}
else
{
coolPointer = (someOperation)tstrMapMetadata(u8Byte3);
}
while(true)
{
tstrMapMetadata * pstrMapMetadata = something();
if(coolPointer == SOMETHING) //<---- this line
{
//Do something special
}
}
Thanks!
There is pointer to member:
uint8_t tstrMapMetadata::*member = condition ?
&tstrMapMetadata::u8Byte1 :
&tstrMapMetadata::u8Byte3;
while (true)
{
tstrMapMetadata* pstrMapMetadata = something();
if (pstrMapMetadata->*member == SOMETHING) // usage with ->* ( .* for objet )
{
//Do something special
}
}

Appending a pointer to a Qlist

this is my first post here!
I'm working on a code that parses Json to fill a struct that I append to a Qlist of tests to run.
My problem is with the payload inside my struct: when I send the first object in my list the payload it is not empty when it should be.
struct FrameTest {
QString protocol;
QString Length;
QByteArray *payload;
};
QList<FrameTest> _validTestLists;
FrameTest TxFrame;
Code of the JSON parser
void Cesa::JsonParser(QJsonObject _ScriptTestObject)
{
TxFrame.protocol = "00";
TxFrame.Length = "00";
TxFrame.payload = new QByteArray();
for(QJsonObject::iterator it = _ScriptTestObject.begin(); it!=_ScriptTestObject.end(); ++it)
{
TxFrame.protocol= "00";
TxFrame.Length = "00";
TxFrame.payload->clear();
_CmdObject = _ScriptTestObject.value(key).toObject(); // Get Json object
if (_CmdObject.contains("Board") && _CmdObject.value("Board") == "Exia")
{
QString payload;
TxFrame.protocol= "03";
TxFrame.Length = "06";
payload = QString("%1").arg(obj.value("hopPeriod").toInt(), 2, 16, QChar('0'));
}
else if (_CmdObject.contains("Board") && _CmdObject.value("Board") == "Cevee")
{
TxFrame.protocol= "03";
TxFrame.Length = "06";
}
_testLists.append(TxFrame);
}
Test(_testLists); // Test with only one object
Function for sending via port COM
bool Cesa::Test(FrameTest frame)
{
QByteArray txFrame;
/* Transmit command */
txFrame.insert(0, frame.protocol);
txFrame.insert(2, frame.Length);
txFrame.insert(4, *frame.payload);
_comPort->write(QByteArray::fromHex(txFrame));
}
I'm a student still learning coding, any help and advice will be appreciated :)

capnproto : Can I get an AnyPointer from a struct?

Given this schema
struct TestObject
{
value1 #0 : Int32 = -5;
value2 #1 : Float32 = 9.4;
}
struct TestContainer
{
object #0: TestObject;
}
Is it possible to get an AnyPointer::Builder from the TestObject::Builder in c++ code?
This is what I am trying to do:
::capnp::MallocMessageBuilder message;
auto container = message.initRoot<TestContainer>();
TestObject::Builder objBuilder = container.initObject();
//Get an AnyPointer
capnp::AnyPointer::Builder anyBuilder = capnp::toAny( objBuilder )(); //No this does not work.
MyTestObject test( 41, 643.7f );
test.serialise( anyBuilder );
What I am trying to do is have an abstract interface with a single argument type
eg.
class ISerialisable
{
virtual void serialise(capnp::AnyPointer::Builder& any) = 0;
}
class MyTestObject: public ISerialisable
{
void serialise(capnp::AnyPointer::Builder& any) override
{
auto testObjBuilder = any.getAs<TestObject>(); or should initAs be used?
testObject.setValue1( whatever1);
testObject.setValue2( whatever2);
}
}
Is it possible to go down this route?