From 9a5e141ae72ca53563316d6785ab8f8b7fc6cc4e Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 29 Sep 2020 10:22:35 -0300 Subject: [PATCH 1/4] Automatically launch parameter services based on a node option Signed-off-by: Ivan Santiago Paunovic --- .../main/java/org/ros2/rcljava/RCLJava.java | 32 ++++++++++++++-- .../java/org/ros2/rcljava/node/NodeImpl.java | 24 ++++++++++-- .../org/ros2/rcljava/node/NodeOptions.java | 37 +++++++++++++------ .../parameters/service/ParameterService.java | 1 + .../service/ParameterServiceImpl.java | 2 +- .../ros2/rcljava/node/NodeOptionsTest.java | 2 +- .../java/org/ros2/rcljava/node/NodeTest.java | 12 ++++-- .../node/NodeUndeclaredParametersTest.java | 2 +- 8 files changed, 88 insertions(+), 24 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java index d23f5122..ae5a9648 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java @@ -244,7 +244,7 @@ public static Context createContext() { * structure. */ public static Node createNode(final String nodeName) { - return createNode(nodeName, "", RCLJava.getDefaultContext(), new NodeOptions()); + return createNode(nodeName, "", new NodeOptions()); } /** @@ -255,13 +255,37 @@ public static Node createNode(final String nodeName) { * @return A @{link Node} that represents the underlying ROS2 node * structure. */ + public static Node createNode(final String nodeName, final String namespace) { + return createNode(nodeName, namespace, new NodeOptions()); + } + + /** + * Create a @{link Node}. + * + * @param nodeName The name that will identify this node in a ROS2 graph. + * @param namespace The namespace of the node. + * @param context Context used for creating the node, @link{RCLJava.getDefaultContext()} will be used if `null`. + * @return A @{link Node} that represents the underlying ROS2 node + * structure. + */ + // TODO(ivanpauno): Deprecate in favor of @link{RCLJava.createNode(name, namespace, nodeOptionsWithContext)}. public static Node createNode(final String nodeName, final String namespace, final Context context) { - return createNode(nodeName, namespace, context, new NodeOptions()); + return createNode(nodeName, namespace, new NodeOptions().setContext(context)); } - public static Node createNode(final String nodeName, final String namespace, final Context context, final NodeOptions options) { + /** + * Create a @{link Node}. + * + * @param nodeName The name that will identify this node in a ROS2 graph. + * @param namespace The namespace of the node. + * @param options Additional options to customize the Node creation. See @link{org.ros2.rcljava.node.NodeOptions}. + * @return A @{link Node} that represents the underlying ROS2 node + * structure. + */ + public static Node createNode(final String nodeName, final String namespace, final NodeOptions options) { + Context context = options.getContext() == null ? RCLJava.getDefaultContext() : options.getContext(); long nodeHandle = nativeCreateNodeHandle(nodeName, namespace, context.getHandle(), options.getCliArgs(), options.getUseGlobalArguments(), options.getEnableRosout()); - Node node = new NodeImpl(nodeHandle, context, options.getAllowUndeclaredParameters()); + Node node = new NodeImpl(nodeHandle, options); nodes.add(node); return node; } diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java index 041232d4..15622b81 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java @@ -28,6 +28,7 @@ import org.ros2.rcljava.graph.NameAndTypes; import org.ros2.rcljava.interfaces.MessageDefinition; import org.ros2.rcljava.interfaces.ServiceDefinition; +import org.ros2.rcljava.node.NodeOptions; import org.ros2.rcljava.parameters.InvalidParametersException; import org.ros2.rcljava.parameters.InvalidParameterValueException; import org.ros2.rcljava.parameters.ParameterAlreadyDeclaredException; @@ -35,6 +36,8 @@ import org.ros2.rcljava.parameters.ParameterNotDeclaredException; import org.ros2.rcljava.parameters.ParameterType; import org.ros2.rcljava.parameters.ParameterVariant; +import org.ros2.rcljava.parameters.service.ParameterService; +import org.ros2.rcljava.parameters.service.ParameterServiceImpl; import org.ros2.rcljava.publisher.Publisher; import org.ros2.rcljava.publisher.PublisherImpl; import org.ros2.rcljava.qos.QoSProfile; @@ -146,15 +149,18 @@ class ParameterAndDescriptor { private Object parameterCallbacksMutex; private List parameterCallbacks; + private final ParameterService parameterService; + /** * Constructor. * * @param handle A pointer to the underlying ROS2 node structure. Must not * be zero. */ - public NodeImpl(final long handle, final Context context, final boolean allowUndeclaredParameters) { + public NodeImpl(final long handle, final NodeOptions nodeOptions) { this.handle = handle; - this.context = context; + this.context = nodeOptions.getContext() == null ? + RCLJava.getDefaultContext() : nodeOptions.getContext(); this.publishers = new LinkedBlockingQueue(); this.subscriptions = new LinkedBlockingQueue(); this.services = new LinkedBlockingQueue(); @@ -162,13 +168,25 @@ public NodeImpl(final long handle, final Context context, final boolean allowUnd this.timers = new LinkedBlockingQueue(); this.parametersMutex = new Object(); this.parameters = new ConcurrentHashMap(); - this.allowUndeclaredParameters = allowUndeclaredParameters; + this.allowUndeclaredParameters = nodeOptions.getAllowUndeclaredParameters(); this.parameterCallbacksMutex = new Object(); this.parameterCallbacks = new ArrayList(); this.clock = new Clock(ClockType.ROS_TIME); this.wall_clock = new Clock(ClockType.STEADY_TIME); this.timeSource = new TimeSource(this); this.timeSource.attachClock(this.clock); + try { + this.parameterService = nodeOptions.getStartParameterServices() ? + new ParameterServiceImpl(this) : null; + // TODO(ivanpauno): Modify createService, createClient so they don't declare + // NoSuchFieldException and IllegalAccessException checked exceptions. + // That only happens if the user passed the wrong Class object, and the exception should + // be rethrown as an unchecked IllegalArgumentException. + } catch (NoSuchFieldException ex) { + throw new IllegalArgumentException(ex.getMessage()); + } catch (IllegalAccessException ex) { + throw new IllegalArgumentException(ex.getMessage()); + } } /** diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/NodeOptions.java b/rcljava/src/main/java/org/ros2/rcljava/node/NodeOptions.java index c64d65e1..cd230123 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeOptions.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeOptions.java @@ -17,18 +17,15 @@ import java.util.ArrayList; +import org.ros2.rcljava.contexts.Context; + public class NodeOptions { - private boolean useGlobalArguments; - private boolean enableRosout; - private boolean allowUndeclaredParameters; - private ArrayList cliArgs; - - public NodeOptions() { - this.useGlobalArguments = true; - this.enableRosout = true; - this.allowUndeclaredParameters = false; - this.cliArgs = new ArrayList(); - } + private boolean useGlobalArguments = true; + private boolean enableRosout = true; + private boolean allowUndeclaredParameters = false; + private boolean startParameterServices = true; + private Context context = null; + private ArrayList cliArgs = new ArrayList(); public final boolean getUseGlobalArguments() { return this.useGlobalArguments; @@ -57,6 +54,24 @@ public NodeOptions setAllowUndeclaredParameters(boolean allow) { return this; } + public final boolean getStartParameterServices() { + return this.startParameterServices; + } + + public NodeOptions setStartParameterServices(boolean startParameterServices) { + this.startParameterServices = startParameterServices; + return this; + } + + public final Context getContext() { + return this.context; + } + + public NodeOptions setContext(Context context) { + this.context = context; + return this; + } + public final ArrayList getCliArgs() { return this.cliArgs; } diff --git a/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java b/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java index 0be72380..6fd04986 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java +++ b/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java @@ -15,4 +15,5 @@ package org.ros2.rcljava.parameters.service; +// TODO(ivanpauno): What is this interface for? public interface ParameterService {} \ No newline at end of file diff --git a/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterServiceImpl.java b/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterServiceImpl.java index b13d8e86..c5baa7c0 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterServiceImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterServiceImpl.java @@ -149,4 +149,4 @@ public void accept(RMWRequestId rmwRequestId, public ParameterServiceImpl(final Node node) throws NoSuchFieldException, IllegalAccessException { this(node, QoSProfile.PARAMETERS); } -} \ No newline at end of file +} diff --git a/rcljava/src/test/java/org/ros2/rcljava/node/NodeOptionsTest.java b/rcljava/src/test/java/org/ros2/rcljava/node/NodeOptionsTest.java index ce3bca84..ac188e1f 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/node/NodeOptionsTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/node/NodeOptionsTest.java @@ -46,7 +46,7 @@ public static void tearDownOnce() { public final void testCreateNodeWithArgs() { NodeOptions options = new NodeOptions(); options.setCliArgs(new ArrayList(Arrays.asList("--ros-args", "-r", "__ns:=/foo"))); - Node node = RCLJava.createNode("test_node", "", RCLJava.getDefaultContext(), options); + Node node = RCLJava.createNode("test_node", "", options); assertEquals("test_node", node.getName()); assertEquals("/foo", node.getNamespace()); diff --git a/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java index 213179c0..21573efb 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/node/NodeTest.java @@ -50,6 +50,7 @@ import org.ros2.rcljava.graph.NameAndTypes; import org.ros2.rcljava.graph.NodeNameInfo; import org.ros2.rcljava.node.Node; +import org.ros2.rcljava.node.NodeOptions; import org.ros2.rcljava.publisher.Publisher; import org.ros2.rcljava.qos.policies.Reliability; import org.ros2.rcljava.qos.QoSProfile; @@ -124,7 +125,8 @@ public final void accept(final T msg) { @Before public void setUp() { - node = RCLJava.createNode("test_node"); + node = RCLJava.createNode( + "test_node", "/", new NodeOptions().setStartParameterServices(false)); primitives1 = new rcljava.msg.Primitives(); primitives2 = new rcljava.msg.Primitives(); @@ -1357,7 +1359,9 @@ public void accept(final Collection local, Collection service1 = node.createService( rcljava.srv.AddTwoInts.class, "test_get_service_names_and_types_one", new TriConsumer< @@ -1475,7 +1479,9 @@ public void accept(final Collection local, Collection client1 = node.createClient( rcljava.srv.AddTwoInts.class, "test_get_client_names_and_types_one"); Client client2 = node.createClient( diff --git a/rcljava/src/test/java/org/ros2/rcljava/node/NodeUndeclaredParametersTest.java b/rcljava/src/test/java/org/ros2/rcljava/node/NodeUndeclaredParametersTest.java index 7a02e961..4feb2a2d 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/node/NodeUndeclaredParametersTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/node/NodeUndeclaredParametersTest.java @@ -55,7 +55,7 @@ public static void tearDownOnce() { public void setUp() { NodeOptions options = new NodeOptions(); options.setAllowUndeclaredParameters(true); - node = RCLJava.createNode("test_node", "", RCLJava.getDefaultContext(), options); + node = RCLJava.createNode("test_node", "", options); } @After From 69d508f72b688a27c9b815cd0d04331054035083 Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 29 Sep 2020 18:32:49 -0300 Subject: [PATCH 2/4] deprecate method Signed-off-by: Ivan Santiago Paunovic --- rcljava/src/main/java/org/ros2/rcljava/RCLJava.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java index ae5a9648..9d8a322e 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java @@ -262,13 +262,14 @@ public static Node createNode(final String nodeName, final String namespace) { /** * Create a @{link Node}. * + * @deprecated Use `RCLJava.createNode(nodeName, namespace, new NodeOptions.setContext(context))` instead. * @param nodeName The name that will identify this node in a ROS2 graph. * @param namespace The namespace of the node. * @param context Context used for creating the node, @link{RCLJava.getDefaultContext()} will be used if `null`. * @return A @{link Node} that represents the underlying ROS2 node * structure. */ - // TODO(ivanpauno): Deprecate in favor of @link{RCLJava.createNode(name, namespace, nodeOptionsWithContext)}. + @Deprecated public static Node createNode(final String nodeName, final String namespace, final Context context) { return createNode(nodeName, namespace, new NodeOptions().setContext(context)); } From 9fabd7ed1ba23770de63956d5a917029058f4052 Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 29 Sep 2020 18:33:43 -0300 Subject: [PATCH 3/4] Replace 'ROS2' with 'ROS 2' Signed-off-by: Ivan Santiago Paunovic --- .../main/java/org/ros2/rcljava/RCLJava.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java index 9d8a322e..e5d2475b 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java @@ -43,7 +43,7 @@ import org.ros2.rcljava.timer.Timer; /** - * Entry point for the ROS2 Java API, similar to the rclcpp API. + * Entry point for the ROS 2 Java API, similar to the rclcpp API. */ public final class RCLJava { private static final Logger logger = LoggerFactory.getLogger(RCLJava.class); @@ -149,7 +149,7 @@ public static boolean isInitialized() { /** * Initialize the RCLJava API. If successful, a valid RMW implementation will - * be loaded and accessible, enabling the creating of ROS2 entities + * be loaded and accessible, enabling the creating of ROS 2 entities * (@{link Node}s, @{link Publisher}s and @{link Subscription}s. * This also initializes the default context. */ @@ -180,18 +180,18 @@ public static synchronized void rclJavaInit(String args[]) { private static native long nativeCreateContextHandle(); /** - * Create a ROS2 node (rcl_node_t) and return a pointer to it as an integer. + * Create a ROS 2 node (rcl_node_t) and return a pointer to it as an integer. * - * @param nodeName The name that will identify this node in a ROS2 graph. + * @param nodeName The name that will identify this node in a ROS 2 graph. * @param namespace The namespace of the node. * @param contextHandle Pointer to a context (rcl_context_t) with which to associated the node. - * @return A pointer to the underlying ROS2 node structure. + * @return A pointer to the underlying ROS 2 node structure. */ private static native long nativeCreateNodeHandle(String nodeName, String namespace, long contextHandle, ArrayList arguments, boolean useGlobalArguments, boolean enableRosout); /** * @return The identifier of the currently active RMW implementation via the - * native ROS2 API. + * native ROS 2 API. */ private static native String nativeGetRMWIdentifier(); @@ -203,7 +203,7 @@ public static String getRMWIdentifier() { } /** - * Call the underlying ROS2 rcl mechanism to check if ROS2 has been shut + * Call the underlying ROS 2 rcl mechanism to check if ROS 2 has been shut * down. * * @return true if RCLJava hasn't been shut down, false otherwise. @@ -239,8 +239,8 @@ public static Context createContext() { /** * Create a @{link Node}. * - * @param nodeName The name that will identify this node in a ROS2 graph. - * @return A @{link Node} that represents the underlying ROS2 node + * @param nodeName The name that will identify this node in a ROS 2 graph. + * @return A @{link Node} that represents the underlying ROS 2 node * structure. */ public static Node createNode(final String nodeName) { @@ -250,9 +250,9 @@ public static Node createNode(final String nodeName) { /** * Create a @{link Node}. * - * @param nodeName The name that will identify this node in a ROS2 graph. + * @param nodeName The name that will identify this node in a ROS 2 graph. * @param namespace The namespace of the node. - * @return A @{link Node} that represents the underlying ROS2 node + * @return A @{link Node} that represents the underlying ROS 2 node * structure. */ public static Node createNode(final String nodeName, final String namespace) { @@ -263,10 +263,10 @@ public static Node createNode(final String nodeName, final String namespace) { * Create a @{link Node}. * * @deprecated Use `RCLJava.createNode(nodeName, namespace, new NodeOptions.setContext(context))` instead. - * @param nodeName The name that will identify this node in a ROS2 graph. + * @param nodeName The name that will identify this node in a ROS 2 graph. * @param namespace The namespace of the node. * @param context Context used for creating the node, @link{RCLJava.getDefaultContext()} will be used if `null`. - * @return A @{link Node} that represents the underlying ROS2 node + * @return A @{link Node} that represents the underlying ROS 2 node * structure. */ @Deprecated @@ -277,10 +277,10 @@ public static Node createNode(final String nodeName, final String namespace, fin /** * Create a @{link Node}. * - * @param nodeName The name that will identify this node in a ROS2 graph. + * @param nodeName The name that will identify this node in a ROS 2 graph. * @param namespace The namespace of the node. * @param options Additional options to customize the Node creation. See @link{org.ros2.rcljava.node.NodeOptions}. - * @return A @{link Node} that represents the underlying ROS2 node + * @return A @{link Node} that represents the underlying ROS 2 node * structure. */ public static Node createNode(final String nodeName, final String namespace, final NodeOptions options) { From 1d28970b84526a67303e210ab3090431338dba1f Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 29 Sep 2020 18:36:53 -0300 Subject: [PATCH 4/4] More nits Signed-off-by: Ivan Santiago Paunovic --- rcljava/src/main/java/org/ros2/rcljava/RCLJava.java | 12 ++++-------- .../rcljava/parameters/service/ParameterService.java | 3 +-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java index e5d2475b..87d7ebe8 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java +++ b/rcljava/src/main/java/org/ros2/rcljava/RCLJava.java @@ -240,8 +240,7 @@ public static Context createContext() { * Create a @{link Node}. * * @param nodeName The name that will identify this node in a ROS 2 graph. - * @return A @{link Node} that represents the underlying ROS 2 node - * structure. + * @return A @{link Node} that represents the underlying ROS 2 node structure. */ public static Node createNode(final String nodeName) { return createNode(nodeName, "", new NodeOptions()); @@ -252,8 +251,7 @@ public static Node createNode(final String nodeName) { * * @param nodeName The name that will identify this node in a ROS 2 graph. * @param namespace The namespace of the node. - * @return A @{link Node} that represents the underlying ROS 2 node - * structure. + * @return A @{link Node} that represents the underlying ROS 2 node structure. */ public static Node createNode(final String nodeName, final String namespace) { return createNode(nodeName, namespace, new NodeOptions()); @@ -266,8 +264,7 @@ public static Node createNode(final String nodeName, final String namespace) { * @param nodeName The name that will identify this node in a ROS 2 graph. * @param namespace The namespace of the node. * @param context Context used for creating the node, @link{RCLJava.getDefaultContext()} will be used if `null`. - * @return A @{link Node} that represents the underlying ROS 2 node - * structure. + * @return A @{link Node} that represents the underlying ROS 2 node structure. */ @Deprecated public static Node createNode(final String nodeName, final String namespace, final Context context) { @@ -280,8 +277,7 @@ public static Node createNode(final String nodeName, final String namespace, fin * @param nodeName The name that will identify this node in a ROS 2 graph. * @param namespace The namespace of the node. * @param options Additional options to customize the Node creation. See @link{org.ros2.rcljava.node.NodeOptions}. - * @return A @{link Node} that represents the underlying ROS 2 node - * structure. + * @return A @{link Node} that represents the underlying ROS 2 node structure. */ public static Node createNode(final String nodeName, final String namespace, final NodeOptions options) { Context context = options.getContext() == null ? RCLJava.getDefaultContext() : options.getContext(); diff --git a/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java b/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java index 6fd04986..8e2106f7 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java +++ b/rcljava/src/main/java/org/ros2/rcljava/parameters/service/ParameterService.java @@ -15,5 +15,4 @@ package org.ros2.rcljava.parameters.service; -// TODO(ivanpauno): What is this interface for? -public interface ParameterService {} \ No newline at end of file +public interface ParameterService {}