Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ROS specific launch #75

Merged
merged 24 commits into from
Jun 15, 2018
Merged

ROS specific launch #75

merged 24 commits into from
Jun 15, 2018

Conversation

wjwwood
Copy link
Member

@wjwwood wjwwood commented Jun 12, 2018

@wjwwood wjwwood added enhancement New feature or request in progress Actively being worked on (Kanban column) labels Jun 12, 2018
@wjwwood wjwwood self-assigned this Jun 12, 2018
@dejanpan
Copy link

@sumanth-nirmal

@wjwwood wjwwood force-pushed the launch_ros branch 3 times, most recently from 735ea98 to 20982c2 Compare June 13, 2018 05:29
@sumanth-nirmal
Copy link

Lifecycle pub/sub example:

Copy/Paste mistake, correct link is https://github.com/ros2/launch/pull/75/files#diff-2086d091e24f813cafb9e343fb009c64

@wjwwood wjwwood force-pushed the launch_ros branch 4 times, most recently from 7fa3f8f to 86a58b1 Compare June 14, 2018 02:50
@wjwwood
Copy link
Member Author

wjwwood commented Jun 14, 2018

Rebased to one commit after #74 was merged. I'll start prepping this for review now.

@wjwwood wjwwood mentioned this pull request Jun 14, 2018
@dhood dhood self-requested a review June 14, 2018 18:34
Copy link
Member

@dhood dhood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partial review

_logger = logging.getLogger(name='launch_ros')


class Node(ExecuteProcess):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest naming this ExecuteNode instead. To me, seeing something trigger an action of "Node" somewhere like here can mislead people, and I don't see the need to risk the confusion (but there might be one)

Copy link
Member Author

@wjwwood wjwwood Jun 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I departed from Execute in these is because of how I planed to reuse them as arguments to the "node container", basically something like this:

LaunchDescription([
  ComponentNodeContainer(name='whatever', nodes=[
     Node(...),
     Node(...), 
  ]),
  Node(...),
])

Where the first two Node would never be visited, and instead would be introspected by the ComponentNodeContainer to figure out how to load them dynamically, and the last Node would actually get visited and therefore executed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there's a better way to organize the names, with that in mind. I don't know.

I also thought about an Execute action which would look like:

LaunchDescription([
  ComponentNodeContainer(name='whatever', nodes=[
     Node(...),
     Node(...), 
  ]),
  Execute(executable=Node(...), cwd='something'),
])

In which case Node would no longer inherit from ExecuteProcess.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if Node doesn't inherit from ExecuteProcess we'd still probably benefit from distinguishing it from something that is actually a node (it is not, in any of these cases). For now, it's namespaced enough that I suppose we can lean on that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's part of the reason I use launch.x.Y in most of the demos, to reinforce those differences.

I also somewhat inconsistently use Launch<thing> and <thing>, e.g. LaunchDescription versus Action (and not LaunchAction). I don't have a good reason for the inconsistency, other than I felt that in context launch.Action was specific enough, but launch.Description was not (mostly due to where and how it's used in the examples.

It might be something we want to revisit in the near future.

Do you want me to change anything for this pr related to this conversation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for this PR we're good, and then there's a chance we'll rename things in the future.

try:
rclpy.init(args=context.argv)
except RuntimeError as exc:
if 'rcl_init called while already initialized' in str(exc):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should make a todo (if we haven't already, apologies if this is something you've already tracked) to add an AlreadyInitializedException to rclpy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already made one 😁: ros2/rclpy#190

self.__rclpy_node = rclpy.create_node('launch_ros')
context.extend_globals({
'ros_startup_action': self,
'rclpy_node': self.__rclpy_node
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the usage will be clearer if this is renamed to something like launcher_node because otherwise being used as context.locals.rclpy_node you don't really know where it came from/who put it there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good 431853f

node_executable: SomeSubstitutionsType,
node_name: Optional[SomeSubstitutionsType] = None,
node_namespace: Optional[SomeSubstitutionsType] = None,
remappings: Optional[Dict[SomeSubstitutionsType, SomeSubstitutionsType]] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't there cases where the specific order of the remapping rules has to be preserved, because applying them in different orders causes different results? If so, another type would be more appropriate e.g. OrderedDict or list of tuples.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, let me look into that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I can't use OrderedDict as a type annotation with subscripted types for key and value, unfortunately. I don't like using a list of tuples because of how it looks/feels in the examples, but I don't see another way, and creating an ordered dict also uses a list of tuples. So I guess I'll do that, but the demos/examples need to be updated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

],
)))

# Make the talker node take the 'configure' transition.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for readability of this file I recommend reversing the creation of these actions so we read about them in the order that they would occur

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event handler must be registered before sending the event. Do you have a suggestion for how to reorder it that works?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if we are talking about the same scale. What I meant was add the action that creates the node; add the action that configures the node; add the action that activates the node; add the action that starts the listener. But I think it wasn't clear so you thought I was referring to this individual add_action call.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checkout 46bd3e1

self.__expanded_node_namespace = perform_substitutions(
context, normalize_to_list_of_substitutions(self.__node_namespace))
if not self.__expanded_node_namespace.startswith('/'):
self.__expanded_node_namespace = '/' + self.__expanded_node_namespace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the point of adding a leading forward slash if the next line does lstrip('/')?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea, 03e9d07

<maintainer email="william@osrfoundation.org">William Woodall</maintainer>
<license>Apache License 2.0</license>

<depend>launch</depend>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also depends on rclpy, lifecycle_msgs, ament_index_python and osrf_pycommon

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I don't think osrf_pycommon directly. cd0ebe2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You were right, I was wrong, here's the fixup: 15ac0c3

Return a LaunchDescription to be included before user descriptions.

:param: prefix_output_with_name if True, each line of output is prefixed
with the name of the node as `[name] `, else it is printed unmodified
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be more generic than referencing nodes since it will prefix all processes' output right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct b4b83da

raise
self.__launch_ros_node = rclpy.create_node('launch_ros')
context.extend_globals({
'ros_startup_action': self,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the intended use of this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future use, nothing for now.

from ...actions import LifecycleNode # noqa


def matches_node_name(node_name: Text) -> Callable[['LifecycleNode'], bool]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like this isn't lifecycle specific (maybe it was at some point but it doesn't look like it's used anymore), and the module could be moved/renamed (maybe in followup)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, not lifecycle specific, but there are no events that target Node right now (there are some that target ExecuteProcess however), so I put it with the lifecycle events which it is used with. We may need to move it later.

super().__init__(node_name=node_name, **kwargs)
self.__rclpy_subscription = None
# first item is unknown state
self.__current_state = next(iter(ChangeState.valid_states.values()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like it'd be more appropriate to explicitly set self.__current_state = ChangeState.valid_states[lifecycle_msgs.msg.State.PRIMARY_STATE_UNKNOWN] (because we want to do this regardless of dict ordering)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, I think I was imagining that the "first" state might change and this would be more future proof, but that's silly (it was probably late): 6955bc8

return
response = self.__rclpy_change_state_client.call(request)
if not response.success:
_logger.warn("Failed to make transition '{}' for LifecycleNode '{}'".format(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this should be an error instead of just a warning.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


def main(argv=sys.argv[1:]):
"""Main."""
def _on_transition_event(context):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not being used FWICT

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right 838242e

@wjwwood wjwwood added in review Waiting for review (Kanban column) and removed in progress Actively being worked on (Kanban column) labels Jun 15, 2018
Copy link
Member

@dhood dhood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢

@wjwwood
Copy link
Member Author

wjwwood commented Jun 15, 2018

CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@wjwwood wjwwood merged commit 86da112 into master Jun 15, 2018
@wjwwood wjwwood deleted the launch_ros branch June 15, 2018 01:20
@wjwwood wjwwood removed the in review Waiting for review (Kanban column) label Jun 15, 2018
@wjwwood
Copy link
Member Author

wjwwood commented Jun 15, 2018

:shipit:

@wjwwood
Copy link
Member Author

wjwwood commented Aug 30, 2018

Retroactive summary for this feature:

add ROS specific features to launch system

Purpose / Use Cases (Why implement this feature?)

This was implemented to provide ROS specific actions, substitutions, events, etc... to the launch system and to add default actions which allow monitoring of child nodes with lifecycle.

Design (How does it work?)

It provides new actions (like Node or LifecycleNode) and other related new entities like substitutions and events, which a user my use in their launch description. These new items are often just syntactic sugar, building entirely on existing features in launch. This is by far not a complete set of new features for ROS, but just the first iteration.

It also adds some "default" ROS related actions that are (will be) included in the ros2 launch command. These default actions do things like create a rclpy node for launch, which is used to monitor the state of child nodes which have lifecycle. It uses this information to generate events in launch when these child nodes change state.

Inputs / Outputs / API (How to use it?)

The API is anything in the launch_ros package:

https://github.com/ros2/launch/tree/baefc077b062ba5cfaff8a4dbecda7aaaeb4a05a/launch_ros/launch_ros

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants