Skip to content

Commit 6c1c5c0

Browse files
author
Shallow Copy Bot
committed
Feature: expose event callback setter in subscription, service, client and timer
Original PR #1496 by nadavelkabets Original: ros2/rclpy#1496
1 parent 18c0ab4 commit 6c1c5c0

File tree

14 files changed

+354
-5
lines changed

14 files changed

+354
-5
lines changed

rclpy/rclpy/impl/_rclpy_pybind11.pyi

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ class Client(Destroyable, Generic[SrvRequestT, SrvResponseT]):
195195
def get_logger_name(self) -> str:
196196
"""Get the name of the logger associated with the node of the client."""
197197

198+
def set_on_new_response_callback(self, callback: Callable[[int], None]) -> None:
199+
"""Set the on new response callback function for the client."""
200+
201+
def clear_on_new_response_callback(self) -> None:
202+
"""Clear the on new response callback function for the client."""
203+
198204

199205
class Context(Destroyable):
200206

@@ -286,6 +292,12 @@ class Service(Destroyable, Generic[SrvRequestT, SrvResponseT]):
286292
def get_logger_name(self) -> str:
287293
"""Get the name of the logger associated with the node of the service."""
288294

295+
def set_on_new_request_callback(self, callback: Callable[[int], None]) -> None:
296+
"""Set the on new request callback function for the service."""
297+
298+
def clear_on_new_request_callback(self) -> None:
299+
"""Clear the on new request callback function for the service."""
300+
289301

290302
class TypeDescriptionService(Destroyable):
291303

@@ -578,6 +590,12 @@ class Timer(Destroyable):
578590
def is_timer_canceled(self) -> bool:
579591
"""Check if a timer is canceled."""
580592

593+
def set_on_reset_callback(self, callback: Callable[[int], None]) -> None:
594+
"""Set the on reset callback function for the timer."""
595+
596+
def clear_on_reset_callback(self) -> None:
597+
"""Clear the on reset callback function for the timer."""
598+
581599

582600
class Subscription(Destroyable, Generic[MsgT]):
583601

@@ -600,6 +618,12 @@ class Subscription(Destroyable, Generic[MsgT]):
600618
def get_publisher_count(self) -> int:
601619
"""Count the publishers from a subscription."""
602620

621+
def set_on_new_message_callback(self, callback: Callable[[int], None]) -> None:
622+
"""Set the on new message callback function for the subscription."""
623+
624+
def clear_on_new_message_callback(self) -> None:
625+
"""Clear the on new message callback function for the subscription."""
626+
603627

604628
class rcl_time_point_t:
605629

rclpy/src/rclpy/client.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,27 @@
2323

2424
#include <memory>
2525
#include <string>
26+
#include <utility>
2627

2728
#include "client.hpp"
2829
#include "clock.hpp"
2930
#include "exceptions.hpp"
3031
#include "node.hpp"
3132
#include "python_allocator.hpp"
3233
#include "utils.hpp"
34+
#include "events_executor/rcl_support.hpp"
3335

3436
namespace rclpy
3537
{
38+
using events_executor::RclEventCallbackTrampoline;
3639

3740
void
3841
Client::destroy()
3942
{
43+
try {
44+
clear_on_new_response_callback();
45+
} catch (RCLError) {
46+
}
4047
rcl_client_.reset();
4148
node_.destroy();
4249
}
@@ -181,6 +188,41 @@ Client::get_logger_name() const
181188
return node_logger_name;
182189
}
183190

191+
void
192+
Client::set_callback(
193+
rcl_event_callback_t callback,
194+
const void * user_data)
195+
{
196+
rcl_ret_t ret = rcl_client_set_on_new_response_callback(
197+
rcl_client_.get(),
198+
callback,
199+
user_data);
200+
201+
if (RCL_RET_OK != ret) {
202+
throw RCLError(std::string("Failed to set the on new response callback for client: ") +
203+
rcl_get_error_string().str);
204+
}
205+
}
206+
207+
void
208+
Client::set_on_new_response_callback(std::function<void(size_t)> callback)
209+
{
210+
clear_on_new_response_callback();
211+
on_new_response_callback_ = std::move(callback);
212+
set_callback(
213+
RclEventCallbackTrampoline,
214+
static_cast<const void *>(&on_new_response_callback_));
215+
}
216+
217+
void
218+
Client::clear_on_new_response_callback()
219+
{
220+
if (on_new_response_callback_) {
221+
set_callback(nullptr, nullptr);
222+
on_new_response_callback_ = nullptr;
223+
}
224+
}
225+
184226
void
185227
define_client(py::object module)
186228
{
@@ -208,6 +250,10 @@ define_client(py::object module)
208250
"Configure whether introspection is enabled")
209251
.def(
210252
"get_logger_name", &Client::get_logger_name,
211-
"Get the name of the logger associated with the node of the client.");
253+
"Get the name of the logger associated with the node of the client.")
254+
.def(
255+
"set_on_new_response_callback", &Client::set_on_new_response_callback,
256+
py::arg("callback"))
257+
.def("clear_on_new_response_callback", &Client::clear_on_new_response_callback);
212258
}
213259
} // namespace rclpy

rclpy/src/rclpy/client.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define RCLPY__CLIENT_HPP_
1717

1818
#include <pybind11/pybind11.h>
19+
#include <pybind11/functional.h>
1920

2021
#include <rcl/client.h>
2122
#include <rcl/service_introspection.h>
@@ -120,10 +121,20 @@ class Client : public Destroyable, public std::enable_shared_from_this<Client>
120121
const char *
121122
get_logger_name() const;
122123

124+
void
125+
set_on_new_response_callback(std::function<void(size_t)> callback);
126+
127+
void
128+
clear_on_new_response_callback();
129+
123130
private:
124131
Node node_;
132+
std::function<void(size_t)> on_new_response_callback_{nullptr};
125133
std::shared_ptr<rcl_client_t> rcl_client_;
126134
rosidl_service_type_support_t * srv_type_;
135+
136+
void
137+
set_callback(rcl_event_callback_t callback, const void * user_data);
127138
};
128139

129140
/// Define a pybind11 wrapper for an rclpy::Client

rclpy/src/rclpy/events_executor/rcl_support.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ namespace events_executor
2626
extern "C" void RclEventCallbackTrampoline(const void * user_data, size_t number_of_events)
2727
{
2828
const auto cb = reinterpret_cast<const std::function<void(size_t)> *>(user_data);
29-
(*cb)(number_of_events);
29+
try {
30+
(*cb)(number_of_events);
31+
} catch (const std::exception & e) {
32+
// Catch and print any exception to avoid propagation to c code
33+
std::fprintf(stderr, "%s\n", e.what());
34+
std::terminate();
35+
}
3036
}
3137

3238
RclCallbackManager::RclCallbackManager(EventsQueue * events_queue)

rclpy/src/rclpy/service.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,26 @@
2222

2323
#include <memory>
2424
#include <string>
25+
#include <utility>
2526

2627
#include "clock.hpp"
2728
#include "exceptions.hpp"
2829
#include "node.hpp"
2930
#include "service.hpp"
3031
#include "utils.hpp"
32+
#include "events_executor/rcl_support.hpp"
3133

3234
namespace rclpy
3335
{
36+
using events_executor::RclEventCallbackTrampoline;
3437

3538
void
3639
Service::destroy()
3740
{
41+
try {
42+
clear_on_new_request_callback();
43+
} catch (RCLError) {
44+
}
3845
rcl_service_.reset();
3946
node_.destroy();
4047
}
@@ -184,6 +191,41 @@ Service::configure_introspection(
184191
}
185192
}
186193

194+
void
195+
Service::set_callback(
196+
rcl_event_callback_t callback,
197+
const void * user_data)
198+
{
199+
rcl_ret_t ret = rcl_service_set_on_new_request_callback(
200+
rcl_service_.get(),
201+
callback,
202+
user_data);
203+
204+
if (RCL_RET_OK != ret) {
205+
throw RCLError(std::string("Failed to set the on new request callback for service: ") +
206+
rcl_get_error_string().str);
207+
}
208+
}
209+
210+
void
211+
Service::set_on_new_request_callback(std::function<void(size_t)> callback)
212+
{
213+
clear_on_new_request_callback();
214+
on_new_request_callback_ = std::move(callback);
215+
set_callback(
216+
RclEventCallbackTrampoline,
217+
static_cast<const void *>(&on_new_request_callback_));
218+
}
219+
220+
void
221+
Service::clear_on_new_request_callback()
222+
{
223+
if (on_new_request_callback_) {
224+
set_callback(nullptr, nullptr);
225+
on_new_request_callback_ = nullptr;
226+
}
227+
}
228+
187229
void
188230
define_service(py::object module)
189231
{
@@ -211,6 +253,10 @@ define_service(py::object module)
211253
"Configure whether introspection is enabled")
212254
.def(
213255
"get_logger_name", &Service::get_logger_name,
214-
"Get the name of the logger associated with the node of the service.");
256+
"Get the name of the logger associated with the node of the service.")
257+
.def(
258+
"set_on_new_request_callback", &Service::set_on_new_request_callback,
259+
py::arg("callback"))
260+
.def("clear_on_new_request_callback", &Service::clear_on_new_request_callback);
215261
}
216262
} // namespace rclpy

rclpy/src/rclpy/service.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define RCLPY__SERVICE_HPP_
1717

1818
#include <pybind11/pybind11.h>
19+
#include <pybind11/functional.h>
1920

2021
#include <rcl/service.h>
2122
#include <rcl/service_introspection.h>
@@ -125,10 +126,20 @@ class Service : public Destroyable, public std::enable_shared_from_this<Service>
125126
void
126127
destroy() override;
127128

129+
void
130+
set_on_new_request_callback(std::function<void(size_t)> callback);
131+
132+
void
133+
clear_on_new_request_callback();
134+
128135
private:
129136
Node node_;
137+
std::function<void(size_t)> on_new_request_callback_{nullptr};
130138
std::shared_ptr<rcl_service_t> rcl_service_;
131139
rosidl_service_type_support_t * srv_type_;
140+
141+
void
142+
set_callback(rcl_event_callback_t callback, const void * user_data);
132143
};
133144

134145
/// Define a pybind11 wrapper for an rclpy::Service

rclpy/src/rclpy/subscription.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,21 @@
2424
#include <memory>
2525
#include <stdexcept>
2626
#include <string>
27+
#include <utility>
2728

2829
#include "exceptions.hpp"
2930
#include "node.hpp"
3031
#include "serialization.hpp"
3132
#include "subscription.hpp"
3233
#include "utils.hpp"
34+
#include "events_executor/rcl_support.hpp"
3335

3436
using pybind11::literals::operator""_a;
3537

3638
namespace rclpy
3739
{
40+
using events_executor::RclEventCallbackTrampoline;
41+
3842
Subscription::Subscription(
3943
Node & node, py::object pymsg_type, std::string topic,
4044
py::object pyqos_profile)
@@ -87,6 +91,10 @@ Subscription::Subscription(
8791

8892
void Subscription::destroy()
8993
{
94+
try {
95+
clear_on_new_message_callback();
96+
} catch (RCLError) {
97+
}
9098
rcl_subscription_.reset();
9199
node_.destroy();
92100
}
@@ -194,6 +202,41 @@ Subscription::get_publisher_count() const
194202
return count;
195203
}
196204

205+
void
206+
Subscription::set_callback(
207+
rcl_event_callback_t callback,
208+
const void * user_data)
209+
{
210+
rcl_ret_t ret = rcl_subscription_set_on_new_message_callback(
211+
rcl_subscription_.get(),
212+
callback,
213+
user_data);
214+
215+
if (RCL_RET_OK != ret) {
216+
throw RCLError(std::string("Failed to set the on new message callback for subscription: ") +
217+
rcl_get_error_string().str);
218+
}
219+
}
220+
221+
void
222+
Subscription::set_on_new_message_callback(std::function<void(size_t)> callback)
223+
{
224+
clear_on_new_message_callback();
225+
on_new_message_callback_ = std::move(callback);
226+
set_callback(
227+
RclEventCallbackTrampoline,
228+
static_cast<const void *>(&on_new_message_callback_));
229+
}
230+
231+
void
232+
Subscription::clear_on_new_message_callback()
233+
{
234+
if (on_new_message_callback_) {
235+
set_callback(nullptr, nullptr);
236+
on_new_message_callback_ = nullptr;
237+
}
238+
}
239+
197240
void
198241
define_subscription(py::object module)
199242
{
@@ -215,6 +258,10 @@ define_subscription(py::object module)
215258
"Return the resolved topic name of a subscription.")
216259
.def(
217260
"get_publisher_count", &Subscription::get_publisher_count,
218-
"Count the publishers from a subscription.");
261+
"Count the publishers from a subscription.")
262+
.def(
263+
"set_on_new_message_callback", &Subscription::set_on_new_message_callback,
264+
py::arg("callback"))
265+
.def("clear_on_new_message_callback", &Subscription::clear_on_new_message_callback);
219266
}
220267
} // namespace rclpy

0 commit comments

Comments
 (0)