Message Definitions¶
Messages are defined in Protocol Buffer (.proto) files. Struct Frame uses these definitions to generate serialization code for each target language.
Why Proto Files¶
Proto files provide: - Language-neutral message definitions - Type safety across language boundaries - Familiar syntax for developers who know Protocol Buffers - Tooling support (syntax highlighting, linting)
Struct Frame uses the proto syntax but generates different code than Google's Protocol Buffers. Messages are fixed-size packed structs, not variable-length encoded.
Packages¶
Packages group related messages and prevent name collisions.
Generated code uses the package name as a prefix or namespace depending on language.
Messages¶
Messages define the structure of data to be serialized.
Field numbers (1, 2, 3) must be unique within a message. They are used for documentation and proto compatibility but do not affect serialization order.
Message Options¶
msgid
Required for messages that will be serialized and sent over a frame.
Message IDs must be unique within a package. Range is 0-255 for basic frame format.
Enums¶
Enums define a set of named integer constants.
Enum values are stored as a single byte (uint8). Use them in messages like any other type:
Basic Data Types¶
| Type | Size | Description |
|---|---|---|
| int8 | 1 byte | Signed -128 to 127 |
| uint8 | 1 byte | Unsigned 0 to 255 |
| int16 | 2 bytes | Signed -32768 to 32767 |
| uint16 | 2 bytes | Unsigned 0 to 65535 |
| int32 | 4 bytes | Signed integer |
| uint32 | 4 bytes | Unsigned integer |
| int64 | 8 bytes | Signed large integer |
| uint64 | 8 bytes | Unsigned large integer |
| float | 4 bytes | IEEE 754 single precision |
| double | 8 bytes | IEEE 754 double precision |
| bool | 1 byte | true or false |
All types use little-endian byte order.
Strings¶
Strings require a size specification.
Fixed-size string
Always uses the specified number of bytes, padded with nulls if shorter.
Variable-size string
Stores up to max_size characters plus a length prefix byte.
Memory layout for variable string: - 1 byte length - N bytes data (up to max_size)
Arrays¶
All repeated fields must specify a size.
Fixed arrays
Always contain exactly the specified number of elements.
repeated float matrix = 1 [size=9]; // 3x3 matrix, always 9 floats
repeated bool flags = 2 [size=8]; // Always 8 booleans
Use for data that always has the same count (matrices, fixed sensor arrays).
Bounded arrays
Variable count up to a maximum. Includes a count byte prefix.
repeated int32 readings = 1 [max_size=100]; // 0-100 integers
repeated float temps = 2 [max_size=16]; // 0-16 floats
Memory layout for bounded array: - 1 byte count - N elements (up to max_size)
String arrays
Arrays of strings require both array size and element size:
This creates an array of up to 10 strings, each up to 32 characters.
Nested Messages¶
Messages can contain other messages.
message Position {
double lat = 1;
double lon = 2;
float alt = 3;
}
message Vehicle {
option msgid = 1;
uint32 id = 1;
Position pos = 2;
float speed = 3;
}
Nested messages are embedded inline. The Vehicle message contains the full Position struct.
Flatten Option¶
The flatten option merges nested message fields into the parent.
Without flatten, access is status.pos.lat. With flatten, access is status.lat.
Flatten is supported in Python and GraphQL only.
Validation Rules¶
The generator enforces these rules:
- Message IDs must be unique within a package (0-255 for basic frame format)
- Field numbers must be unique within a message
- All repeated fields must have size or max_size
- All string fields must have size or max_size
- String arrays must have both max_size and element_size
- Flattened fields must not cause name collisions
- Array max_size limited to 255 elements (count fits in 1 byte)
Complete Example¶
package robot_control;
enum RobotState {
IDLE = 0;
MOVING = 1;
CHARGING = 2;
ERROR = 3;
}
message Position {
double lat = 1;
double lon = 2;
float altitude = 3;
}
message WaypointList {
repeated Position waypoints = 1 [max_size=20];
}
message RobotStatus {
option msgid = 1;
uint32 robot_id = 1;
string name = 2 [size=16];
RobotState state = 3;
Position current_pos = 4;
float battery_percent = 5;
int64 uptime_ms = 6;
repeated float joint_angles = 7 [size=6];
string error_msg = 8 [max_size=128];
}
message RobotCommand {
option msgid = 2;
uint32 target_robot = 1;
RobotState desired_state = 2;
Position target_pos = 3;
repeated float joint_targets = 4 [size=6];
}