Calling a service on the same node which it's advertised
I'm trying to write a subclass of a node in order to dynamically test some publishers and services. I'd like to be able to verify that certain members of the node are correctly modified upon calling services and publishing certain messages.
However, when I try to do this with a service, I always get false returned upon calling it.
Is what I'm trying to do possible? Is there some conflict occurring in trying to advertise and call a server on the same node?
Here is the relevant portion of my main node main.cpp
MainNode::MainNode() {
nh_ = ros::NodeHandle("~");
...
my_service_ = nh_.advertiseService("source", &MainNode::myServiceCb, this);
ROS_INFO("Main is up");
}
...
bool MainNode::myServiceCb(my_msgs::TestService::Request& req,
my_msgs::TestService::Response& res) {
ROS_INFO("Callback called");
res.status = 1;
return true;
}
subclass.cpp
int main(int argc, char** argv) {
ros::init(argc, argv, kNodeName);
TestMainNode test_main;
test_main.runTests();
test_main.run();
return 0;
}
TestMainNode::TestMainNode() : MainNode() {
ROS_INFO("TEST launched");
}
void TestCommandNode::runTests() {
//This was originally omitted, tried adding to see if there was some conflict with using the same node handler
nh2_ = ros::NodeHandle("~");
if(ros::service::waitForService("test_node/source",-1)) {
ROS_WARN("Service available, initiating tests");
serv_client_ = nh2_.serviceClient<my_msgs::TestService>("test_node/source");
if(!testService()) {
ROS_ERROR("[TEST] Source switching test failed");
}
}
}
bool TestCommandNode::testSourceSwitching() {
// Test for all inputs that should return SUCCESS
int types[] = {0,1,2,3};
for(int sour: types) {
ROS_INFO("Yup its %d", sour);
my_msgs::TestService srv_call;
srv_call.request.type = (int) sour;
//Tried both of the following within "if", both return same result
//ros::service::call("source", srv_call)
//serv_client_.call(srv_call)
if(ros::service::call("source", srv_call)) {
ROS_INFO("Call success!");
if(srv_call.response.status == my_msgs::TestService::Response::FAILURE) {
ROS_ERROR("[TEST] Tried %d but failed!", sour);
}else{
ROS_INFO("[TEST] Tried %d, success", sour);
}
}else{
ROS_ERROR("[TEST] Service call failed for unknown reason, %d",srv_call.response.status);
}
}
}
TestService.srv
# Request
int32 type
# Request type constants
int32 ZERO=0
int32 ONE=1
int32 TWO=2
int32 THREE=3
---
# Response
int32 status
# Response status constants
int32 FAILURE=0
int32 SUCCESS=1
Here is the output I get
[ INFO] [1527897132.736164872]: TEST launched
[ INFO] [1527897132.736205987]: Main is up
[ WARN] [1527897132.737292582]: Service available, initiating tests
[ INFO] [1527897132.737314987]: Yup its 0
[ERROR] [1527897132.737641323]: [TEST] Service call failed for unknown reason, 0
[ INFO] [1527897132.737652149]: Yup its 1
[ERROR] [1527897132.737926956]: [TEST] Service call failed for unknown reason, 0
[ INFO] [1527897132.737937159]: Yup its 2
[ERROR] [1527897132.738307667]: [TEST] Service call failed for unknown reason, 0
[ INFO] [1527897132.738317346]: Yup its 3
Indicating the call is returning false each time. I've tried all permutations of ros::service:call
and serv_client_
using topic source
and test_node/source
.
With ros::service::call
the call never returns if the topic is test_node/source
, but returns false if it's source
. The opposite is the case using serv_client_
.
Any help is appreciated. If there's a better way I could be testing this, do let me know.
Not an answer, but a question: why do you make your test inherit from the cut? I realise that is a common approach in some contexts / testing workflows, but I've not seen it done often in ROS node (ie: rostest) tests. A more typical approach would be to keep your regular node as-is, and write an ..
.. additional node that is your test node. That node only gets used in the rostest you then setup, which will take care of starting up a
roscore
, your nodes, etc.There are some private members of the main node I'd like to be able to poke around at, but yes it's looking like the two node solution is what I'll have to use. Half of the question is just curiosity into whether this kind of thing is possible.
Well, as I wrote, it's definitely not an answer, and I don't like avoiding a problem by using a different approach (ie: work-around).
Who is doing the spinning in your setup?
The main node is doing the spinning, with the test node (or hopefully subclass) just running once.