CARLA
Loading...
Searching...
No Matches
CycloneDDSSubscriberMiddleware.h
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB).
2// This work is licensed under the terms of the MIT license.
3// For a copy, see <https://opensource.org/licenses/MIT>.
4
5#pragma once
6
12#include "carla/Logging.h"
13
14#include <atomic>
15
16#ifndef CARLA_ROS2_MIDDLEWARE_TESTING
17#ifdef CARLA_ROS2_MIDDLEWARE_CYCLONEDDS
18
19#include <dds/dds.h>
20#include <dds/ddsi/ddsi_serdata.h>
21#include <dds/ddsi/ddsi_sertype.h>
22
23namespace carla {
24namespace ros2 {
25
26/// CycloneDDS implementation of ISubscriberMiddleware.
27///
28/// Receives raw CDR bytes via dds_takecdr() and deserializes them into the
29/// caller-supplied msg::* POD struct via CdrSerialization.h. No IDL-generated
30/// type files or CycloneDDS C++ bindings are required.
31///
32/// Parameterized on a traits type S that provides:
33/// S::msg_type — the message type (a carla::ros2::msg::* POD struct)
34template<typename S>
35class CycloneDDSSubscriberMiddleware : public ISubscriberMiddleware {
36 public:
37 using msg_type = typename S::msg_type;
38
39 CycloneDDSSubscriberMiddleware() = default;
40 CycloneDDSSubscriberMiddleware(const CycloneDDSSubscriberMiddleware&) = delete;
41 CycloneDDSSubscriberMiddleware& operator=(const CycloneDDSSubscriberMiddleware&) = delete;
42
43 ~CycloneDDSSubscriberMiddleware() override {
44 // Delete only our own entities. The shared participant is NOT deleted here;
45 // it lives for the process lifetime so that sertypes interned by CycloneDDS
46 // are never freed while other subscribers are still using them.
47 if (_reader > 0) {
48 // Detach the listener before deleting the reader. dds_set_listener with
49 // a null listener acquires the entity lock and waits for any in-flight
50 // carla_on_data_available / carla_on_subscription_matched callback to
51 // complete before returning, preventing a use-after-free of `this` on
52 // the CycloneDDS receive thread.
53 dds_set_listener(_reader, nullptr);
54 dds_delete(_reader);
55 }
56 if (_topic > 0) { dds_delete(_topic); }
57 }
58
59 bool Init(
60 const std::string& topic_name,
61 void* message_ptr,
62 bool* new_message_flag) override
63 {
64 return InitImpl(topic_name, message_ptr, new_message_flag, nullptr);
65 }
66
67 bool Init(
68 const std::string& topic_name,
69 void* message_ptr,
70 bool* new_message_flag,
71 const QosProfile& qos) override
72 {
73 return InitImpl(topic_name, message_ptr, new_message_flag, &qos);
74 }
75
76 private:
77 /// Shared Init body. When @a qos_profile is non-null the profile overrides
78 /// the RELIABLE / KEEP_LAST-1 reader defaults below; when null the defaults
79 /// are kept, preserving the pre-QoS behavior for every existing subscriber.
80 bool InitImpl(
81 const std::string& topic_name,
82 void* message_ptr,
83 bool* new_message_flag,
84 const QosProfile* qos_profile)
85 {
86 _message_ptr = static_cast<msg_type*>(message_ptr);
87 _new_message_ptr = new_message_flag;
88
89 dds_entity_t participant = carla_cdr_get_participant();
90 if (participant < 0) {
91 log_error("CycloneDDSSubscriberMiddleware: Shared participant unavailable "
92 "(topic '", topic_name, "', code:", participant, ")");
93 return false;
94 }
95
96 const char* type_name = CdrTopicInfo<msg_type>::type_name();
97 _topic = carla_cdr_create_topic(
98 participant, topic_name.c_str(), type_name, &_sertype);
99 if (_topic < 0) {
100 log_error("CycloneDDSSubscriberMiddleware: Failed to create topic '",
101 topic_name, "' (code:", _topic, ")");
102 return false;
103 }
104
105 dds_qos_t* qos = dds_create_qos();
106 if (qos_profile != nullptr) {
107 dds_qset_reliability(qos,
108 qos_profile->reliability == QosProfile::Reliability::Reliable
109 ? DDS_RELIABILITY_RELIABLE
110 : DDS_RELIABILITY_BEST_EFFORT,
111 DDS_SECS(1));
112 dds_qset_durability(qos,
113 qos_profile->durability == QosProfile::Durability::TransientLocal
114 ? DDS_DURABILITY_TRANSIENT_LOCAL
115 : DDS_DURABILITY_VOLATILE);
116 dds_qset_history(qos,
117 qos_profile->history == QosProfile::History::KeepLast
118 ? DDS_HISTORY_KEEP_LAST
119 : DDS_HISTORY_KEEP_ALL,
120 qos_profile->history_depth);
121 } else {
122 dds_qset_reliability(qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(1));
123 dds_qset_history(qos, DDS_HISTORY_KEEP_LAST, 1);
124 }
125 // Set USER_DATA (PID_USER_DATA = 0x002c per OMG DDSI-RTPS v2.5 §9.6.2.2.2)
126 // to the REP-2016 type-hash KV payload "typehash=RIHS01_<hex>;".
127 auto ud = build_user_data_for<msg_type>();
128 if (!ud.empty()) {
129 dds_qset_userdata(qos, ud.data(), ud.size());
130 }
131 dds_listener_t* listener = dds_create_listener(this);
132 dds_lset_subscription_matched(listener, carla_on_subscription_matched);
133 dds_lset_data_available(listener, carla_on_data_available);
134
135 _reader = dds_create_reader(participant, _topic, qos, listener);
136 dds_delete_listener(listener);
137 dds_delete_qos(qos);
138 if (_reader < 0) {
139 log_error("CycloneDDSSubscriberMiddleware: Failed to create reader "
140 "(topic '", topic_name, "', code:", _reader, ")");
141 return false;
142 }
143
144 _topic_name = topic_name;
145 return true;
146 }
147
148 public:
149 bool IsAlive() const override {
150 return _alive.load(std::memory_order_relaxed);
151 }
152
153 std::string GetTopicName() const override {
154 return _topic_name;
155 }
156
157 private:
158 static void carla_on_subscription_matched(
159 dds_entity_t /*reader*/,
160 const dds_subscription_matched_status_t status,
161 void* arg)
162 {
163 CycloneDDSSubscriberMiddleware* self =
164 static_cast<CycloneDDSSubscriberMiddleware*>(arg);
165 self->_alive.store(status.current_count > 0, std::memory_order_relaxed);
166 }
167
168 static void carla_on_data_available(dds_entity_t reader, void* arg) {
169 CycloneDDSSubscriberMiddleware* self =
170 static_cast<CycloneDDSSubscriberMiddleware*>(arg);
171
172 struct ddsi_serdata* sd = nullptr;
173 dds_sample_info_t info;
174 dds_return_t rc = dds_takecdr(reader, &sd, 1u, &info, DDS_ANY_STATE);
175 if (rc <= 0 || !sd) { return; }
176
177 if (info.valid_data) {
178 const uint8_t* data = carla_cdr_data(sd);
179 const uint32_t size = carla_cdr_size(sd);
180 if (!deserialize_from_cdr(data, static_cast<size_t>(size),
181 *self->_message_ptr)) {
182 log_error("CycloneDDSSubscriberMiddleware::on_data_available (",
183 self->_topic_name, "): deserialization failed");
184 } else {
185 *self->_new_message_ptr = true;
186 }
187 }
188 ddsi_serdata_unref(sd);
189 }
190
191 dds_entity_t _topic { DDS_ENTITY_NIL };
192 dds_entity_t _reader { DDS_ENTITY_NIL };
193 struct ddsi_sertype* _sertype { nullptr };
194
195 msg_type* _message_ptr { nullptr };
196 bool* _new_message_ptr { nullptr };
197
198 std::string _topic_name;
199 std::atomic<bool> _alive { false };
200};
201
202} // namespace ros2
203} // namespace carla
204
205#endif // CARLA_ROS2_MIDDLEWARE_CYCLONEDDS
206#endif // !CARLA_ROS2_MIDDLEWARE_TESTING
bool deserialize_from_cdr(const uint8_t *data, size_t size, T &msg)
Deserialize a msg::X from a CDR byte buffer that was produced by serialize_to_cdr() or by any ROS2-co...
This file contains definitions of common data structures used in traffic manager.
static double sd[6]
Definition odrSpiral.cpp:52