0
$\begingroup$

I have a ROS2 node that spins and gets the constant input from a topic (joystick commands) and when a certain key combination is pressed it calls a ROS2 service. In my implementation, it is really important for the service call to be blocking (temporary disable the input it gets from joystick) until the service returns success. I have tried to use spin_until_future_complete() but that does not work since the node is already spinning and i get an executor error...

Any other smart way to implement this logic?

$\endgroup$

2 Answers 2

0
$\begingroup$

I think you need to read up on multithreaded executors and callback groups, see e.g. here.

If you understand those concepts, then have a look here for a code example in which a timer callback is used to wait for the result of a service request (by using a multithreaded executor and separate callback groups). For your case it should work in the same way but you would wait in the message callback instead of in a timer callback.

Also, have a look at the QoS settings (see the documentation and e.g. here for more info), as these determine the behavior wrt the messages that arrive while waiting (e.g. in case of history depth 1 they will be discarded, but in case of depth 5 the last 5 will be kept, etc.)

$\endgroup$
1
$\begingroup$

I kind of faced a similar dilemma recently (but with the publisher and with Python).

It's probably not an ideal way to do it, but I introduced a boolean variable in the class that triggers the various functions inside the callback.

For a simple python example which can also be implemented in cpp

Note: This code is purely for the logic and is not been tested.

class MyNode(Node)
  def __init__(self):
    super().__init__('my_node'):
    self.cli = self.create_client(SetBool, 'on_off')
    self.subscription = self.create_subscription(Joy, 'topic', self.listener_callback, 10)
    self._future = None
    self._sub = True
    self.req = SetBool.Request()
    self.subscription

  def send_request(self):
    self._sub = False
    self.req.data = True
    self._future = self.cli.call_async(self.req)
    self._future.add_done_callback(self._cont_sub)

  def _cont_sub(self):
    ret = self._future.result()
    if ret.success == True:
      self.get_logger.info('service call success')
      self._sub = True
    else:
      self.get_logger.info('service call failed')
    self._future = None

  def listener_callback(self, msg):
    if self._sub:
      self.get_logger.info('listener reading msg')
      if msg.buttons[idx] == 'something':
        self.send_request()
    else:
      self.get_logger.info('listener not reading msg')
            
$\endgroup$
1
  • $\begingroup$ Thanks, but in C++ there is no add_done_callback() (as far as I know). Anyways, I implemented this using callback groups as @JRTG mentioned. $\endgroup$ Commented Oct 22 at 8:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.