Skip to content

Commit

Permalink
Support to get remapped service name (#1156)
Browse files Browse the repository at this point in the history
* Support to get remapped service name

Signed-off-by: Barry Xu <[email protected]>

* Use new property name service_name in service & client class

Signed-off-by: Barry Xu <[email protected]>

* Use self.handle for better check

Signed-off-by: Barry Xu <[email protected]>

---------

Signed-off-by: Barry Xu <[email protected]>
  • Loading branch information
Barry-Xu-2018 authored Sep 2, 2023
1 parent 251305e commit 03bcc32
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 0 deletions.
1 change: 1 addition & 0 deletions rclpy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ if(BUILD_TESTING)
test/test_rate.py
test/test_rosout_subscription.py
test/test_serialization.py
test/test_service.py
test/test_service_introspection.py
test/test_subscription.py
test/test_task.py
Expand Down
5 changes: 5 additions & 0 deletions rclpy/rclpy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,10 @@ def configure_introspection(
def handle(self):
return self.__client

@property
def service_name(self) -> str:
with self.handle:
return self.__client.service_name

def destroy(self):
self.__client.destroy_when_not_in_use()
5 changes: 5 additions & 0 deletions rclpy/rclpy/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,10 @@ def configure_introspection(
def handle(self):
return self.__service

@property
def service_name(self) -> str:
with self.handle:
return self.__service.name

def destroy(self):
self.__service.destroy_when_not_in_use()
9 changes: 9 additions & 0 deletions rclpy/src/rclpy/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,20 @@ Client::configure_introspection(
}
}

const char *
Client::get_service_name()
{
return rcl_client_get_service_name(rcl_client_.get());
}

void
define_client(py::object module)
{
py::class_<Client, Destroyable, std::shared_ptr<Client>>(module, "Client")
.def(py::init<Node &, py::object, const std::string &, py::object>())
.def_property_readonly(
"service_name", &Client::get_service_name,
"Get the name of the service")
.def_property_readonly(
"pointer", [](const Client & client) {
return reinterpret_cast<size_t>(client.rcl_ptr());
Expand Down
4 changes: 4 additions & 0 deletions rclpy/src/rclpy/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ class Client : public Destroyable, public std::enable_shared_from_this<Client>
void
destroy() override;

/// Get the service name.
const char *
get_service_name();

private:
Node node_;
std::shared_ptr<rcl_client_t> rcl_client_;
Expand Down
47 changes: 47 additions & 0 deletions rclpy/test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import rclpy
import rclpy.executors
from rclpy.utilities import get_rmw_implementation_identifier
from test_msgs.srv import Empty

# TODO(sloretz) Reduce fudge once wait_for_service uses node graph events
TIME_FUDGE = 0.3
Expand All @@ -38,6 +39,23 @@ def tearDownClass(cls):
cls.node.destroy_node()
rclpy.shutdown(context=cls.context)

@classmethod
def do_test_service_name(cls, test_service_name_list):
for service_name, ns, cli_args, target_service_name in test_service_name_list:
node = rclpy.create_node(
node_name='node_name',
context=cls.context,
namespace=ns,
cli_args=cli_args,
start_parameter_services=False)
client = node.create_client(
srv_type=Empty,
srv_name=service_name
)
assert client.service_name == target_service_name
client.destroy()
node.destroy_node()

def test_wait_for_service_5sec(self):
cli = self.node.create_client(GetParameters, 'get/parameters')
try:
Expand Down Expand Up @@ -136,6 +154,35 @@ def test_different_type_raises(self):
self.node.destroy_client(cli)
self.node.destroy_service(srv)

def test_get_service_name(self):
test_service_name_list = [
# test_service_name, namespace, cli_args for remap, expected service name
# No namespaces
('service', None, None, '/service'),
('example/service', None, None, '/example/service'),
# Using service names with namespaces
('service', 'ns', None, '/ns/service'),
('example/service', 'ns', None, '/ns/example/service'),
('example/service', 'my/ns', None, '/my/ns/example/service'),
('example/service', '/my/ns', None, '/my/ns/example/service'),
# Global service name
('/service', 'ns', None, '/service'),
('/example/service', 'ns', None, '/example/service')
]
TestClient.do_test_service_name(test_service_name_list)

def test_get_service_name_after_remapping(self):
test_service_name_list = [
('service', None, ['--ros-args', '--remap', 'service:=new_service'], '/new_service'),
('service', 'ns', ['--ros-args', '--remap', 'service:=new_service'],
'/ns/new_service'),
('service', 'ns', ['--ros-args', '--remap', 'service:=example/new_service'],
'/ns/example/new_service'),
('example/service', 'ns', ['--ros-args', '--remap', 'example/service:=new_service'],
'/ns/new_service')
]
TestClient.do_test_service_name(test_service_name_list)


if __name__ == '__main__':
unittest.main()
80 changes: 80 additions & 0 deletions rclpy/test/test_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright 2023 Sony Group Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pytest

import rclpy
from rclpy.node import Node

from test_msgs.srv import Empty


@pytest.fixture(autouse=True)
def default_context():
rclpy.init()
yield
rclpy.shutdown()


@pytest.mark.parametrize('service_name, namespace, expected', [
# No namespaces
('service', None, '/service'),
('example/service', None, '/example/service'),
# Using service names with namespaces
('service', 'ns', '/ns/service'),
('example/service', 'ns', '/ns/example/service'),
('example/service', 'my/ns', '/my/ns/example/service'),
('example/service', '/my/ns', '/my/ns/example/service'),
# Global service name
('/service', 'ns', '/service'),
('/example/service', 'ns', '/example/service'),
])
def test_get_service_name(service_name, namespace, expected):
node = Node('node_name', namespace=namespace, cli_args=None, start_parameter_services=False)
srv = node.create_service(
srv_type=Empty,
srv_name=service_name,
callback=lambda _: None
)

assert srv.service_name == expected

srv.destroy()
node.destroy_node()


@pytest.mark.parametrize('service_name, namespace, cli_args, expected', [
('service', None, ['--ros-args', '--remap', 'service:=new_service'], '/new_service'),
('service', 'ns', ['--ros-args', '--remap', 'service:=new_service'], '/ns/new_service'),
('service', 'ns', ['--ros-args', '--remap', 'service:=example/new_service'],
'/ns/example/new_service'),
('example/service', 'ns', ['--ros-args', '--remap', 'example/service:=new_service'],
'/ns/new_service'),
])
def test_get_service_name_after_remapping(service_name, namespace, cli_args, expected):
node = Node(
'node_name',
namespace=namespace,
cli_args=cli_args,
start_parameter_services=False)
srv = node.create_service(
srv_type=Empty,
srv_name=service_name,
callback=lambda _: None
)

assert srv.service_name == expected

srv.destroy()
node.destroy_node()

0 comments on commit 03bcc32

Please sign in to comment.