Protocol buffer polymorphism - c++

I have a C++ program that sends out various events, e.g. StatusEvent and DetectionEvent with different proto message definitions to a message service (currently Active MQ, via activemq-cpp APU). I want to write a message listener that receives these messages, parses them and writes them to cout, for debugging purposes. The listener has status_event_pb.h and detection_event_pb.h linked.
My question is: How can I parse the received event without knowing its type? I want to do something like (in pseudo code)
receive event
type = parseEventType(event);
if( type == events::StatusEventType) {
events::StatusEvent se = parseEvent(event);
// do stuff with se
}
else {
// handle the case when the event is a DetectionEvent
}
I looked at this question but I'm not sure if extensions are the right way to go here. A short code snippet pointing the way will be much appreciated. Examples on protobuf are so rare!
Thanks!
It seems extensions are indeed the way to go but I've got one last point to clear up. Here's the proto definition that I have so far:
// A general event, can be thought as base Event class for other event types.
message Event {
required int64 task_id = 1;
required string module_name = 2; // module that sent the event
extensions 100 to 199; // for different event types
}
// Extend the base Event with additional types of events.
extend Event {
optional StatusEvent statusEvent = 100;
optional DetectionEvent detectionEvent = 101;
}
// Contains one bounding box detected in a video frame,
// representing a region of interest.
message DetectionEvent {
optional int64 frame = 2;
optional int64 time = 4;
optional string label = 6;
}
// Indicate status change of current module to other modules in same service.
// In addition, parameter information that is to be used to other modules can
// be passed, e.g. the video frame dimensions.
message StatusEvent {
enum EventType {
MODULE_START = 1;
MODULE_END = 2;
MODULE_FATAL = 3;
}
required EventType type = 1;
required string module_name = 2; // module that sent the event
// Optional key-value pairs for data to be passed on.
message Data {
required string key = 1;
required string value = 2;
}
repeated Data data = 3;
}
My problem now is (1) how to know which specific event that the Event message contains and (2) make sure that it contains only one such event (according to the definition, it can contain both a StatusEvent and a DetectionEvent).

I would not use Protocol Buffers for that, but that's perhaps a combination of little use and other habits.
Anyway, I think I would use an abstract class here, to ease general handling and to contain routing information. Class that would not be defined using protobuf, and would contain a protobuf message.
class Message
{
public:
Type const& GetType() const;
Origin const& GetOrigin() const;
Destination const& GetDestination() const;
// ... other informations
template <class T>
void GetContent(T& proto) const
{
proto.ParseFromIstream(&mContent); // perhaps a try/catch ?
}
private:
// ...
std::stringstream mContent;
};
With this structure, you have both general and specific handling at the tip of your fingers:
void receive(Message const& message)
{
LOG("receive - " << message.GetType() << " from " << message.GetOrigin()
<< " to " << message.GetDestination());
if (message.GetType() == "StatusEvent")
{
StatusEvent statusEvent;
message.Decode(statusEvent);
// do something
}
else if (message.GetType() == "DetectionEvent")
{
DetectionEvent detectionEvent;
message.Decode(detectionEvent);
// do something
}
else
{
LOG("receive - Unhandled type");
}
}
Of course, it would be prettier if you used a std::unordered_map<Type,Handler> instead of a hardcoded if / else if + / else chain, but the principle remains identical:
Encode the type of message sent in the header
Decode only the header upon reception and dispatch based on this type
Decode the protobuf message in a part of the code where the type is known statically

Related

How to create protobuf messages that use "oneof" definition

I'm trying to create 3 messages, start_now, stop_now, and HeartBeat,as defined in the .proto file below. I have already compiled the .proto file using the c++ compiler and have the necessary .h file. It is my understanding that the "oneof" definition allows for only one of the fields to be set at a time. My goal is to create a message,Serialize it using SerializeToString(), then on the receiving end I want to utilize the ParseToString() to get back my message in readable form.
syntax = "proto3";
package jomba;
message SimpleMessage {
}
message Jacket {
uint32 value = 1;
}
message Message {
oneof payload {
SimpleMessage start_now = 1;
SimpleMessage stop_now = 2;
Jacket heart_beat = 3;
}
}
Within the generated header file there is an enum created within class Message which shows the individual fields of the Message. I don't see any set_() functions within this class to generate any of the messages.
enum PayloadCase {
kStartNow = 1,
kStopNow = 2,
kHeartBeat = 3,
PAYLOAD_NOT_SET = 0,
};

Code uses a switch function to process messages separately, how can I use data from 2 messages?

So at work I inherited a massive project taking 1553 messages from an airplane and translating them to a completely different message type. My problem is that I need to use the data from 2 different messages, which are processed completely separately, together.
This is how I inherited it: there's a completely different process for processing the 2Rx message (which has the "plane altitude" data) and the 17Rx message (which has the "structure altitude" data).
I tried to simplify as much of the code that's relevant as possible, but this is the first experience I've had with coding and I've had no programming education, so please tell me if there's something I can do to make it more clear (for example I don't fully understand the Process1553 function).
My job is to do the actual conversions: in my example the altitude data for both the plane and a ground structure is being received in meters and needs to be sent out as feet. However, I ALSO need to send out data comparing the 2 altitudes (well, I'm actually converting Lat/Long/Alt to North/East/Down, but for simplicity let's just say I need the difference in altitudes of the plane and the structure).
void AirplaneComm::Process1553()
{
M1553RawMsgClass *currentMessage;
//check for new messages
while(incomingMessages->size()>0)
{
//grab the message from the top of the stack
currentMessage=(M1553RawMsgClass*)incomingMessages->front();
//pop the stack
incomingMessages->pop_front();
//We are only interested in R messages
if(currentMessage->MessageType()==M1553RawMsgClass::RECEIVE_MSG)
{
M1553RxMsgClass newRxMessage(*currentMessage);
switch (newRxMessage.RxMsgType())
{
case M1553RxMsgClass::RX_2: {
ProcessRx2Msg(newRxMessage);
break;}
// .... more messages
case M1553RxMsgClass::RX_17: {
ProcessRx17Msg(newRxMessage);
break;}
default:
// No processing for this message
break;
}
}
//clean up
if(currentMessage)
{
delete currentMessage;
}
}
}
void AirplaneComm::ProcessRx2Msg(M1553RxMsgClass incoming2Rmessage)
{
M1760Rx2MsgClass newMessage(incoming2Rmessage); //M1760 classes "get" data from
//the 1553 messages and "set"
//it to send as serial
WordType platformAltitudeMeters = newMessage.GetPlatAltitude(); //received altitude in meters
double platformAltitudeFeet = platformAltitudeMeters * 3.2808; //converted to feet
mSerial.SetPlatAltitude(platformAltitudeFeet); //sets the altitude info
}
void AirplaneComm::ProcessRx17Msg(M1553RxMsgClass incoming17Rmessage)
{
M1760Rx17MsgClass newMessage(incoming17Rmessage);
WordType structureAltitudeMeters = newMessage.GetStructAltitude(); //received altitude in meters
double structureAltitudeFeet = structureAltitudeMeters * 3.2808; //converted to feet
mSerial.SetStructAltitude(structureAltitudeFeet); //sets the altitude info
}
So as far as I can tell, the "switch" function takes each "case" individually. I tried creating a new 2Rx AND 17Rx process ("ProcessRx2MsgRx17Msg") assigning those messages (Rx2Msg, Rx17Msg) to variables I declared in each "case":
switch (newRxMessage.RxMsgType())
{
case M1553RxMsgClass::RX_2:
ProcessRx2Msg(newRxMessage);
M1553RxMsgClass Rx2Msg; //M1553RxMsgClass is a variable type taking
Rx2Msg = newRxMessage; //the whole message
break;}
case M1553RxMsgClass::RX_17:
ProcessRx17Msg(newRxMessage);
M1553RxMsgClass Rx17Msg = newRxMessage;
break;
default:
// No processing for this message
break;
ProcessRx2MsgRx17Msg(Rx2Msg, Rx17Msg);
}
but when I did that, it claimed that I was jumping switch labels. I get that this means they are in the same "scope" (ok, I googled it) but when I put brackets around each "case":
case M1553RxMsgClass::RX_2: {
ProcessRx2Msg(newRxMessage);
M1553RxMsgClass Rx2Msg;
Rx2Msg = newRxMessage;
break;}
case M1553RxMsgClass::RX_17: {
ProcessRx17Msg(newRxMessage);
M1553RxMsgClass Rx17Msg = newRxMessage;
break;}
default:
// No processing for this message
break;
ProcessRx2MsgRx17Msg(Rx2Msg, Rx17Msg);
}
but then my ProcessRx2MsgRx17Msg doesn't recognize "Rx2Msg" or "Rx17Msg".
I also considered making "ProcessRx2Msg" a function that could return a value instead of a void function so that I could use it in "ProcessRx17Msg", but I actually need multiple variables from each message (latitude, longitude, and altitude).
So, is it even possible to accomplish what I want with the way it's set up since I inherited it? Also, sorry if I used the wrong terms for certain things.
EDIT: Thank you JarMan and SoronelHaetir. This is how I ended up getting it to work:
void AirplaneComm::Process1553()
{
M1553RawMsgClass *currentMessage;
static M1553RxMsgClass Rx2Msg, Rx17Msg;
//check for new messages
while(incomingMessages->size()>0)
{
//grab the message from the top of the stack
currentMessage=(M1553RawMsgClass*)incomingMessages->front();
//pop the stack
incomingMessages->pop_front();
//We are only interested in R messages
if(currentMessage->MessageType()==M1553RawMsgClass::RECEIVE_MSG)
{
M1553RxMsgClass newRxMessage(*currentMessage);
switch (newRxMessage.RxMsgType())
{
case M1553RxMsgClass::RX_2:
ProcessRx2Msg(newRxMessage);
Rx2Msg = newRxMessage;
break;
// .... more messages
case M1553RxMsgClass::RX_17:
ProcessRx17Msg(newRxMessage);
Rx17Msg = newRxMessage;
ProcessRx2MsgRx17Msg(Rx2Msg, Rx17Msg);
break;
default:
// No processing for this message
break;
}
}
//clean up
if(currentMessage)
{
delete currentMessage;
}
}
}
Then, I was able to use ProcessRx2MsgRx17Msg to convert the airplane geodetic data from Rx2 AND the structure geodetic data from Rx17 to North/East/Down coordinates.
If you need to process data from multiple messages you will have to alter the earlier message processing to save the needed data somewhere then when the later message comes in refer to both the later message parameters as well as the saved message parameters from the earlier message(s). The saved data will need a lifetime longer than the single function call (whether you make it static to the message processing function or global is up to the design choices your organization uses).

How to use Union Types Protobuf C++

I am new in using Protobuf. I have a server client communication (UDP) in C++. Now I use Protobuf to send a message which contains some Information to the Server.
package Test;
message vName{
required int32 name = 1;
}
message vNat{
required int32 nat = 1;
}
message vTan{
required int32 tan = 1;
}
message Test{
enum Type { vName = 1; vNAT = 2; vTAN = 3;}
required Type type = 1;
optional vName name = 2;
optional vNat nat = 2;
optional vTan tan = 2;
}
Now i want only send the Information which is set. For example Type is 1. Then how can i access or set the name?
Can anybody make an small snippet that i can understand how to use it?
I apologize for my english skills :D
Protobuf version: 2.5.0
OS: Windows
Enviroment: Visual Studio
Language: C++
From https://developers.google.com/protocol-buffers/docs/techniques#union
You may also want to have an enum field that identifies which message is filled in, so that you can switch on it:
message OneMessage {
enum Type { FOO = 1; BAR = 2; BAZ = 3; }
// Identifies which field is filled in.
required Type type = 1;
// One of the following will be filled in.
optional Foo foo = 2;
optional Bar bar = 3;
optional Baz baz = 4;
}
How can I use this in the Code? I think this is what I want. Have anybody an idea where i can find an example?
It sounds like what you're looking for is this, instead of using optional fields and a workaround-enum.

Assign randomly chosen, but matching parameters for connected OMNeT++ gates?

I'm developing a network model in OMNeT++ in which I have introduced a custom channel type to represent links in my network. For one property of this channel type's instances, I'd like to assign a random parameter. However, the random number should be the same for connected gates.
My node definition has the following gates definition:
simple GridAgent
{
/* ... other paramters/definitions omitted ... */
gates:
inout agentConnections[];
}
In my network configuration, I connect nodes using the simple <--> syntax, e.g.:
someSwitchyard.agentConnections++ <--> AgentConnectionChannel <--> someWindfarm.agentConnections++;
Now, this AgentConnectionChannel has a property called impedance, which I'd like to randomly assign. This impedance property should be the same for both A -> B and B -> A. I have tried to add { impedance = default(unitform(1, 10)) } to the network definition, as well as putting **.agentConnections$o[*].channel.impedance = uniform(1, 10) into omnetpp.ini. In both cases, however, A -> B has a different value assigned than B -> A.
As indicated on the OMNet++ mailing list, this happens because the <--> syntax is actually a shorthand for creating two distinct connections, hence two drawings from the random number distribution happen.
How can I assign a random parameter to a connection's property and have the same value for both directions of two connected gates? Is there a way to do this in the omnetpp.ini file, or do I need to create a script in, e.g., Perl, Ruby, or Python to generate the omnetpp.ini for my runs?
There is no simple solution of your problem, and it could not be resolved manipulating omnetpp.ini file merely.
I propose manual rewriting a parameter value for the second direction. It requires preparing a C++ class for a channel (which you have probably done).
Assuming that your channel definition in NED is following:
channel AgentConnectionChannel extends ned.DatarateChannel {
#class(AgentConnectionChannel);
double impedance;
}
and in omnetpp.ini you has:
**.agentConnections$o[*].channel.impedance = uniform(1, 10)
you should prepare C++ class AgentConnectionChannel:
class AgentConnectionChannel: public cDatarateChannel {
public:
AgentConnectionChannel() : parAlreadyRewritten(false) {}
void setParAlreadyRewritten() {parAlreadyRewritten=true;}
protected:
virtual void initialize();
private:
bool parAlreadyRewritten;
private:
double impedance;
};
Define_Channel(AgentConnectionChannel);
void AgentConnectionChannel::initialize() {
if (parAlreadyRewritten == false) {
parAlreadyRewritten = true;
cGate * srcOut = this->getSourceGate();
cModule *owner = srcOut->getOwnerModule();
int index = srcOut->isVector() ? srcOut->getIndex() : -1;
cGate *srcIn = owner->gateHalf(srcOut->getBaseName(), cGate::INPUT,
index);
cChannel * channel = srcIn->findIncomingTransmissionChannel();
AgentConnectionChannel * reverseChan =
dynamic_cast<AgentConnectionChannel*>(channel);
if (reverseChan) {
reverseChan->setParAlreadyRewritten();
// assigning a value from forward direction channel
reverseChan->par("impedance") = this->par("impedance");
}
}
// and now read a parameter as usual
impedance = par("impedance").doubleValue();
EV << getFullPath() << ", impedance=" << impedance << endl;
}

C++ MapiMessageW will not open mail client if recipients are assigned

I have a working method which will successfully open Outlook (and other mail clients), open a new message window, and set the body and subject fields with provided strings, and attach one or more files indicated by a vector of our internal filepath object. I'm trying to extend this to add a recipient in the To: field. The code below omits all of the subject/body/path stuff for brevity and just shows what I've done with recipients.
void createMailMessage( const char *recipientAddr, const char *subject, const char *body, const std::vector<FSPath> &attachments) {
// first get all of the const char* into std::wstring
// ommitted here for brevity
MapiMessageW message = { 0 };
MapiRecipDescW *recipients = NULL;
if (recipientAddr != NULL) {
recipients = new MapiRecipDescW[1];
memset(recipients, 0x00, sizeof(MapiRecipDescW) ) // only one recipient
recipients[0].lpszAddress = (PWSTR) recipStrW.c_str(); // recipStrW is from omitted code above
recipients[0].ulRecipClass = MAPI_TO;
message.nRecipCount = (ULONG) 1;
message.lpRecips = recipients;
}
// fill in the rest of the message info here
// this stuff is already working and i left it unchanged
MAPISendMailHelper(NULL, NULL, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
}
In the debugger I can see that the message struct is still well-formed, simply with the addition of a pointer to the recipients struct and the nRecipCount field filled correctly. That struct is also well-formed, with the expected address string and class value. When the code executes, it reaches the same call that produces the new message dialog ( pfnMapiSendMailA() in MapiUnicodeHelp.h ), but does not seem to execute it.
Help! What am I missing?!
Thanks in advance!