From d92c1bd7babab05c99b5b9616bd50063768bbc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 24 May 2024 19:45:59 +0200 Subject: [PATCH] Launch gz_spawn_model from xml (#551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spawn models from XML. Signed-off-by: Carlos Agüero Co-authored-by: Addisu Z. Taddese --- ros_gz_sim/CMakeLists.txt | 1 + ros_gz_sim/launch/gz_spawn_model.launch | 28 +++ ros_gz_sim/ros_gz_sim/actions/__init__.py | 2 + .../ros_gz_sim/actions/gz_spawn_model.py | 207 ++++++++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 ros_gz_sim/launch/gz_spawn_model.launch create mode 100644 ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py diff --git a/ros_gz_sim/CMakeLists.txt b/ros_gz_sim/CMakeLists.txt index 0b7e2c25..dc21f8a9 100644 --- a/ros_gz_sim/CMakeLists.txt +++ b/ros_gz_sim/CMakeLists.txt @@ -99,6 +99,7 @@ install(FILES install(FILES "launch/gz_server.launch" "launch/gz_server.launch.py" + "launch/gz_spawn_model.launch" "launch/gz_spawn_model.launch.py" "launch/ros_gz_sim.launch" "launch/ros_gz_sim.launch.py" diff --git a/ros_gz_sim/launch/gz_spawn_model.launch b/ros_gz_sim/launch/gz_spawn_model.launch new file mode 100644 index 00000000..3f289048 --- /dev/null +++ b/ros_gz_sim/launch/gz_spawn_model.launch @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/ros_gz_sim/ros_gz_sim/actions/__init__.py b/ros_gz_sim/ros_gz_sim/actions/__init__.py index 03fe5afc..bea18edd 100644 --- a/ros_gz_sim/ros_gz_sim/actions/__init__.py +++ b/ros_gz_sim/ros_gz_sim/actions/__init__.py @@ -14,9 +14,11 @@ """Actions module.""" +from .gz_spawn_model import GzSpawnModel from .gzserver import GzServer __all__ = [ + 'GzSpawnModel', 'GzServer', ] diff --git a/ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py b/ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py new file mode 100644 index 00000000..7626eb01 --- /dev/null +++ b/ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py @@ -0,0 +1,207 @@ +# Copyright 2024 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Module for the gz_spawn_model action.""" + +from typing import List +from typing import Optional + +from launch.action import Action +from launch.actions import IncludeLaunchDescription +from launch.frontend import Entity, expose_action, Parser +from launch.launch_context import LaunchContext +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.some_substitutions_type import SomeSubstitutionsType +from launch.substitutions import PathJoinSubstitution +from launch_ros.substitutions import FindPackageShare + + +@expose_action('gz_spawn_model') +class GzSpawnModel(Action): + """Action that executes a gz_spawn_model ROS node.""" + + def __init__( + self, + *, + world: Optional[SomeSubstitutionsType] = None, + file: Optional[SomeSubstitutionsType] = None, + xml_string: Optional[SomeSubstitutionsType] = None, + topic: Optional[SomeSubstitutionsType] = None, + name: Optional[SomeSubstitutionsType] = None, + allow_renaming: Optional[SomeSubstitutionsType] = None, + x: Optional[SomeSubstitutionsType] = None, + y: Optional[SomeSubstitutionsType] = None, + z: Optional[SomeSubstitutionsType] = None, + roll: Optional[SomeSubstitutionsType] = None, + pitch: Optional[SomeSubstitutionsType] = None, + yaw: Optional[SomeSubstitutionsType] = None, + **kwargs + ) -> None: + """ + Construct a gz_spawn_model action. + + All arguments are forwarded to `ros_gz_sim.launch.gz_spawn_model.launch.py`, + so see the documentation of that class for further details. + + :param: world World name. + :param: file SDF filename. + :param: xml_string XML string. + :param: topic Get XML from this topic. + :param: name Name of the entity. + :param: allow_renaming Whether the entity allows renaming or not. + :param: x X coordinate. + :param: y Y coordinate. + :param: z Z coordinate. + :param: roll Roll orientation. + :param: pitch Pitch orientation. + :param: yaw Yaw orientation. + """ + super().__init__(**kwargs) + self.__world = world + self.__file = file + self.__xml_string = xml_string + self.__topic = topic + self.__name = name + self.__allow_renaming = allow_renaming + self.__x = x + self.__y = y + self.__z = z + self.__roll = roll + self.__pitch = pitch + self.__yaw = yaw + + @classmethod + def parse(cls, entity: Entity, parser: Parser): + """Parse gz_spawn_model.""" + _, kwargs = super().parse(entity, parser) + + world = entity.get_attr( + 'world', data_type=str, + optional=True) + + file = entity.get_attr( + 'file', data_type=str, + optional=True) + + xml_string = entity.get_attr( + 'xml_string', data_type=str, + optional=True) + + topic = entity.get_attr( + 'topic', data_type=str, + optional=True) + + name = entity.get_attr( + 'name', data_type=str, + optional=True) + + allow_renaming = entity.get_attr( + 'allow_renaming', data_type=str, + optional=True) + + x = entity.get_attr( + 'x', data_type=str, + optional=True) + + y = entity.get_attr( + 'y', data_type=str, + optional=True) + + z = entity.get_attr( + 'z', data_type=str, + optional=True) + + roll = entity.get_attr( + 'roll', data_type=str, + optional=True) + + pitch = entity.get_attr( + 'pitch', data_type=str, + optional=True) + + yaw = entity.get_attr( + 'yaw', data_type=str, + optional=True) + + if isinstance(world, str): + world = parser.parse_substitution(world) + kwargs['world'] = world + + if isinstance(file, str): + file = parser.parse_substitution(file) + kwargs['file'] = file + + if isinstance(xml_string, str): + xml_string = parser.parse_substitution(xml_string) + kwargs['xml_string'] = xml_string + + if isinstance(topic, str): + topic = parser.parse_substitution(topic) + kwargs['topic'] = topic + + if isinstance(name, str): + name = parser.parse_substitution(name) + kwargs['name'] = name + + if isinstance(allow_renaming, str): + allow_renaming = parser.parse_substitution(allow_renaming) + kwargs['allow_renaming'] = allow_renaming + + if isinstance(x, str): + x = parser.parse_substitution(x) + kwargs['x'] = x + + if isinstance(y, str): + y = parser.parse_substitution(y) + kwargs['y'] = y + + if isinstance(z, str): + z = parser.parse_substitution(z) + kwargs['z'] = z + + if isinstance(roll, str): + roll = parser.parse_substitution(roll) + kwargs['roll'] = roll + + if isinstance(pitch, str): + pitch = parser.parse_substitution(pitch) + kwargs['pitch'] = pitch + + if isinstance(yaw, str): + yaw = parser.parse_substitution(yaw) + kwargs['yaw'] = yaw + + return cls, kwargs + + def execute(self, context: LaunchContext) -> Optional[List[Action]]: + """Execute the action.""" + gz_spawn_model_description = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + [PathJoinSubstitution([FindPackageShare('ros_gz_sim'), + 'launch', + 'gz_spawn_model.launch.py'])]), + launch_arguments=[('world', self.__world), + ('file', self.__file), + ('xml_string', self.__xml_string), + ('topic', self.__topic), + ('name', self.__name), + ('allow_renaming', self.__allow_renaming), + ('x', self.__x), + ('y', self.__y), + ('z', self.__z), + ('roll', self.__roll), + ('pitch', self.__pitch), + ('yaw', self.__yaw), ]) + + return [gz_spawn_model_description]