Skip to content

Commit 8586e84

Browse files
Merge pull request #123 from orocos/rtt_std_srvs-for-standard-operations
Add wrappers for common operation signatures to rtt_std_srvs
2 parents 09002dd + fd14e71 commit 8586e84

File tree

9 files changed

+301
-47
lines changed

9 files changed

+301
-47
lines changed

rtt_roscomm/cmake/GenerateRTTROSCommPackage.cmake.em

+2
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ macro(rtt_roscomm_debug)
4242
endmacro()
4343

4444
macro(ros_generate_rtt_typekit package)
45+
cmake_parse_arguments(ros_generate_rtt_typekit "" "" "EXTRA_INCLUDES" ${ARGN})
4546
set(_package ${package})
4647
add_subdirectory(${rtt_roscomm_TEMPLATES_DIR}/typekit ${package}_typekit)
4748
endmacro(ros_generate_rtt_typekit)
4849

4950
macro(ros_generate_rtt_service_proxies package)
51+
cmake_parse_arguments(ros_generate_rtt_service_proxies "" "" "EXTRA_INCLUDES" ${ARGN})
5052
set(_package ${package})
5153
add_subdirectory(${rtt_roscomm_TEMPLATES_DIR}/service ${package}_service_proxies)
5254
endmacro(ros_generate_rtt_service_proxies)

rtt_roscomm/include/rtt_roscomm/rtt_rosservice_proxy.h

+107-34
Original file line numberDiff line numberDiff line change
@@ -23,75 +23,148 @@ class ROSServiceProxyBase
2323

2424
//! Abstract ROS Service Server Proxy
2525
class ROSServiceServerProxyBase : public ROSServiceProxyBase
26-
{
26+
{
2727
public:
2828
ROSServiceServerProxyBase(const std::string &service_name) :
29-
ROSServiceProxyBase(service_name),
30-
proxy_operation_caller_()
29+
ROSServiceProxyBase(service_name)
3130
{ }
32-
31+
3332
//! Connect an RTT Operation to this ROS service server
34-
bool connect(RTT::TaskContext *owner, RTT::OperationInterfacePart* operation) {
35-
// Link the caller with the operation
36-
return proxy_operation_caller_->setImplementation(
37-
operation->getLocalOperation(),
38-
RTT::internal::GlobalEngine::Instance());
39-
}
33+
virtual bool connect(RTT::TaskContext *owner, RTT::OperationInterfacePart* operation) = 0;
4034

4135
protected:
4236
//! The underlying ROS service server
4337
ros::ServiceServer server_;
44-
//! The underlying RTT operation caller
45-
boost::shared_ptr<RTT::base::OperationCallerBaseInvoker> proxy_operation_caller_;
4638
};
4739

4840
template<class ROS_SERVICE_T>
49-
class ROSServiceServerProxy : public ROSServiceServerProxyBase
50-
{
41+
class ROSServiceServerOperationCallerBase {
42+
public:
43+
typedef boost::shared_ptr<ROSServiceServerOperationCallerBase<ROS_SERVICE_T> > Ptr;
44+
virtual ~ROSServiceServerOperationCallerBase() {}
45+
virtual bool call(typename ROS_SERVICE_T::Request& request, typename ROS_SERVICE_T::Response& response) const = 0;
46+
};
47+
48+
template<class ROS_SERVICE_T, int variant = 0>
49+
struct ROSServiceServerOperationCallerWrapper;
50+
51+
// Default implementation of an OperationCaller that fowards ROS service calls to Orocos operations
52+
// that have the default bool(Request&, Response&) signature. You can add more variants of this class
53+
// to add support for custom operation types.
54+
//
55+
// See package std_srvs for an example.
56+
//
57+
template<class ROS_SERVICE_T>
58+
struct ROSServiceServerOperationCallerWrapper<ROS_SERVICE_T,0> {
59+
typedef bool Signature(typename ROS_SERVICE_T::Request&, typename ROS_SERVICE_T::Response&);
60+
typedef RTT::OperationCaller<Signature> ProxyOperationCallerType;
61+
template <typename Callable> static bool call(Callable& call, typename ROS_SERVICE_T::Request& request, typename ROS_SERVICE_T::Response& response) {
62+
return call(request, response);
63+
}
64+
};
65+
66+
template<class ROS_SERVICE_T, int variant = 0>
67+
class ROSServiceServerOperationCaller : public ROSServiceServerOperationCallerBase<ROS_SERVICE_T> {
68+
public:
69+
using typename ROSServiceServerOperationCallerBase<ROS_SERVICE_T>::Ptr;
70+
71+
//! The wrapper type for this variant
72+
typedef ROSServiceServerOperationCallerWrapper<ROS_SERVICE_T, variant> Wrapper;
73+
74+
//! Default operation caller for a ROS service server proxy
75+
typedef typename Wrapper::ProxyOperationCallerType ProxyOperationCallerType;
76+
typedef boost::shared_ptr<ProxyOperationCallerType> ProxyOperationCallerTypePtr;
77+
78+
private:
79+
template<typename Dummy> struct Void { typedef void type; };
80+
81+
template<typename R = void, typename Enabled = void> struct EnableIfHasNextVariant { };
82+
83+
template<typename R> struct EnableIfHasNextVariant<R,
84+
typename Void<typename ROSServiceServerOperationCallerWrapper<ROS_SERVICE_T, variant + 1>::ProxyOperationCallerType>::type> {
85+
typedef R type;
86+
};
87+
88+
template<typename R = void, typename Enabled = void>
89+
struct NextVariant {
90+
static Ptr connect(RTT::OperationInterfacePart*) { return Ptr(); }
91+
};
92+
93+
template<typename R>
94+
struct NextVariant<R, typename EnableIfHasNextVariant<R>::type> {
95+
static Ptr connect(RTT::OperationInterfacePart* operation) {
96+
return ROSServiceServerOperationCaller<ROS_SERVICE_T, variant + 1>::connect(operation);
97+
}
98+
};
99+
51100
public:
52-
//! Operation caller for a ROS service server proxy
53-
typedef RTT::OperationCaller<bool(typename ROS_SERVICE_T::Request&, typename ROS_SERVICE_T::Response&)> ProxyOperationCallerType;
101+
static Ptr connect(RTT::OperationInterfacePart* operation) {
102+
ProxyOperationCallerTypePtr proxy_operation_caller
103+
= boost::make_shared<ProxyOperationCallerType>(operation->getLocalOperation(), RTT::internal::GlobalEngine::Instance());
104+
if (proxy_operation_caller->ready()) {
105+
return Ptr(new ROSServiceServerOperationCaller<ROS_SERVICE_T, variant>(proxy_operation_caller));
106+
}
107+
return NextVariant<void>::connect(operation);
108+
}
109+
110+
virtual bool call(typename ROS_SERVICE_T::Request& request, typename ROS_SERVICE_T::Response& response) const {
111+
// Check if the operation caller is ready, and then call it.
112+
if (!proxy_operation_caller_->ready()) return false;
113+
return Wrapper::call(*proxy_operation_caller_, request, response);
114+
}
115+
116+
ROSServiceServerOperationCaller(const boost::shared_ptr<ProxyOperationCallerType>& impl)
117+
: proxy_operation_caller_(impl) {}
118+
119+
ProxyOperationCallerTypePtr proxy_operation_caller_;
120+
};
54121

122+
template<class ROS_SERVICE_T>
123+
class ROSServiceServerProxy : public ROSServiceServerProxyBase
124+
{
125+
public:
55126
/** \brief Construct a ROS service server and associate it with an Orocos
56127
* task's required interface and operation caller.
57128
*/
58129
ROSServiceServerProxy(const std::string &service_name) :
59130
ROSServiceServerProxyBase(service_name)
60131
{
61-
// Construct operation caller
62-
proxy_operation_caller_.reset(new ProxyOperationCallerType("ROS_SERVICE_SERVER_PROXY"));
63-
64132
// Construct the ROS service server
65133
ros::NodeHandle nh;
66134
server_ = nh.advertiseService(
67-
service_name,
68-
&ROSServiceServerProxy<ROS_SERVICE_T>::ros_service_callback,
135+
service_name,
136+
&ROSServiceServerProxy<ROS_SERVICE_T>::ros_service_callback,
69137
this);
70138
}
71139

72140
~ROSServiceServerProxy()
73141
{
74142
// Clean-up advertized ROS services
75-
server_.shutdown();
143+
server_.shutdown();
144+
}
145+
146+
virtual bool connect(RTT::TaskContext *, RTT::OperationInterfacePart* operation)
147+
{
148+
impl_ = ROSServiceServerOperationCaller<ROS_SERVICE_T>::connect(operation);
149+
return (impl_.get() != 0);
76150
}
77151

78152
private:
79-
80153
//! The callback called by the ROS service server when this service is invoked
81154
bool ros_service_callback(typename ROS_SERVICE_T::Request& request, typename ROS_SERVICE_T::Response& response) {
82-
// Downcast the proxy operation caller
83-
ProxyOperationCallerType &proxy_operation_caller = *dynamic_cast<ProxyOperationCallerType*>(proxy_operation_caller_.get());
84-
// Check if the operation caller is ready, and then call it
85-
return proxy_operation_caller_->ready() && proxy_operation_caller(request, response);
155+
if (!impl_) return false;
156+
return impl_->call(request, response);
86157
}
158+
159+
boost::shared_ptr<ROSServiceServerOperationCallerBase<ROS_SERVICE_T> > impl_;
87160
};
88161

89162

90163
//! Abstract ROS Service Client Proxy
91164
class ROSServiceClientProxyBase : public ROSServiceProxyBase
92165
{
93166
public:
94-
ROSServiceClientProxyBase(const std::string &service_name) :
167+
ROSServiceClientProxyBase(const std::string &service_name) :
95168
ROSServiceProxyBase(service_name),
96169
proxy_operation_()
97170
{ }
@@ -107,12 +180,12 @@ class ROSServiceClientProxyBase : public ROSServiceProxyBase
107180
protected:
108181
//! The underlying ROS service client
109182
ros::ServiceClient client_;
110-
//! The underlying RTT operation
183+
//! The underlying RTT operation
111184
boost::shared_ptr<RTT::base::OperationBase> proxy_operation_;
112185
};
113186

114187
template<class ROS_SERVICE_T>
115-
class ROSServiceClientProxy : public ROSServiceClientProxyBase
188+
class ROSServiceClientProxy : public ROSServiceClientProxyBase
116189
{
117190
public:
118191

@@ -122,7 +195,7 @@ class ROSServiceClientProxy : public ROSServiceClientProxyBase
122195
ROSServiceClientProxy(const std::string &service_name) :
123196
ROSServiceClientProxyBase(service_name)
124197
{
125-
// Construct a new
198+
// Construct a new
126199
proxy_operation_.reset(new ProxyOperationType("ROS_SERVICE_CLIENT_PROXY"));
127200

128201
// Construct the underlying service client
@@ -137,7 +210,7 @@ class ROSServiceClientProxy : public ROSServiceClientProxyBase
137210
}
138211

139212
private:
140-
213+
141214
//! The callback for the RTT operation
142215
bool orocos_operation_callback(typename ROS_SERVICE_T::Request& request, typename ROS_SERVICE_T::Response& response) {
143216
// Make sure the ROS service client exists and then call it (blocking)
@@ -147,7 +220,7 @@ class ROSServiceClientProxy : public ROSServiceClientProxyBase
147220

148221

149222
//! Abstract factory for ROS Service Proxy Factories
150-
class ROSServiceProxyFactoryBase
223+
class ROSServiceProxyFactoryBase
151224
{
152225
public:
153226

@@ -166,7 +239,7 @@ class ROSServiceProxyFactoryBase
166239
};
167240

168241
template<class ROS_SERVICE_T>
169-
class ROSServiceProxyFactory : public ROSServiceProxyFactoryBase
242+
class ROSServiceProxyFactory : public ROSServiceProxyFactoryBase
170243
{
171244
public:
172245

rtt_roscomm/src/templates/service/CMakeLists.txt

+11-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ foreach( FILE ${SRV_FILES} )
7676

7777
endforeach()
7878

79+
# Include custom headers provided by the typekit package
80+
set(ROS_SRV_EXTRA_HEADERS)
81+
if(ros_generate_rtt_service_proxies_EXTRA_INCLUDES)
82+
foreach( header ${ros_generate_rtt_service_proxies_EXTRA_INCLUDES} )
83+
set(ROS_SRV_EXTRA_HEADERS "${ROS_SRV_EXTRA_HEADERS}#include <${header}>\n")
84+
endforeach()
85+
endif()
86+
7987
# Service proxy factories
8088
configure_file(
8189
rtt_rosservice_proxies.cpp.in
@@ -89,7 +97,9 @@ include_directories(
8997
${${_package}_INCLUDE_DIRS})
9098

9199
# Targets
92-
set(CMAKE_BUILD_TYPE MinSizeRel)
100+
if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "Release")
101+
set(CMAKE_BUILD_TYPE MinSizeRel)
102+
endif()
93103
orocos_service( rtt_${ROSPACKAGE}_rosservice_proxies ${CMAKE_CURRENT_BINARY_DIR}/rtt_rosservice_proxies.cpp)
94104
target_link_libraries( rtt_${ROSPACKAGE}_rosservice_proxies ${catkin_LIBRARIES} ${USE_OROCOS_LIBRARIES})
95105
if(DEFINED ${_package}_EXPORTED_TARGETS)

rtt_roscomm/src/templates/service/rtt_rosservice_proxies.cpp.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <rtt_roscomm/rtt_rosservice_proxy.h>
88

99
////////////////////////////////////////////////////////////////////////////////
10-
@ROS_SRV_HEADERS@
10+
@ROS_SRV_HEADERS@@ROS_SRV_EXTRA_HEADERS@
1111
////////////////////////////////////////////////////////////////////////////////
1212

1313
namespace rtt_@ROSPACKAGE@_ros_service_proxies {

tests/rtt_roscomm_tests/test/transport_tests.cpp

+41-10
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,23 @@ TEST(TransportTest, VectorTest)
118118
}
119119

120120
static int callback_called = 0;
121-
bool callback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
121+
bool callback0(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
122122
{
123123
++callback_called;
124124
return true;
125125
}
126126

127+
bool callback1()
128+
{
129+
++callback_called;
130+
return true;
131+
}
132+
133+
void callback2()
134+
{
135+
++callback_called;
136+
}
137+
127138
TEST(TransportTest, ServiceServerTest)
128139
{
129140
std::string service = ros::names::resolve("~empty");
@@ -135,33 +146,53 @@ TEST(TransportTest, ServiceServerTest)
135146

136147
// Create a TaskContext
137148
RTT::TaskContext *tc = new RTT::TaskContext("TaskContext");
138-
tc->addOperation("empty", &callback);
149+
tc->addOperation("empty0", &callback0);
150+
tc->addOperation("empty1", &callback1);
151+
tc->addOperation("empty2", &callback2);
139152

140153
// Load the rosservice service
141154
boost::weak_ptr<rtt_rosservice::ROSService> rosservice;
142155
rosservice = tc->getProvider<rtt_rosservice::ROSService>("rosservice");
143156
ASSERT_FALSE(rosservice.expired());
144157

145158
// Create a service server
146-
EXPECT_TRUE(rosservice.lock()->connect("empty", service, "std_srvs/Empty"));
159+
EXPECT_TRUE(rosservice.lock()->connect("empty0", service + "0", "std_srvs/Empty"));
160+
EXPECT_TRUE(rosservice.lock()->connect("empty1", service + "1", "std_srvs/Empty"));
161+
EXPECT_TRUE(rosservice.lock()->connect("empty2", service + "2", "std_srvs/Empty"));
147162

148163
// Check that the service server has been successfully registered:
149-
EXPECT_TRUE(ros::ServiceManager::instance()->lookupServicePublication(service).get());
164+
EXPECT_TRUE(ros::ServiceManager::instance()->lookupServicePublication(service + "0").get());
165+
EXPECT_TRUE(ros::ServiceManager::instance()->lookupServicePublication(service + "1").get());
166+
EXPECT_TRUE(ros::ServiceManager::instance()->lookupServicePublication(service + "2").get());
150167

151168
// Create a service client
152-
RTT::OperationCaller<bool(std_srvs::Empty::Request&, std_srvs::Empty::Response&)> service_caller("empty");
153-
tc->requires()->addOperationCaller(service_caller);
154-
EXPECT_TRUE(rosservice.lock()->connect("empty", service, "std_srvs/Empty"));
155-
EXPECT_TRUE(service_caller.ready());
169+
RTT::OperationCaller<bool(std_srvs::Empty::Request&, std_srvs::Empty::Response&)> service_caller0("empty0");
170+
RTT::OperationCaller<bool(std_srvs::Empty::Request&, std_srvs::Empty::Response&)> service_caller1("empty1");
171+
RTT::OperationCaller<bool(std_srvs::Empty::Request&, std_srvs::Empty::Response&)> service_caller2("empty2");
172+
tc->requires()->addOperationCaller(service_caller0);
173+
tc->requires()->addOperationCaller(service_caller1);
174+
tc->requires()->addOperationCaller(service_caller2);
175+
EXPECT_TRUE(rosservice.lock()->connect("empty0", service + "0", "std_srvs/Empty"));
176+
EXPECT_TRUE(service_caller0.ready());
177+
EXPECT_TRUE(rosservice.lock()->connect("empty1", service + "1", "std_srvs/Empty"));
178+
EXPECT_TRUE(service_caller1.ready());
179+
EXPECT_TRUE(rosservice.lock()->connect("empty2", service + "2", "std_srvs/Empty"));
180+
EXPECT_TRUE(service_caller2.ready());
156181

157182
// Call the service
158183
EXPECT_EQ(0, callback_called);
159184
std_srvs::Empty empty;
160-
EXPECT_TRUE(service_caller(empty.request, empty.response));
185+
EXPECT_TRUE(service_caller0(empty.request, empty.response));
161186
EXPECT_EQ(1, callback_called);
187+
EXPECT_TRUE(service_caller1(empty.request, empty.response));
188+
EXPECT_EQ(2, callback_called);
189+
EXPECT_TRUE(service_caller2(empty.request, empty.response));
190+
EXPECT_EQ(3, callback_called);
162191

163192
// Disconnect the service
164-
EXPECT_TRUE(rosservice.lock()->disconnect(service));
193+
EXPECT_TRUE(rosservice.lock()->disconnect(service + "0"));
194+
EXPECT_TRUE(rosservice.lock()->disconnect(service + "1"));
195+
EXPECT_TRUE(rosservice.lock()->disconnect(service + "2"));
165196

166197
// Check that the service server has been destroyed
167198
EXPECT_FALSE(ros::ServiceManager::instance()->lookupServicePublication(service));

typekits/rtt_std_srvs/CMakeLists.txt

+8-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ project(rtt_std_srvs)
33

44
find_package(catkin REQUIRED COMPONENTS rtt_roscomm)
55

6-
ros_generate_rtt_service_proxies(std_srvs)
6+
include_directories(include)
7+
8+
ros_generate_rtt_service_proxies(std_srvs
9+
EXTRA_INCLUDES
10+
rtt_std_srvs/Empty.h
11+
rtt_std_srvs/SetBool.h
12+
rtt_std_srvs/Trigger.h
13+
)
714

815
orocos_generate_package(
916
DEPENDS rosgraph_msgs

0 commit comments

Comments
 (0)