VLink 2.0.0
A high-performance communication middleware
载入中...
搜索中...
未找到
proxy_server.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 proxy_server.h
26 * @brief VLink proxy server daemon -- singleton per process.
27 *
28 * @details
29 * @c ProxyServer is the server-side half of the VLink proxy subsystem. It
30 * inherits from @c MessageLoop and runs an event loop that:
31 *
32 * -# Hosts a @c DiscoveryViewer to enumerate all active publishers and
33 * subscribers on the DDS domain.
34 * -# Accepts @c Control messages from @c ProxyAPI clients via a
35 * security-authenticated DDS channel.
36 * -# Broadcasts a 1-second @c Time heartbeat carrying CPU/memory usage,
37 * version string, hostname, and wall-clock / boot-time.
38 * -# Publishes per-topic statistics (@c freq, @c rate, @c loss, @c latency)
39 * once per second via a security-authenticated @c InfoList channel.
40 * -# Relays raw message bytes from discovered publishers to connected
41 * @c ProxyAPI listeners when operating in observe, record, or play mode.
42 * -# Optionally manages an embedded Iceoryx RouDi daemon when
43 * @c Config::use_iox is @c true.
44 * -# Loads and manages @c RunablePluginInterface shared-library plugins
45 * from @c Config::runnable_list.
46 *
47 * @par Singleton Constraint
48 * Only one @c ProxyServer may be constructed per operating-system process.
49 * A second construction attempt logs a fatal message and returns without
50 * initialising any channels.
51 *
52 * @par Communication Architecture
53 * @code
54 * ProxyAPI (kController)
55 * |--- ControlPub --> [DDS] --> ControlSub ---|
56 * | v
57 * | ProxyServer (this)
58 * | |
59 * |<-- TimeSub <--- [DDS] <--- TimePub -------|
60 * |<-- InfoSub <--- [DDS] <--- InfoPub -------|
61 * |<-- DataSub <--- [DDS/SHM] <- DataPub -----|
62 * @endcode
63 *
64 * @par Runnable Plugin Lifecycle
65 * Plugins listed in @c Config::runnable_list are loaded in the constructor.
66 * When the MessageLoop starts (@c on_begin) each plugin's @c on_init() and
67 * @c async_run() are called. When the loop stops (@c on_end) each plugin's
68 * @c on_deinit(), @c quit(), and @c wait_for_quit() are called in order.
69 *
70 * @par Environment Variables
71 * - @c VLINK_INTRA_BIND -- when set (any value), the server subscribes to
72 * @c intra:// topics in addition to DDS/SHM, enabling in-process observation.
73 *
74 * @par Usage Example
75 * @code
76 * vlink::ProxyServer::Config cfg;
77 * cfg.dds_impl = "dds";
78 * cfg.domain_id = 0;
79 * cfg.reliable = false;
80 * cfg.async = true;
81 * cfg.use_iox = false;
82 *
83 * vlink::ProxyServer server(cfg);
84 * server.async_run(); // start event loop in a background thread
85 * // ... application runs ...
86 * server.quit(true);
87 * server.wait_for_quit();
88 * @endcode
89 *
90 * @note
91 * - The constructor must be called on the main thread before any @c ProxyAPI
92 * clients connect on the same domain.
93 * - @c ProxyServer must be destroyed before the process exits; the destructor
94 * stops all timers, waits for the DiscoveryViewer, and releases every DDS
95 * handle in a deterministic order to avoid dangling callbacks.
96 */
97
98#pragma once
99
100#undef VLINK_PROXY_SERVER_EXPORT
101#ifdef VLINK_PROXY_SERVER_LIBRARY_STATIC
102#define VLINK_PROXY_SERVER_EXPORT
103#elif defined(_WIN32) || defined(__CYGWIN__)
104#ifdef VLINK_PROXY_SERVER_LIBRARY
105#define VLINK_PROXY_SERVER_EXPORT __declspec(dllexport)
106#else
107#define VLINK_PROXY_SERVER_EXPORT __declspec(dllimport)
108#endif
109#else
110#define VLINK_PROXY_SERVER_EXPORT __attribute__((visibility("default")))
111#endif
112
113#include <cstdint>
114#include <memory>
115#include <string>
116#include <vector>
117
118#include "../base/message_loop.h"
119
120namespace vlink {
121
122/**
123 * @class ProxyServer
124 * @brief VLink proxy server daemon backed by a MessageLoop.
125 *
126 * @details
127 * Manages all DDS/SHM channels, a @c DiscoveryViewer, heartbeat timers, and
128 * optional runnable plugins. Only one instance may exist per process.
129 * Use @c async_run() (inherited from @c MessageLoop) to start the event loop.
130 */
132 public:
133 /**
134 * @struct Config
135 * @brief Construction-time configuration for @c ProxyServer.
136 *
137 * @details
138 * The fields @c reliable, @c enable_tcp, and @c direct are broadcast in every
139 * @c Time heartbeat so that connecting @c ProxyAPI clients can verify
140 * compatibility. Mismatches result in the client reporting an error and
141 * refusing to connect.
142 *
143 * @par Field Summary
144 *
145 * | Field | Default | Description |
146 * | ----------------------- | -------- | --------------------------------------------------------------------- |
147 * | async | false | Publish data on the MessageLoop thread; false = inline on subscriber. |
148 * | reliable | false | Use reliable DDS QoS for data channels. |
149 * | enable_tcp | false | Use TCP transport for data channels. |
150 * | direct | false | Use SHM (Iceoryx) instead of DDS for data forwarding. |
151 * | native_mode | false | Restrict all DDS traffic to 127.0.0.1 (loopback). |
152 * | domain_id | 0 | DDS domain ID shared with all clients. |
153 * | buf_size | 0 | DDS socket buffer size in bytes; 0 = built-in default. |
154 * | mtu_size | 0 | DDS MTU size in bytes; 0 = built-in default. |
155 * | max_packet_size | 0 | Maximum relayed message size in MiB; 0 = unlimited. |
156 * | security_key | "" | Security key for Time, Info, and Control DDS channels. |
157 * | bind_ip | "" | Bind DDS sockets to this IP; empty = any interface. |
158 * | peer_ip | "" | Unicast peer IP for DDS discovery; empty = multicast. |
159 * | dds_impl | "dds" | DDS implementation: "dds", "ddsc", "ddsr", etc. |
160 * | use_iox | false | Launch an embedded Iceoryx RouDi daemon at startup. |
161 * | iox_monitoring | true | Enable Iceoryx introspection/monitoring. |
162 * | iox_strategy | 1 | Iceoryx memory strategy index passed to ShmConf::init_roudi(). |
163 * | iox_config | "" | Path to a custom Iceoryx TOML configuration file; empty = default. |
164 * | runnable_version_major | 1 | Required major version for loaded runnable plugins. |
165 * | runnable_version_minor | 0 | Required minor version for loaded runnable plugins. |
166 * | runnable_prefix | "" | Library name prefix for plugin shared objects. |
167 * | runnable_list | {} | Names of runnable plugins (@c RunablePluginInterface in the API). |
168 */
169 struct Config final {
170 bool async{false}; ///< Async data forwarding on the MessageLoop thread.
171 bool reliable{false}; ///< Use reliable DDS QoS; must match all client configs.
172 bool enable_tcp{false}; ///< Use TCP transport for DDS data channels.
173 bool direct{false}; ///< Use SHM channels for data (requires use_iox or external RouDi).
174 bool native_mode{false}; ///< Restrict all DDS traffic to loopback (127.0.0.1).
175 int domain_id{0}; ///< DDS domain ID.
176 uint32_t buf_size{0}; ///< DDS socket send/receive buffer in bytes; 0 = default.
177 uint32_t mtu_size{0}; ///< DDS fragment MTU in bytes; 0 = default.
178 double max_packet_size{0}; ///< Maximum relayed payload in MiB; 0 = no limit.
179 std::string security_key; ///< Security key for authenticated DDS channels.
180 std::string bind_ip; ///< Local IP to bind DDS sockets; empty = any.
181 std::string peer_ip; ///< Peer unicast IP for DDS; empty = multicast.
182 std::string dds_impl{"dds"}; ///< DDS implementation transport.
183 bool use_iox{false}; ///< Initialise embedded Iceoryx RouDi daemon.
184 bool iox_monitoring{true}; ///< Enable Iceoryx introspection monitoring.
185 int iox_strategy{1}; ///< Iceoryx memory allocation strategy.
186 std::string iox_config; ///< Path to Iceoryx TOML config file; empty = default.
187 uint16_t runnable_version_major{1}; ///< Required major ABI version for runnable plugins.
188 uint16_t runnable_version_minor{0}; ///< Required minor ABI version for runnable plugins.
189 std::string runnable_prefix; ///< Library filename prefix for plugin discovery.
190 std::vector<std::string> runnable_list; ///< Ordered list of plugin names to load on startup.
191 };
192
193 /**
194 * @brief Constructs a @c ProxyServer and initialises all subsystems.
195 *
196 * @details
197 * Performs the following steps in order:
198 * -# Checks the process-global singleton guard; logs a fatal message and
199 * returns early if another instance already exists.
200 * -# Reads the @c VLINK_INTRA_BIND environment variable.
201 * -# If @c config.use_iox is @c true, calls @c init_shm_roudi() to start
202 * an embedded Iceoryx RouDi process.
203 * -# Calls @c init_server() to create all DDS/SHM channels, subscribe to
204 * @c Control messages, and start the 1-second @c time_timer and
205 * @c info_timer on the @c DiscoveryViewer's loop.
206 * -# Calls @c init_runnable() to load all plugins listed in
207 * @c config.runnable_list.
208 *
209 * @param config Server configuration. See @c Config for field details.
210 *
211 * @note The constructor does not start the MessageLoop; call @c async_run()
212 * (or @c run()) separately.
213 */
214 explicit ProxyServer(const Config& config);
215
216 /**
217 * @brief Destructor.
218 *
219 * @details
220 * Stops and waits for the MessageLoop, all runnable plugins, the
221 * @c DiscoveryViewer, and all DDS handles in a deterministic teardown
222 * sequence. Also marks the singleton guard so future constructions in
223 * the same process behave correctly.
224 */
225 ~ProxyServer() override;
226
227 protected:
228 size_t get_max_task_count() const override;
229
230 uint32_t get_max_elapsed_time() const override;
231
232 void on_begin() override;
233
234 void on_end() override;
235
236 private:
237 void init_shm_roudi();
238
239 void init_server();
240
241 void init_runnable();
242
243 void send_time();
244
245 void send_control(const void* control_data);
246
247 void update_all();
248
249 std::unique_ptr<struct ProxyServerImpl> impl_;
250
252};
253
254} // namespace vlink
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
定义 macros.h:184
Single-threaded event loop with three queue types, timer management and task scheduling.
#define VLINK_PROXY_SERVER_EXPORT
定义 proxy_server.h:110