|
| 1 | +# Refactoring ExecuteProcess in ROS2 |
| 2 | +Initially proposed in [114](https://github.com/ros2/launch/issues/114), this document describes changes to `ExecuteProcess` and related classes. As initially implemented, some aspects of this are tightly coupled to both the implementation chosen and the assumption that processes will execute only in the local environment of the launch process itself. The following changes are designed to provide a more decoupled architecture that will ease support of launching processes locally, remotely, or potentially in containerized environments. Additionally, these changes will support the goal of decoupling the description of an executable from the actual action of executing it. These changes are designed so that existing systems may continue to use the interface before these changes without modification; newer systems may find benefits from adapting to the newer syntax, particularly when composing more complicated environments. |
| 3 | +## Affected classes before any changes |
| 4 | +The following classes currently exist and are relevant to the proposed changes. A brief summary of some relevant concepts is included with each. |
| 5 | +### launch.actions.ExecuteProcess |
| 6 | +Extends the `Action` class. Allows many items to be set via constructor, including information both on how to launch the process, and how to interact with it after launching. The available parameters and their documentation follow, split into categories considering whether they are more relevant to launching the process, or to dealing with its execution following launch. |
| 7 | + |
| 8 | +_Launch Details_ |
| 9 | + |
| 10 | +|Argument|Description| |
| 11 | +|---|---| |
| 12 | +|cmd|a list where the first item is the executable and the rest are arguments to the executable, each item may be a string or a list of strings and `Substitution`s to be resolved at runtime| |
| 13 | +|name|the label used to represent the process, as a string or a `Substitution` to be resolved at runtime, defaults to the basename of the executable| |
| 14 | +|cwd|the directory in which to run the executable| |
| 15 | +|env|dictionary of environment variables to be used, starting from a clean environment. If `None`, the current environment is used.| |
| 16 | +|additional\_env|dictionary of environment variables to be added. If `env` was `None`, they are added to the current environment. If not, `env` is updated with `additional_env`.| |
| 17 | + |
| 18 | +_Execution Details_ |
| 19 | + |
| 20 | +|Argument|Description| |
| 21 | +|---|---| |
| 22 | +|shell|if True, a shell is used to execute the cmd| |
| 23 | +|sigterm\_timeout|time until shutdown should escalate to `SIGTERM`, as a string or a list of strings and `Substitution`s to be resolved at runtime, defaults to the `LaunchConfiguration` called `sigterm_timeout`| |
| 24 | +|sigkill\_timeout|time until escalating to `SIGKILL` after `SIGTERM`, as a string or a list of strings and `Substitution`s to be resolved at runtime, defaults to the `LaunchConfiguration` called `sigkill_timeout`| |
| 25 | +|emulate\_tty|emulate a tty (terminal), defaults to `False`, but can be overridden with the `LaunchConfiguration` called `emulate_tty`, the value of which is evaluated as true or false according to `evaluate_condition_expression`.| |
| 26 | +|prefix|a set of commands/arguments to preceed the `cmd`, used for things like `gdb`/`valgrind` and defaults to the `LaunchConfiguration` called `launch-prefix`| |
| 27 | +|output|configuration for process output logging. Defaults to `log` i.e. log both `stdout` and `stderr` to launch main log file and stderr to the screen.| |
| 28 | +|output\_format|for logging each output line, supporting `str.format()` substitutions with the following keys in scope: `line` to reference the raw output line and `this` to reference this action instance.| |
| 29 | +|log\_cmd|if `True`, prints the final cmd before executing the process, which is useful for debugging when substitutions are involved.| |
| 30 | +|on\_exit|list of actions to execute upon process exit.| |
| 31 | + |
| 32 | +### launch_ros.actions.Node |
| 33 | + |
| 34 | +Extends the `ExecuteProcess` class. Added constructor parameters for this class all focus on concepts similar to the Launch Details discussed above, but the class also overrides the `execute` method of `ExecuteProcess`. The purpose of this override is to provide more granular structure for the command used to launch a ROS node. All the provided constructor parameters eventually reduce into a collection of substitution elements to compose the `cmd` parameter of `ExecuteProcess`. |
| 35 | + |
| 36 | +|Argument|Description| |
| 37 | +|---|---| |
| 38 | +|node_executable|the name of the executable to find if a package is provided or otherwise a path to the executable to run.| |
| 39 | +|package|the package in which the node executable can be found| |
| 40 | +|node_name|the name of the node| |
| 41 | +|node_namespace|the ros namespace for this Node| |
| 42 | +|parameters|list of names of yaml files with parameter rules, or dictionaries of parameters.| |
| 43 | +|remappings|ordered list of 'to' and 'from' string pairs to be passed to the node as ROS remapping rules| |
| 44 | +|arguments|list of extra arguments for the node| |
| 45 | + |
| 46 | +### launch_ros.descriptions.ComposableNode |
| 47 | + |
| 48 | +Does not extend another class. Constructor parameters closely mirror those of `Node`, and represent a node which may be executed as part of a composite process hosting multiple nodes. |
| 49 | + |
| 50 | +|Argument|Description| |
| 51 | +|---|---| |
| 52 | +|package|name of the ROS package the node plugin lives in| |
| 53 | +|node_plugin|name of the plugin to be loaded| |
| 54 | +|node_name|name the node should have| |
| 55 | +|node_namespace|namespace the node should create topics/services/etc in| |
| 56 | +|parameters|list of either paths to yaml files or dictionaries of parameters| |
| 57 | +|remappings|list of from/to pairs for remapping names| |
| 58 | +|extra_arguments|container specific arguments to be passed to the loaded node| |
| 59 | + |
| 60 | +### launch_ros.actions.ComposableNodeContainer |
| 61 | + |
| 62 | +Extends the `Node` class. Added constructor parameters for this class allow specification of the node container class name and namespace, along with a list of `Node`s to include. This class overrides the `execute` method of `Node` to add additional post-execution actions, loading the composable nodes into the now-executing container process. |
| 63 | + |
| 64 | +|Argument|Description| |
| 65 | +|---|---| |
| 66 | +|node_name|the name of the node, mandatory for full container node name resolution| |
| 67 | +|node_namespace|the ros namespace for this Node, mandatory for full container node name resolution| |
| 68 | +|composable\_node\_descriptions|optional descriptions of composable nodes to be loaded| |
| 69 | + |
| 70 | +### launch_ros.actions.LifecycleNode |
| 71 | + |
| 72 | +Extends the `Node` class. This class overrides the `execute` method of `Node` to register lifecycle message handlers, and exposes events to allow reactions to said lifecycle state transitions. |
| 73 | + |
| 74 | +## Considerations for Refactored Classes |
| 75 | + |
| 76 | +The primary goal of the refactor is to attempt to split the definition aspect of processes/nodes apart from the logic which executes those elements. `ComposableNode` provides a basis for similar breakouts of other elements. |
| 77 | + |
| 78 | +In general, classes should have similar analogs even in the refactor to ensure that existing classes can be replaced with backwards-compatible replacements that use the new structure. These replacements can be implemented to warn the user that the older definitions are deprecated, and suggest moving to the newer refactored style. |
| 79 | + |
| 80 | +Further consideration will be required for alternate frontends, such as XML, to determine how to implement these changes with streamlined syntax where possible. |
| 81 | + |
| 82 | +One difficulty when looking forward to allowing functionality such as remote and containerized execution is that the target environment is likely different than the local environment. Different packages may be installed, different environment variables may be defined, and so forth. To support this, it may be helpful to allow an alternate `launch.LaunchContext` to be defined for specific executions. |
| 83 | + |
| 84 | +## Proposed New Classes |
| 85 | +### launch.descriptions.Process |
| 86 | + |
| 87 | +This class would represent only the information required to define a process which might be run. It would not include execution-time details, nor provide monitoring of the process after launch. The constructor parameters would be pulled directly from the current `launch.actions.ExecuteProcess` class. |
| 88 | + |
| 89 | +|Argument|Description| |
| 90 | +|---|---| |
| 91 | +|cmd|a list where the first item is the executable and the rest are arguments to the executable, each item may be a string or a list of strings and `Substitution`s to be resolved at runtime| |
| 92 | +|name|the label used to represent the process, as a string or a `Substitution` to be resolved at runtime, defaults to the basename of the executable| |
| 93 | +|cwd|the directory in which to run the executable| |
| 94 | +|env|dictionary of environment variables to be used, starting from a clean environment. If `None`, the current environment is used.| |
| 95 | +|additional\_env|dictionary of environment variables to be added. If `env` was `None`, they are added to the current environment. If not, `env` is updated with `additional_env`.| |
| 96 | +|shell|if True, a shell is used to execute the cmd| |
| 97 | + |
| 98 | +### launch_ros.descriptions.Node |
| 99 | + |
| 100 | +This class would represent only the information required to define a node which might be run. It would inherit from `launch.descriptions.Process`, and populate its fields in the same manner as the current `launch.actions.Node`. |
| 101 | + |
| 102 | +|Argument|Description| |
| 103 | +|---|---| |
| 104 | +|node_executable|the name of the executable to find if a package is provided or otherwise a path to the executable to run.| |
| 105 | +|package|the package in which the node executable can be found| |
| 106 | +|node_name|the name of the node| |
| 107 | +|node_namespace|the ros namespace for this Node| |
| 108 | +|parameters|list of names of yaml files with parameter rules, or dictionaries of parameters.| |
| 109 | +|remappings|ordered list of 'to' and 'from' string pairs to be passed to the node as ROS remapping rules| |
| 110 | +|arguments|list of extra arguments for the node| |
| 111 | + |
| 112 | +### launch_ros.descriptions.ComposableNode |
| 113 | + |
| 114 | +This class would be modified to inherit from `launch_ros.descriptions.Node`. Most parameters would be passed to the new superclass. |
| 115 | + |
| 116 | +|Argument|Description| |
| 117 | +|---|---| |
| 118 | +|node_plugin|name of the plugin to be loaded| |
| 119 | + |
| 120 | +### launch.actions.ExecuteLocalProcess |
| 121 | + |
| 122 | +This class would represent the execution-time aspects of launch.actions.ExecuteProcess. The new `process_description` constructor parameter could be either a `launch.descriptions.Process` or a `launch_ros.descriptions.Node`; node-specific functionality is limited to proper configuration of the command line to be executed, so no special execution wrapper is needed. This is not the case for lifecycle nodes or composable nodes, which do require custom execution wrappers, described below. |
| 123 | + |
| 124 | +(Details regarding remote/containerized execution would not be considered here, and should be implemented in alternate execute actions as appropriate. They could, however, reuse the same process/node description classes, while providing alternate `launch.LaunchContext` information.) |
| 125 | + |
| 126 | +|Argument|Description| |
| 127 | +|---|---| |
| 128 | +|process\_description|the `launch.descriptions.Process` to execute as a local process| |
| 129 | +|shell|if True, a shell is used to execute the cmd| |
| 130 | +|sigterm\_timeout|time until shutdown should escalate to `SIGTERM`, as a string or a list of strings and `Substitution`s to be resolved at runtime, defaults to the `LaunchConfiguration` called `sigterm_timeout`| |
| 131 | +|sigkill\_timeout|time until escalating to `SIGKILL` after `SIGTERM`, as a string or a list of strings and `Substitution`s to be resolved at runtime, defaults to the `LaunchConfiguration` called `sigkill_timeout`| |
| 132 | +|emulate\_tty|emulate a tty (terminal), defaults to `False`, but can be overridden with the `LaunchConfiguration` called `emulate_tty`, the value of which is evaluated as true or false according to `evaluate_condition_expression`.| |
| 133 | +|prefix|a set of commands/arguments to preceed the `cmd`, used for things like `gdb`/`valgrind` and defaults to the `LaunchConfiguration` called `launch-prefix`| |
| 134 | +|output|configuration for process output logging. Defaults to `log` i.e. log both `stdout` and `stderr` to launch main log file and stderr to the screen.| |
| 135 | +|output\_format|for logging each output line, supporting `str.format()` substitutions with the following keys in scope: `line` to reference the raw output line and `this` to reference this action instance.| |
| 136 | +|log\_cmd|if `True`, prints the final cmd before executing the process, which is useful for debugging when substitutions are involved.| |
| 137 | +|on\_exit|list of actions to execute upon process exit.| |
| 138 | + |
| 139 | +### launch_ros.actions.ExecuteLocalLifecycleNode |
| 140 | + |
| 141 | +This class would extend the `launch.actions.ExecuteLocalProcess` class, and provide the additional functionality required for managing lifecycle node events that are currently defined in `launch_ros.actions.LifecycleNode`. No additional constructor parameters are defined in this class. |
| 142 | + |
| 143 | +### launch_ros.actions.ExecuteLocalNodeContainer |
| 144 | + |
| 145 | +This class would extend the `launch.actions.ExecuteLocalProcess` class, and provide the additional functionality required for loading multiple nodes into a single container. The description of the composable node container itself should be provided as the `process_description` parameter to pass to `ExecuteLocalProcess`; child nodes would be provided as an additional constructor parameter. |
| 146 | + |
| 147 | +|Argument|Description| |
| 148 | +|---|---| |
| 149 | +|composable\_node\_descriptions|optional descriptions of composable nodes to be loaded| |
0 commit comments