3. Proto message for a DDS system¶
Individual declarations in a .proto
file can be annotated with a number
of options. Options do not change the overall meaning of a declaration,
but may affect the way it is handled in a particular context.
Options can be defined at different levels:
File-level options: meaning they should be written at the top-level scope, not inside any message, enum, or service definition.
Message-level options: meaning they should be written inside message definitions.
Field-level options: meaning they should be written inside field definitions. Enum types, enum values, service types, and service methods.
3.1. Use case: Person¶
In this use case example, a system capable of describing the personal data of persons must be built using the GPB data-model
The layout can be:
string name
integer age
sequence phone-number + type
sequence friends
3.2. Proto file for the Person example¶
This use case is described in this .proto
file:
import "omg/dds/descriptor.proto";
package address;
message Person {
required string name = 1;
required int32 age = 2;
optional string email = 3;
enum PhoneType {
UNDEFINED = 0;
MOBILE = 1;
HOME = 2;
WORK = 3;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
repeated Person friend = 5;
}
GPB labels every field as either a required or an optional field. Required fields are always used/filled; optional fields may or may not be.
Different data models are compatible if all required fields are the same. Data models can be extended with extra fields; if those new fields are all optional, then the new model will still be compatible with older applications using the old data model.
In our example the name and age are always required. The email string is optional, as extra information for this person. The sequences with phone numbers and friends are allowed to be empty.
Detailed explanation for the layout of a .proto
file can be found in
the Google Protocol buffer documentation on
https://developers.google.com/protocol-buffers/docs/proto
3.3. Annotating a proto message for use as a type in DDS¶
For the GPB message to be able to be handled correctly in a DDS system,
some options are needed in the .proto
file which define how the GPB
message shall behave in the DDS system.
At the message level there is an extra option .omg.dds.type
.
This tells the protocol buffer compiler that this message is also a dds type message.
This type option has a optional extra parameter for giving this type a dds type name.
By default it has the same name in the DDS domain as it has in GPB.
The Person example with this option:
import "omg/dds/descriptor.proto";
package address;
message Person {
option (.omg.dds.type) = {};
required string name = 1;
required int32 age = 2;
enum PhoneType {
UNDEFINED = 0;
MOBILE = 1;
HOME = 2;
WORK = 3;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
repeated Person friend = 5;
}
3.3.1. Proto file with omg.dds.member.key option¶
For support of a key value in the datamodel, the option key
can be given as a
field-member option. One or more fields containing this option will indicate that
these members make a unique key identifier in the data model. A field
indicated as a key field must always be a required field for GPB. Also a key
field is automatically a filterable field, as described below.
The Person example with name as a unique key (this means that each unique value of the name will lead to a separate instance in DDS with its own history):
import "omg/dds/descriptor.proto";
package address;
message Person {
option (.omg.dds.type) = {};
required string name = 1 [(.omg.dds.member).key = true];
required int32 age = 2;
optional string email = 3;
}
enum PhoneType {
UNDEFINED = 0;
MOBILE = 1;
HOME = 2;
WORK = 3;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
repeated Person friend = 5;
3.3.2. Proto file with omg.dds.member.filterable option¶
For support of filterable fields in the datamodel, the option filterable
can be
given as a field-member option.
One or more fields with this option indicates that these members are available
for dynamic querying and filtering by means of a QueryCondition
or
ContentFilteredTopic
in DDS.
A field marked as a filterable field must always be a required field in GPB. A key field is always filterable, by definition.
The Person example with age as a filterable attribute:
import "omg/dds/descriptor.proto";
package address;
message Person {
option (.omg.dds.type) = {};
required string name = 1 [(.omg.dds.member).key = true];
required int32 age = 2 [(.omg.dds.member).filterable = true];
optional string email = 3;
enum PhoneType {
UNDEFINED = 0;
MOBILE = 1;
HOME = 2;
WORK = 3;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
repeated Person friend = 5;
}
3.3.3. Proto file with omg.dds.member.name option¶
The previous examples will result in a DDS type with the directly-mapped fields in IDL with the same name as in proto. (Key fields and filterable fields are directly mapped.)
If a different name is needed in the DDS domain for a fieldname in the generated
IDL and dds type, a name can be given as an omg.dds.member
option.
Example where the age field will be named AgeInYears
in the DDS domain:
import "omg/dds/descriptor.proto";
package address;
message Person {
option (.omg.dds.type) = {};
required string name = 1 [(.omg.dds.member).key = true];
required int32 age = 2 [(.omg.dds.member) = { name: "AgeInYears" filterable: true }];
optional string email = 3 ;
enum PhoneType {
UNDEFINED = 0;
MOBILE = 1;
HOME = 2;
WORK = 3;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
repeated Person friend = 5;
}