Skip to content

Commit 0f7b031

Browse files
pablogs9Acuadros95
andauthored
Add memory type handling (micro-ROS#312)
* Add memory type handling * Apply suggestions from code review Co-authored-by: Antonio Cuadros <49162117+Acuadros95@users.noreply.github.com> Co-authored-by: Antonio Cuadros <49162117+Acuadros95@users.noreply.github.com>
1 parent 77e3fdb commit 0f7b031

2 files changed

Lines changed: 194 additions & 0 deletions

File tree

_data/docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
- tutorials/advanced/overview
5858
- tutorials/advanced/microxrcedds_rmw_configuration
5959
- tutorials/advanced/create_new_type
60+
- tutorials/advanced/handling_type_memory
6061
- tutorials/advanced/create_dds_entities_by_ref
6162
- tutorials/advanced/create_custom_transports
6263
- tutorials/advanced/create_custom_static_library
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
---
2+
title: Handling messages memory in micro-ROS
3+
permalink: /docs/tutorials/advanced/handling_type_memory/
4+
---
5+
6+
This page aims to explain how to handle messages and types memory in micro-ROS.
7+
8+
First of all, since the micro-ROS user is in an embedded C99 environment, it is important to be aware of what messages and ROS 2 types are being used in order to handle memory correctly.
9+
10+
By watching the `.msg` or `.srv` of the types used in a micro-ROS application, you can determine the type of each member. Currently, the following types are supported:
11+
- Basic type
12+
- Array type
13+
- Sequence type
14+
- Compound type
15+
16+
Let's take an example `.mgs` for clarification:
17+
18+
```
19+
# MyType.msg
20+
std_msgs/Header header
21+
int32[] values
22+
float64 duration
23+
int8[10] coefficients
24+
string name
25+
```
26+
27+
In this example:
28+
- the member `duration` is a **basic type member**,.
29+
- the member `values` is a **sequence type member** because it has a unbounded sequence of `int32`, in this case.
30+
- the member `coefficients` is an **array type member** because it has a bounded sequence of 10 units of `int8`, in this case.
31+
- the member `header` is an **compound type member** because it refers to complex type described in the same or other ROS 2 package.
32+
- the member `name` is an **string type member** and should be understood as a `char[]` (sequence type member).
33+
34+
When dealing with the **micro-ROS typesupport** the developer needs to take into account how this message is going to be handled in the C99 API of micro-ROS. In general, the micro-ROS typesupport will create a C99 struct representation of the message:
35+
36+
```c
37+
typedef struct mypackage__msg__MyType
38+
{
39+
std_msgs__msg__Header header;
40+
rosidl_runtime_c__int32__Sequence values;
41+
double duration;
42+
int8 coefficients[10];
43+
rosidl_runtime_c__String name; // equal to rosidl_runtime_c__char__Sequence
44+
} mypackage__msg__MyType;
45+
```
46+
47+
So when in an application has a variable of this type, for example `mypackage__msg__MyType mymsg;`, we know that:
48+
- `mymsg.coefficients` has a C array of `int8`
49+
- `mymsg.duration` is a `double` member
50+
51+
but, what happens with the `...Sequence` and the compound type member?
52+
53+
## Sequence types in micro-ROS
54+
55+
A **sequence type member** is an especial type member that hosts a pointer `data`, a `size` and a `capacity` value. The pointer should have memory for storing up to `capacity` values and `size` member shows how many element are currently in the sequence. Usually in micro-ROS, the user is in charge of assigning memory and values to this sequence members.
56+
57+
In the case of `MyType.msg`, the `values` sequence member is represented in C99 as this struct:
58+
59+
```c
60+
typedef struct rosidl_runtime_c__int32__Sequence
61+
{
62+
int32_t* data; /* The pointer to an array of int32 */
63+
size_t size; /* The number of valid items in data */
64+
size_t capacity; /* The number of allocated items in data */
65+
} rosidl_runtime_c__int32__Sequence;
66+
```
67+
68+
So user need to handle the type like:
69+
70+
```c
71+
mypackage__msg__MyType mymsg;
72+
73+
// mymsg.values.data is NULL or garbage now
74+
// mymsg.values.size is 0 or garbage now
75+
// mymsg.values.capacity is 0 or garbage now
76+
77+
// Assigning dynamic memory to the sequence
78+
mymsg.values.capacity = 100;
79+
mymsg.values.data = (int32_t*) malloc(mymsg.values.capacity * sizeof(int32_t));
80+
mymsg.values.size = 0;
81+
82+
// Assigning static memory to the sequence
83+
static int32_t memory[100];
84+
mymsg.values.capacity = 100;
85+
mymsg.values.data = memory;
86+
mymsg.values.size = 0;
87+
88+
// Filling some data
89+
for(int32_t i = 0; i < 3; i++){
90+
mymsg.values.data = i;
91+
mymsg.values.size++;
92+
}
93+
```
94+
95+
## Compound types in micro-ROS
96+
97+
When dealing with a compound type, the user should recursively inspect the types in order to determine how to handle each internal member.
98+
99+
For example in the `MyType.msg` example, the `header` member has the following structure:
100+
101+
```c
102+
typedef struct std_msgs__msg__Header
103+
{
104+
builtin_interfaces__msg__Time stamp;
105+
rosidl_runtime_c__String frame_id;
106+
} std_msgs__msg__Header;
107+
```
108+
109+
Remember that `rosidl_runtime_c__String` is equivalent to `rosidl_runtime_c__char__Sequence`. And `builtin_interfaces__msg__Time` looks like:
110+
111+
```c
112+
typedef struct builtin_interfaces__msg__Time
113+
{
114+
int32_t sec;
115+
uint32_t nanosec;
116+
} builtin_interfaces__msg__Time;
117+
```
118+
119+
To initialize the `header` member of `MyType.msg`:
120+
121+
```c
122+
mypackage__msg__MyType mymsg;
123+
124+
// Assigning dynamic memory to the frame_id char sequence
125+
mymsg.header.frame_id.capacity = 100;
126+
mymsg.header.frame_id.data = (char*) malloc(mymsg.values.capacity * sizeof(char));
127+
mymsg.header.frame_id.size = 0;
128+
129+
// Assigning value to the frame_id char sequence
130+
strcpy(mymsg.header.frame_id.data, "Hello World");
131+
mymsg.header.frame_id.size = strlen(mymsg.header.frame_id.data);
132+
133+
// Assigning value to other members
134+
mymsg.stamp.sec = 10;
135+
mymsg.stamp.nanosec = 20;
136+
```
137+
138+
## Sequences of compound types
139+
140+
Users should take into account that **sequence type member** of **compound type member** are also valid ROS 2 type. For example, let's modify the previous example:
141+
142+
```
143+
# MyComplexType.msg
144+
std_msgs/Header[] multiheaders
145+
int32[] values
146+
float64 duration
147+
int8[10] coefficients
148+
string name
149+
```
150+
151+
In this case, the generated typesupport will be:
152+
153+
```c
154+
typedef struct mypackage__msg__MyComplexType
155+
{
156+
std_msgs__msg__Header__Sequence multiheaders;
157+
rosidl_runtime_c__int32__Sequence values;
158+
double duration;
159+
int8 coefficients[10];
160+
rosidl_runtime_c__String name; // equal to rosidl_runtime_c__char__Sequence
161+
} mypackage__msg__MyComplexType;
162+
```
163+
164+
Notice that `multiheaders` is a **sequence type member**, so it should be handled properly, but also it is a **compound type member** which needs to be handled recursively, initializing its own members. For example:
165+
166+
```c
167+
mypackage__msg__MyComplexType mymsg;
168+
169+
// Init the multiheaders sequence
170+
mymsg.multiheaders.capacity = 10;
171+
mymsg.multiheaders.data = (std_msgs__msg__Header*) malloc(mymsg.values.capacity * sizeof(std_msgs__msg__Header));
172+
mymsg.multiheaders.size = 0;
173+
174+
// Filling some data
175+
for(int32_t i = 0; i < 3; i++){
176+
mymsg.values.data = i;
177+
178+
// Add memory to this sequence element frame_id
179+
mymsg.multiheaders.data[i].frame_id.capacity = 100;
180+
mymsg.multiheaders.data[i].frame_id.data = (char*) malloc(mymsg.multiheaders.data[i].frame_id.capacity * sizeof(char));
181+
mymsg.multiheaders.data[i].frame_id.size = 0;
182+
183+
// Assigning value to the frame_id char sequence
184+
strcpy(mymsg.multiheaders.data[i].frame_id.data, "Hello World");
185+
mymsg.multiheaders.data[i].frame_id.size = strlen(mymsg.multiheaders.data[i].frame_id.data);
186+
187+
// Assigning value to other members
188+
mymsg.multiheaders.data[i].stamp.sec = 10;
189+
mymsg.multiheaders.data[i].stamp.nanosec = 20;
190+
191+
mymsg.multiheaders.size++;
192+
}
193+
```

0 commit comments

Comments
 (0)