From 39b29a959c0ddd689f81434ede5ab7a7b35ca7d6 Mon Sep 17 00:00:00 2001 From: Tully Foote Date: Wed, 16 Mar 2022 12:11:07 -0700 Subject: [PATCH 1/3] multicast working proof of concept Unicast extension in progress. Signed-off-by: Tully Foote --- setup.py | 1 + src/rocker/ros_extension.py | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/rocker/ros_extension.py diff --git a/setup.py b/setup.py index ff2e92d7..0e5b89bd 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,7 @@ 'nvidia = rocker.nvidia_extension:Nvidia', 'privileged = rocker.extensions:Privileged', 'pulse = rocker.extensions:PulseAudio', + 'ros = rocker.ros_extension:RosPorts', 'ssh = rocker.ssh_extension:Ssh', 'user = rocker.extensions:User', 'x11 = rocker.nvidia_extension:X11', diff --git a/src/rocker/ros_extension.py b/src/rocker/ros_extension.py new file mode 100644 index 00000000..56c2ecf7 --- /dev/null +++ b/src/rocker/ros_extension.py @@ -0,0 +1,95 @@ +# Copyright 2019 Open Source Robotics Foundation + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from argparse import ArgumentTypeError +import getpass +import os +from rocker.extensions import RockerExtension + + + +# From Section 9.6.2 of DDSI-RTPS Spec +# https://www.omg.org/spec/DDSI-RTPS/2.5/PDF + +# TODO answer here too: https://answers.ros.org/question/347630/ros2-port-forwarding/ + + +PB = 7400 +DG = 250 +PG = 2 +d0 = 0 +d1 = 10 +d2 = 1 +d3 = 11 + +def get_multicast_dds_ports(ros_domain_id): + + discovery_multicast_port = PB + DG * ros_domain_id + d0 + user_multicast_port = PB + DG * ros_domain_id + d2 + + return [discovery_multicast_port, user_multicast_port] + +def get_unicast_dds_ports(ros_domain_id, number_of_participants): + + ports = [] + + for participant_id in range(number_of_participants): + discovery_unicast_port = PB + DG * ros_domain_id + d3 + PG * participant_id + user_unicast_port = PB + DG * ros_domain_id + d3 + PG * participant_id + ports.append(discovery_unicast_port) + ports.append(user_unicast_port) + + return ports + + + +class RosPorts(RockerExtension): + + name = 'ros_ports' + + @classmethod + def get_name(cls): + return cls.name + + + def get_docker_args(self, cli_args): + args = '' + # only parameterized for testing + ros_domain_id_str = cli_args.get('ros_domain_id') + if not ros_domain_id_str: + ros_domain_id_str = os.environ.get('ROS_DOMAIN_ID', '0') + ros_domain_id = int(ros_domain_id_str) + + # if cli_args.get('ros_ports_type') == 'multicast': + (discovery_multicast_port, user_multicast_port) = get_multicast_dds_ports(ros_domain_id) + + args += ' -p {discovery_multicast_port} -p {user_multicast_port}'.format(**locals()) + args += ' -e ROS_DOMAIN_ID={ros_domain_id_str}'.format(**locals()) + return args + + @staticmethod + def register_arguments(parser, defaults={}): + parser.add_argument('--ros-ports', + action='store_true', + default=defaults.get(RosPorts.get_name(), None), + help="Expose ROS ports for DDS from container") + parser.add_argument('--ros-domain-id', + action='store', + default=defaults.get('ros_domain_id', None), + help="Override the ROS_DOMAIN_ID from the automatically detected one in the environment for mapping ROS Ports") + parser.add_argument('--ros-ports-type', + action='store', + choices=['multicast', 'unicast'], + default=defaults.get('ros_ports_type', 'multicast'), + help="Override the ROS_DOMAIN_ID from the automatically detected one in the environment for mapping ROS Ports") From 785edcd74ab605dab5ff8c6013ace0aae6fa693b Mon Sep 17 00:00:00 2001 From: Tully Foote Date: Wed, 16 Mar 2022 12:24:06 -0700 Subject: [PATCH 2/3] add support for unicast ports Signed-off-by: Tully Foote --- src/rocker/ros_extension.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/rocker/ros_extension.py b/src/rocker/ros_extension.py index 56c2ecf7..ab4d7b25 100644 --- a/src/rocker/ros_extension.py +++ b/src/rocker/ros_extension.py @@ -68,13 +68,18 @@ def get_docker_args(self, cli_args): # only parameterized for testing ros_domain_id_str = cli_args.get('ros_domain_id') if not ros_domain_id_str: - ros_domain_id_str = os.environ.get('ROS_DOMAIN_ID', '0') + ros_domain_id_str = os.environ.get('ROS_DOMAIN_ID', 0) ros_domain_id = int(ros_domain_id_str) - # if cli_args.get('ros_ports_type') == 'multicast': - (discovery_multicast_port, user_multicast_port) = get_multicast_dds_ports(ros_domain_id) + ports_type = cli_args.get('ros_ports_type') + ports = [] + if ports_type == 'multicast' or ports_type == 'both': + ports += get_multicast_dds_ports(ros_domain_id) + if ports_type == 'unicast' or ports_type == 'both': + ports += get_unicast_dds_ports(ros_domain_id, cli_args.get('ros_ports_unicast_participants')) - args += ' -p {discovery_multicast_port} -p {user_multicast_port}'.format(**locals()) + for port in ports: + args += ' -p {port}'.format(**locals()) args += ' -e ROS_DOMAIN_ID={ros_domain_id_str}'.format(**locals()) return args @@ -86,10 +91,16 @@ def register_arguments(parser, defaults={}): help="Expose ROS ports for DDS from container") parser.add_argument('--ros-domain-id', action='store', + type=int, default=defaults.get('ros_domain_id', None), help="Override the ROS_DOMAIN_ID from the automatically detected one in the environment for mapping ROS Ports") parser.add_argument('--ros-ports-type', action='store', - choices=['multicast', 'unicast'], + choices=['multicast', 'unicast', 'both'], default=defaults.get('ros_ports_type', 'multicast'), - help="Override the ROS_DOMAIN_ID from the automatically detected one in the environment for mapping ROS Ports") + help="Expose ports for unicast or multicast") + parser.add_argument('--ros-ports-unicast-participants', + action='store', + type=int, + default=defaults.get('ros_ports_unicast_participants', 1), + help="Set the number of unicast participants to map ports for.") From 656dcd2897fd3db5942eb8e53e784af28fc70ff4 Mon Sep 17 00:00:00 2001 From: Tully Foote Date: Wed, 16 Mar 2022 12:36:19 -0700 Subject: [PATCH 3/3] rename DDS variable for clarity Signed-off-by: Tully Foote --- src/rocker/ros_extension.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rocker/ros_extension.py b/src/rocker/ros_extension.py index ab4d7b25..79585329 100644 --- a/src/rocker/ros_extension.py +++ b/src/rocker/ros_extension.py @@ -25,18 +25,18 @@ # TODO answer here too: https://answers.ros.org/question/347630/ros2-port-forwarding/ -PB = 7400 -DG = 250 -PG = 2 -d0 = 0 -d1 = 10 -d2 = 1 -d3 = 11 +DDS_PB = 7400 +DDS_DG = 250 +DDS_PG = 2 +DDS_d0 = 0 +DDS_d1 = 10 +DDS_d2 = 1 +DDS_d3 = 11 def get_multicast_dds_ports(ros_domain_id): - discovery_multicast_port = PB + DG * ros_domain_id + d0 - user_multicast_port = PB + DG * ros_domain_id + d2 + discovery_multicast_port = DDS_PB + DDS_DG * ros_domain_id + DDS_d0 + user_multicast_port = DDS_PB + DDS_DG * ros_domain_id + DDS_d2 return [discovery_multicast_port, user_multicast_port] @@ -45,8 +45,8 @@ def get_unicast_dds_ports(ros_domain_id, number_of_participants): ports = [] for participant_id in range(number_of_participants): - discovery_unicast_port = PB + DG * ros_domain_id + d3 + PG * participant_id - user_unicast_port = PB + DG * ros_domain_id + d3 + PG * participant_id + discovery_unicast_port = DDS_PB + DDS_DG * ros_domain_id + DDS_d3 + DDS_PG * participant_id + user_unicast_port = DDS_PB + DDS_DG * ros_domain_id + DDS_d3 + DDS_PG * participant_id ports.append(discovery_unicast_port) ports.append(user_unicast_port)