GithubHelp home page GithubHelp logo

ros2 / launch Goto Github PK

View Code? Open in Web Editor NEW
117.0 27.0 138.0 1.89 MB

Tools for launching multiple processes and for writing tests involving multiple processes.

License: Apache License 2.0

Python 98.93% Makefile 0.05% Batchfile 0.08% CMake 0.80% C++ 0.13% Shell 0.01%

launch's People

Contributors

adityapande-1995 avatar ahcorde avatar audrow avatar blast545 avatar christophebedard avatar clalancette avatar cottsay avatar dhood avatar dirk-thomas avatar esteve avatar gerkey avatar hidmic avatar ivanpauno avatar jacobperron avatar jacquelinekay avatar khush-dev avatar m-elwin avatar marcoag avatar methyldragon avatar mikaelarguedas avatar mjcarroll avatar mjeronimo avatar nuclearsandwich avatar paudrow avatar pbaughman avatar rotu avatar sloretz avatar wjwwood avatar yadunund avatar zmk5 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

launch's Issues

[launch] Multi-machine Launching

Feature request

Feature description

Add a way to launch processes on multiple machines using ssh in a fashion similar to the old launch system. Would allow for the launching of more complex systems.

[launch] prevent Actions from being reused

Currently if you provide an instance of an Action to be visited more than once it will be and that breaks the assumption of one time use of some Actions like ExecuteProcess.

Instead Actions should declare if they're reusable or not (default to not probably) and then get an appropriate error when they are accidentally reused.

Follow up of #90 (comment)

Load node configuration or parameters during launch

Feature request

Many nodes need configuration or parameters to make it flexible, however I didn't found a good example to show how to load parameters, as rosparam or param do in ROS. I think it's a necessary feature to ROS2.

Feature description

Similar as rosparam and param in ROS.

Implementation considerations

Define some keywords in Descriptors, load or parser the parameters and configurations, pass them to node. Node set them through ParameterService.

roslaunch as a perpetually running daemon with integrated, automated, and programmable monitoring + relaunch

roslaunch itself runs as a daemon that is perpetually up once initially launched which monitors and relaunches nodes when needed

I'm proposing/requesting a way for roslaunch to detect failures and restart components using reasonable defaults and programmable parameters, and I'm hopeful that this proposal complements the ROS 2 document about node lifecycles nicely, and will perhaps become how such lifecycles are managed.

This idea probably needs some refinement but here is my first pass based on the conversation at:
https://groups.google.com/forum/#!topic/ros-sig-ng-ros/0HzF5J6hu4k

This bears some similarities for roscore in ROS 1, but it is for a different purpose.

details of proposed functionality

Launch files specify expected behavior of the nodes, and roslaunch verifies that those conditions are met. If the conditions are violated the node is automatically killed and relaunched.

For example, a minimum and maximum frequency of messages published on specified topics, and perhaps a heartbeat message that exists and is enabled by default. If the heartbeat fails or publishing rates exceed the max then the node is killed and relaunched.

All nodes would have this behavior integrated and enabled by default with reasonable default parameters that work for 90% of users. Assuming launch files are python (I think this was discussed in the mailing list) when other requirements exist this functionality can be disabled or modified with explicit changes to the launch file code.

Note that the roslaunch command can still be used exactly as it is now by users. The only difference is that when run, a new roslaunch process would look for an existing running daemon and if one exists, it would pass its parameters to the perpetual daemon to perform the actual launching.

sub-proposal 1: roslaunch daemon automatic startup at OS boot

The roslaunch daemon is supplied as one of the items to launch at OS boot by default #31

If not by default, I suggest a simple one line command which enables booting at OS launch. This should hopefully be cross platform, i.e. work for ubuntu, linux, mac, and perhaps other OSes.

sub-proposal 2: roslaunch daemon lifetime

The roslaunch daemon should detect its own crashes and/or death and automatically restart itself, except when a user runs something such as roslaunch shutdown which tells it to exit permanently.

This could be done by an extremely simple micro-daemon or script which only manages roslaunch.

[launch] consider refactoring ExecuteProcess into Execute and Executable

I believe it might be wise to separate the description of a process to be executed and its state while running from the Action which executes it.

Currently all of this information is encapsulated in the ExecuteProcess action, and my proposal would be to replace that with an action called Execute that takes an Executable (not an action or launch description entity, just a new kind of class). The Executable would contain any information needed by Execute to actually execute the executable described or introspect it. There could be sub classes of both Execute and Executable. For instance, a sub class of Executable might be Node or LifecycleNode, and a sub class of Execute might be ExecuteRemotely (execute on a different machine).

The reasons for doing this include:

  • using Executables (or subclasses thereof) with other actions
  • cleaner abstractions
    • i.e. the ExecuteProcess class is really big now and it would be nice to break into smaller parts
  • more readable
    • e.g. Execute(Executable(...)) or Execute(Node(...)) rather than ExecuteProcess(...) and Node(...)

The first reason is the most impactful, because it allows for a few use cases that aren't currently easy to implement:

  • Collect or generate Executable instances and later decide whether to pass to Execute, ExecuteRemotely, or ExecuteInDocker (just ideas)
  • Pass ComponentNode (possibly a subclass of Executable which represents a node that can be loaded dynamically) to either Execute() or ExecuteNodesInSharedProcess() (name needs work)
    • where the latter would take several ComponentNode instances and run them all in a single process

I still have some design questions in mind, like:

  • should Node inherit from Executable
  • should Executable contain process related things like pid and produce events for stdout and process exit, etc...
    • or should there be a Process that also inherits from Executable
    • because Node might not be associated with a process, or rather a process might not be associated with a single Node
  • what is the contract between Executable and Execute?
    • which creates event handlers and stores information?
    • what exactly are the responsibilities of Execute?

Add support for test processes

The launch library can be used to turn system tests into nosetests. However if the subcomponents of the system tests are either nosetests or gtests their xml output is not captured, only the return code is utilized.

rostest had a specific tag "test" which was aware that the subprocess was going to generate an xunit output passed appropriate flags to the subprocess and gathered up the results of the subprocess. We should support the same functionality to support system tests.

Launcher hangs with keyboard interrupt on Windows

Most of our usage of launch files is in tests, where launch_testing causes signals to be sent. But if you run them manually and ctrl-c them, they hang (for example demo_nodes_cpp's test_set_and_get_parameters__rmw_fastrtps_cpp_Release.py). I'm yet to find a launch file that doesn't cause this to happen.

In ros2/demos#175 (comment) @dirk-thomas noted that

The process hangs for me in launcher.py on the loop.close() call. Both subprocesses have been SIGTERMed and the loop is not running anymore. But the close() call still hangs forever.

Simple ROS2-agnostic example:

$ cat launch_wait.py
import os

from launch import LaunchDescriptor
from launch.launcher import DefaultLauncher

def main():
    launcher = DefaultLauncher()
    launch_descriptor = LaunchDescriptor()

    if os.name == 'nt':
        cmd=['ping', '127.0.0.1', '-n', '60']
    else:
        cmd=['sleep', '60']
    launch_descriptor.add_process(
        cmd=cmd,
        name='dummy_process',
    )
    launcher.add_launch_descriptor(launch_descriptor)

    rc = launcher.launch()
    print('Return code: ' + str(rc))
    exit(rc)


if __name__ == '__main__':
    main()

Behaviour on linux:

$ python3 launch_wait.py
(dummy_process) pid 13687: ['sleep', '60'] (stderr > stdout, all > console)
^C(dummy_process) signal SIGINT
(dummy_process) rc -2
Return code: 1

Behaviour on windows:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>python C:\Users\osrf\Downloads\r2b3-testing\overlay\src\launch_nothing.py
(dummy_process) pid 4416: ['ping', '127.0.0.1', '-n', '60'] (stderr > stdout, all > console)
[dummy_process]
[dummy_process] Pinging 127.0.0.1 with 32 bytes of data:
[dummy_process] Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
[dummy_process] Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
[dummy_process] Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
<manually ctrl c>
(dummy_process) signal SIGTERM
(dummy_process) rc 3221225786
<hangs>

Launching on arm64 with Fast-RTPS with fat archive from 2018-06-21 never quits

Bug report

Required Info:

Steps to reproduce issue

$ ros2 launch share/demo_nodes_cpp/launch/topics/talker_listener.launch.py

Let it run for a few seconds, then Ctrl-C:

root@changeling:~/ros2-linux# ros2 launch share/demo_nodes_cpp/launch/topics/talker_listener.launch.py
[INFO] [launch]: process[talker-1]: started with pid [21936]
[INFO] [launch]: process[listener-2]: started with pid [21937]
[INFO] [talker]: Publishing: 'Hello World: 1'
[INFO] [listener]: I heard: [Hello World: 1]
[INFO] [talker]: Publishing: 'Hello World: 2'
[INFO] [listener]: I heard: [Hello World: 2]
[INFO] [talker]: Publishing: 'Hello World: 3'
[INFO] [listener]: I heard: [Hello World: 3]
^C[WARNING] [launch.LaunchService]: user interrupted with ctrl-c (SIGINT)
signal_handler(2)
signal_handler(2)
[INFO] [launch]: process[talker-1]: process has finished cleanly
[INFO] [launch]: process[listener-2]: process has finished cleanly

It hangs there forever, until the user hits Ctrl-C.

Expected behavior

Launch file terminates cleanly on first Ctrl-C

Actual behavior

Launch hangs around "forever".

'ros2 launch' invalid choice

I do not get the option of launch after typing ros2
This is the error msg
invalid choice: 'launch' (choose from 'daemon', 'extension_points', 'extensions', 'lifecycle', 'msg', 'node', 'param', 'pkg', 'run', 'security', 'service', 'srv', 'topic')
How do I use launch command?

[launch] allow IncludeLaunchDescription to include other python launch files

Requires #115

At first IncludeLaunchDescription will require the creator to pass a LaunchDescription object, but it should also be able to take a path to a .launch.py file and then interpret it, call generate_launch_description() from it, and use that LaunchDescription. This might need a new Action or it could just be a feature of IncludeLaunchDescription.

[launch] add concept of Conditions

The concept of a "Condition" is a predicate that applies to certain kinds of entities like (but not exclusively) launch.Action.

This is the equivalent of if and unless attributes in roslaunch from ROS 1:

https://wiki.ros.org/roslaunch/XML#if_and_unless_attributes

The proposed launch.Condition would:

  • take a predicate which would be a list of substitutions
  • convert the list of substitutions into a string when the associated entity is visited
  • evaluate the resulting predicate string for true/false

There could be sub classes for convenience like launch.IfCondition and launch.UnlessCondition.

Conditions will need to be part of the interface of any entity which can be affected by them (i.e. either Action or LaunchDescriptionEntity should have a condition argument to the constructor.

One thing I haven't decided on is how to combine multiple conditions, the options are:

  • only ever take one condition, require compound conditions to be evaluated with Substitutions like launch.substitutions.PythonExpression
  • take a list of conditions, and do a boolean 'and' across all of them
  • take only one condition, but have additional convenience classes based on Condition, like CompoundCondition or AndCondition/OrCondition.

Reentrant call to stderr on interrupted Launch on Windows + Opensplice

Using last night's packaging job:

This only happens for rmw_opensplice_cpp:

C:\J\workspace\packaging_windows\ws>ros2 launch demo_nodes_cpp talker_listener.launch.py
[INFO] [launch]: process[talker.EXE-1]: started with pid [800]
[INFO] [launch]: process[listener.EXE-2]: started with pid [6560]
[INFO] [talker]: Publishing: 'Hello World: 1'
[INFO] [listener]: I heard: [Hello World: 1]
[INFO] [talker]: Publishing: 'Hello World: 2'
[INFO] [listener]: I heard: [Hello World: 2]
ssignal_handler(2)
[WARNING] [launch.LaunchService]: user interrupted with ctrl-c (SIGINT)
--- Logging error ---
Traceback (most recent call last):
  File "c:\python36\lib\logging\__init__.py", line 995, in emit
    stream.write(self.terminator)
RuntimeError: reentrant call inside <_io.BufferedWriter name='<stderr>'>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\python36\lib\logging\__init__.py", line 995, in emit
    stream.write(self.terminator)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\utilities\signal_management.py", line 121, in __on_sigint
    __custom_sigint_handler(signum, frame)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\launch_service.py", line 291, in _on_sigint
    _logger.warn(base_msg)
  File "c:\python36\lib\logging\__init__.py", line 1323, in warn
    self.warning(msg, *args, **kwargs)
  File "c:\python36\lib\logging\__init__.py", line 1318, in warning
    self._log(WARNING, msg, args, **kwargs)
  File "c:\python36\lib\logging\__init__.py", line 1442, in _log
    self.handle(record)
  File "c:\python36\lib\logging\__init__.py", line 1452, in handle
    self.callHandlers(record)
  File "c:\python36\lib\logging\__init__.py", line 1514, in callHandlers
    hdlr.handle(record)
  File "c:\python36\lib\logging\__init__.py", line 863, in handle
    self.emit(record)
  File "c:\python36\lib\logging\__init__.py", line 998, in emit
    self.handleError(record)
  File "c:\python36\lib\logging\__init__.py", line 915, in handleError
    sys.stderr.write('--- Logging error ---\n')
RuntimeError: reentrant call inside <_io.BufferedWriter name='<stderr>'>
Call stack:
  File "C:\J\workspace\packaging_windows\ws\install\Scripts\ros2-script.py", line 11, in <module>
    load_entry_point('ros2cli==0.4.0', 'console_scripts', 'ros2')()
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\ros2cli\cli.py", line 69, in main
    rc = extension.main(parser=parser, args=args)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\ros2launch\command\launch.py", line 78, in main
    debug=args.debug
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\ros2launch\api\api.py", line 127, in launch_a_python_launch_file
    return launch_service.run()
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\launch_service.py", line 314, in run
    self.__loop_from_run_thread.run_until_complete(run_loop_task)
  File "c:\python36\lib\asyncio\base_events.py", line 455, in run_until_complete
    self.run_forever()
  File "c:\python36\lib\asyncio\base_events.py", line 422, in run_forever
    self._run_once()
  File "c:\python36\lib\asyncio\base_events.py", line 1432, in _run_once
    handle._run()
  File "c:\python36\lib\asyncio\events.py", line 145, in _run
    self._callback(*self._args)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\launch_service.py", line 177, in _process_one_event
    await self.__process_event(next_event)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\launch_service.py", line 186, in __process_event
    entities = event_handler.handle(event, self.__context)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\event_handlers\on_process_io.py", line 73, in handle
    return self.__on_stdout(event)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch_ros\default_launch_description.py", line 57, in _on_process_output
    print(text, end='')
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\utilities\signal_management.py", line 121, in __on_sigint
    __custom_sigint_handler(signum, frame)
  File "C:\J\workspace\packaging_windows\ws\install\Lib\site-packages\launch\launch_service.py", line 291, in _on_sigint
    _logger.warn(base_msg)
Message: 'user interrupted with ctrl-c (SIGINT)'
Arguments: ()
ignal_handler(2)
[ERROR] [launch]: process[listener.EXE-2] process has died [pid 6560, exit code 3221225786, cmd 'C:\J\workspace\packaging_windows\ws\install\lib\demo_nodes_cpp\listener.EXE'].
[ERROR] [launch]: process[talker.EXE-1] process has died [pid 800, exit code 3221225786, cmd 'C:\J\workspace\packaging_windows\ws\install\lib\demo_nodes_cpp\talker.EXE'].

Works fine for e.g. connext:

C:\J\workspace\packaging_windows\ws>set RMW_IMPLEMENTATION=rmw_connext_cpp

C:\J\workspace\packaging_windows\ws>ros2 launch demo_nodes_cpp talker_listener.launch.py
RTI Data Distribution Service Evaluation License issued to OSRF (OSRF01) [email protected] For non-production use only.
Expires on 5-Nov-2018 See www.rti.com for more information.
[INFO] [launch]: process[talker.EXE-1]: started with pid [7312]
[INFO] [launch]: process[listener.EXE-2]: started with pid [10856]
RTI Data Distribution Service Evaluation License issued to OSRF (OSRF01) [email protected] For non-production use only.
Expires on 5-Nov-2018 See www.rti.com for more information.
RTI Data Distribution Service Evaluation License issued to OSRF (OSRF01) [email protected] For non-production use only.
Expires on 5-Nov-2018 See www.rti.com for more information.
[INFO] [talker]: Publishing: 'Hello World: 1'
[INFO] [listener]: I heard: [Hello World: 1]
[INFO] [talker]: Publishing: 'Hello World: 2'
[INFO] [listener]: I heard: [Hello World: 2]
[WARNING] [launch.LaunchService]: user interrupted with ctrl-c (SIGINT)
signal_handler(2)
signal_handler(2)
[INFO] [launch]: process[listener.EXE-2]: process has finished cleanly
[INFO] [launch]: process[talker.EXE-1]: process has finished cleanly

integrated and automated launch on OS boot

I'm proposing/requesting a way for a launch file or launch command to automatically be run when the computer boots, which is fully integrated and automated via ros2/launch.

Based on conversation at:
https://groups.google.com/forum/#!topic/ros-sig-ng-ros/0HzF5J6hu4k

Here is one proposal for how this could work, although alternative designs are worth considering:

roslaunch <my_params> --add-at-startup

expected behavior:

  1. launching may or may not occur right away (tbd)
  2. shut down machine
  3. turn on machine
  4. launch command runs

roslaunch --list-at-startup

expected behavior:

prints a list of commands that run every time the machine starts (perhaps with numeric ids?)

roslaunch --remove-at-startup <id>

expected behavior:

remove the item with id from the startup list.

<lauch_at_startup>

a tag that goes in .launch files, whenever the launch is run this automatically adds it to the startup list

[launch] add new concept for LaunchArgument

The idea behind the concept of LaunchArgument is that it should be something you can declare (perhaps as an Action) in a launch description which indicates some input configurations to the launch description.

If the launch description is included by another launch description then the arguments would be part of that action, but if the launch description is "run" by ros2 launch it would be possible to pass them as command line arguments.

This is partially equivalent to the <arg> tag in roslaunch from ROS 1:

https://wiki.ros.org/roslaunch/XML/arg

It should take a name, optionally a default value, optionally documentation for the argument, and possibly other things commonly associated with command line arguments, like being a flag rather than a key-value option or with special validation logic (is it the right type or in the right range)? Initially it should always have a type of string, and it can be evaluated by the things consuming it.

The result (default value or user provided) would be stored in a LaunchConfiguration of the same name.

Using types for process vs dict checking

Right now there is a lot of this in the launcher code:

if 'protocol' in dir(p):
    do stuff for process
if 'coroutine' in dir(p):
    do stuff for coroutine 

Does it make sense to do something like this instead:

if isinstance(p, ProcessDescriptor):
    do stuff for process
if isinstance(p, CoroutineDescriptor):
    do stuff for coroutine 

Or does that not canonical python?

ros2 launch can't find launch files installed with symlink-install

This isn't currently supported but it would be convenient; opening for visibility/tracking.

e.g. if I have an ament_python package, when I swap to installing with symlink, a launch invocation that was otherwise successful then gives:

$ ros2 launch topic_monitor launch_reliability_demo.launch.py
file 'launch_reliability_demo.launch.py' was not found in the share directory of package 'topic_monitor' which is at '/Users/dhood/ros2_ws/install_isolated/topic_monitor/share/topic_monitor'

Asyncio error on Xenial with (new) launch

$ ros2 launch demo_nodes_cpp add_two_ints.launch.py 
[INFO] [launch]: process[add_two_ints_server-1]: started with pid [17647]
[INFO] [launch]: process[add_two_ints_client-2]: started with pid [17648]
[INFO] [add_two_ints_server]: Incoming request
a: 2 b: 3
[INFO] [add_two_ints_client]: Result of add_two_ints: 5
[INFO] [launch]: process[add_two_ints_client-2]: process has finished cleanly
[INFO] [launch]: sending signal 'SIGINT' to process[add_two_ints_server-1]
signal_handler(2)
[INFO] [launch]: process[add_two_ints_server-1]: process has finished cleanly
Exception ignored in: <bound method BaseEventLoop.__del__ of <_UnixSelectorEventLoop running=False closed=True debug=False>>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/base_events.py", line 431, in __del__
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 58, in close
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 139, in remove_signal_handler
  File "/usr/lib/python3.5/signal.py", line 47, in signal
TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object

Seems like it's from https://bugs.python.org/issue23548 (you may have fixed this before @wjwwood , you are in the thread ๐Ÿ˜„ )

[launch] add new Action called GroupAction

Previously I had described a "Group" or "Group of Actions" as a separate entity from "Action", but since then I think we can just have a special Action that yields other actions and optionally does things like pushes/pops the launch configurations and set/unset specific launch configurations.

I believe the action I've described is capable of fulfilling the use cases I originally imagined for groups:

  • push containing nodes into a namespace
    • would be done by modifying the ros_namespace LaunchConfiguration that affects only the actions it yields
  • conditionally include other launch descriptions or actions
    • done with proposed Condition concept which would work on any Action, including this one
  • scope arbitrary launch configurations
    • done by using the LaunchContext to push and pop them
      • see:
        def _push_launch_configurations(self):
        self.__launch_configurations_stack.append(dict(self.__launch_configurations))
        def _pop_launch_configurations(self):
        if not self.__launch_configurations_stack:
        raise RuntimeError('launch_configurations stack unexpectedly empty')
        self.__launch_configurations = self.__launch_configurations_stack.pop()
        @property
        def launch_configurations(self) -> Dict[Text, Text]:
        """Getter for launch_configurations dictionary."""
        return self.__launch_configurations

We should also update the design document (ros2/design#163).

Unit tests based on new launch API

While porting some ROS 1 packages to ROS 2 I wanted to also migrate the rostests. Currently there is no API yet in launch_testing (beside the legacy API).

I tried two different approaches - the idea would be to move the "generic" stuff from one of these prototypes into launch_testing in order to be able to reuse it across tests:

  1. Creating custom classes for the LaunchDescription as well as the LaunchService (GT-RAIL/async_web_server_cpp@1dad9c5). In this case the whole launch file uses those custom classes instead of the original ones. The custom description contains the logic for the return code handling and shutting down. The custom service contains the logic to use the return codes from the test actions as the global return code.

The downside of this approach is that requires the launch file specifically being written for testing (by using these custom classes).

  1. A custom class (LaunchTestService) is used in addition to the normal launch entities and the actions which are tests are being registered at that class and the class also wraps the run invocation on the "normal" launch service: (GT-RAIL/async_web_server_cpp@fc6f35a)

Any kind of feedback is highly appreciated.

Features for ROS 2 Crystal

Features:

  • Launch with life cycle
    • Details TBD
  • Launch components into a "shared" process (see also https://github.com/ros2/launch/issues/160)
    • [rclcpp_components] CMake support macros for building Node's as "components"๐Ÿ‘ท
      • [rclcpp] Consolidate node arguments into a single options class to simplify user defined Node classes ๐Ÿ‘ท
        • [rclcpp] Consolidate how arguments are converted into configurations for the node (i.e. command line arguments into remappings and initial parameter values) ๐Ÿ‘ท
    • [rclcpp_components] Node component container executable(s)๐Ÿ‘ท
    • [launch_ros] Define nodes and then specify where they run separately
      • [launch] consider refactoring ExecuteProcess into Execute and Executable (#114)
  • Nest launch files with namespacing
    • [launch] add new Action called IncludeLaunchDescription (#115) โœ…
    • [launch] add new Action called GroupAction (#106) โœ…
    • [launch] allow IncludeLaunchDescription to include other python launch files (#116) โœ…
  • Launch into/with Docker containers
    • Details TBD
  • Pass params to nodes/components
    • [launch_ros] add ability to pass parameters to Node actions (#117) โœ…

Minor Features:

  • [launch] add new concept for LaunchArgument (#107) โœ…
  • [launch] provide default console and filesystem logging for executed processes (#104) ๐Ÿ‘ท
  • [launch] add concept of Conditions (#105) โœ…
  • [launch] add new Action called UnsetLaunchConfiguration (#108) โœ…
  • [launch] add new Action called UnregisterEventHandler (#110) โœ…
  • [launch] add option to RegisterEventHandler so that it unregisters itself after one event (#111) โœ…

Technical Enhancements and Bugs:

  • [launch] improve test coverage for non-core classes (#102) ๐Ÿ‘ท
  • [launch] prevent launch from hanging when unexpected exceptions occur internally (#112) ๐Ÿ‘ท
  • [launch] prevent Actions from being reused (#113)

Excluded Features:

  • Distributed / remote launch
    • [launch] Multi-machine Launching (#79)

Excluded Technical Enhancements and Bugs:

  • [launch] refactor introspection system (#103)
  • [launch] check that iterable contains only types in ensure_argument_type() (#109)

Anti-goals (things launch should not do itself):

launch does not exit after ctrl-c and subprocesses exit on Linux

Bug report

Required Info:

  • Operating System:
    • Ubuntu Linux 18.04 (but not on macOS 10.13.4)
  • Installation type:
    • Source
  • Version or commit hash:
    • N/A
  • DDS implementation:
    • Fast-RTPS (but should not matter)
  • Client library (if applicable):
    • N/A

Steps to reproduce issue

ros2 launch demo_nodes_cpp talker_listener.launch.py

Expected behavior

Exits after ctrl-c.

Actual behavior

Does not.

Additional information

See: ros2/demos#244 (review)

LaunchService.shutdown() fails to terminate run loop

Bug report

Required Info:

  • Operating System:
    • Ubuntu 16.04
  • Python Version:
  • Python 3.5.2
  • Installation type:
    • source
  • Version or commit hash:
    • 0.6.0
  • DDS implementation:
    • Fast-RTPS
  • Client library (if applicable):

Steps to reproduce issue

Run the following python code

#!/usr/bin/env python

import sys
import time
import threading
from launch import LaunchDescription
from launch import LaunchService
import launch_ros.actions

def main():
    ld = LaunchDescription([
        launch_ros.actions.Node(
            package='demo_nodes_cpp', node_executable='listener', output='screen'),
            ])
    ls = LaunchService()
    ls.include_launch_description(ld)
    t = threading.Thread(target=ls.run)
    t.start()
    time.sleep(5) # wait for listener to fully start
    ls.shutdown()
    t.join()

if __name__=="__main__":
    main()

Expected behavior

I expect the listener executable to terminate and the python code to exit.

Actual behavior

The python script produces the following output but never terminates.

[INFO] [launch]: process[listener-1]: started with pid [20208]
Starting shutdown
[INFO] [launch]: sending signal 'SIGINT' to process[listener-1]
[ERROR] [launch]: process[listener-1] failed to terminate '5' seconds after receiving 'SIGINT', escalating to 'SIGTERM'
[INFO] [launch]: sending signal 'SIGTERM' to process[listener-1]
[ERROR] [launch]: process[listener-1] failed to terminate '10.0' seconds after receiving 'SIGTERM', escalating to 'SIGKILL'
[INFO] [launch]: sending signal 'SIGKILL' to process[listener-1]

Additional information

The listener executable in fact terminates when the SIGINT is sent and remains as a zombie process.

20208 pts/20   Z+     0:00 [listener] <defunct>

[launch] provide default console and filesystem logging for executed processes

Currently in launch an executed process does not have anything printed to the console and nothing is logged to a file.

Some of the examples will create custom event handlers for this:

# Setup a custom event handler for all stdout/stderr from processes.
# Later, this will be a configurable, but always present, extension to the LaunchService.
def on_output(event: launch.Event) -> None:
for line in event.text.decode().splitlines():
print('[{}] {}'.format(
cast(launch.events.process.ProcessIO, event).process_name, line))
ld.add_action(launch.actions.RegisterEventHandler(launch.event_handlers.OnProcessIO(
# this is the action ^ and this, the event handler ^
on_stdout=on_output,
on_stderr=on_output,
)))

The "default launch description" provided by launch_ros also creates an event handler to cause executed processes to print to the console if configured to do so (with the output= option to ExecuteProcess and it's derived classes like Node):

def _on_process_output(event: launch.Event, *, file_name: Text, prefix_output: bool):
typed_event = cast(launch.events.process.ProcessIO, event)
text = event.text.decode()
if typed_event.execute_process_action.output == 'screen':
if prefix_output:
for line in text.splitlines():
print('[{}] {}'.format(event.process_name, line))
else:
print(text, end='')
elif typed_event.execute_process_action.output == 'log':
if file_name == 'stderr':
if prefix_output:
for line in text.splitlines():
print('[{}:{}] {}'.format(event.process_name, file_name, line))
else:
print(text, end='')
# TODO(wjwwood): implement file logging

But even this doesn't handle writing process output to a log file:

# TODO(wjwwood): implement file logging

Ideally, there would be some facility for this built into launch and launch_ros could additionally configure it to write files to something like ROS_HOME where as it might go to ~/.launch_logs (or something like that) instead. The user could further configure this default event handler if desired.

The user would still be able to provide their own event handler for output from process, to be run along with the default or instead of it.

API and doc

Hi,
I was trying to find out how to use this launch script. After some googling I found this: https://github.com/ros2/turtlebot2_demo/blob/master/turtlebot2_teleop/launch/turtlebot_joy.py
With the following example:

def launch(launch_descriptor, argv):
    ld = launch_descriptor
    package = 'turtlebot2_drivers'
    ld.add_process(
        cmd=[get_executable_path(package_name=package, executable_name='kobuki_node')],
        name='kobuki_node',
        exit_handler=restart_exit_handler,
    )
package = 'teleop_twist_joy'

Is this the final API? It looks quite heavy. The first thing I did was to make my own function:

def add(ld, pkg, name, exit_on_die=True):
    if exit_on_die:
        handler = restart_exit_handler,
    else:
        handler = default_exit_handler
    ld.add_process(
        cmd=[get_executable_path(package_name=pkg, executable_name=name)],
        name=name,
        exit_handler=handler
    )

So this bug request is about two things
1- If this is the expected API/syntax in launch file. Can I update README/Wiki to document it?
2- If this is not the final API, what will it be? What is missing?

Thanks

Launch system hungs while shutdown

Bug report

Required Info:

  • Operating System:
    • Ubuntu 16.04
  • Installation type:
    • Source
  • Version or commit hash:
  • DDS implementation:
    • Fast-RTPS
  • Client library (if applicable):
    • rclpy

Steps to reproduce issue

Launch system for 2 nodes (talker --> lifecycle node and listener --> regular node) with following actions:

  • When talker is in the Inactive state, trigger a transition to Active state
  • When talker reaches Active state, start the listener node.
    Launch file is below:
from launch import LaunchDescription
import launch
import launch.actions
import launch.events
import launch_ros.actions
import launch_ros.events
import launch_ros.events.lifecycle

import lifecycle_msgs.msg

def generate_launch_description():
    lifecycle_talker = launch_ros.actions.LifecycleNode(
        node_name = 'talker', package='lifecycle', node_executable='lifecycle_talker', output='screen')

    lifecycle_listener = launch_ros.actions.LifecycleNode(
        node_name='listener', package='lifecycle', node_executable='lifecycle_listener', output='screen')

    # When the talker reaches the 'inactive' state, make it take the 'activate' transition
    register_event_handler_for_talker_reaches_inactive_state = launch.actions.RegisterEventHandler(
        launch_ros.event_handlers.OnStateTransition(
            target_lifecycle_node=lifecycle_talker, goal_state='inactive',
            entities=[
                launch.actions.LogInfo(
                    msg="node 'talker' reached the 'inactive' state, 'activating'."),
                launch.actions.EmitEvent(event=launch_ros.events.lifecycle.ChangeState(
                    lifecycle_node_matcher=launch.events.process.matches_action(lifecycle_talker),
                    transition_id=lifecycle_msgs.msg.Transition.TRANSITION_ACTIVATE,
                )),
            ],
        )
    )

    # When the talker node reaches the 'active' state, log a message and start the listener node.
    register_event_handler_for_talker_reaches_active_state = launch.actions.RegisterEventHandler(
        launch_ros.event_handlers.OnStateTransition(
            target_lifecycle_node=lifecycle_talker, goal_state='active',
            entities=[
                launch.actions.LogInfo(
                    msg="node 'talker' reached the 'active' state, launching 'listener'."),
                lifecycle_listener,
            ],
        )
    )

    # Make the talker node take the 'configure' transition.
    emit_event_to_request_that_talker_does_configure_transition = launch.actions.EmitEvent(
        event=launch_ros.events.lifecycle.ChangeState(
            lifecycle_node_matcher=launch.events.process.matches_action(lifecycle_talker),
            transition_id=lifecycle_msgs.msg.Transition.TRANSITION_CONFIGURE,
        )
    )
    """Launch a talker and a listener."""
    return LaunchDescription([
        register_event_handler_for_talker_reaches_inactive_state,
        register_event_handler_for_talker_reaches_active_state,
        lifecycle_talker,
        emit_event_to_request_that_talker_does_configure_transition
    ])

Start the launch and when talker node is in Active state, trigger transition to Inactive with

ros2 lifecycle set talker deactivate

Once the talker node is back in the Active state, Ctrl-C makes the launch to hung with the following error:

^C[WARNING] [launch.LaunchService]: user interrupted with ctrl-c (SIGINT)
signal_handler(2)
signal_handler(2)
[INFO] [launch]: process[lifecycle_talker-1]: process has finished cleanly
[INFO] [launch]: process[lifecycle_listener-3]: process has finished cleanly
[INFO] [launch]: process[lifecycle_listener-2]: process has finished cleanly
[ERROR] [asyncio]: Task exception was never retrieved
future: <Task finished coro=<ExecuteProcess.__execute_process() done, defined at /home/sumanth.nirmal/ros2_ws/install/lib/python3.5/site-packages/launch/actions/execute_process.py:380> exception=InvalidStateError('FINISHED: <Future finished result=None>',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "/home/sumanth.nirmal/ros2_ws/install/lib/python3.5/site-packages/launch/actions/execute_process.py", line 424, in __execute_process
    self.__cleanup()
  File "/home/sumanth.nirmal/ros2_ws/install/lib/python3.5/site-packages/launch/actions/execute_process.py", line 321, in __cleanup
    self.__completed_future.set_result(None)
  File "/usr/lib/python3.5/asyncio/futures.py", line 329, in set_result
    raise InvalidStateError('{}: {!r}'.format(self._state, self))
asyncio.futures.InvalidStateError: FINISHED: <Future finished result=None>
signal_handler(2)

Expected behavior

The launch system cleanly exit on Ctrl-C

Actual behavior

The launch system hangs on Ctrl-C with the above launch file

Additional information

Also every time the talker node reaches Active state it starts a new Listener node.

Launch files don't shutdown properly with opensplice

If you run the pub sub example of the new-style launch files with debug hard-coded to True, after a keyboard interrupt you can see all of the shutdown handling (with fastrtps/connext):

RMW_IMPLEMENTATION=rmw_fastrtps_cpp python3 src/ros2/launch/launch_ros/examples/pub_sub_launch.py 

...
[INFO] [talker]: Publishing: 'Hello World: 4'
[DEBUG] [launch]: emitting event synchronously: 'launch.events.process.ProcessStdout'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0b1819ef98>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0b1819ef98>' โœ“ '<launch.event_handlers.on_process_io.OnProcessIO object at 0x7f0b1b2cf550>'
[INFO] [listener]: I heard: [Hello World: 4]
^C[WARNING] [launch.LaunchService]: user interrupted with ctrl-c (SIGINT)
[INFO] [asyncio]: %r was closed by peer
[INFO] [asyncio]: %r was closed by peer
[DEBUG] [launch]: emitting event: 'launch.events.Shutdown'
[DEBUG] [launch]: emitting event synchronously: 'launch.events.process.ProcessStdout'
[DEBUG] [launch]: emitting event synchronously: 'launch.events.process.ProcessStdout'
[INFO] [asyncio]: %r was closed by peer
[INFO] [asyncio]: %r was closed by peer
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f0b19194f98>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f0b19194f98>' โœ“ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7f0b1819e748>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f0b19194f98>' โœ“ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7f0b1819e0b8>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f0b19194f98>' โœ“ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7f0b1b2cfe10>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f0b19194f98>' โœ“ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7f0b1b2cf390>'
[WARNING] [asyncio]: Executing %s took %.3f seconds
[INFO] [asyncio]: %r exited with return code %r
[INFO] [asyncio]: %r exited with return code %r
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0b14f3f518>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0b14f3f518>' โœ“ '<launch.event_handlers.on_process_io.OnProcessIO object at 0x7f0b1b2cf550>'
signal_handler(2)
[INFO] [launch]: process[talker-1]: process has finished cleanly
[DEBUG] [launch]: emitting event: 'launch.events.process.ProcessExited'
[INFO] [launch]: process[listener-2]: process has finished cleanly
[DEBUG] [launch]: emitting event: 'launch.events.process.ProcessExited'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0b14f3f208>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0b14f3f208>' โœ“ '<launch.event_handlers.on_process_io.OnProcessIO object at 0x7f0b1b2cf550>'
signal_handler(2)
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_exited.ProcessExited object at 0x7f0b1b2cf5c0>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_exited.ProcessExited object at 0x7f0b1b2cf5c0>' โœ“ '<launch.event_handler.EventHandler object at 0x7f0b1b2cf4e0>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_exited.ProcessExited object at 0x7f0b14f3f518>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_exited.ProcessExited object at 0x7f0b14f3f518>' โœ“ '<launch.event_handler.EventHandler object at 0x7f0b1b2cf4e0>'

But for Opensplice you get a segfault:

$ RMW_IMPLEMENTATION=rmw_opensplice_cpp python3 src/ros2/launch/launch_ros/examples/pub_sub_launch.py 
...
[INFO] [talker]: Publishing: 'Hello World: 2'
[DEBUG] [launch]: emitting event synchronously: 'launch.events.process.ProcessStdout'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0975e89e10>'
[DEBUG] [launch.LaunchService]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7f0975e89e10>' โœ“ '<launch.event_handlers.on_process_io.OnProcessIO object at 0x7f0978fac5c0>'
[INFO] [listener]: I heard: [Hello World: 2]
^CSegmentation fault (core dumped)

I was not able to produce it with legacy launch but I did notice that it also happens even with only the default ROS launch descriptor included.

Default exit handler always has return code of 0 on windows

In ros2/rcl#198 I tried to add a test that checks for non-zero return codes for a particular process. On windows, the process would have a non-zero return code, but the launch would always have a return code of 0.

I think, using the default exit handler, it's not possible for the return code to ever be set on windows, because this line:

(os.name != 'nt' or not context.launch_state.teardown) and

combined with the line above that always ensures that context.launch_state.teardown is True:

context.launch_state.teardown = True

means that the branch where the return code of the launch is set can never be entered.

[launch] improve test coverage for non-core classes

Currently there are minimal tests for some of the core classes like LaunchDescription, LaunchService, and Action, but there's not really any tests for things like:

  • launch.LaunchInstrospector
  • launch.Substitution
  • anything in the namespaces:
    • launch.actions
    • launch.event_handlers
    • launch.events
    • launch.substitutions
    • launch.utilities

launch_testing always ignores license output when checking regexes

launch_testing has support for filtering the RTI license output when checking that the received output matches the expected output. If the license output appears and is not being filtered, then the check fails. I think that this is the desired behaviour.

However, this concept seems to be implemented differently for the case when regex_match is True. The license output is always ignored, whether or not it was supposed to be. (And any other output that appears that doesn't match the regex)

These non-matching, un-filtered lines are written here and read here, but since self.matched is True, the assertion does not fail.

Am I right in interpreting this as a bug that I should fix?

[launch] refactor introspection system

Some effort was already invested to express intent without side-effects in the new launch api, and each entity in a launch description can express itself and related, or "sub" entities. However, the introspection interfaces of various entities (Actions, Event Handlers, Events, Subsitutions, etc...) need to be refined to avoid impassible and opaque boundaries in the description when introspecting.

I have some ideas of what needs to change already, but ran out of time before the bouncy release. The launch.LaunchDescriptionEntity interfaces for this are the most "mature" and reflect somewhat closely what I'd like to update the other entities (like EventHandler and Substitution) to look like in the future:

def describe(self) -> Text:
"""
Return a description of this entity as a string.
When inherited from, calling this base class's default method is not
required, and in fact it will raise NotImplementedError.
"""
raise NotImplementedError()
def describe_sub_entities(self) -> List['LaunchDescriptionEntity']:
"""
Return a list of sub-entities which need to be described as well.
The list may be empty.
The sub-entities in this list should always be returned when this
entity is visited at runtime.
If there are entities which are only returned under some condition,
use the describe_conditional_sub_entities() method instead.
In the case of multiple layers of inheritance, you may wish to call
other base class's describe_sub_entities() and extend your own list of
sub-entities depending on the behavior of your class, but calling this
default method is not required.
"""
return []
def describe_conditional_sub_entities(self) -> List[Tuple[
Text, # text description of the condition
Iterable['LaunchDescriptionEntity'], # list of conditional sub-entities
]]:
"""
Return a list of condition descriptions and lists of sub-entities.
The list of conditional sub-entities are made up of two item tuples,
where the first item is a text description of the condition, and the
second item is a list of sub-entities which would be visited if the
condition is evaluated to be true during launch.
In the case of multiple layers of inheritance, you may wish to call the
base class's describe_conditional_sub_entities() and extend your own
list of sub-entities depending on the behavior of your class, but
calling this default method is not required.
"""
return []

The above interfaces may also need some work. Additionally, I think that right now the mechanism to "render" an entity during introspection (render to text for printing on the console in the case of launch.LaunchIntrospector) needs to be placed with or along side the entities themselves. Currently this logic is in the LaunchIntrospector, which was only done to get something working rapidly:

def format_entities(entities: List[LaunchDescriptionEntity]) -> List[Text]:
"""Return a list of lines of text that represent of a list of LaunchDescriptionEntity's."""
result = []
for entity in entities:
if is_a(entity, Action):
result.extend(format_action(cast(Action, entity)))
else:
result.append("Unknown entity('{}')".format(entity))
return result
def format_substitutions(substitutions: SomeSubstitutionsType) -> Text:
"""Return a text representation of some set of substitutions."""
normalized_substitutions = normalize_to_list_of_substitutions(substitutions)
result = ''
for sub in normalized_substitutions:
result += ' + '
if is_a(sub, TextSubstitution):
result += "'{}'".format(cast(TextSubstitution, sub).text)
elif is_a(sub, EnvironmentVariable):
result += 'EnvVarSub({})'.format(
format_substitutions(cast(EnvironmentVariable, sub).name))
elif is_a(sub, FindExecutable):
result += 'FindExecSub({})'.format(
format_substitutions(cast(FindExecutable, sub).name))
else:
result += "Substitution('{}')".format(sub)
return result[3:]
def format_event_handler(event_handler: EventHandler) -> List[Text]:
"""Return a text representation of an event handler."""
if hasattr(event_handler, 'describe'):
# TODO(wjwwood): consider supporting mode complex descriptions of branching
description, entities = event_handler.describe() # type: ignore
result = [description]
result.extend(indent(format_entities(entities)))
return result
else:
return ["EventHandler('{}')".format(hex(id(event_handler)))]
def format_action(action: Action) -> List[Text]:
"""Return a text representation of an action."""
if is_a(action, LogInfo):
return ['LogInfo({})'.format(format_substitutions(cast(LogInfo, action).msg))]
elif is_a(action, EmitEvent):
return ["EmitEvent(event='{}')".format(cast(EmitEvent, action).event.name)]
elif is_a(action, ExecuteProcess):
typed_action = cast(ExecuteProcess, action)
msg = 'ExecuteProcess(cmd=[{}], cwd={}, env={}, shell={})'.format(
', '.join([format_substitutions(x) for x in typed_action.cmd]),
typed_action.cwd if typed_action.cwd is None else "'{}'".format(
format_substitutions(typed_action.cwd)
),
typed_action.env if typed_action.env is None else '{' + ', '.join(
['{}: {}'.format(format_substitutions(k), format_substitutions(v))
for k, v in typed_action.env.items()] + '}'),
typed_action.shell,
)
return [msg]
elif is_a(action, RegisterEventHandler):
# Different variable name used to assist with type checking.
typed_action2 = cast(RegisterEventHandler, action)
result = ["RegisterEventHandler('{}'):".format(typed_action2.event_handler)]
result.extend(indent(format_event_handler(typed_action2.event_handler)))
return result
else:
return ["Action('{}')".format(action)]

Lastly, specific entities need to implement these interfaces and handle any special logic to make them useful during introspection. Specifically I noticed:

  • launch.EventHandlers could report what is yielded when an event is handled
  • Update introspection support for some launch.Actions (e.g. the ExecuteProcess action could report more of what it might do in certain situations)

A stretch goal of this refactoring would be to add some notion of "details" when introspecting, e.g. you might just want to see that a process is getting run and not all the various and tedious things it might do in every situation, unless you asked for those details, in which case it would show them. Basically some way for an entity to express that some sub entities and parts of its own description are implementation details and not usually important to express.

asyncio errors on Windows

On my latest Windows job I'm seeing errors like this:

http://ci.ros2.org/job/ros2_batch_ci_windows/299/console

19:09:05 39: ======================================================================
19:09:05 39: ERROR: test_publisher_subscriber_cpp__nested__rmw_connext_dynamic_cpp__rmw_connext_cpp.test_publisher_subscriber
19:09:05 39: ----------------------------------------------------------------------
19:09:05 39: Traceback (most recent call last):
19:09:05 39:   File "C:\tools\python\lib\site-packages\nose\case.py", line 198, in runTest
19:09:05 39:     self.test(*self.arg)
19:09:05 39:   File "C:\Jenkins\workspace\ros2_batch_ci_windows\workspace\build\test_communication\test_publisher_subscriber_cpp__nested__rmw_connext_dynamic_cpp__rmw_connext_cpp.py", line 22, in test_publisher_subscriber
19:09:05 39:     rc = launcher.launch()
19:09:05 39:   File "C:\Jenkins\workspace\ros2_batch_ci_windows\workspace\install\Lib\site-packages\launch\launcher.py", line 57, in launch
19:09:05 39:     asyncio.get_event_loop_policy().set_child_watcher(None)
19:09:05 39:   File "C:\tools\python\lib\asyncio\events.py", line 486, in set_child_watcher
19:09:05 39:     raise NotImplementedError
19:09:05 39: nose.proxy.NotImplementedError: 
19:09:05 39: -------------------- >> begin captured stdout << ---------------------

I think it might be related to this change?: 3684223#diff-64876ece93fadad6549af3a8bcb112c7R57

I'm not sure though.

launch not running processes on Windows

Bug report

Required Info:

  • Operating System:
    • Windows 10
  • Installation type:
    • Source
  • Version or commit hash:
    • Latest
  • DDS implementation:
    • N/A
  • Client library (if applicable):
    • N/A

Steps to reproduce issue

> ros2 launch demo_nodes_cpp talker_listener.launch.py

Expected behavior

Runs the talker and listener.

Actual behavior

Blocks with no output.

Additional information

I've already been looking into the issue for a while, but I'm going to stop and pick it up tomorrow. Just wanted to open this for visibility.

roslaunch daemon as an update tool

I'm proposing/requesting a way for a roslaunch daemon to automate software updates

This is dependent on #32, and related to #31.

This idea probably needs some refinement but here is my first pass based on the conversation at:
https://groups.google.com/forum/#!topic/ros-sig-ng-ros/0HzF5J6hu4k

The roslaunch daemon would provide a facility where it receives an update message for a specific component or set of components. The daemon would take down all relevant nodes (or potentially all running nodes), then run a command which downloads/updates packages. (perhaps rosdep update?)

It should be possible to support updates from forks of ros, and provide packages on a local network and/or on a USB stick.

If possible, I would suggest these updates be verified with some sort of signature from the start.

Once the update is completed, which may include updates to the list of roslaunch commands proposed in #31, all nodes which are down should be automatically relaunched.

[launch_ros] add ability to pass parameters to Node actions

Currently you can pass remapping arguments to Node as a dict, but to set parameters in a node from a launch file you need to pass a normal argument which points to an existing .yaml file.

To pass parameters via the launch file (without predefining them in an external .yaml file) will ultimately require the ability to pass parameters via the command line one at a time, which as of right now is not implemented yet.

In the meantime, individually specified parameters in a launch file for a node could be written into a temporary .yaml file by launch and then passed to the node, deleting the file after the node exits. However, I think this would step on a user specified parameter .yaml file unless the node can take more than one right now (it was discussed, but I don't know off-hand if it was implemented). In either case, if launch also lets you specify one or more .yaml files to be used as parameters (in addition to, and at the same time as, individually specified parameters for a node), then launch could just combine all of the yaml files and parameter kay-value pairs into a single .yaml file, cascading them in order (overwriting keys that appear more than once, with last wins).

It might look something like this:

launch_ros.actions.Node(...,
  parameters=[
    '/path/to/first_parameter_file.yaml',
    launch_ros.actions.node.NodeParameters({
      'foo': '1',
      // can have substitutions
      [LaunchConfiguration('ros_namespace'), '.my_node.executable_location']:
        [ExecutableInPackage('my_node', 'my_package')],
      ...,
    },
    [PackageShareDir('my_package'), Dir('config') / 'my_config_file.yaml'],
  ],
)

Launch testing doesn't kill nodes correctly if process fails to be spawned

If two processes are added to a launch descriptor, and one of them launches fine but the other one causes an error, the first one keeps persisting as a zombie process.

Steps to reproduce:

Use this fork with a test that tries to spawn a talker node and a nonexistent fail node: https://github.com/dhood/launch/blob/zombie_nodes/launch_testing/test/test_zombie_nodes.py

$ ament test --isolated --only-packages launch_testing

The talker will start then the test will crash because of the nonexistent node.

======================================================================
ERROR: test_zombie_nodes.test_zombie_nodes
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/dhood/.local/lib/python3.5/site-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/dhood/ros2_ws/src/ros2/launch/launch_testing/test/test_zombie_nodes.py", line 22, in test_zombie_nodes
    rc = launcher.launch()
  File "/home/dhood/ros2_ws/install_isolated/launch/lib/python3.5/site-packages/launch/launcher.py", line 96, in launch
    raise e.exception
  File "/home/dhood/ros2_ws/install_isolated/launch/lib/python3.5/site-packages/launch/launcher.py", line 127, in _run
    await self._spawn_process(index)
  File "/home/dhood/ros2_ws/install_isolated/launch/lib/python3.5/site-packages/launch/launcher.py", line 351, in _spawn_process
    **kwargs)
  File "/usr/lib/python3.5/asyncio/base_events.py", line 1079, in subprocess_exec
    bufsize, **kwargs)
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 184, in _make_subprocess_transport
    **kwargs)
  File "/usr/lib/python3.5/asyncio/base_subprocess.py", line 40, in __init__
    stderr=stderr, bufsize=bufsize, **kwargs)
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 649, in _start
    universal_newlines=False, bufsize=bufsize, **kwargs)
  File "/usr/lib/python3.5/subprocess.py", line 947, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.5/subprocess.py", line 1551, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'fail'
-------------------- >> begin captured logging << --------------------
asyncio: DEBUG: Using selector: EpollSelector
--------------------- >> end captured logging << ---------------------
Exception ignored in: <bound method BaseEventLoop.__del__ of <_UnixSelectorEventLoop running=False closed=True debug=False>>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/base_events.py", line 431, in __del__
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 58, in close
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 139, in remove_signal_handler
  File "/usr/lib/python3.5/signal.py", line 47, in signal
TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object
Exception ignored in: <bound method BaseSubprocessTransport.__del__ of <_UnixSubprocessTransport closed pid=3339 running stdin=<_UnixWritePipeTransport closing fd=15 open> stdout=<_UnixReadPipeTransport fd=16 open>>>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/base_subprocess.py", line 126, in __del__
  File "/usr/lib/python3.5/asyncio/base_subprocess.py", line 101, in close
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 568, in close
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 560, in write_eof
  File "/usr/lib/python3.5/asyncio/base_events.py", line 497, in call_soon
  File "/usr/lib/python3.5/asyncio/base_events.py", line 506, in _call_soon
  File "/usr/lib/python3.5/asyncio/base_events.py", line 334, in _check_closed
RuntimeError: Event loop is closed

Starting a listener you will notice the talker is still alive:

$ listener
I heard: [Hello World: 508]
I heard: [Hello World: 509]
I heard: [Hello World: 510]

New test failure with Python 3.6

I'm assuming this is related to something new in Python 3.6, since some asyncio stuff changed in that version. This is the new test failure after updating the macOS CI machines:

http://ci.ros2.org/job/ci_osx/1720/testReport/junit/(root)/test_interrupt_asynchronous_launcher/test_interrupt_asynchronous_launcher/

Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/Users/osrf/jenkins/workspace/ci_osx/ws/src/ros2/launch/launch/launch/launcher.py", line 411, in run
    self.launcher.launch()
  File "/Users/osrf/jenkins/workspace/ci_osx/ws/src/ros2/launch/launch/launch/launcher.py", line 96, in launch
    raise e.exception
  File "/Users/osrf/jenkins/workspace/ci_osx/ws/src/ros2/launch/launch/launch/launcher.py", line 127, in _run
    await self._spawn_process(index)
  File "/Users/osrf/jenkins/workspace/ci_osx/ws/src/ros2/launch/launch/launch/launcher.py", line 351, in _spawn_process
    **kwargs)
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py", line 1190, in subprocess_exec
    bufsize, **kwargs)
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/unix_events.py", line 194, in _make_subprocess_transport
    self._child_watcher_callback, transp)
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/unix_events.py", line 858, in add_child_handler
    "Cannot add child handler, "
RuntimeError: Cannot add child handler, the child watcher does not have a loop attached

I looked into the problem for a while, but I'm not sure why this error is happening. I think the loop (main thread loop) should be attached to the child watcher, but maybe I'm missing something.

@dirk-thomas do you have any idea about this issue?

Launch doesn't always bring down all nodes on keyboard interrupt

I have a launch script that starts various ROS 2 nodes. Most times if I kill it with ctrl-c it dies gracefully with no issues. Occassionally (I haven't figured out under what circumstances yet) it gives:

^CTraceback (most recent call last):
  File "/home/ubuntu/tb2_demo_ws/build_isolated/launch/launch/launcher.py", line 91, in launch
    returncode = loop.run_until_complete(generator)
  File "/usr/lib/python3.5/asyncio/base_events.py", line 375, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.5/asyncio/base_events.py", line 345, in run_forever
    self._run_once()
  File "/usr/lib/python3.5/asyncio/base_events.py", line 1276, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.5/selectors.py", line 441, in select
    fd_event_list = self._epoll.poll(timeout, max_ev)
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/ubuntu/tb2_demo_ws/install_isolated/launch/bin/launch", line 9, in <module>
    load_entry_point('launch', 'console_scripts', 'launch')()
  File "/home/ubuntu/tb2_demo_ws/build_isolated/launch/launch/main.py", line 47, in main
    rc = launcher.launch()
  File "/home/ubuntu/tb2_demo_ws/build_isolated/launch/launch/launcher.py", line 99, in launch
    loop.run_forever()
  File "/usr/lib/python3.5/asyncio/base_events.py", line 345, in run_forever
    self._run_once()
  File "/usr/lib/python3.5/asyncio/base_events.py", line 1276, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.5/selectors.py", line 441, in select
    fd_event_list = self._epoll.poll(timeout, max_ev)
KeyboardInterrupt
Exception ignored in: <bound method BaseEventLoop.__del__ of <_UnixSelectorEventLoop running=False closed=True debug=False>>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/base_events.py", line 431, in __del__
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 58, in close
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 139, in remove_signal_handler
  File "/usr/lib/python3.5/signal.py", line 47, in signal
TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object
Exception ignored in: <bound method BaseSubprocessTransport.__del__ of <_UnixSubprocessTransport closed pid=1247 running stdin=<_UnixWritePipeTransport closing fd=23 open> stdout=<_UnixReadPipeTransport fd=24 open>>>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/base_subprocess.py", line 126, in __del__
  File "/usr/lib/python3.5/asyncio/base_subprocess.py", line 101, in close
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 568, in close
  File "/usr/lib/python3.5/asyncio/unix_events.py", line 560, in write_eof
  File "/usr/lib/python3.5/asyncio/base_events.py", line 497, in call_soon
  File "/usr/lib/python3.5/asyncio/base_events.py", line 506, in _call_soon
  File "/usr/lib/python3.5/asyncio/base_events.py", line 334, in _check_closed
RuntimeError: Event loop is closed

On at least one occasion this has left one of the nodes running (as was happening in #54 ).

I've seen this issue https://mail.python.org/pipermail/python-bugs-list/2016-January/290450.html that suggests that the output isn't necessarily something to be concerned about, but perhaps in our use case it prevents the remaining nodes from being killed.

I know that this is vague: I will try to add more information about how to reproduce this when I find out.

[launch] add new Action called IncludeLaunchDescription

Already described in the architecture document:

https://github.com/ros2/launch/blob/88ae20ee2e950df91e5594975a82bffe1ebd928b/launch/doc/source/architecture.rst#basic-actions

Basically it will allow you to include another launch description, which is given to the constructor of the new action. You can already do this by emitting an event, launch.events.IncludeLaunchDescription (https://github.com/ros2/launch/blob/b78d87f31bb995c7226df66826e1a57079ae3a3f/launch/launch/events/include_launch_description.py), so at first this action will be only syntactic sugar but later it should interact with launch arguments (see: #107).

Nosetests results interpreted wrong by Jenkins

On Windows the nosetests have one failure, see generated xml file:

<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="launch.nosetests" tests="4" errors="1" failures="0" skip="0">
  <testcase classname="test_launch_with_coroutine" name="test_launch_with_coroutine" time="0.031">
    <error type="builtins.NotImplementedError" message="&#10;-------------------- &gt;&gt; begin captured logging &lt;&lt; --------------------&#10;asyncio: DEBUG: Using selector: SelectSelector&#10;--------------------- &gt;&gt; end captured logging &lt;&lt; ---------------------">
      <![CDATA[  File "C:\tools\python\lib\unittest\case.py", line 58, in testPartExecutor
    yield
  File "C:\tools\python\lib\unittest\case.py", line 577, in run
    testMethod()
  File "C:\tools\python\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "C:\Jenkins\workspace\ros2_batch_ci_windows\workspace\src\ros2\launch\launch\test\test_launch_with_coroutine.py", line 55, in test_launch_with_coroutine
    rc = default_launcher.launch()
  File "C:\Jenkins\workspace\ros2_batch_ci_windows\workspace\src\ros2\launch\launch\launch\launcher.py", line 49, in launch
    returncode = loop.run_until_complete(self._run())
  File "C:\tools\python\lib\asyncio\base_events.py", line 316, in run_until_complete
    return future.result()
  File "C:\tools\python\lib\asyncio\futures.py", line 275, in result
    raise self._exception
  File "C:\tools\python\lib\asyncio\tasks.py", line 238, in _step
    result = next(coro)
  File "C:\Jenkins\workspace\ros2_batch_ci_windows\workspace\src\ros2\launch\launch\launch\launcher.py", line 68, in _run
    yield from self._spawn_process(index)
  File "C:\Jenkins\workspace\ros2_batch_ci_windows\workspace\src\ros2\launch\launch\launch\launcher.py", line 228, in _spawn_process
    **kwargs)
  File "C:\tools\python\lib\asyncio\base_events.py", line 944, in subprocess_exec
    bufsize, **kwargs)
  File "C:\tools\python\lib\asyncio\coroutines.py", line 141, in coro
    res = func(*args, **kw)
  File "C:\tools\python\lib\asyncio\base_events.py", line 248, in _make_subprocess_transport
    raise NotImplementedError

-------------------- >> begin captured logging << --------------------
asyncio: DEBUG: Using selector: SelectSelector
--------------------- >> end captured logging << ---------------------]]>
    </error>
    <system-err><![CDATA[launch
]]></system-err>
  </testcase>
  <testcase classname="test_module_level_launch" name="test_one" time="1.000">
    <system-out><![CDATA[(foo) pid 1584: ['C:\\tools\\python\\python.exe', '-u', 'C:\\Jenkins\\workspace\\ros2_batch_ci_windows\\workspace\\src\\ros2\\launch\\launch\\test\\counter.py', '--limit', '15', '--sleep', '0.5'] (stderr > stdout, all > file:C:\Windows\TEMP\foo_celvr29w)
]]></system-out>
    <system-err><![CDATA[one
]]></system-err>
  </testcase>
  <testcase classname="test_module_level_launch" name="test_two" time="1.001">
    <system-err><![CDATA[two
]]></system-err>
  </testcase>
  <testcase classname="test_module_level_launch" name="test_three" time="1.016">
    <system-err><![CDATA[three
]]></system-err>
  </testcase>
</testsuite>

But Jenkins interprets the result as "successful": http://54.183.26.131:8080/view/ros2/job/ros2_batch_ci_windows/166/testReport/(root)/test_launch_with_coroutine/

This might require selecting a different result file type in the Jenkins job.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.