VLink  2.1.0
A high-performance communication middleware
dds_conf.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file dds_conf.h
26  * @brief Transport configuration for the @c dds:// eProsima Fast-DDS / Fast-RTPS transport.
27  *
28  * @details
29  * @c DdsConf is the default cross-machine transport in VLink and binds the @c dds://
30  * URL scheme to the eProsima Fast-DDS implementation of the OMG DDS specification.
31  * It supports the full RTPS wire protocol over UDP/TCP and SHM, scales from a single
32  * LAN segment to wide-area deployments, and interoperates with any other compliant
33  * DDS vendor on the same Domain. Pub/sub, RPC (request/response over DDS topics)
34  * and field-style state synchronisation are all routed through Fast-DDS DataReaders
35  * and DataWriters under the hood.
36  *
37  * @par Supported Node Types
38  *
39  * | Publisher | Subscriber | Server | Client | Getter | Setter |
40  * | :-------: | :--------: | :----: | :----: | :----: | :----: |
41  * | yes | yes | yes | yes | yes | yes |
42  *
43  * @par URL Format
44  * @code
45  * dds://<topic>[?domain=<N>&depth=<N>&qos=<profile>]
46  * dds://<topic>[?domain=<N>&part=<v>&topic=<v>&pub=<v>&sub=<v>&writer=<v>&reader=<v>]
47  * @endcode
48  *
49  * | Component | Description |
50  * | ---------- | ---------------------------------------------------------------------------- |
51  * | @c topic | DDS topic name; URL host concatenated with the URL path |
52  * | @c domain | DDS Domain ID (@c ?domain=); defaults from the @c VLINK_DDS_DOMAIN env var |
53  * | @c depth | Optional history-depth override; @c 0 keeps the QoS-selected depth |
54  * | @c qos | Named QoS profile registered with @c register_qos() (@c ?qos=) |
55  * | @c qos_ext | Remaining query keys after @c domain, @c depth, @c qos have been removed |
56  *
57  * @par Environment Variables
58  *
59  * | Variable | Description | Default |
60  * | ------------------ | ------------------------------------------------------------ | ------- |
61  * | VLINK_DDS_DOMAIN | Default DDS Domain ID when @c ?domain= is not present in URL | 0 |
62  *
63  * @par QoS Registration
64  * Named profiles must be registered before any endpoint that references them is created.
65  * A typical reliable + transient-local profile for late-joining subscribers looks like:
66  * @code
67  * vlink::Qos late_joiner;
68  * late_joiner.reliability.kind = vlink::Qos::Reliability::kReliable;
69  * late_joiner.durability.kind = vlink::Qos::Durability::kTransientLocal;
70  * late_joiner.history.kind = vlink::Qos::History::kKeepLast;
71  * late_joiner.history.depth = 16;
72  * vlink::DdsConf::register_qos("late_joiner", late_joiner);
73  * @endcode
74  *
75  * @par Type-Support Registration
76  * CDR messages can use a type name alone for unkeyed byte-stream transport.
77  * Register the Fast-DDS @c TopicDataType factory before opening an endpoint
78  * when key extraction, TypeObject metadata, or an accurate native maximum
79  * serialised size is required. An optional response type can be registered at
80  * the same time for RPC topics; the helper appends the @c "___resp" suffix.
81  * @code
82  * vlink::DdsConf::register_topic<MyMsgPubSubType>("my_topic");
83  * vlink::DdsConf::register_topic<MyReqPubSubType, MyRespPubSubType>("my_rpc");
84  * vlink::DdsConf::register_url<MyMsgPubSubType>("dds://my_topic?domain=1");
85  * @endcode
86  *
87  * @par Example
88  * @code
89  * vlink::DdsConf::load_global_qos_file("/etc/vlink/dds_profile.xml");
90  *
91  * vlink::Qos qos;
92  * qos.reliability.kind = vlink::Qos::Reliability::kReliable;
93  * qos.durability.kind = vlink::Qos::Durability::kTransientLocal;
94  * vlink::DdsConf::register_qos("reliable_tl", qos);
95  *
96  * auto pub = vlink::Publisher<MyMsg>::create_unique("dds://state?domain=42&qos=reliable_tl");
97  * auto sub = vlink::Subscriber<MyMsg>::create_unique("dds://state?domain=42&qos=reliable_tl");
98  * @endcode
99  *
100  * @note Compiled only when @c VLINK_SUPPORT_DDS is defined.
101  * @note @c qos and @c qos_ext are mutually exclusive; setting both forces @c is_valid() to @c false.
102  * @note RPC reply topics are derived by appending @c "___resp" to the topic name.
103  */
104 
105 #pragma once
106 
107 #ifdef VLINK_SUPPORT_DDS
108 
109 #include <cstdint>
110 #include <map>
111 #include <shared_mutex>
112 #include <string>
113 #include <tuple>
114 #include <vector>
115 
116 #include "../base/functional.h"
117 #include "../extension/qos.h"
118 #include "../impl/conf.h"
119 
120 namespace eprosima::fastdds::dds {
121 class TopicDataType;
122 }
123 
124 namespace vlink {
125 
126 /**
127  * @struct DdsConf
128  * @brief Concrete @c Conf describing a Fast-DDS endpoint addressed by a @c dds:// URL.
129  *
130  * @details
131  * Captures the topic, Domain ID, history-depth override, and either a named QoS
132  * profile or a per-entity QoS property map. Both constructors share the same
133  * @c topic and @c domain fields; the second constructor populates @c qos_ext
134  * instead of @c qos.
135  */
136 struct VLINK_EXPORT DdsConf final : public Conf {
137  std::string topic; ///< Fast-DDS topic name (URL host concatenated with path).
138  int32_t domain{0}; ///< DDS Domain ID joined by the underlying DomainParticipant.
139  int32_t depth{0}; ///< History-depth override; @c 0 keeps the QoS-selected depth.
140  std::string qos; ///< Named QoS profile key registered via @c register_qos().
141  PropertiesMap qos_ext; ///< Per-entity property map; populated from query keys outside @c domain / @c depth / @c qos.
142 
143  /**
144  * @brief Builds a @c DdsConf from topic, Domain, depth, and an optional named QoS profile.
145  *
146  * @param _topic Fast-DDS topic name.
147  * @param _domain Domain ID; defaults to @c 0.
148  * @param _depth History-depth override; defaults to @c 0 (use QoS depth).
149  * @param _qos Named QoS profile key; empty by default.
150  */
151  explicit DdsConf(const std::string& _topic, int32_t _domain = 0, int32_t _depth = 0, const std::string& _qos = "");
152 
153  /**
154  * @brief Builds a @c DdsConf from topic, Domain, and an explicit per-entity QoS map.
155  *
156  * @details
157  * Use this overload when QoS must be assembled at runtime instead of being
158  * registered as a named profile. Mutually exclusive with the @c qos field;
159  * @c is_valid() returns @c false if both are non-empty on the same instance.
160  *
161  * @param _topic Fast-DDS topic name.
162  * @param _domain DDS Domain ID.
163  * @param _qos_ext Property map carrying per-entity QoS overrides.
164  */
165  explicit DdsConf(const std::string& _topic, int32_t _domain, const PropertiesMap& _qos_ext);
166 
167  /**
168  * @brief Component-wise equality on all configuration fields.
169  *
170  * @param conf Configuration to compare with.
171  * @return @c true when @c topic, @c domain, @c depth, @c qos and @c qos_ext all match.
172  */
173  [[nodiscard]] bool operator==(const DdsConf& conf) const noexcept;
174 
175  /**
176  * @brief Logical negation of @c operator==.
177  *
178  * @param conf Configuration to compare with.
179  * @return @c true when any field differs from @p conf.
180  */
181  [[nodiscard]] bool operator!=(const DdsConf& conf) const noexcept;
182 
183  /**
184  * @brief Reports this object's transport tag.
185  *
186  * @return @c TransportType::kDds.
187  */
188  [[nodiscard]] TransportType get_transport_type() const override;
189 
190  /**
191  * @brief Returns the topics currently discovered on the given DDS Domain.
192  *
193  * @details
194  * Each entry is a @c (topic_name, type_name) pair captured from the
195  * @c DdsFactory discovery cache. The result is a point-in-time snapshot and may
196  * be empty when discovery has not yet completed.
197  *
198  * @param _domain DDS Domain ID to query.
199  * @return Vector of @c (topic_name, type_name) tuples; may be empty.
200  */
201  [[nodiscard]] static std::vector<std::tuple<std::string, std::string>> get_discovered_topics(int32_t _domain);
202 
203  /**
204  * @brief Loads a Fast-DDS XML QoS profile file as the process-wide default.
205  *
206  * @details
207  * Must be invoked before any @c dds:// participant is created; profile names
208  * declared in the file become available to all Fast-DDS endpoints in the process.
209  *
210  * @param filepath Absolute or relative path to a Fast-DDS XML profile file.
211  * @return @c true when the file was loaded successfully, @c false otherwise.
212  */
213  static bool load_global_qos_file(const std::string& filepath);
214 
215  /**
216  * @brief Registers a Fast-DDS @c TopicDataType factory for a topic name.
217  *
218  * @details
219  * Optional for unkeyed CDR byte-stream transport. Call once per topic before
220  * any endpoint is opened when native key extraction, TypeObject metadata, or
221  * an accurate maximum serialised size is required. When
222  * @c TypeSupportRespT is not @c void, the response type is also registered
223  * under the topic name with a trailing @c "___resp" suffix.
224  *
225  * @tparam TypeSupportT Fast-DDS @c TopicDataType subclass for the request/message type.
226  * @tparam TypeSupportRespT Fast-DDS @c TopicDataType subclass for the response type; @c void to skip.
227  * @param name DDS topic name the factory is bound to.
228  */
229  template <typename TypeSupportT, typename TypeSupportRespT = void>
230  static void register_topic(const std::string& name);
231 
232  /**
233  * @brief Convenience wrapper that derives the topic name from a @c dds:// URL.
234  *
235  * @details
236  * Parses @p name with @c UrlParser, extracts the topic part, and forwards to
237  * @c register_topic<TypeSupportT, TypeSupportRespT>().
238  *
239  * @tparam TypeSupportT Fast-DDS @c TopicDataType subclass for the request type.
240  * @tparam TypeSupportRespT Fast-DDS @c TopicDataType subclass for the response type; @c void to skip.
241  * @param name Full URL string, for example @c "dds://my_topic?domain=1".
242  */
243  template <typename TypeSupportT, typename TypeSupportRespT = void>
244  static void register_url(const std::string& name);
245 
246  /**
247  * @brief Registers a named QoS profile that endpoints may reference via @c ?qos=.
248  *
249  * @details
250  * Profile names share a global namespace. Collisions with DDS-reserved tokens
251  * (@c part, @c topic, @c pub, @c sub, @c writer, @c reader, @c depth) or with an
252  * already registered profile abort with a fatal log entry.
253  *
254  * @param name Unique profile key; must not collide with any reserved token.
255  * @param qos @c Qos value associated with the key.
256  */
257  static void register_qos(const std::string& name, const Qos& qos);
258 
259  private:
260  template <typename TypeSupportT>
261  static void register_topic_internal(const std::string& name);
262 
263  static void register_qos_internal(const std::string& name, const Qos& qos);
264 
265  static Function<void*()> find_type_support(const std::string& name);
266 
267  static const Qos& find_qos(const std::string& name);
268 
269  static std::string get_topic_for_url(const std::string& url);
270 
271  friend class DdsFactory;
272  static std::map<std::string, Function<void*()>> type_support_map_;
273  static std::map<std::string, Qos> qos_map_;
274  static std::shared_mutex mtx_;
275  static constexpr const char* kRespSuffix{"___resp"};
276 #ifndef VLINK_ENABLE_C_INTERFACE
278 #endif
280  VLINK_CONF_IMPL(DdsConf)
281 };
282 
283 ////////////////////////////////////////////////////////////////
284 /// Details
285 ////////////////////////////////////////////////////////////////
286 
287 inline DdsConf::DdsConf(const std::string& _topic, int32_t _domain, int32_t _depth, const std::string& _qos)
288  : topic(_topic), domain(_domain), depth(_depth), qos(_qos) {}
289 
290 inline DdsConf::DdsConf(const std::string& _topic, int32_t _domain, const PropertiesMap& _qos_ext)
291  : topic(_topic), domain(_domain), qos_ext(_qos_ext) {}
292 
293 inline bool DdsConf::operator==(const DdsConf& conf) const noexcept {
294  return topic == conf.topic && domain == conf.domain && depth == conf.depth && qos == conf.qos &&
295  qos_ext == conf.qos_ext;
296 }
297 
298 inline bool DdsConf::operator!=(const DdsConf& conf) const noexcept { return !(*this == conf); }
299 
300 inline TransportType DdsConf::get_transport_type() const { return TransportType::kDds; }
301 
302 template <typename TypeSupportT, typename TypeSupportRespT>
303 inline void DdsConf::register_topic(const std::string& name) {
304  std::lock_guard lock(mtx_);
305  register_topic_internal<TypeSupportT>(name);
306  if constexpr (!std::is_same_v<TypeSupportRespT, void>) {
307  register_topic_internal<TypeSupportRespT>(name + kRespSuffix);
308  }
309 }
310 
311 template <typename TypeSupportT, typename TypeSupportRespT>
312 inline void DdsConf::register_url(const std::string& name) {
313  register_topic<TypeSupportT, TypeSupportRespT>(get_topic_for_url(name));
314 }
315 
316 template <typename TypeSupportT>
317 inline void DdsConf::register_topic_internal(const std::string& name) {
318  static_assert(std::is_base_of_v<eprosima::fastdds::dds::TopicDataType, TypeSupportT>, "Must be dds type.");
319 
320  type_support_map_[name] = [] { return new TypeSupportT(); };
321 }
322 
323 } // namespace vlink
324 
325 #endif
#define VLINK_CONF_IMPL(classname)
Convenience macro that emits the standard concrete conf boilerplate.
Definition: conf.h:250
#define VLINK_DECLARE_GLOBAL_PROPERTY()
Declares per-transport static configuration storage and access helpers.
Definition: conf.h:308
#define VLINK_ALLOW_IMPL_TYPE(type)
Records the bitmask of ImplType values supported by a conf.
Definition: conf.h:291
#define VLINK_EXPORT
Definition: macros.h:81