Use callback groups to allow an Action to trigger a service of the same Node

asked 2021-03-08 09:31:39 -0500

CraigH92 gravatar image

I have a Node that hosts an Action and a Service Client:

    callback_group=ReentrantCallbackGroup()
    self.__driver = GrappleCraneDriverClient(self, callback_group)

    self.move_joint_absolute_action_server = ActionServer(self,
        action_type=MoveJointAbsolute,
        action_name='move_joint_absolute',
        execute_callback=move_joint_absolute_execute_callback,
        goal_callback=move_joint_absolute_goal_callback,
        cancel_callback=move_joint_absolute_cancel_callback,
        callback_group=callback_group
    )

The service client adds clients to the Node:

class GrappleCraneDriverClient:

    def __init__(self, node: Node, callback_group=None):

        self.__node = node

        # Service clients

        self.__move_x_abs=node.create_client(
            srv_type=MoveOneAxis,
            srv_name='accs/grapple_crane/service/move_x_abs',
            callback_group=callback_group
        )

    def move_x_abs(self, target_position: float) -> MoveOneAxis_Response:
        self.__node.get_logger().info(
            f'[GrappleCraneDriverClient]: calling service {self.__move_x_abs.srv_name}')

       future_trigger_result = self.__move_x_abs.call_async(
           MoveOneAxis_Request(target_position=target_position))
        try: 
            return asyncio.run(asyncio.wait_for(future_trigger_result, timeout=5))
        except asyncio.TimeoutError as e:
            self.__node.get_logger().error(f'TimeoutError: {e}')
            return MoveOneAxis_Response(success=False, message=f'TimeoutError: {e}')

The Node hosting the services is running in a different process.

If I test the GrappleCraneDriverClient by constructing it with a new Node, and then launching it in the same process as the Node that hosts the services, then it works fine.

However when I use the GrappleCraneDriverClient to compose part of my Node that hosts the actions, then I get a timeout error because the service never gets called.

Why would this be?

A key difference between the test code (which works), and the action server code is that in the action server a callback is already running (the move_joint_absolute_execute_callback) when the service is called. However, I am using a ReentrantCallbackGroup(), so this should allow the callbacks to run at the same time, should it not?

I am also using a MultiThreadedExecutor.

If I give the GrappleCraneDriverClient a new Node and run that Node alongside the Node hosting the action (but still in the same process), it also works.

How do I correctly get the service to be called while the action is running, and have the client and action server elonging to the same Node?

Thanks.

edit retag flag offensive close merge delete

Comments

I tried to think of the root cause but it was way beyond me. Just a small clarification: Can we assume GrappleCraneDriverClient.move_x_abs gets called from the callback move_joint_absolute_goal_callback?

130s gravatar image 130s  ( 2023-05-07 10:30:12 -0500 )edit

In the example above, it calls the service from the execute_callback.

In the latest version, it gets called from the handle_accepted_callback.

I have used the goal_callback to determine if it should be accepted or rejected, the handle_accepted_callback then writes to the hardware and polls the system until the motion has started, and then the execute_callback polls the system to see if it has succeeded, failed, or been canceled.

It now works, although I am not sure if this is due to this change, or if it is because I am now using humle.

CraigH92 gravatar image CraigH92  ( 2023-05-09 03:37:37 -0500 )edit