-
Notifications
You must be signed in to change notification settings - Fork 340
/
Copy pathlifecycle_talker.cpp
308 lines (271 loc) · 12.2 KB
/
lifecycle_talker.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include "lifecycle_msgs/msg/transition.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp_lifecycle/lifecycle_node.hpp"
#include "rclcpp_lifecycle/lifecycle_publisher.hpp"
#include "rcutils/logging_macros.h"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
/// LifecycleTalker inheriting from rclcpp_lifecycle::LifecycleNode
/**
* The lifecycle talker does not like the regular "talker" node
* inherit from node, but rather from lifecyclenode. This brings
* in a set of callbacks which are getting invoked depending on
* the current state of the node.
* Every lifecycle node has a set of services attached to it
* which make it controllable from the outside and invoke state
* changes.
* Available Services as for Beta1:
* - <node_name>__get_state
* - <node_name>__change_state
* - <node_name>__get_available_states
* - <node_name>__get_available_transitions
* Additionally, a publisher for state change notifications is
* created:
* - <node_name>__transition_event
*/
class LifecycleTalker : public rclcpp_lifecycle::LifecycleNode
{
public:
/// LifecycleTalker constructor
/**
* The lifecycletalker/lifecyclenode constructor has the same
* arguments a regular node.
*/
explicit LifecycleTalker(const std::string & node_name, bool intra_process_comms = false)
: rclcpp_lifecycle::LifecycleNode(node_name,
rclcpp::NodeOptions().use_intra_process_comms(intra_process_comms))
{}
/// Callback for walltimer in order to publish the message.
/**
* Callback for walltimer. This function gets invoked by the timer
* and executes the publishing.
* For this demo, we ask the node for its current state. If the
* lifecycle publisher is not activate, we still invoke publish, but
* the communication is blocked so that no messages is actually transferred.
*/
void
publish()
{
static size_t count = 0;
auto msg = std::make_unique<std_msgs::msg::String>();
msg->data = "Lifecycle HelloWorld #" + std::to_string(++count);
// Print the current state for demo purposes
if (!pub_->is_activated()) {
RCLCPP_INFO(
get_logger(), "Lifecycle publisher is currently inactive. Messages are not published.");
} else {
RCLCPP_INFO(
get_logger(), "Lifecycle publisher is active. Publishing: [%s]", msg->data.c_str());
}
// We independently from the current state call publish on the lifecycle
// publisher.
// Only if the publisher is in an active state, the message transfer is
// enabled and the message actually published.
pub_->publish(std::move(msg));
}
/// Transition callback for state configuring
/**
* on_configure callback is being called when the lifecycle node
* enters the "configuring" state.
* Depending on the return value of this function, the state machine
* either invokes a transition to the "inactive" state or stays
* in "unconfigured".
* TRANSITION_CALLBACK_SUCCESS transitions to "inactive"
* TRANSITION_CALLBACK_FAILURE transitions to "unconfigured"
* TRANSITION_CALLBACK_ERROR or any uncaught exceptions to "errorprocessing"
*/
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_configure(const rclcpp_lifecycle::State &)
{
// This callback is supposed to be used for initialization and
// configuring purposes.
// We thus initialize and configure our publishers and timers.
// The lifecycle node API does return lifecycle components such as
// lifecycle publishers. These entities obey the lifecycle and
// can comply to the current state of the node.
// As of the beta version, there is only a lifecycle publisher
// available.
pub_ = this->create_publisher<std_msgs::msg::String>("lifecycle_chatter", 10);
timer_ = this->create_wall_timer(
1s, std::bind(&LifecycleTalker::publish, this));
RCLCPP_INFO(get_logger(), "on_configure() is called.");
// We return a success and hence invoke the transition to the next
// step: "inactive".
// If we returned TRANSITION_CALLBACK_FAILURE instead, the state machine
// would stay in the "unconfigured" state.
// In case of TRANSITION_CALLBACK_ERROR or any thrown exception within
// this callback, the state machine transitions to state "errorprocessing".
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
}
/// Transition callback for state activating
/**
* on_activate callback is being called when the lifecycle node
* enters the "activating" state.
* Depending on the return value of this function, the state machine
* either invokes a transition to the "active" state or stays
* in "inactive".
* TRANSITION_CALLBACK_SUCCESS transitions to "active"
* TRANSITION_CALLBACK_FAILURE transitions to "inactive"
* TRANSITION_CALLBACK_ERROR or any uncaught exceptions to "errorprocessing"
*/
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_activate(const rclcpp_lifecycle::State & state)
{
// The parent class method automatically transition on managed entities
// (currently, LifecyclePublisher).
// pub_->on_activate() could also be called manually here.
// Overriding this method is optional, a lot of times the default is enough.
LifecycleNode::on_activate(state);
RCUTILS_LOG_INFO_NAMED(get_name(), "on_activate() is called.");
// Let's sleep for 2 seconds.
// We emulate we are doing important
// work in the activating phase.
std::this_thread::sleep_for(2s);
// We return a success and hence invoke the transition to the next
// step: "active".
// If we returned TRANSITION_CALLBACK_FAILURE instead, the state machine
// would stay in the "inactive" state.
// In case of TRANSITION_CALLBACK_ERROR or any thrown exception within
// this callback, the state machine transitions to state "errorprocessing".
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
}
/// Transition callback for state deactivating
/**
* on_deactivate callback is being called when the lifecycle node
* enters the "deactivating" state.
* Depending on the return value of this function, the state machine
* either invokes a transition to the "inactive" state or stays
* in "active".
* TRANSITION_CALLBACK_SUCCESS transitions to "inactive"
* TRANSITION_CALLBACK_FAILURE transitions to "active"
* TRANSITION_CALLBACK_ERROR or any uncaught exceptions to "errorprocessing"
*/
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_deactivate(const rclcpp_lifecycle::State & state)
{
// The parent class method automatically transition on managed entities
// (currently, LifecyclePublisher).
// pub_->on_deactivate() could also be called manually here.
// Overriding this method is optional, a lot of times the default is enough.
LifecycleNode::on_deactivate(state);
RCUTILS_LOG_INFO_NAMED(get_name(), "on_deactivate() is called.");
// We return a success and hence invoke the transition to the next
// step: "inactive".
// If we returned TRANSITION_CALLBACK_FAILURE instead, the state machine
// would stay in the "active" state.
// In case of TRANSITION_CALLBACK_ERROR or any thrown exception within
// this callback, the state machine transitions to state "errorprocessing".
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
}
/// Transition callback for state cleaningup
/**
* on_cleanup callback is being called when the lifecycle node
* enters the "cleaningup" state.
* Depending on the return value of this function, the state machine
* either invokes a transition to the "unconfigured" state or stays
* in "inactive".
* TRANSITION_CALLBACK_SUCCESS transitions to "unconfigured"
* TRANSITION_CALLBACK_FAILURE transitions to "inactive"
* TRANSITION_CALLBACK_ERROR or any uncaught exceptions to "errorprocessing"
*/
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_cleanup(const rclcpp_lifecycle::State &)
{
// In our cleanup phase, we release the shared pointers to the
// timer and publisher. These entities are no longer available
// and our node is "clean".
timer_.reset();
pub_.reset();
RCUTILS_LOG_INFO_NAMED(get_name(), "on cleanup is called.");
// We return a success and hence invoke the transition to the next
// step: "unconfigured".
// If we returned TRANSITION_CALLBACK_FAILURE instead, the state machine
// would stay in the "inactive" state.
// In case of TRANSITION_CALLBACK_ERROR or any thrown exception within
// this callback, the state machine transitions to state "errorprocessing".
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
}
/// Transition callback for state shutting down
/**
* on_shutdown callback is being called when the lifecycle node
* enters the "shuttingdown" state.
* Depending on the return value of this function, the state machine
* either invokes a transition to the "finalized" state or stays
* in its current state.
* TRANSITION_CALLBACK_SUCCESS transitions to "finalized"
* TRANSITION_CALLBACK_FAILURE transitions to current state
* TRANSITION_CALLBACK_ERROR or any uncaught exceptions to "errorprocessing"
*/
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_shutdown(const rclcpp_lifecycle::State & state)
{
// In our shutdown phase, we release the shared pointers to the
// timer and publisher. These entities are no longer available
// and our node is "clean".
timer_.reset();
pub_.reset();
RCUTILS_LOG_INFO_NAMED(
get_name(),
"on shutdown is called from state %s.",
state.label().c_str());
// We return a success and hence invoke the transition to the next
// step: "finalized".
// If we returned TRANSITION_CALLBACK_FAILURE instead, the state machine
// would stay in the current state.
// In case of TRANSITION_CALLBACK_ERROR or any thrown exception within
// this callback, the state machine transitions to state "errorprocessing".
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
}
private:
// We hold an instance of a lifecycle publisher. This lifecycle publisher
// can be activated or deactivated regarding on which state the lifecycle node
// is in.
// By default, a lifecycle publisher is inactive by creation and has to be
// activated to publish messages into the ROS world.
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::String>> pub_;
// We hold an instance of a timer which periodically triggers the publish function.
// As for the beta version, this is a regular timer. In a future version, a
// lifecycle timer will be created which obeys the same lifecycle management as the
// lifecycle publisher.
std::shared_ptr<rclcpp::TimerBase> timer_;
};
/**
* A lifecycle node has the same node API
* as a regular node. This means we can spawn a
* node, give it a name and add it to the executor.
*/
int main(int argc, char * argv[])
{
// force flush of the stdout buffer.
// this ensures a correct sync of all prints
// even when executed simultaneously within the launch file.
setvbuf(stdout, NULL, _IONBF, BUFSIZ);
rclcpp::init(argc, argv);
rclcpp::executors::SingleThreadedExecutor exe;
std::shared_ptr<LifecycleTalker> lc_node =
std::make_shared<LifecycleTalker>("lc_talker");
exe.add_node(lc_node->get_node_base_interface());
exe.spin();
rclcpp::shutdown();
return 0;
}