Skip to content

Commit 3332095

Browse files
authored
Support QoS options for ros2 service call (#966)
* add QoS option for ros2 service call. Signed-off-by: Tomoya Fujita <[email protected]> * add unit test for ros2 service call with all QoS options. Signed-off-by: Tomoya Fujita <[email protected]> --------- Signed-off-by: Tomoya Fujita <[email protected]>
1 parent 4e51c02 commit 3332095

File tree

5 files changed

+75
-18
lines changed

5 files changed

+75
-18
lines changed

ros2service/package.xml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<exec_depend>python3-yaml</exec_depend>
2121
<exec_depend>rclpy</exec_depend>
2222
<exec_depend>ros2cli</exec_depend>
23+
<exec_depend>ros2topic</exec_depend>
2324
<exec_depend>rosidl_runtime_py</exec_depend>
2425

2526
<test_depend>ament_copyright</test_depend>

ros2service/ros2service/verb/call.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
import time
1717

1818
import rclpy
19+
from rclpy.qos import QoSPresetProfiles
1920
from ros2cli.helpers import collect_stdin
2021
from ros2cli.node import NODE_NAME_PREFIX
2122
from ros2service.api import ServiceNameCompleter
2223
from ros2service.api import ServicePrototypeCompleter
2324
from ros2service.api import ServiceTypeCompleter
2425
from ros2service.verb import VerbExtension
26+
from ros2topic.api import add_qos_arguments, profile_configure_short_keys
2527
from rosidl_runtime_py import set_message_fields
2628
import yaml
2729

@@ -54,22 +56,28 @@ def add_arguments(self, parser, cli_name):
5456
parser.add_argument(
5557
'-r', '--rate', metavar='N', type=float,
5658
help='Repeat the call at a specific rate in Hz')
59+
add_qos_arguments(parser, 'service client', 'services_default')
5760

5861
def main(self, *, args):
5962
if args.rate is not None and args.rate <= 0:
6063
raise RuntimeError('rate must be greater than zero')
6164
period = 1. / args.rate if args.rate else None
6265

66+
default_profile = QoSPresetProfiles.get_from_short_key(args.qos_profile)
67+
profile_configure_short_keys(
68+
default_profile, args.qos_reliability, args.qos_durability,
69+
args.qos_depth, args.qos_history)
70+
6371
if args.stdin:
6472
values = collect_stdin()
6573
else:
6674
values = args.values
6775

6876
return requester(
69-
args.service_type, args.service_name, values, period)
77+
args.service_type, args.service_name, values, period, default_profile)
7078

7179

72-
def requester(service_type, service_name, values, period):
80+
def requester(service_type, service_name, values, period, qos_profile):
7381
# TODO(wjwwood) this logic should come from a rosidl related package
7482
try:
7583
parts = service_type.split('/')
@@ -92,7 +100,10 @@ def requester(service_type, service_name, values, period):
92100
with rclpy.init():
93101
node = rclpy.create_node(NODE_NAME_PREFIX + '_requester_%s_%s' % (package_name, srv_name))
94102

95-
cli = node.create_client(srv_module, service_name)
103+
cli = node.create_client(
104+
srv_module,
105+
service_name,
106+
qos_profile=qos_profile)
96107

97108
request = srv_module.Request()
98109

ros2service/test/test_cli.py

+37
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,40 @@ def test_repeated_call(self):
326326
bool_value=True, int32_value=1, float64_value=1.0, string_value='foobar'
327327
)
328328
), timeout=10)
329+
330+
@launch_testing.markers.retry_on_failure(times=5, delay=1)
331+
def test_call_with_qos_option(self):
332+
with self.launch_service_command(
333+
arguments=[
334+
'call',
335+
'--qos-profile',
336+
'system_default',
337+
'--qos-depth',
338+
'5',
339+
'--qos-history',
340+
'system_default',
341+
'--qos-reliability',
342+
'system_default',
343+
'--qos-durability',
344+
'system_default',
345+
'--qos-liveliness',
346+
'system_default',
347+
'--qos-liveliness-lease-duration',
348+
'0',
349+
'/my_ns/echo',
350+
'test_msgs/srv/BasicTypes',
351+
'{bool_value: false, int32_value: -1, float64_value: 0.1, string_value: bazbar}'
352+
]
353+
) as service_command:
354+
assert service_command.wait_for_shutdown(timeout=10)
355+
assert service_command.exit_code == launch_testing.asserts.EXIT_OK
356+
assert launch_testing.tools.expect_output(
357+
expected_lines=get_echo_call_output(
358+
bool_value=False,
359+
int32_value=-1,
360+
float64_value=0.1,
361+
string_value='bazbar'
362+
),
363+
text=service_command.output,
364+
strict=True
365+
)

ros2topic/ros2topic/api/__init__.py

+20-14
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def profile_configure_short_keys(
173173
profile: rclpy.qos.QoSProfile = None, reliability: Optional[str] = None,
174174
durability: Optional[str] = None, depth: Optional[int] = None, history: Optional[str] = None,
175175
liveliness: Optional[str] = None, liveliness_lease_duration_s: Optional[int] = None,
176-
) -> rclpy.qos.QoSProfile:
176+
) -> None:
177177
"""Configure a QoSProfile given a profile, and optional overrides."""
178178
if history:
179179
profile.history = rclpy.qos.QoSHistoryPolicy.get_from_short_key(history)
@@ -206,50 +206,56 @@ def qos_profile_from_short_keys(
206206
return profile
207207

208208

209-
def add_qos_arguments(parser: ArgumentParser, subscribe_or_publish: str, default_profile_str):
209+
def add_qos_arguments(
210+
parser: ArgumentParser, entity_type: str,
211+
default_profile_str: str = 'default', extra_message: str = ''
212+
) -> None:
210213
parser.add_argument(
211214
'--qos-profile',
212215
choices=rclpy.qos.QoSPresetProfiles.short_keys(),
213216
help=(
214-
f'Quality of service preset profile to {subscribe_or_publish} with'
217+
f'Quality of service preset profile to {entity_type} with'
215218
f' (default: {default_profile_str})'),
216219
default=default_profile_str)
217220
default_profile = rclpy.qos.QoSPresetProfiles.get_from_short_key(default_profile_str)
218221
parser.add_argument(
219222
'--qos-depth', metavar='N', type=int,
220223
help=(
221-
f'Queue size setting to {subscribe_or_publish} with '
222-
'(overrides depth value of --qos-profile option)'))
224+
f'Queue size setting to {entity_type} with '
225+
'(overrides depth value of --qos-profile option, default: '
226+
f'{default_profile.depth})'))
223227
parser.add_argument(
224228
'--qos-history',
225229
choices=rclpy.qos.QoSHistoryPolicy.short_keys(),
226230
help=(
227-
f'History of samples setting to {subscribe_or_publish} with '
231+
f'History of samples setting to {entity_type} with '
228232
'(overrides history value of --qos-profile option, default: '
229233
f'{default_profile.history.short_key})'))
230234
parser.add_argument(
231235
'--qos-reliability',
232236
choices=rclpy.qos.QoSReliabilityPolicy.short_keys(),
233237
help=(
234-
f'Quality of service reliability setting to {subscribe_or_publish} with '
238+
f'Quality of service reliability setting to {entity_type} with '
235239
'(overrides reliability value of --qos-profile option, default: '
236-
'Compatible profile with running endpoints )'))
240+
f'{default_profile.reliability.short_key} {extra_message})'))
237241
parser.add_argument(
238242
'--qos-durability',
239243
choices=rclpy.qos.QoSDurabilityPolicy.short_keys(),
240244
help=(
241-
f'Quality of service durability setting to {subscribe_or_publish} with '
245+
f'Quality of service durability setting to {entity_type} with '
242246
'(overrides durability value of --qos-profile option, default: '
243-
'Compatible profile with running endpoints )'))
247+
f'{default_profile.durability.short_key} {extra_message})'))
244248
parser.add_argument(
245249
'--qos-liveliness',
246250
choices=rclpy.qos.QoSLivelinessPolicy.short_keys(),
247251
help=(
248-
f'Quality of service liveliness setting to {subscribe_or_publish} with '
249-
'(overrides liveliness value of --qos-profile option'))
252+
f'Quality of service liveliness setting to {entity_type} with '
253+
'(overrides liveliness value of --qos-profile option, default '
254+
f'{default_profile.liveliness.short_key})'))
250255
parser.add_argument(
251256
'--qos-liveliness-lease-duration-seconds',
252257
type=float,
253258
help=(
254-
f'Quality of service liveliness lease duration setting to {subscribe_or_publish} '
255-
'with (overrides liveliness lease duration value of --qos-profile option'))
259+
f'Quality of service liveliness lease duration setting to {entity_type} '
260+
'with (overrides liveliness lease duration value of --qos-profile option, default: '
261+
f'{default_profile.liveliness_lease_duration})'))

ros2topic/ros2topic/verb/echo.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ def add_arguments(self, parser, cli_name):
5757
parser.add_argument(
5858
'message_type', nargs='?',
5959
help="Type of the ROS message (e.g. 'std_msgs/msg/String')")
60-
add_qos_arguments(parser, 'subscribe', 'sensor_data')
60+
add_qos_arguments(
61+
parser, 'subscribe', 'sensor_data',
62+
' / compatible profile with running endpoints')
6163
parser.add_argument(
6264
'--csv', action='store_true',
6365
help=(

0 commit comments

Comments
 (0)