Skip to content

Commit

Permalink
Add support for Node post-initialization (#81)
Browse files Browse the repository at this point in the history
Signed-off-by: Michel Hidalgo <[email protected]>
  • Loading branch information
mhidalgo-bdai authored Mar 27, 2024
1 parent 03038ab commit cf9817b
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
12 changes: 12 additions & 0 deletions bdai_ros2_wrappers/bdai_ros2_wrappers/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ def managed(
def load(self, factory: NodeFactoryCallable, *args: typing.Any, **kwargs: typing.Any) -> rclpy.node.Node:
"""Instantiates and loads a ROS 2 node.
If a __post_init__ method is defined by the instantiated ROS 2 node, it will be invoked
after the node is added to the scope executor. This allows for blocking calls during
initialization, provided the scope executor can serve them in the background.
Args:
factory: callable to instantiate a ROS 2 node.
It is expected to accept `rclpy.node.Node` arguments.
Expand All @@ -333,6 +337,10 @@ def load(
) -> typing.List[rclpy.node.Node]:
"""Instantiates and loads a collection (or graph) of ROS 2 nodes.
For each ROS 2 node instantiated, if a __post_init__ method is defined it will be invoked
after the corresponding node has been added to the scope executor. This allows for blocking
calls during initialization, provided the scope executor can serve them in the background.
Args:
factory: callable to instantiate a collection of ROS 2 nodes.
It is expected to accept `rclpy.node.Node` arguments.
Expand Down Expand Up @@ -366,10 +374,14 @@ def load(
graph = list(node_or_graph)
for node in graph:
self._executor.add_node(node)
if hasattr(node, "__post_init__"):
node.__post_init__()
self._graph.extend(graph)
return graph
node = node_or_graph
self._executor.add_node(node)
if hasattr(node, "__post_init__"):
node.__post_init__()
self._graph.append(node)
return node

Expand Down
27 changes: 26 additions & 1 deletion bdai_ros2_wrappers/test/test_node.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# Copyright (c) 2024 Boston Dynamics AI Institute Inc. All rights reserved.

import threading
from typing import Generator
from typing import Any, Generator

import pytest
import rclpy
from rcl_interfaces.srv import GetParameters
from rclpy.context import Context
from std_srvs.srv import Trigger

from bdai_ros2_wrappers.executors import AutoScalingMultiThreadedExecutor
from bdai_ros2_wrappers.node import Node
from bdai_ros2_wrappers.scope import ROSAwareScope


@pytest.fixture
Expand Down Expand Up @@ -67,3 +69,26 @@ def dummy_server_callback(_: Trigger.Request, response: Trigger.Response) -> Tri
barrier.reset()
executor.remove_node(node)
executor.shutdown()


def test_node_post_initialization(ros: ROSAwareScope) -> None:
"""Asserts that Node post initialization is honored and that it affords blocking calls."""

class ComplexProxyNode(Node):
def __init__(self, *args: Any, remote_node_name: str, **kwargs: Any) -> None:
super().__init__(f"proxy_node_for_{remote_node_name}", *args, **kwargs)
self._get_remote_node_parameters_client = self.create_client(
GetParameters,
f"{remote_node_name}/get_parameters",
)

def __post_init__(self) -> None:
response = self._get_remote_node_parameters_client.call(
GetParameters.Request(names=["verbose"]),
)
self.verbose = response.values[0].bool_value

assert ros.node is not None
ros.node.declare_parameter("verbose", True)
with ros.managed(ComplexProxyNode, remote_node_name=ros.node.get_name()) as node:
assert node.verbose

0 comments on commit cf9817b

Please sign in to comment.