# Service client node class problem

**URL:** <https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021>\
**Category:** ROS2 Basics in 5 Days Humble (C++)\
**Tags:** quiz, services, ros2\
**Created:** [June 21, 2023, 4:52pm UTC](https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021 "2023-06-21T16:52:45Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![pabled91](https://get-help.theconstruct.ai/user_avatar/get-help.theconstruct.ai/pabled91/32/6967_2.png) [@pabled91](https://get-help.theconstruct.ai/u/pabled91)\
**Post date:** [June 21, 2023, 4:52pm UTC](https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021/1 "2023-06-21T16:52:46Z")

</div>

hello there,

I’m trying to do the service client node for the quiz 4.7 on services.

My service works well when I do

```shell
ros2 service call /rotate services_quiz_srv/srv/Spin "{direction: 'right', angular_velocity: 1, time: 3}"

```

My services gives me back the good behaviour and this message:

```shell
user:~/ros2_ws$ ros2 launch services_quiz services_quiz_server.launch.py
[INFO] [launch]: All log files can be found below /home/user/.ros/log/2023-06-21-16-36-24-289049-4_xterm-1148
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [rotate_server-1]: process started with pid [1149]
[rotate_server-1] [INFO] [1687365401.469843609] [rotate_service]: start rotating right
[rotate_server-1] [INFO] [1687365404.470299481] [rotate_service]: stop rotating right

```

When I try to do the client in a class I don’t have the same behaviour:

```shell
user:~/ros2_ws$ ros2 launch services_quiz services_quiz_server.launch.py
[INFO] [launch]: All log files can be found below /home/user/.ros/log/2023-06-21-16-45-08-354879-4_xterm-2032
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [rotate_server-1]: process started with pid [2033]
[rotate_server-1] [INFO] [1687365908.995054596] [rotate_service]: start rotating right
[rotate_server-1] [INFO] [1687365911.996097658] [rotate_service]: stop rotating right
[rotate_server-1] [INFO] [1687365911.996305815] [rotate_service]: start rotating right
[rotate_server-1] [INFO] [1687365914.996546838] [rotate_service]: stop rotating right
[rotate_server-1] [INFO] [1687365914.996641613] [rotate_service]: start rotating right
[rotate_server-1] [INFO] [1687365917.996940060] [rotate_service]: stop rotating right
[rotate_server-1] [INFO] [1687365917.997047116] [rotate_service]: start rotating right
[rotate_server-1] [INFO] [1687365920.997297565] [rotate_service]: stop rotating right
[rotate_server-1] [INFO] [1687365920.997418616] [rotate_service]: start rotating right
[rotate_server-1] [INFO] [1687365923.997756524] [rotate_service]: stop rotating right

```

My code looks like this:

```cpp
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/timer.hpp"
#include "services_quiz_srv/srv/spin.hpp"

#include <chrono>
#include <cstdlib>
#include <future>
#include <memory>

using namespace std::chrono_literals;
using Spin = services_quiz_srv::srv::Spin;

class ServiceClient : public rclcpp::Node {
private:
  rclcpp::Client<Spin>::SharedPtr client_;
  rclcpp::TimerBase::SharedPtr timer_;
  bool service_done_ = false;

  void timer_callback() {
    RCLCPP_INFO(this->get_logger(), "timer callback");
    while (!client_->wait_for_service(1s)) {
      if (!rclcpp::ok()) {
        RCLCPP_ERROR(
            this->get_logger(),
            "Client interrupted while waiting for service. Terminating...");
        return;
      }
      RCLCPP_INFO(this->get_logger(),
                  "Service Unavailable. Waiting for Service...");
    }
    RCLCPP_INFO(this->get_logger(), "service reached");
    auto request = std::make_shared<Spin::Request>();
    request->direction = "right";
    request->angular_velocity = 1;
    request->time = 3;

    service_done_ = false;
    auto result_future = client_->async_send_request(
        request, std::bind(&ServiceClient::response_callback, this,
                           std::placeholders::_1));

    RCLCPP_INFO(this->get_logger(), "end of timer callback");
  }

  void response_callback(rclcpp::Client<Spin>::SharedFuture future) {
    RCLCPP_INFO(this->get_logger(), "response callback");
    auto status = future.wait_for(1s);
    if (status == std::future_status::ready) {
      RCLCPP_INFO(this->get_logger(), "Result: success");
      service_done_ = true;
    } else {
      RCLCPP_INFO(this->get_logger(), "Service In-Progress...");
    }
    RCLCPP_INFO(this->get_logger(), "end response callback");
  }

public:
  ServiceClient() : Node("rotate_client") {
    RCLCPP_INFO(this->get_logger(), "rotate_client launched");
    client_ = this->create_client<Spin>("rotate");
    timer_ = this->create_wall_timer(
        1s, std::bind(&ServiceClient::timer_callback, this));
  }

  bool is_service_done() const { return this->service_done_; }
};

int main(int argc, char *argv[]) {
  rclcpp::init(argc, argv);

  auto service_client = std::make_shared<ServiceClient>();
  while (!service_client->is_service_done()) {
    rclcpp::spin_some(service_client);
  }

  rclcpp::shutdown();
  return 0;
}

```

and displays this:

```shell
user:~$ ros2 launch services_quiz services_quiz_client.launch.py
[INFO] [launch]: All log files can be found below /home/user/.ros/log/2023-06-21-16-45-05-194765-4_xterm-2010
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [rotate_client-1]: process started with pid [2011]
[rotate_client-1] [INFO] [1687365905.358862317] [rotate_client]: rotate_client launched
[rotate_client-1] [INFO] [1687365906.360016374] [rotate_client]: timer callback
[rotate_client-1] [INFO] [1687365907.360192368] [rotate_client]: Service Unavailable. Waiting for Service...
[rotate_client-1] [INFO] [1687365908.360392928] [rotate_client]: Service Unavailable. Waiting for Service...
[rotate_client-1] [INFO] [1687365908.865549225] [rotate_client]: service reached
[rotate_client-1] [INFO] [1687365908.865757831] [rotate_client]: end of timer callback
[rotate_client-1] [INFO] [1687365908.865839867] [rotate_client]: timer callback
[rotate_client-1] [INFO] [1687365908.865877264] [rotate_client]: service reached
[rotate_client-1] [INFO] [1687365908.865957374] [rotate_client]: end of timer callback
[rotate_client-1] [INFO] [1687365909.359922030] [rotate_client]: timer callback
[rotate_client-1] [INFO] [1687365909.360033576] [rotate_client]: service reached
[rotate_client-1] [INFO] [1687365909.360135143] [rotate_client]: end of timer callback
[rotate_client-1] [INFO] [1687365910.359893384] [rotate_client]: timer callback
[rotate_client-1] [INFO] [1687365910.359983630] [rotate_client]: service reached
[rotate_client-1] [INFO] [1687365910.360072448] [rotate_client]: end of timer callback
[rotate_client-1] [INFO] [1687365911.359869095] [rotate_client]: timer callback
[rotate_client-1] [INFO] [1687365911.359959513] [rotate_client]: service reached
[rotate_client-1] [INFO] [1687365911.360050950] [rotate_client]: end of timer callback
[rotate_client-1] [INFO] [1687365911.996393542] [rotate_client]: response callback
[rotate_client-1] [INFO] [1687365911.996450567] [rotate_client]: Result: success
[rotate_client-1] [INFO] [1687365911.996471368] [rotate_client]: end response callback
[INFO] [rotate_client-1]: process has finished cleanly [pid 2011]

```

The robot starts rotating at the first “service reached”. I never get to see “Service In-Progress…”.  
It seems that while the robot executes the first service it starts a new timer callback…

I don’t understand what I did wrong. My code is inspired by the service client code using node inheritance of the course.

Thank you in advance for your help.  
Pablo

---

<div class="post-metadata">

**Author:** ![girishkumar.kannan](https://get-help.theconstruct.ai/user_avatar/get-help.theconstruct.ai/girishkumar.kannan/32/6994_2.png) [@girishkumar.kannan](https://get-help.theconstruct.ai/u/girishkumar.kannan)\
**Post date:** [June 22, 2023, 6:27am UTC](https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021/2 "2023-06-22T06:27:28Z")

</div>

Hi @pabled91 ,

Welcome to this Community!

I see that you have tried to implement the Service Client logic from my post : [ROS2 Service Client in C++ with Classes with Node Inheritance](https://get-help.robotigniteacademy.com/t/ros2-service-client-in-c-with-classes-with-node-inheritance/19647).

The problem is that the way you have implemented it is wrong.  
The timer callback will keep calling the service after a certain time. The best way to avoid this is to wait for the amount of duration until the timer finishes. But unfortunately, you cannot change the timeout in the timer callback dynamically. (You can but it is a big process not suitable for beginners.)

The simpler method is to not use the timer. The steps to do that will be as follows:

1. Move the `wait_for_service` code to the constructor.
2. Move the `async_send_request` part to a function call.
3. Call the function call to initialize a service in the `main` function.

Here is the altered version [**WARNING: NOT TESTED**]:

```cpp
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/timer.hpp"
#include "services_quiz_srv/srv/spin.hpp"

#include <chrono>
#include <cstdlib>
#include <future>
#include <memory>

using namespace std::chrono_literals;
using Spin = services_quiz_srv::srv::Spin;

class ServiceClient : public rclcpp::Node {
private:
  rclcpp::Client<Spin>::SharedPtr client_;
  rclcpp::TimerBase::SharedPtr timer_;
  bool service_done_ = false;

  void response_callback(rclcpp::Client<Spin>::SharedFuture future) {
    RCLCPP_INFO(this->get_logger(), "response callback");
    auto status = future.wait_for(1s);
    if (status == std::future_status::ready) {
      RCLCPP_INFO(this->get_logger(), "Result: success");
      service_done_ = true;
    } else {
      RCLCPP_INFO(this->get_logger(), "Service In-Progress...");
    }
    RCLCPP_INFO(this->get_logger(), "end response callback");
  }

public:
  ServiceClient() : Node("rotate_client") {
    RCLCPP_INFO(this->get_logger(), "rotate_client launched");
    client_ = this->create_client<Spin>("rotate");

    while (!client_->wait_for_service(1s)) {
      if (!rclcpp::ok()) {
        RCLCPP_ERROR(
            this->get_logger(),
            "Client interrupted while waiting for service. Terminating...");
        return;
      }
      RCLCPP_INFO(this->get_logger(),
                  "Service Unavailable. Waiting for Service...");
    }
    RCLCPP_INFO(this->get_logger(), "service reached");
  }

  void call_service(std::string direction, float ang_vel, int time) {
  	auto request = std::make_shared<Spin::Request>();
    request->direction = direction;
    request->angular_velocity = ang_vel;
    request->time = time;

    service_done_ = false;
    auto result_future = client_->async_send_request(
        request, std::bind(&ServiceClient::response_callback, this,
                           std::placeholders::_1));
  }

  bool is_service_done() const { return this->service_done_; }
};

int main(int argc, char *argv[]) {
  rclcpp::init(argc, argv);

  auto service_client = std::make_shared<ServiceClient>();
  service_client->call_service();
  while (!service_client->is_service_done()) {
    rclcpp::spin_some(service_client);
  }

  rclcpp::shutdown();
  return 0;
}

// End of Code

```

**Here is Tip:** Don’t just copy-paste the code and change a few lines hoping you can adapt the code to your functionality. It will never work that way. Understand the logic flow and then make changes to suit your logic.

Regards,  
Girish

---

<div class="post-metadata">

**Author:** ![pabled91](https://get-help.theconstruct.ai/user_avatar/get-help.theconstruct.ai/pabled91/32/6967_2.png) [@pabled91](https://get-help.theconstruct.ai/u/pabled91)\
**Post date:** [June 22, 2023, 9:08am UTC](https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021/3 "2023-06-22T09:08:47Z")

</div>

Hello,

thank you @girishkumar.kannan for your help.

I tried to implement your logic because it was added in the service course (unit 4). I’m trying to put all my nodes in classes to avoid putting it all in the main.

When I did the example in the course I didn’t see any problem. Maybe because the execution is short enough to dont let time for another timer callback.

It works now, I think I understand now thanks to your help. I just have a concern: it seems that the part of response callback saying that the service is in progress is never reached, because the response callback is only executed once the service ended.

How could we now that the service is in progress from the client side.

Again thank you for your help.

```shell
user:~/ros2_ws$ ros2 launch services_quiz services_quiz_client.launch.py
[INFO] [launch]: All log files can be found below /home/user/.ros/log/2023-06-22-09-04-06-495352-3_xterm-14852
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [rotate_client-1]: process started with pid [14853]
[rotate_client-1] [INFO] [1687424646.690291784] [rotate_client]: rotate_client launched
[rotate_client-1] [INFO] [1687424646.691614330] [rotate_client]: service reached
[rotate_client-1] [INFO] [1687424646.691660807] [rotate_client]: calling service
[rotate_client-1] [INFO] [1687424651.693192236] [rotate_client]: response callback
[rotate_client-1] [INFO] [1687424651.693255160] [rotate_client]: Result: success
[rotate_client-1] [INFO] [1687424651.693285092] [rotate_client]: end response callback
[INFO] [rotate_client-1]: process has finished cleanly [pid 14853]

```

```auto
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/timer.hpp"
#include "services_quiz_srv/srv/spin.hpp"

#include <chrono>
#include <cstdlib>
#include <future>
#include <memory>

using namespace std::chrono_literals;
using Spin = services_quiz_srv::srv::Spin;

class ServiceClient : public rclcpp::Node {
private:
  rclcpp::Client<Spin>::SharedPtr client_;
  rclcpp::TimerBase::SharedPtr timer_;
  bool service_done_ = false;

  void response_callback(rclcpp::Client<Spin>::SharedFuture future) {
    RCLCPP_INFO(this->get_logger(), "response callback");
    auto status = future.wait_for(1s);
    if (status == std::future_status::ready) {
      RCLCPP_INFO(this->get_logger(), "Result: success");
      service_done_ = true;
    } else {
      RCLCPP_INFO(this->get_logger(), "Service In-Progress...");
    }
    RCLCPP_INFO(this->get_logger(), "end response callback");
  }

public:
  ServiceClient() : Node("rotate_client") {
    RCLCPP_INFO(this->get_logger(), "rotate_client launched");
    client_ = this->create_client<Spin>("rotate");
    while (!client_->wait_for_service(1s)) {
      if (!rclcpp::ok()) {
        RCLCPP_ERROR(
            this->get_logger(),
            "Client interrupted while waiting for service. Terminating...");
        return;
      }
      RCLCPP_INFO(this->get_logger(),
                  "Service Unavailable. Waiting for Service...");
    }
    RCLCPP_INFO(this->get_logger(), "service reached");
  }

  void call_service(std::string direction, float angular_velocity, int time) {
    RCLCPP_INFO(this->get_logger(), "calling service");
    auto request = std::make_shared<Spin::Request>();
    request->direction = direction;
    request->angular_velocity = angular_velocity;
    request->time = time;

    service_done_ = false;
    auto result_future = client_->async_send_request(
        request, std::bind(&ServiceClient::response_callback, this,
                           std::placeholders::_1));
  }

  bool is_service_done() const { return this->service_done_; }
};

int main(int argc, char *argv[]) {
  rclcpp::init(argc, argv);

  auto service_client = std::make_shared<ServiceClient>();
  service_client->call_service("right", 1, 5);
  while (!service_client->is_service_done()) {
    rclcpp::spin_some(service_client);
  }

  rclcpp::shutdown();
  return 0;
}

```

---

<div class="post-metadata">

**Author:** ![girishkumar.kannan](https://get-help.theconstruct.ai/user_avatar/get-help.theconstruct.ai/girishkumar.kannan/32/6994_2.png) [@girishkumar.kannan](https://get-help.theconstruct.ai/u/girishkumar.kannan)\
**Post date:** [June 22, 2023, 10:25am UTC](https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021/4 "2023-06-22T10:25:06Z")

</div>

Hi @pabled91 ,

Glad to know it worked for you!

Yes, most of the times, the `"Service In-Progress..."` will never be printed.  
The reason is that this `response_callback` will be called only once by the service client per service call.  
If you were to have this `response_callback` running as a loop, then you will see the `Service In-Progress...` message.  
For example, if you call the service in the `main` function **and** instead of using the `response_callback` you use a `while` loop within the `main` function, then you ~~will~~ might see the message that service is in progress.

But to know answer your question, How to know if service is in progress?, You must create another thread to check if the `future_status` is not `ready`. Services are meant to be run async to avoid this “blocking” effect. Therefore, when the service is in progress, the message will not be printed.  
I also believe that the `status` is set to `ready` immediately in the `future` by ROS2, so any state in between is not reported, which also might be the reason why the else block never gets executed (which would display service in-progress).

I hope that clarified your doubts.

Regards,  
Girish

---

<div class="post-metadata">

**Author:** ![pabled91](https://get-help.theconstruct.ai/user_avatar/get-help.theconstruct.ai/pabled91/32/6967_2.png) [@pabled91](https://get-help.theconstruct.ai/u/pabled91)\
**Post date:** [June 22, 2023, 1:05pm UTC](https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021/5 "2023-06-22T13:05:57Z")

</div>

Thank you for your help.  
Maybe I’ll experiment it later.

Regards.

Pablo

---

<div class="post-metadata">

**Author:** ![system](https://get-help.theconstruct.ai/uploads/default/original/2X/9/9d3ce8305cc0d0d9b6e07d15767a6f245c526f1b.jpeg) [@system](https://get-help.theconstruct.ai/u/system)\
**Post date:** [June 27, 2023, 1:06pm UTC](https://get-help.theconstruct.ai/t/service-client-node-class-problem/25021/6 "2023-06-27T13:06:06Z")

</div>

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.
