Skip to content

Commit 0f8db17

Browse files
committed
Add clients and servers info by service
Signed-off-by: Minju, Lee <dlalswn531@naver.com>
1 parent e6addf2 commit 0f8db17

10 files changed

+1824
-1
lines changed

rmw/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ set(rmw_sources
4242
"src/qos_string_conversions.c"
4343
"src/sanity_checks.c"
4444
"src/security_options.c"
45+
"src/service_endpoint_info_array.c"
46+
"src/service_endpoint_info.c"
4547
"src/subscription_content_filter_options.c"
4648
"src/subscription_options.c"
4749
"src/time.c"
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// Copyright 2025 Minju Lee (이민주). All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef RMW__GET_SERVICE_ENDPOINT_INFO_H_
16+
#define RMW__GET_SERVICE_ENDPOINT_INFO_H_
17+
18+
#ifdef __cplusplus
19+
extern "C"
20+
{
21+
#endif
22+
23+
#include "rmw/service_endpoint_info_array.h"
24+
#include "rmw/visibility_control.h"
25+
/// Retrieve endpoint information for each known client of a given service.
26+
/**
27+
* This function returns an array of endpoint information for each client
28+
* of a given service, as discovered so far by the given node.
29+
* Endpoint information includes the client's node name and namespace,
30+
* the associated service type, the client's gid, and the client QoS profile.
31+
* Names of non-existent services are allowed, in which case an empty array will be returned.
32+
*
33+
* Depending on the RMW in use, discovery may be asynchronous. Therefore, creating a client
34+
* and then calling this API may not show the newly created client immediately.
35+
*
36+
* \par Internal behavior
37+
* The internal representation of services depends on the underlying RMW implementation.
38+
* For example, in DDS-based RMWs, services are implemented using two topics:
39+
* one for requests and one for responses. The client and server each create both
40+
* a DataReader and a DataWriter — the client writes requests and reads responses,
41+
* while the server reads requests and writes responses.
42+
* In this case, all fields of `rmw_topic_endpoint_info_t` can be queried from the graph cache.
43+
*
44+
* Other middleware implementations, such as Zenoh, may offer native support for services,
45+
* without using topics internally. This API is designed to support both models and
46+
* can return endpoint information regardless of the underlying implementation strategy.
47+
*
48+
* \par QoS that are correctly read
49+
* The QoS profiles returned might have some invalid fields.
50+
* The rmw implementation must set the invalid fields to `RMW_QOS_POLICY_*_UNKNOWN`.
51+
* For DDS based implementations, the only QoS policies that are guaranteed to be shared
52+
* during discovery are the ones that participate in endpoint matching.
53+
* From the current QoS settings available, the only ones not shared by DDS based
54+
* implementations are `history` and `history_depth`.
55+
*
56+
* <hr>
57+
* Attribute | Adherence
58+
* ------------------ | -------------
59+
* Allocates Memory | Yes
60+
* Thread-Safe | Yes
61+
* Uses Atomics | Maybe [1]
62+
* Lock-Free | Maybe [1]
63+
* <i>[1] rmw implementation defined, check the implementation documentation</i>
64+
*
65+
* \par Runtime behavior
66+
* To query the ROS graph is a synchronous operation.
67+
* It is also non-blocking, but it is not guaranteed to be lock-free.
68+
* Generally speaking, implementations may synchronize access to internal resources using
69+
* locks but are not allowed to wait for events with no guaranteed time bound (barring
70+
* the effects of starvation due to OS scheduling).
71+
*
72+
* \par Thread-safety
73+
* Nodes are thread-safe objects, and so are all operations on them except for finalization.
74+
* Therefore, it is safe to query the ROS graph using the same node concurrently.
75+
* However, when querying service names and types:
76+
* - Access to the array of service endpoint information is not synchronized.
77+
* It is not safe to read or write `clients_info`
78+
* while rmw_get_clients_info_by_service() uses it.
79+
* - Access to C-style string arguments is read-only but it is not synchronized.
80+
* Concurrent `service_name` reads are safe, but concurrent reads and writes are not.
81+
* - The default allocators are thread-safe objects, but any custom `allocator` may not be.
82+
* Check your allocator documentation for further reference.
83+
*
84+
* \pre Given `node` must be a valid node handle, as returned by rmw_create_node().
85+
* \pre Given `clients_info` must be a zero-initialized array of endpoints' information,
86+
* as returned by rmw_get_zero_initialized_service_endpoint_info_array().
87+
*
88+
* \param[in] node Node to query the ROS graph.
89+
* \param[in] allocator Allocator to be used when populating the `clients_info` array.
90+
* \param[in] service_name Name of the service for client lookup, often a fully qualified
91+
* service name but not necessarily (see rmw_create_client()).
92+
* \param[in] no_mangle Whether to mangle the service name before client lookup or not.
93+
* Note: DDS-based RMWs do not support `no_mangle = true` because services are implemented
94+
* as topics with mangled names. Use `rmw_get_publishers_info_by_topic` or
95+
* `rmw_get_subscriptions_info_by_topic` for unmangled topic queries in such cases.
96+
* Other RMWs (e.g., Zenoh) may support `no_mangle = true` if they natively handle services
97+
* without topic-based mangling.
98+
* \param[out] clients_info Array of client information, populated on success,
99+
* left unchanged on failure.
100+
* If populated, it is up to the caller to finalize this array later on,
101+
* using rmw_service_endpoint_info_array_fini().
102+
* QoS Profiles in the info array will use RMW_DURATION_INFINITE for infinite durations,
103+
* avoiding exposing any implementation-specific values.
104+
* \return `RMW_RET_OK` if the query was successful, or
105+
* \return `RMW_RET_INVALID_ARGUMENT` if `node` is NULL, or
106+
* \return `RMW_RET_INVALID_ARGUMENT` if `allocator` is not valid,
107+
* by rcutils_allocator_is_valid() definition, or
108+
* \return `RMW_RET_INVALID_ARGUMENT` if `service_name` is NULL, or
109+
* \return `RMW_RET_INVALID_ARGUMENT` if `clients_info` is NULL, or
110+
* \return `RMW_RET_INVALID_ARGUMENT` if `clients_info` is not a
111+
* zero-initialized array, or
112+
* \return `RMW_RET_INCORRECT_RMW_IMPLEMENTATION` if the `node` implementation
113+
* identifier does not match this implementation, or
114+
* \return `RMW_RET_BAD_ALLOC` if memory allocation fails, or
115+
* \return `RMW_RET_ERROR` if an unspecified error occurs.
116+
*/
117+
RMW_PUBLIC
118+
RMW_WARN_UNUSED
119+
rmw_ret_t
120+
rmw_get_clients_info_by_service(
121+
const rmw_node_t * node,
122+
rcutils_allocator_t * allocator,
123+
const char * service_name,
124+
bool no_mangle,
125+
rmw_service_endpoint_info_array_t * clients_info);
126+
127+
/// Retrieve endpoint information for each known server of a given server.
128+
/**
129+
* This function returns an array of endpoint information for each server
130+
* of a given service, as discovered so far by the given node.
131+
* Endpoint information includes the server's node name and namespace,
132+
* the associated service type, the server's gid, and the server QoS profile.
133+
* Names of non-existent services are allowed, in which case an empty array will be returned.
134+
*
135+
* Depending on the RMW in use, discovery may be asynchronous. Therefore, creating a server
136+
* and then calling this API may not show the newly created server immediately.
137+
*
138+
* \par Internal behavior
139+
* The internal representation of services depends on the underlying RMW implementation.
140+
* For example, in DDS-based RMWs, services are implemented using two topics:
141+
* one for requests and one for responses. The client and server each create both
142+
* a DataReader and a DataWriter — the client writes requests and reads responses,
143+
* while the server reads requests and writes responses.
144+
* In this case, all fields of `rmw_topic_endpoint_info_t` can be queried from the graph cache.
145+
*
146+
* Other middleware implementations, such as Zenoh, may offer native support for services,
147+
* without using topics internally. This API is designed to support both models and
148+
* can return endpoint information regardless of the underlying implementation strategy.
149+
*
150+
* \par QoS that are correctly read
151+
* Not all QoS may be read correctly, \sa rmw_get_publishers_info_by_topic() for more details.
152+
*
153+
* <hr>
154+
* Attribute | Adherence
155+
* ------------------ | -------------
156+
* Allocates Memory | Yes
157+
* Thread-Safe | Yes
158+
* Uses Atomics | Maybe [1]
159+
* Lock-Free | Maybe [1]
160+
* <i>[1] rmw implementation defined, check the implementation documentation</i>
161+
*
162+
* \par Runtime behavior
163+
* To query the ROS graph is a synchronous operation.
164+
* It is also non-blocking, but it is not guaranteed to be lock-free.
165+
* Generally speaking, implementations may synchronize access to internal resources using
166+
* locks but are not allowed to wait for events with no guaranteed time bound (barring
167+
* the effects of starvation due to OS scheduling).
168+
*
169+
* \par Thread-safety
170+
* Nodes are thread-safe objects, and so are all operations on them except for finalization.
171+
* Therefore, it is safe to query the ROS graph using the same node concurrently.
172+
* However, when querying service names and types:
173+
* - Access to the array of service endpoint information is not synchronized.
174+
* It is not safe to read or write `servers_info`
175+
* while rmw_get_servers_info_by_service() uses it.
176+
* - Access to C-style string arguments is read-only but it is not synchronized.
177+
* Concurrent `service_name` reads are safe, but concurrent reads and writes are not.
178+
* - The default allocators are thread-safe objects, but any custom `allocator` may not be.
179+
* Check your allocator documentation for further reference.
180+
*
181+
* \pre Given `node` must be a valid node handle, as returned by rmw_create_node().
182+
* \pre Given `servers_info` must be a zero-initialized array of endpoints' information,
183+
* as returned by rmw_get_zero_initialized_service_endpoint_info_array().
184+
*
185+
* \param[in] node Node to query the ROS graph.
186+
* \param[in] allocator Allocator to be used when populating the `servers_info` array.
187+
* \param[in] service_name Name of the service for server lookup, often a fully qualified
188+
* service name but not necessarily (see rmw_create_service()).
189+
* \param[in] no_mangle Whether to mangle the service name before client lookup or not.
190+
* Note: DDS-based RMWs do not support `no_mangle = true` because services are implemented
191+
* as topics with mangled names. Use `rmw_get_publishers_info_by_topic` or
192+
* `rmw_get_subscriptions_info_by_topic` for unmangled topic queries in such cases.
193+
* Other RMWs (e.g., Zenoh) may support `no_mangle = true` if they natively handle services
194+
* without topic-based mangling.
195+
* \param[out] servers_info Array of server information, populated on success,
196+
* left unchanged on failure.
197+
* If populated, it is up to the caller to finalize this array later on,
198+
* using rmw_service_endpoint_info_array_fini().
199+
* QoS Profiles in the info array will use RMW_DURATION_INFINITE for infinite durations,
200+
* avoiding exposing any implementation-specific values.
201+
* \return `RMW_RET_OK` if the query was successful, or
202+
* \return `RMW_RET_INVALID_ARGUMENT` if `node` is NULL, or
203+
* \return `RMW_RET_INVALID_ARGUMENT` if `allocator` is not valid,
204+
* by rcutils_allocator_is_valid() definition, or
205+
* \return `RMW_RET_INVALID_ARGUMENT` if `service_name` is NULL, or
206+
* \return `RMW_RET_INVALID_ARGUMENT` if `servers_info` is NULL, or
207+
* \return `RMW_RET_INVALID_ARGUMENT` if `servers_info` is not a
208+
* zero-initialized array, or
209+
* \return `RMW_RET_INCORRECT_RMW_IMPLEMENTATION` if the `node` implementation
210+
* identifier does not match this implementation, or
211+
* \return `RMW_RET_BAD_ALLOC` if memory allocation fails, or
212+
* \return `RMW_RET_ERROR` if an unspecified error occurs.
213+
*/
214+
RMW_PUBLIC
215+
RMW_WARN_UNUSED
216+
rmw_ret_t
217+
rmw_get_servers_info_by_service(
218+
const rmw_node_t * node,
219+
rcutils_allocator_t * allocator,
220+
const char * service_name,
221+
bool no_mangle,
222+
rmw_service_endpoint_info_array_t * servers_info);
223+
224+
#ifdef __cplusplus
225+
}
226+
#endif
227+
228+
#endif // RMW__GET_SERVICE_ENDPOINT_INFO_H_

0 commit comments

Comments
 (0)