From 2c41efa0e3944eeba0b3c951a88034ff09945563 Mon Sep 17 00:00:00 2001 From: ahr Date: Tue, 7 Jan 2025 18:48:46 +0100 Subject: [PATCH] Rebase from 'debian/humble/automatika_ros_sugar' --- .github/workflows/release_mirror.yml | 24 --- CHANGELOG.rst | 6 + debian/changelog | 193 ----------------- debian/changelog.em | 7 + debian/compat | 1 - debian/compat.em | 1 + debian/control | 12 -- debian/control.em | 14 ++ debian/{copyright => copyright.em} | 10 +- debian/gbp.conf | 3 - debian/gbp.conf.em | 3 + debian/{rules => rules.em} | 22 +- debian/source/format | 1 - debian/source/format.em | 1 + debian/source/{options => options.em} | 3 +- package.xml | 2 +- ros_sugar/config/base_config.py | 18 +- ros_sugar/core/component.py | 4 +- ros_sugar/core/event.py | 15 +- ros_sugar/io/supported_types.py | 13 +- ros_sugar/io/topic.py | 2 +- ros_sugar/launch/_lifecycle_transition.py | 249 ++++++++++++++++++++++ ros_sugar/launch/launcher.py | 19 +- ros_sugar/utils.py | 11 +- 24 files changed, 346 insertions(+), 288 deletions(-) delete mode 100644 .github/workflows/release_mirror.yml delete mode 100644 debian/changelog create mode 100644 debian/changelog.em delete mode 100644 debian/compat create mode 100644 debian/compat.em delete mode 100644 debian/control create mode 100644 debian/control.em rename debian/{copyright => copyright.em} (50%) delete mode 100644 debian/gbp.conf create mode 100644 debian/gbp.conf.em rename debian/{rules => rules.em} (71%) delete mode 100644 debian/source/format create mode 100644 debian/source/format.em rename debian/source/{options => options.em} (81%) create mode 100644 ros_sugar/launch/_lifecycle_transition.py diff --git a/.github/workflows/release_mirror.yml b/.github/workflows/release_mirror.yml deleted file mode 100644 index ef7eb55..0000000 --- a/.github/workflows/release_mirror.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Copy repo to release mirror - -on: - workflow_dispatch: - release: - types: - - published - - -jobs: - release_mirror: - name: Push main to release repo - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - ref: main - - - uses: automatika-robotics/push-to-release-repo-action@v2 - with: - destination-username: "${{ secrets.DESTINATION_USERNAME }}" - destination-access-token: ${{ secrets.DESTINATION_ACCESS_TOKEN }} - destination-repository: "automatika-robotics/ros-sugar-release" diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e100b28..827ad5a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package automatika_ros_sugar ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.2.5 (2025-01-07) +------------------ +* (fix) Gets imports and default values based on installed distro +* (fix) Fix launch and launch_ros imports based on ros distro +* Contributors: ahr, mkabtoul + 0.2.4 (2024-12-27) ------------------ * (fix) Adds algorithm auto re-configuration from YAML file diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 3ce3631..0000000 --- a/debian/changelog +++ /dev/null @@ -1,193 +0,0 @@ -ros-humble-automatika-ros-sugar (0.2.4-1jammy) jammy; urgency=high - - * (fix) Adds algorithm auto re-configuration from YAML file - * (fix) Fixes callback got_msg property - * (feature) Adds topics callbacks/conversions reparsing to component - Supports running components from different packages in one script and each component uses its own package callbacks/conversions - * (fix) Updates AllowedTopics config and its validator - * (refactor) Removes PIL as a dependancy - * (fix) Fixes component state transition logging - * (fix) Fixes order to custom method execution in component lifecycle transition methods - * (refactor) Removes BaseNode class - * (fix) Fixes packaging workflow formatting - * (fix) Removes redundant methods from components - * (chore) Increments release action version - * (chore) Adds new action in debs creation workflow - * (refactor) Formats utils - * (refactor) Minor refactoring in utils - * (fix) Removes fix for color correction as the transformation is now applied at the time of visualization - * (fix) Adds color transformation when reading images of yuv encoding - * (chore) Changes name of release action - * (feature) Adds component algorithm config management to the api - * (fix) fixes datatypes update method for using multiple packages - * (chore) Cleans up cmake and packaging - * (refactor) Improves error message when a topic of unsupported type is created - * (refactor) Handles additional datatypes provided by user packages - * (fix) Pins release mirror workflow to run only on release publishing - * (fix) Adds branch name to release workflow - * (fix) Fixes name of action - * (feature) Adds release mirror action - * (docs) Removes autogenerated docs - * (docs) Adds minor modification to readme - * (docs) Changes package description - * (feature) Adds ExecuteMethod service to BaseComponent - * (fix) Minor fix in conversion method - * (refactor) Makes compressed image a realization of image - * (fix) Fixes ros compressed image conversion util - * (feature) Adds support for CompressedImage msg - * (feature) Adds ros_log_level option to each added package - * (feature) Adds additional supported types argument to BaseComponent and Topic validators - * (fix) Adds algorithm auto re-configuration from YAML file - * (fix) Fixes callback got_msg property - * (feature) Adds topics callbacks/conversions reparsing to component - Supports running components from different packages in one script and each component uses its own package callbacks/conversions - * (fix) Updates AllowedTopics config and its validator - * (refactor) Removes PIL as a dependancy - * (fix) Fixes component state transition logging - * (fix) Fixes order to custom method execution in component lifecycle transition methods - * (refactor) Removes BaseNode class - * (fix) Fixes packaging workflow formatting - * (fix) Removes redundant methods from components - * (chore) Increments release action version - * (chore) Adds new action in debs creation workflow - * (refactor) Formats utils - * (refactor) Minor refactoring in utils - * (fix) Removes fix for color correction as the transformation is now applied at the time of visualization - * (fix) Adds color transformation when reading images of yuv encoding - * (chore) Changes name of release action - * (feature) Adds component algorithm config management to the api - * (fix) fixes datatypes update method for using multiple packages - * (chore) Cleans up cmake and packaging - * (refactor) Improves error message when a topic of unsupported type is created - * (refactor) Handles additional datatypes provided by user packages - * (fix) Pins release mirror workflow to run only on release publishing - * (fix) Adds branch name to release workflow - * (fix) Fixes name of action - * (feature) Adds release mirror action - * (docs) Removes autogenerated docs - * (docs) Adds minor modification to readme - * (docs) Changes package description - * (feature) Adds ExecuteMethod service to BaseComponent - * (fix) Fixes OccupnacyGrid data publishing from numpy - * (fix) Minor fix in conversion method - * (refactor) Makes compressed image a realization of image - * (fix) Fixes ros compressed image conversion util - * (feature) Adds support for CompressedImage msg - * (feature) Adds ros_log_level option to each added package - * (feature) Adds additional supported types argument to BaseComponent and Topic validators - * (fix) Merge pull request #14 - * (chore) Updates package name to automatika_ros_sugar - * (fix) Checks numpy array shape in OccupancyGrid converter - * (feature) Adds stamped header and frame_id to ros publishers/callbacks - * (docs) Updates install instructions - * Contributors: ahr, mkabtoul - - -- Automatika Robotics Thu, 26 Dec 2024 23:00:00 -0000 - -ros-humble-automatika-ros-sugar (0.2.3-1jammy) jammy; urgency=high - - * (chore) bump version 0.2.2 -> 0.2.3 - * (chore) Adds deb packaging scripts and actions (#13 ) - * (docs) Removes notice - * Contributors: ahr - - -- Automatika Robotics Tue, 12 Nov 2024 23:00:00 -0000 - -ros-humble-automatika-ros-sugar (0.2.2-1jammy) jammy; urgency=high - - * (chore) bump version 0.2.1 -> 0.2.2 - * (feature) Adds activation timeout to monitor and launcher - * (fix) Fixes publishing numpy data to ROS OcuupancyGrid - * (refactor) Updates OccupancyGrid get_output using numpy operations - * Contributors: mkabtoul - - -- Automatika Robotics Sun, 03 Nov 2024 23:00:00 -0000 - -ros-humble-automatika-ros-sugar (0.2.1-1jammy) jammy; urgency=high - - * (chore) bump version 0.2.0 -> 0.2.1 - * (feature) Adds support for external tool calling in multiprocessing - * Contributors: ahr - - -- Automatika Robotics Mon, 28 Oct 2024 23:00:00 -0000 - -ros-humble-automatika-ros-sugar (0.2.0-1jammy) jammy; urgency=high - - * (chore) Bump version 0.1.1 -> 0.2.0 - * Merge pull request #12 from automatika-robotics/feature/external_processors - Adds external processor support when running components in multiprocessing - * (refactor) Makes msgpack a global dependancy - * (fix) Fixes deserialization of external processors and handling of processor result in launcher - * (fix) Corrects the serialization of numpy arrays within lists - * (feature) Changes defaults for launcher parameters when using multiprocessing - * (fix) Fixes handling composite type check for deserialization and input/output deserialization in components - * (fix) Adds node name as parameter to callbacks for init - * (fix) Adds alias to attrs private attribute in BaseComponentConfig - * (fix) Restores executable to old version - * Merge branch 'feature/external_processors' of github.com:automatika-robotics/ros-sugar into feature/external_processors - * (fix) Fixes new method name in launcher - * (fix) Moves callbackgroup to BaseComponentConfig and changes initialization of inputs/outputs in component - * (fix) Fixes serialization of callbackgroup in config - * (fix) Fixes type hints for compatibility - * (docs) Fixes ubuntu version for dependancy problems - * (refactor) Makes msgpack a functional dependency - * (refactor) Adds handling of callback group and input/output initialization to facilitate multiprocessing - * (feature) Adds handling of callback group for multiprocess launch - * (fix) Adds serialization of np arrays and tuples - * (fix) Adds converter for QoS profile for serialization - * (refactor) Changes inputs/outputs handling in executable - * (refactor) Changes name of enum convert utility function - * (fix) Fixes use of multi processors for same topic in launcher - * (fix) Fix package installation for documentation workflow - * (feature) Adds support for multiple external processors on the same topic - * (fix) Fixes visibility of external_processors to protected - * (fix) Fixes typo in attaching external preprocessors - * (feature) Adds unix socket based listener threads for using external processors with components being run in multiprocessing - - Modifies executable to add an argument for external processors - - Adds setting and getting for external processor json in component - - Adds setting up of external processors on component activation and destruction on component stop - - Adds setup of external processor sockets and thread pool in launcher - * (fix) Moves callbackgroup to BaseComponentConfig and changes initialization of inputs/outputs in component - * (fix) Fixes serialization of callbackgroup in config - * (fix) Fixes type hints for compatibility - * (docs) Fixes ubuntu version for dependancy problems - * (refactor) Makes msgpack a functional dependency - * (refactor) Adds handling of callback group and input/output initialization to facilitate multiprocessing - * (feature) Adds handling of callback group for multiprocess launch - * (fix) Adds serialization of np arrays and tuples - * (fix) Adds converter for QoS profile for serialization - * (refactor) Changes inputs/outputs handling in executable - * (refactor) Changes name of enum convert utility function - * (feature) Adds event processing options and supports lists in event values - Adds options to handle an event once or handle with a time delay - * (fix) Uses List from typing in type hints - * (feature) Adds handle_once and event_delay options to Event - * (feature) Adds list to supported event trigger values - * (fix) Handles keep_alive in component parameter update service requests - * (fix) Passes monitor executor to service client send_req - * (fix) Fixes use of multi processors for same topic in launcher - * (fix) Fix package installation for documentation workflow - * (feature) Adds support for multiple external processors on the same topic - * (fix) Fixes visibility of external_processors to protected - * (fix) Fixes typo in attaching external preprocessors - * (feature) Adds unix socket based listener threads for using external processors with components being run in multiprocessing - - Modifies executable to add an argument for external processors - - Adds setting and getting for external processor json in component - - Adds setting up of external processors on component activation and destruction on component stop - - Adds setup of external processor sockets and thread pool in launcher - * (fix) Fixes minor bugs in base component and launcher (#10 ) - * (fix) Fixes the handling of yuv422_yuy2 encoding in image reading util function - * (fix) Adds process id to monitor node name - * (fix) Fixes type check for callables in attaching post and pre processors - * (fix) Updates component launch arguments after parsing events_actions - * (docs) Updates docs url links in readme - * (docs) Adds github workflow for docs (#9 ) - * (fix) Adds handling image encodings with alpha channel - * Create LICENSE - * Initial release version 0.1.1 (#8 ) - * init commit - * Contributors: ahr, aleph-ra, mkabtoul - - -- Automatika Robotics Thu, 24 Oct 2024 22:00:00 -0000 - - diff --git a/debian/changelog.em b/debian/changelog.em new file mode 100644 index 0000000..3585909 --- /dev/null +++ b/debian/changelog.em @@ -0,0 +1,7 @@ +@[for change_version, change_date, changelog, main_name, main_email in changelogs]@(Package) (@(change_version)@(DebianInc)@(Distribution)) @(Distribution); urgency=high + +@(changelog) + + -- @(main_name) <@(main_email)> @(change_date) + +@[end for] diff --git a/debian/compat b/debian/compat deleted file mode 100644 index ec63514..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/debian/compat.em b/debian/compat.em new file mode 100644 index 0000000..7a87216 --- /dev/null +++ b/debian/compat.em @@ -0,0 +1 @@ +@(debhelper_version) diff --git a/debian/control b/debian/control deleted file mode 100644 index 7daff09..0000000 --- a/debian/control +++ /dev/null @@ -1,12 +0,0 @@ -Source: ros-humble-automatika-ros-sugar -Section: misc -Priority: optional -Maintainer: Automatika Robotics -Build-Depends: debhelper (>= 9.0.0), python3-jinja2, python3-msgpack, python3-numpy, python3-opencv, ros-humble-ament-cmake, ros-humble-ament-cmake-python, ros-humble-builtin-interfaces, ros-humble-geometry-msgs, ros-humble-lifecycle-msgs, ros-humble-nav-msgs, ros-humble-rclcpp, ros-humble-rclpy, ros-humble-rosidl-default-generators, ros-humble-sensor-msgs, ros-humble-std-msgs, ros-humble-tf2-ros, ros-humble-ros-workspace, ros-humble-rosidl-typesupport-fastrtps-c, ros-humble-rosidl-typesupport-fastrtps-cpp -Homepage: https://github.com/automatika/ros-sugar -Standards-Version: 3.9.2 - -Package: ros-humble-automatika-ros-sugar -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, python3-jinja2, python3-msgpack, python3-numpy, python3-opencv, ros-humble-builtin-interfaces, ros-humble-geometry-msgs, ros-humble-lifecycle-msgs, ros-humble-nav-msgs, ros-humble-rclcpp, ros-humble-rclpy, ros-humble-rosidl-default-runtime, ros-humble-sensor-msgs, ros-humble-std-msgs, ros-humble-tf2-ros, ros-humble-ros-workspace -Description: Syntactic sugar for ROS2 nodes creation and management diff --git a/debian/control.em b/debian/control.em new file mode 100644 index 0000000..6d7b65c --- /dev/null +++ b/debian/control.em @@ -0,0 +1,14 @@ +Source: @(Package) +Section: misc +Priority: optional +Maintainer: @(Maintainer) +Build-Depends: debhelper (>= @(debhelper_version).0.0), @(', '.join(BuildDepends)) +Homepage: @(Homepage) +Standards-Version: 3.9.2 + +Package: @(Package) +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, @(', '.join(Depends)) +@[if Conflicts]Conflicts: @(', '.join(Conflicts))@\n@[end if]@ +@[if Replaces]Replaces: @(', '.join(Replaces))@\n@[end if]@ +Description: @(Description) diff --git a/debian/copyright b/debian/copyright.em similarity index 50% rename from debian/copyright rename to debian/copyright.em index af7e644..bc82fd5 100644 --- a/debian/copyright +++ b/debian/copyright.em @@ -1,7 +1,11 @@ Format: Bloom subset of https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: automatika_ros_sugar +Upstream-Name: @(Name) +@[if BugTracker]Upstream-Contact: @(BugTracker)@\n@[end if]@ +@[if Source]Source: @(Source)@\n@[end if]@ +@[for License, Text in Licenses]@ Files: See file headers in repository for details Copyright: See package copyright in source code for details -License: MIT - See repository for full license text +License: @(License) + @(Text) +@[end for]@ diff --git a/debian/gbp.conf b/debian/gbp.conf deleted file mode 100644 index 92ad7ba..0000000 --- a/debian/gbp.conf +++ /dev/null @@ -1,3 +0,0 @@ -[git-buildpackage] -upstream-tag=release/humble/automatika_ros_sugar/0.2.4-1 -upstream-tree=tag diff --git a/debian/gbp.conf.em b/debian/gbp.conf.em new file mode 100644 index 0000000..ad24a16 --- /dev/null +++ b/debian/gbp.conf.em @@ -0,0 +1,3 @@ +[git-buildpackage] +upstream-tag=@(release_tag) +upstream-tree=tag diff --git a/debian/rules b/debian/rules.em similarity index 71% rename from debian/rules rename to debian/rules.em index bb6544f..a856369 100755 --- a/debian/rules +++ b/debian/rules.em @@ -13,7 +13,7 @@ export DH_VERBOSE=1 # https://code.ros.org/trac/ros/ticket/2977 # https://code.ros.org/trac/ros/ticket/3842 export LDFLAGS= -export PKG_CONFIG_PATH=/opt/ros/humble/lib/pkgconfig +export PKG_CONFIG_PATH=@(InstallationPrefix)/lib/pkgconfig # Explicitly enable -DNDEBUG, see: # https://github.com/ros-infrastructure/bloom/issues/327 export DEB_CXXFLAGS_MAINT_APPEND=-DNDEBUG @@ -24,24 +24,24 @@ endif DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) %: - dh $@ -v --buildsystem=cmake --builddirectory=.obj-$(DEB_HOST_GNU_TYPE) + dh $@@ -v --buildsystem=cmake --builddirectory=.obj-$(DEB_HOST_GNU_TYPE) override_dh_auto_configure: # In case we're installing to a non-standard location, look for a setup.sh # in the install tree and source it. It will set things like # CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. - if [ -f "/opt/ros/humble/setup.sh" ]; then . "/opt/ros/humble/setup.sh"; fi && \ + if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi && \ dh_auto_configure -- \ - -DCMAKE_INSTALL_PREFIX="/opt/ros/humble" \ - -DAMENT_PREFIX_PATH="/opt/ros/humble" \ - -DCMAKE_PREFIX_PATH="/opt/ros/humble" \ + -DCMAKE_INSTALL_PREFIX="@(InstallationPrefix)" \ + -DAMENT_PREFIX_PATH="@(InstallationPrefix)" \ + -DCMAKE_PREFIX_PATH="@(InstallationPrefix)" \ $(BUILD_TESTING_ARG) override_dh_auto_build: # In case we're installing to a non-standard location, look for a setup.sh # in the install tree and source it. It will set things like # CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. - if [ -f "/opt/ros/humble/setup.sh" ]; then . "/opt/ros/humble/setup.sh"; fi && \ + if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi && \ dh_auto_build override_dh_auto_test: @@ -49,19 +49,19 @@ override_dh_auto_test: # in the install tree and source it. It will set things like # CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. echo -- Running tests. Even if one of them fails the build is not canceled. - if [ -f "/opt/ros/humble/setup.sh" ]; then . "/opt/ros/humble/setup.sh"; fi && \ + if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi && \ dh_auto_test || true override_dh_shlibdeps: # In case we're installing to a non-standard location, look for a setup.sh # in the install tree and source it. It will set things like # CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. - if [ -f "/opt/ros/humble/setup.sh" ]; then . "/opt/ros/humble/setup.sh"; fi && \ - dh_shlibdeps -l$(CURDIR)/debian/ros-humble-automatika-ros-sugar//opt/ros/humble/lib/:$(CURDIR)/debian/ros-humble-automatika-ros-sugar//opt/ros/humble/opt/automatika_ros_sugar/lib/ + if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi && \ + dh_shlibdeps -l$(CURDIR)/debian/@(Package)/@(InstallationPrefix)/lib/:$(CURDIR)/debian/@(Package)/@(InstallationPrefix)/opt/@(Name)/lib/ override_dh_auto_install: # In case we're installing to a non-standard location, look for a setup.sh # in the install tree and source it. It will set things like # CMAKE_PREFIX_PATH, PKG_CONFIG_PATH, and PYTHONPATH. - if [ -f "/opt/ros/humble/setup.sh" ]; then . "/opt/ros/humble/setup.sh"; fi && \ + if [ -f "@(InstallationPrefix)/setup.sh" ]; then . "@(InstallationPrefix)/setup.sh"; fi && \ dh_auto_install diff --git a/debian/source/format b/debian/source/format deleted file mode 100644 index 163aaf8..0000000 --- a/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/debian/source/format.em b/debian/source/format.em new file mode 100644 index 0000000..9666bf4 --- /dev/null +++ b/debian/source/format.em @@ -0,0 +1 @@ +3.0 (@(format)) diff --git a/debian/source/options b/debian/source/options.em similarity index 81% rename from debian/source/options rename to debian/source/options.em index 8bc9182..8c4c78b 100644 --- a/debian/source/options +++ b/debian/source/options.em @@ -1,5 +1,6 @@ +@[if format and format == 'quilt']@ # Automatically add upstream changes to the quilt overlay. # http://manpages.ubuntu.com/manpages/trusty/man1/dpkg-source.1.html # This supports reusing the orig.tar.gz for debian increments. auto-commit - +@[end if] diff --git a/package.xml b/package.xml index 24f9027..9a80492 100644 --- a/package.xml +++ b/package.xml @@ -2,7 +2,7 @@ automatika_ros_sugar - 0.2.4 + 0.2.5 Syntactic sugar for ROS2 nodes creation and management Automatika Robotics https://github.com/automatika/ros-sugar diff --git a/ros_sugar/config/base_config.py b/ros_sugar/config/base_config.py index 02c875c..e034011 100644 --- a/ros_sugar/config/base_config.py +++ b/ros_sugar/config/base_config.py @@ -29,10 +29,7 @@ class QoSConfig(BaseAttrs): history: int = field( converter=_get_enum_value, default=qos.HistoryPolicy.KEEP_LAST, - validator=base_validators.in_([ - qos.HistoryPolicy.KEEP_LAST, - qos.HistoryPolicy.KEEP_ALL, - ]), + validator=base_validators.in_(list(qos.HistoryPolicy)), ) # used only if the “history” policy was set to “keep last” @@ -45,10 +42,7 @@ class QoSConfig(BaseAttrs): reliability: int = field( converter=_get_enum_value, default=qos.ReliabilityPolicy.RELIABLE, - validator=base_validators.in_([ - qos.ReliabilityPolicy.BEST_EFFORT, - qos.ReliabilityPolicy.RELIABLE, - ]), + validator=base_validators.in_(list(qos.ReliabilityPolicy)), ) # Transient local: the publisher becomes responsible for persisting samples for “late-joining” subscriptions @@ -56,13 +50,7 @@ class QoSConfig(BaseAttrs): durability: int = field( converter=_get_enum_value, default=qos.DurabilityPolicy.VOLATILE, - validator=base_validators.in_([ - qos.DurabilityPolicy.TRANSIENT_LOCAL, - qos.DurabilityPolicy.VOLATILE, - # qos.DurabilityPolicy.BEST_AVAILABLE, # Only available in iron -> TODO: Get values from rclpy - qos.DurabilityPolicy.UNKNOWN, - qos.DurabilityPolicy.SYSTEM_DEFAULT, - ]), + validator=base_validators.in_(list(qos.DurabilityPolicy)), ) # TODO: Fix default values diff --git a/ros_sugar/core/component.py b/ros_sugar/core/component.py index 0d33617..62fbe25 100644 --- a/ros_sugar/core/component.py +++ b/ros_sugar/core/component.py @@ -187,7 +187,7 @@ def _reparse_inputs_callbacks(self, inputs: Sequence[Topic]) -> Sequence[Topic]: :rtype: List[Topic] """ for inp in inputs: - if not isinstance(inp.msg_type.callback, List): + if not inp or not isinstance(inp.msg_type.callback, List): continue module_name = ( self.__module__[: self.__module__.index(".")] @@ -217,7 +217,7 @@ def _reparse_outputs_converts(self, outputs: Sequence[Topic]) -> Sequence[Topic] :rtype: List[Topic] """ for out in outputs: - if not isinstance(out.msg_type.convert, List): + if not out or not isinstance(out.msg_type.convert, List): continue module_name = self.__module__ # Get first callback by default diff --git a/ros_sugar/core/event.py b/ros_sugar/core/event.py index eefb68e..eea5cf3 100644 --- a/ros_sugar/core/event.py +++ b/ros_sugar/core/event.py @@ -12,16 +12,7 @@ from ..io.topic import Topic from .action import Action - -# Get ROS distro -__installed_distro = os.environ.get("ROS_DISTRO", "").lower() - -if __installed_distro == "foxy": - from launch.some_actions_type import SomeActionsType as SomeType -else: - from launch.some_entities_type import SomeEntitiesType as SomeType - -SomeEntitiesType = SomeType +from ..utils import SomeEntitiesType class Timer: @@ -380,12 +371,12 @@ def __init__( # Check if given trigger is of valid type if trigger_value and not _check_attribute( - self.event_topic.msg_type._ros_type, + self.event_topic.msg_type.get_ros_type(), type(self.trigger_ref_value), self._attrs, ): raise TypeError( - f"Cannot initiate with trigger of type {type(trigger_value)} for a data of type {_get_attribute_type(self.event_topic.msg_type._ros_type, self._attrs)}" + f"Cannot initiate with trigger of type {type(trigger_value)} for a data of type {_get_attribute_type(self.event_topic.msg_type.get_ros_type(), self._attrs)}" ) # Init trigger as False diff --git a/ros_sugar/io/supported_types.py b/ros_sugar/io/supported_types.py index 3596a61..801f2bc 100644 --- a/ros_sugar/io/supported_types.py +++ b/ros_sugar/io/supported_types.py @@ -103,7 +103,9 @@ def add_additional_datatypes(types: List[type]) -> None: _update_supportedtype_callback(existing_class, new_type) - if not existing_class._ros_type: + if hasattr(new_type, "_ros_type") and ( + not hasattr(existing_class, "_ros_type") or not existing_class._ros_type + ): existing_class._ros_type = new_type._ros_type _update_supportedtype_conversion(existing_class, new_type) @@ -154,6 +156,15 @@ def convert(cls, output, **_) -> Any: """ return output + @classmethod + def get_ros_type(cls) -> type: + """Getter of the ROS2 message type + + :return: ROS2 type + :rtype: type + """ + return cls._ros_type + class String(SupportedType): """String.""" diff --git a/ros_sugar/io/topic.py b/ros_sugar/io/topic.py index 4fba766..9b69f98 100644 --- a/ros_sugar/io/topic.py +++ b/ros_sugar/io/topic.py @@ -154,7 +154,7 @@ def _msg_type_validator(self, _, val): f"Got value of 'msg_type': {val}, which is not in available datatypes. Topics can only be created with one of the following types: { {msg_t.__name__: msg_t for msg_t in msg_types} }" ) # Set ros type - self.ros_msg_type = self.msg_type._ros_type + self.ros_msg_type = self.msg_type.get_ros_type() @define(kw_only=True) diff --git a/ros_sugar/launch/_lifecycle_transition.py b/ros_sugar/launch/_lifecycle_transition.py new file mode 100644 index 0000000..46bdc9c --- /dev/null +++ b/ros_sugar/launch/_lifecycle_transition.py @@ -0,0 +1,249 @@ +# This Action file is copied here from OSRF to support distributions prior to Iron +# Copyright 2022 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. + +import functools + +from typing import Iterable +from typing import List +from typing import Optional +from typing import Union + + +import launch +from launch import LaunchContext, SomeSubstitutionsType +from launch.action import Action +from launch.actions import EmitEvent, RegisterEventHandler +from launch.utilities import normalize_to_list_of_substitutions +from launch.utilities import perform_substitutions + +from launch_ros.event_handlers import OnStateTransition +from launch_ros.events.lifecycle import ChangeState, StateTransition +from launch_ros.events.matchers import matches_node_name +from lifecycle_msgs.msg import Transition + + +class LifecycleTransition(Action): + """An action that simplifies execution of lifecycle transitions.""" + + transition_targets = { + Transition.TRANSITION_CONFIGURE: { + "start_state": "configuring", + "goal_state": "inactive", + }, + Transition.TRANSITION_CLEANUP: { + "start_state": "cleaningup", + "goal_state": "unconfigured", + }, + Transition.TRANSITION_ACTIVATE: { + "start_state": "activating", + "goal_state": "active", + }, + Transition.TRANSITION_DEACTIVATE: { + "start_state": "deactivating", + "goal_state": "inactive", + }, + Transition.TRANSITION_UNCONFIGURED_SHUTDOWN: { + "start_state": "shuttingdown", + "goal_state": "finalized", + }, + Transition.TRANSITION_INACTIVE_SHUTDOWN: { + "start_state": "shuttingdown", + "goal_state": "finalized", + }, + Transition.TRANSITION_ACTIVE_SHUTDOWN: { + "start_state": "shuttingdown", + "goal_state": "finalized", + }, + } + + def __init__( + self, + *, + lifecycle_node_names: Iterable[SomeSubstitutionsType], + transition_ids: Iterable[Union[int, SomeSubstitutionsType]], + **kwargs, + ) -> None: + """ + Construct a LifecycleTransition action. + + The action will execute the passed in lifecycle transition for the + lifecycle nodes with the indicated node names. The action will emit + an event that triggers the first lifecycle transition of each node + wait that the node reaches the transition goal and trigger the next + transition in the list. + You need to make sure, that the sequence of lifecyle transition you + pass in is possible. + + :param lifecycle_node_names: The names of the lifecycle nodes to transition + :param transitions_ids: The transitions to be executed. + """ + super().__init__(**kwargs) + if len(transition_ids) == 0: + raise ValueError("No transition_ids provided.") + + if len(lifecycle_node_names) == 0: + raise ValueError("No lifecycle_node_names provided.") + + self.__lifecycle_node_names = [ + normalize_to_list_of_substitutions(name) for name in lifecycle_node_names + ] + transition_ids = [ + str(transition_id) if isinstance(transition_id, int) else transition_id + for transition_id in transition_ids + ] + self.__transition_ids = [ + normalize_to_list_of_substitutions(transition_id) + for transition_id in transition_ids + ] + + self.__event_handlers = {} + self.__logger = launch.logging.get_logger(__name__) + + def _remove_event_handlers( + self, context: LaunchContext, node_name: str, reason: str = None + ): + """Remove all consequent transitions if error occurs.""" + if reason is not None: + self.__logger.info( + f"Stopping transitions for {node_name} because '{reason}'" + ) + + for event_handler in self.__event_handlers[node_name]: + # Unregister event handlers and ignore failures, as these are + # already unregistered event handlers. + try: + context.unregister_event_handler(event_handler=event_handler) + except ValueError: + pass + + def execute(self, context: launch.LaunchContext) -> Optional[List[Action]]: + """ + Execute the LifecycleTransition action. + + :return Returns a list of actions to be executed to achieve specified transitions. + These are EventHandlers and EventEmitters for ChangeState and + StateTransition events of the nodes indicated. + """ + lifecycle_node_names = [ + perform_substitutions(context, name) for name in self.__lifecycle_node_names + ] + subs_transition_ids = [ + perform_substitutions(context, id_) for id_ in self.__transition_ids + ] + transition_ids = [] + for tid in subs_transition_ids: + try: + transition_ids.append(int(tid)) + except ValueError as e: + raise ValueError( + f"expected integer for lifecycle transition, got {tid}" + ) from e + + emit_actions = {} + actions: List[Action] = [] + + # Create EmitEvents for ChangeStates and store + for name in lifecycle_node_names: + own_emit_actions = [] + for tid in transition_ids: + change_event = ChangeState( + lifecycle_node_matcher=matches_node_name(name), transition_id=tid + ) + emit_action = EmitEvent(event=change_event) + own_emit_actions.append(emit_action) + emit_actions[name] = own_emit_actions + self.__event_handlers[name] = [] + # Create Transition EventHandlers and Registration actions + i = 1 + for tid in transition_ids: + # Create Transition handler for all indicated nodes + for node_name in lifecycle_node_names: + states = self.transition_targets[tid] + event_handler = None + # For all transitions except the last, emit next ChangeState Event + if i < len(transition_ids): + event_handler = OnStateTransition( + matcher=match_node_name_start_goal( + node_name, states["start_state"], states["goal_state"] + ), + entities=[emit_actions[node_name][i]], + handle_once=True, + ) + # For last transition emit Log message and remove untriggered error handlers + else: + event_handler = OnStateTransition( + matcher=match_node_name_start_goal( + node_name, states["start_state"], states["goal_state"] + ), + entities=[ + launch.actions.OpaqueFunction( + function=functools.partial( + self._remove_event_handlers, node_name=node_name + ) + ), + ], + handle_once=True, + ) + self.__event_handlers[node_name].append(event_handler) + # Create register event handler action + register_action = RegisterEventHandler(event_handler=event_handler) + # Append to actions + actions.append(register_action) + # increment next ChangeState action by one + i += 1 + # Create Error processing event handlers. + for node_name in lifecycle_node_names: + event_handler = launch.EventHandler( + matcher=match_node_name_goal(node_name, "errorprocessing"), + entities=[ + launch.actions.OpaqueFunction( + function=functools.partial( + self._remove_event_handlers, + node_name=node_name, + reason="error occured during transitions", + ) + ) + ], + handle_once=True, + ) + self.__event_handlers[node_name].append(event_handler) + context.register_event_handler(event_handler=event_handler) + + # Add first Emit actions to actions + for node_name in lifecycle_node_names: + actions.append(emit_actions[node_name][0]) + + return actions + + +def match_node_name_start_goal(node_name: str, start_state: str, goal_state: str): + if not node_name.startswith("/"): + node_name = f"/{node_name}" + return lambda event: ( + isinstance(event, StateTransition) + and (event.action.node_name == node_name) + and (event.goal_state == goal_state) + and (event.start_state == start_state) + ) + + +def match_node_name_goal(node_name: str, goal_state: str): + if not node_name.startswith("/"): + node_name = f"/{node_name}" + return lambda event: ( + isinstance(event, StateTransition) + and (event.action.node_name == node_name) + and (event.goal_state == goal_state) + ) diff --git a/ros_sugar/launch/launcher.py b/ros_sugar/launch/launcher.py index d8d324d..40b5cb7 100644 --- a/ros_sugar/launch/launcher.py +++ b/ros_sugar/launch/launcher.py @@ -20,7 +20,6 @@ import msgpack import msgpack_numpy as m_pack import launch -import launch_ros import rclpy import setproctitle from launch import LaunchDescription, LaunchIntrospector, LaunchService @@ -32,7 +31,6 @@ OpaqueFunction, Shutdown, ) -from launch.some_entities_type import SomeEntitiesType from launch_ros.actions import LifecycleNode as LifecycleNodeLaunchAction from launch_ros.actions import Node as NodeLaunchAction from launch_ros.actions import PushRosNamespace @@ -48,7 +46,16 @@ from ..core.monitor import Monitor from ..core.event import OnInternalEvent, Event from .launch_actions import ComponentLaunchAction -from ..utils import InvalidAction, action_handler, has_decorator +from ..utils import InvalidAction, action_handler, has_decorator, SomeEntitiesType + +# Get ROS distro +__installed_distro = os.environ.get("ROS_DISTRO", "").lower() + +if __installed_distro in ["humble", "galactic", "foxy"]: + # Get local copy for older distributions + from ._lifecycle_transition import LifecycleTransition +else: + from launch_ros.actions import LifecycleTransition # patch msgpack for numpy arrays m_pack.patch() @@ -304,7 +311,7 @@ def start(self, node_name: str, **_) -> SomeEntitiesType: :rtype: List[SomeEntitiesType] """ actions = [ - launch_ros.actions.LifecycleTransition( + LifecycleTransition( lifecycle_node_names=[node_name], transition_ids=[ Transition.TRANSITION_CONFIGURE, @@ -325,7 +332,7 @@ def stop(self, node_name: str, **_) -> SomeEntitiesType: :rtype: List[SomeEntitiesType] """ actions = [ - launch_ros.actions.LifecycleTransition( + LifecycleTransition( lifecycle_node_names=[node_name], transition_ids=[Transition.TRANSITION_DEACTIVATE], ) @@ -343,7 +350,7 @@ def restart(self, node_name: str, **_) -> SomeEntitiesType: :rtype: List[SomeEntitiesType] """ actions = [ - launch_ros.actions.LifecycleTransition( + LifecycleTransition( lifecycle_node_names=[node_name], transition_ids=[ Transition.TRANSITION_DEACTIVATE, diff --git a/ros_sugar/utils.py b/ros_sugar/utils.py index 73791d0..b8da96a 100644 --- a/ros_sugar/utils.py +++ b/ros_sugar/utils.py @@ -7,7 +7,16 @@ from rclpy.lifecycle import Node as LifecycleNode from launch import LaunchContext from launch.actions import OpaqueFunction -from launch.some_entities_type import SomeEntitiesType +import os + +# Get ROS distro +__installed_distro = os.environ.get("ROS_DISTRO", "").lower() + +if __installed_distro in ["humble", "galactic", "foxy"]: + # Get some_action_type for older distributions + from launch.some_actions_type import SomeActionsType as SomeEntitiesType +else: + from launch.some_entities_type import SomeEntitiesType class IncompatibleSetup(Exception):