Skip to content

Commit 67d986f

Browse files
norroJanStaschulat
authored andcommitted
Provide lifecycle services in the rclc lifecycle nodes (#51)
* Adds service callback with service context Adds an additional callback type and handling that allows passing additional service context (void *) to a service callback. #35 Signed-off-by: Arne Nordmann <[email protected]> * Fixed service callback handling Signed-off-by: Arne Nordmann <[email protected]> * Adds user API and tests * Adds user API to add service with additional context * Tests (copied and adapted from service with request id tests) Signed-off-by: Arne Nordmann <[email protected]> * Proper tear-down of msgs Signed-off-by: Arne Nordmann <[email protected]> * Fixes service call with context tests Signed-off-by: Arne Nordmann <[email protected]> * Adds service callbacks to lifecycle node Declares service callbacks for lifecycle services: get state and get available states micro-ROS#40 Signed-off-by: Arne Nordmann <[email protected]> * Adds callback for GetState service micro-ROS#40 Signed-off-by: Arne Nordmann <[email protected]> * Provide GetAvailableStates service Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * Example lifecycle node proides services Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * Implemented ChangeState service Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * Linting, documentation, example Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * commnted out check for received sequence id - error in build-job for rolling https://build.ros2.org/job/Rpr__rclc__ubuntu_focal_amd64/72/ see also PR #46 and issue #49 * Cmake linting Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * Adds missing declaration of callbacks Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * Pre-initialize all strings Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * Lifecycle services working * Functions to initialize lifecycle services for nodes * Exemplary lifecycle nodes with services micro-ROS#40 Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * Tests for service initialization Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * commnted out check for received sequence id - error in build-job for rolling https://build.ros2.org/job/Rpr__rclc__ubuntu_focal_amd64/72/ see also PR #46 and issue #49 Signed-off-by: Jan Staschulat <[email protected]> * commnted out check for received sequence id - error in build-job for rolling https://build.ros2.org/job/Rpr__rclc__ubuntu_focal_amd64/72/ see also PR #46 and issue #49 Signed-off-by: Jan Staschulat <[email protected]> * Ignoring unsuccessful SERVICE_TAKE (#175) * resolved bug by ignoring RCL_RET_SERVICE_TAKE_FAILED Signed-off-by: Jan Staschulat <[email protected]> * data_available has to be reset to false, if rcl_take fails - this needs to be updated for subscriptions as well. Signed-off-by: Jan Staschulat <[email protected]> * Fixes init and cleanup Signed-off-by: Nordmann Arne (CR/ADT3) <[email protected]> * commnted out check for received sequence id - error in build-job for rolling https://build.ros2.org/job/Rpr__rclc__ubuntu_focal_amd64/72/ see also PR #46 and issue #49 Signed-off-by: Jan Staschulat <[email protected]> Co-authored-by: Jan Staschulat <[email protected]> (cherry picked from commit 865b02b)
1 parent 313235d commit 67d986f

File tree

10 files changed

+456
-108
lines changed

10 files changed

+456
-108
lines changed

rclc/CMakeLists.txt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@ find_package(rosidl_generator_c REQUIRED)
3434
find_package(std_msgs REQUIRED)
3535

3636
if("${rcl_VERSION}" VERSION_LESS "1.0.0")
37-
message(STATUS "Found rcl version ${rcl_VERSION}, which belongs to Dashing or Eloquent")
37+
message(STATUS
38+
"Found rcl version ${rcl_VERSION}, which belongs to Dashing or Eloquent")
3839
# Later, with CMake 3.12+ use:
3940
# add_compile_definitions(USE_RCL_WAIT_SET_IS_VALID_BACKPORT)
4041
add_definitions(-DUSE_RCL_WAIT_SET_IS_VALID_BACKPORT)
4142
else()
42-
message(STATUS "Found rcl version ${rcl_VERSION}, which belongs to Foxy or later")
43+
message(STATUS
44+
"Found rcl version ${rcl_VERSION}, which belongs to Foxy or later")
4345
find_package(rosidl_runtime_c REQUIRED)
4446
endif()
4547

@@ -60,7 +62,8 @@ add_library(${PROJECT_NAME}
6062
src/rclc/sleep.c
6163
)
6264
if("${rcl_VERSION}" VERSION_LESS "1.0.0")
63-
target_sources(${PROJECT_NAME} PRIVATE src/rclc/rcl_wait_set_is_valid_backport.c)
65+
target_sources(${PROJECT_NAME}
66+
PRIVATE src/rclc/rcl_wait_set_is_valid_backport.c)
6467
endif()
6568

6669
target_include_directories(${PROJECT_NAME}
@@ -119,11 +122,13 @@ endif()
119122
if(BUILD_TESTING)
120123
find_package(ament_cmake_gtest REQUIRED)
121124
find_package(ament_lint_auto REQUIRED)
122-
set(ament_cmake_copyright_FOUND TRUE) # No copyright header check since link to NOTICE file is not recognized properly.
123125
find_package(osrf_testing_tools_cpp REQUIRED)
124126
find_package(std_msgs REQUIRED)
125127
find_package(example_interfaces REQUIRED)
126128

129+
# No copyright header check since link to NOTICE file is not recognized properly.
130+
set(ament_cmake_copyright_FOUND TRUE)
131+
127132
ament_lint_auto_find_test_dependencies()
128133

129134
ament_add_gtest(${PROJECT_NAME}_test

rclc_examples/src/example_client_node.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ int main(int argc, const char * const * argv)
4747
RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
4848

4949
// create node
50-
rcl_node_t node = rcl_get_zero_initialized_node();
50+
rcl_node_t node;
5151
RCCHECK(rclc_node_init_default(&node, "add_twoints_client_rclc", "", &support));
5252

5353
// create client

rclc_examples/src/example_lifecycle_node.c

Lines changed: 36 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,15 @@
2626
#include <lifecycle_msgs/srv/get_available_states.h>
2727
#include <lifecycle_msgs/srv/get_available_transitions.h>
2828

29+
#include <rclc/executor.h>
30+
2931
#include "rclc_lifecycle/rclc_lifecycle.h"
3032

33+
#define RCCHECK(fn) {rcl_ret_t temp_rc = fn; if ((temp_rc != RCL_RET_OK)) {printf( \
34+
"Failed status on line %d: %d. Aborting.\n", __LINE__, (int)temp_rc); return 1;}}
35+
#define RCSOFTCHECK(fn) {rcl_ret_t temp_rc = fn; if ((temp_rc != RCL_RET_OK)) {printf( \
36+
"Failed status on line %d: %d. Continuing.\n", __LINE__, (int)temp_rc);}}
37+
3138
rcl_ret_t my_on_configure()
3239
{
3340
printf(" >>> lifecycle_node: on_configure() callback called.\n");
@@ -66,15 +73,15 @@ int main(int argc, const char * argv[])
6673
}
6774

6875
// create rcl_node
69-
rcl_node_t my_node = rcl_get_zero_initialized_node();
76+
rcl_node_t my_node;
7077
rc = rclc_node_init_default(&my_node, "lifecycle_node", "rclc", &support);
7178
if (rc != RCL_RET_OK) {
7279
printf("Error in rclc_node_init_default\n");
7380
return -1;
7481
}
7582

7683
// make it a lifecycle node
77-
printf("make it a lifecycle node...\n");
84+
printf("creating lifecycle node...\n");
7885
rcl_lifecycle_state_machine_t state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine();
7986
rclc_lifecycle_node_t lifecycle_node;
8087
rc = rclc_make_node_a_lifecycle_node(
@@ -88,62 +95,38 @@ int main(int argc, const char * argv[])
8895
return -1;
8996
}
9097

91-
// register callbacks
98+
// Executor
99+
rclc_executor_t executor;
100+
RCCHECK(rclc_executor_init(
101+
&executor,
102+
&support.context,
103+
4, // 1 for the node + 1 for each lifecycle service
104+
&allocator));
105+
106+
unsigned int rcl_wait_timeout = 1000; // in ms
107+
RCCHECK(rclc_executor_set_timeout(&executor, RCL_MS_TO_NS(rcl_wait_timeout)));
108+
109+
// Register lifecycle services
110+
printf("registering lifecycle services...\n");
111+
RCCHECK(rclc_lifecycle_init_get_state_server(&lifecycle_node, &executor));
112+
RCCHECK(rclc_lifecycle_init_get_available_states_server(&lifecycle_node, &executor));
113+
RCCHECK(rclc_lifecycle_init_change_state_server(&lifecycle_node, &executor));
114+
115+
// Register lifecycle service callbacks
116+
printf("registering callbacks...\n");
92117
rclc_lifecycle_register_on_configure(&lifecycle_node, &my_on_configure);
118+
rclc_lifecycle_register_on_activate(&lifecycle_node, &my_on_activate);
93119
rclc_lifecycle_register_on_deactivate(&lifecycle_node, &my_on_deactivate);
120+
rclc_lifecycle_register_on_cleanup(&lifecycle_node, &my_on_cleanup);
94121

95-
printf(" >configuring lifecycle node...\n");
96-
rc = rclc_lifecycle_change_state(
97-
&lifecycle_node,
98-
lifecycle_msgs__msg__Transition__TRANSITION_CONFIGURE,
99-
true);
100-
if (rc != RCL_RET_OK) {
101-
printf("Error in TRANSITION_CONFIGURE\n");
102-
return -1;
103-
}
104-
105-
printf(" >activating lifecycle node...\n");
106-
rc = rclc_lifecycle_change_state(
107-
&lifecycle_node,
108-
lifecycle_msgs__msg__Transition__TRANSITION_ACTIVATE,
109-
true);
110-
if (rc != RCL_RET_OK) {
111-
printf("Error in TRANSITION_ACTIVATE\n");
112-
return -1;
113-
}
114-
115-
printf(" >deactivating lifecycle node...\n");
116-
rc = rclc_lifecycle_change_state(
117-
&lifecycle_node,
118-
lifecycle_msgs__msg__Transition__TRANSITION_DEACTIVATE,
119-
true);
120-
if (rc != RCL_RET_OK) {
121-
printf("Error in TRANSITION_DEACTIVATE\n");
122-
return -1;
123-
}
124-
125-
printf(" >cleaning rcl node up...\n");
126-
rc = rclc_lifecycle_change_state(
127-
&lifecycle_node,
128-
lifecycle_msgs__msg__Transition__TRANSITION_CLEANUP,
129-
true);
130-
if (rc != RCL_RET_OK) {
131-
printf("Error in TRANSITION_CLEANUP\n");
132-
return -1;
133-
}
134-
135-
printf(" >destroying lifecycle node...\n");
136-
rc = rclc_lifecycle_change_state(
137-
&lifecycle_node,
138-
lifecycle_msgs__msg__Transition__TRANSITION_UNCONFIGURED_SHUTDOWN,
139-
true);
140-
if (rc != RCL_RET_OK) {
141-
printf("Error in TRANSITION_UNCONFIGURED_SHUTDOWN\n");
142-
return -1;
143-
}
122+
// Run
123+
RCSOFTCHECK(rclc_executor_spin(&executor));
144124

125+
// Cleanup
145126
printf("cleaning up...\n");
146-
rc = rcl_lifecycle_node_fini(&lifecycle_node, &allocator);
127+
rc = rclc_lifecycle_node_fini(&lifecycle_node, &allocator);
128+
rc += rcl_node_fini(&my_node);
129+
rc += rclc_executor_fini(&executor);
147130
rc += rclc_support_fini(&support);
148131

149132
if (rc != RCL_RET_OK) {

rclc_examples/src/example_service_node.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ int main(int argc, const char * const * argv)
5454
RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
5555

5656
// create node
57-
rcl_node_t node = rcl_get_zero_initialized_node();
57+
rcl_node_t node;
5858
RCCHECK(rclc_node_init_default(&node, "add_twoints_client_rclc", "", &support));
5959

6060
// create service
@@ -68,7 +68,7 @@ int main(int argc, const char * const * argv)
6868
rclc_executor_t executor = rclc_executor_get_zero_initialized_executor();
6969
RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
7070

71-
unsigned int rcl_wait_timeout = 10; // in ms
71+
unsigned int rcl_wait_timeout = 1000; // in ms
7272
RCCHECK(rclc_executor_set_timeout(&executor, RCL_MS_TO_NS(rcl_wait_timeout)));
7373

7474
RCCHECK(rclc_executor_add_service(&executor, &service, &req, &res, service_callback));

rclc_lifecycle/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ if(NOT CMAKE_C_STANDARD)
66
set(CMAKE_C_STANDARD 99)
77
endif()
88

9+
# maximum string length - should cover all lifecycle state and transition names
10+
set(RCLC_LIFECYCLE_MAX_STRING_LENGTH 20 CACHE STRING "Set the maximum length for strings.")
11+
add_definitions(-DRCLC_LIFECYCLE_MAX_STRING_LENGTH=${RCLC_LIFECYCLE_MAX_STRING_LENGTH})
12+
913
# find dependencies
1014
find_package(ament_cmake_ros REQUIRED)
15+
find_package(std_msgs REQUIRED)
1116
find_package(lifecycle_msgs REQUIRED)
1217
find_package(rcl_lifecycle REQUIRED)
1318
find_package(rclc REQUIRED)
@@ -41,6 +46,7 @@ ament_target_dependencies(${PROJECT_NAME}
4146
rclc
4247
rcutils
4348
rcl_lifecycle
49+
std_msgs
4450
lifecycle_msgs
4551
)
4652

rclc_lifecycle/README.md

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The API of the RCLC Lifecycle Node can be divided in several phases: Initializat
1111

1212
### Initialization
1313

14-
Creation of a lifecycle node as a bundle of an rcl node and the rcl Node Lifecycle state machine.
14+
Creation of a lifecycle node as a bundle of an rcl node and the rcl Node Lifecycle state machine:
1515

1616
```C
1717
#include "rclc_lifecycle/rclc_lifecycle.h"
@@ -22,11 +22,11 @@ rcl_ret_t rc;
2222

2323
// create rcl node
2424
rc = rclc_support_init(&support, argc, argv, &allocator);
25-
rcl_node_t my_node = rcl_get_zero_initialized_node();
25+
rcl_node_t my_node;
2626
rc = rclc_node_init_default(&my_node, "lifecycle_node", "rclc", &support);
2727

2828
// rcl state machine
29-
rcl_lifecycle_state_machine_t state_machine_ =
29+
rcl_lifecycle_state_machine_t state_machine_ =
3030
rcl_lifecycle_get_zero_initialized_state_machine();
3131
...
3232

@@ -39,45 +39,36 @@ rcl_ret_t rc = rclc_make_node_a_lifecycle_node(
3939
&allocator);
4040
```
4141

42-
Optionally create hooks for lifecycle state changes.
42+
Register lifecycle services and optionally create callbacks for state changes. Executor needsto be equipped with 1 handle per node _and_ per service:
4343

4444
```C
45-
// declare callback
46-
rcl_ret_t my_on_configure() {
47-
printf(" >>> lifecycle_node: on_configure() callback called.\n");
48-
return RCL_RET_OK;
49-
}
45+
// Executor
46+
rclc_executor_t executor = rclc_executor_get_zero_initialized_executor();
47+
rclc_executor_init(
48+
&executor,
49+
&support.context,
50+
4, // 1 for the node + 1 for each lifecycle service
51+
&allocator));
5052
...
5153

52-
// register callbacks
53-
rclc_lifecycle_register_on_configure(&lifecycle_node, &my_on_configure);
54-
```
55-
56-
### Running
54+
// Register lifecycle services
55+
rclc_lifecycle_add_get_state_service(&lifecycle_node, &executor);
56+
rclc_lifecycle_add_get_available_states_service(&lifecycle_node, &executor);
57+
rclc_lifecycle_add_change_state_service(&lifecycle_node, &executor);
5758

58-
Change states of the lifecycle node, e.g.
59-
60-
```C
61-
bool publish_transition = true;
62-
rc += rclc_lifecycle_change_state(
63-
&lifecycle_node,
64-
lifecycle_msgs__msg__Transition__TRANSITION_CONFIGURE,
65-
publish_transition);
66-
rc += rclc_lifecycle_change_state(
67-
&lifecycle_node,
68-
lifecycle_msgs__msg__Transition__TRANSITION_ACTIVATE,
69-
publish_transition);
59+
// Register lifecycle service callbacks
60+
rclc_lifecycle_register_on_configure(&lifecycle_node, &my_on_configure);
61+
rclc_lifecycle_register_on_activate(&lifecycle_node, &my_on_activate);
7062
...
7163
```
7264
73-
Except for error processing transitions, transitions are usually triggered from outside, e.g., by ROS 2 services.
74-
7565
### Cleaning Up
7666
77-
To clean everything up, simply do
67+
To clean everything up, do:
7868
7969
```C
8070
rc += rcl_lifecycle_node_fini(&lifecycle_node, &allocator);
71+
...
8172
```
8273

8374
## Example
@@ -86,4 +77,4 @@ An example, how to use the RCLC Lifecycle Node is given in the file `lifecycle_n
8677

8778
## Limitations
8879

89-
The state machine publishes state changes, however, lifecycle services are not yet exposed via ROS 2 services (tbd).
80+
* Lifecycle services cannot yet be called via `ros2 lifecycle` CLI, e.g., `ros2 lifecycle set /node configure`. Instead use the `ros2 service` CLI, e.g., `ros2 service call /node/change_state lifecycle_msgs/ChangeState "{transition: {id: 1, label: configure}}"`.

rclc_lifecycle/include/rclc_lifecycle/rclc_lifecycle.h

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
#include <rcl/error_handling.h>
2424
#include <rcl_lifecycle/rcl_lifecycle.h>
2525

26-
#include "rclc/node.h"
26+
#include <lifecycle_msgs/srv/change_state.h>
27+
#include <lifecycle_msgs/srv/get_state.h>
28+
#include <lifecycle_msgs/srv/get_available_states.h>
29+
30+
#include <rclc/node.h>
31+
#include <rclc/executor.h>
2732
#include "rclc_lifecycle/visibility_control.h"
2833

2934
typedef struct rclc_lifecycle_callback_map_t
@@ -38,8 +43,61 @@ typedef struct rclc_lifecycle_node_t
3843
rcl_node_t * node;
3944
rcl_lifecycle_state_machine_t * state_machine;
4045
rclc_lifecycle_callback_map_t callbacks;
46+
bool publish_transitions;
47+
48+
lifecycle_msgs__srv__ChangeState_Request cs_req;
49+
lifecycle_msgs__srv__ChangeState_Response cs_res;
50+
lifecycle_msgs__srv__GetState_Request gs_req;
51+
lifecycle_msgs__srv__GetState_Response gs_res;
52+
lifecycle_msgs__srv__GetAvailableStates_Request gas_req;
53+
lifecycle_msgs__srv__GetAvailableStates_Response gas_res;
4154
} rclc_lifecycle_node_t;
4255

56+
/// Structure which encapsulates a ROS Lifecycle Node.
57+
typedef struct rclc_lifecycle_service_context_t
58+
{
59+
rclc_lifecycle_node_t * lifecycle_node;
60+
} rclc_lifecycle_service_context_t;
61+
62+
RCLC_LIFECYCLE_PUBLIC
63+
rcl_ret_t
64+
rclc_lifecycle_init_get_state_server(
65+
rclc_lifecycle_node_t * lifecycle_node,
66+
rclc_executor_t * executor);
67+
68+
RCLC_LIFECYCLE_PUBLIC
69+
rcl_ret_t
70+
rclc_lifecycle_init_get_available_states_server(
71+
rclc_lifecycle_node_t * lifecycle_node,
72+
rclc_executor_t * executor);
73+
74+
RCLC_LIFECYCLE_PUBLIC
75+
rcl_ret_t
76+
rclc_lifecycle_init_change_state_server(
77+
rclc_lifecycle_node_t * lifecycle_node,
78+
rclc_executor_t * executor);
79+
80+
RCLC_LIFECYCLE_PUBLIC
81+
void
82+
rclc_lifecycle_get_state_callback(
83+
const void * req,
84+
void * res,
85+
void * context);
86+
87+
RCLC_LIFECYCLE_PUBLIC
88+
void
89+
rclc_lifecycle_get_available_states_callback(
90+
const void * req,
91+
void * res,
92+
void * context);
93+
94+
RCLC_LIFECYCLE_PUBLIC
95+
void
96+
rclc_lifecycle_change_state_callback(
97+
const void * req,
98+
void * res,
99+
void * context);
100+
43101
RCLC_LIFECYCLE_PUBLIC
44102
rcl_ret_t
45103
rclc_make_node_a_lifecycle_node(
@@ -95,7 +153,7 @@ rclc_lifecycle_execute_callback(
95153

96154
RCLC_LIFECYCLE_PUBLIC
97155
rcl_ret_t
98-
rcl_lifecycle_node_fini(
156+
rclc_lifecycle_node_fini(
99157
rclc_lifecycle_node_t * node,
100158
rcl_allocator_t * allocator);
101159

0 commit comments

Comments
 (0)