git commit -m "first commit"
This commit is contained in:
9
navigations/rospy_message_converter/.flake8
Executable file
9
navigations/rospy_message_converter/.flake8
Executable file
@@ -0,0 +1,9 @@
|
||||
[flake8]
|
||||
# The line length here has to match the black config in pyproject.toml
|
||||
max-line-length = 120
|
||||
exclude =
|
||||
.git,
|
||||
__pycache__
|
||||
extend-ignore =
|
||||
# See https://github.com/PyCQA/pycodestyle/issues/373
|
||||
E203,
|
||||
41
navigations/rospy_message_converter/.github/workflows/github-actions.yml
vendored
Executable file
41
navigations/rospy_message_converter/.github/workflows/github-actions.yml
vendored
Executable file
@@ -0,0 +1,41 @@
|
||||
name: Build and run ROS tests
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
rosdistro: [noetic]
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ros:${{ matrix.rosdistro }}-ros-core
|
||||
steps:
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential clang-format-10 file git python3-catkin-lint python3-pip python3-rosdep
|
||||
- name: Install pip dependencies
|
||||
run: pip install pre-commit
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: src/rospy_message_converter
|
||||
- name: Use rosdep to install remaining dependencies
|
||||
run: |
|
||||
sudo rosdep init
|
||||
rosdep update
|
||||
rosdep install --from-paths src -i -y --rosdistro ${{ matrix.rosdistro }}
|
||||
- name: Build
|
||||
run: |
|
||||
. /opt/ros/${{ matrix.rosdistro }}/setup.sh
|
||||
catkin_make install
|
||||
- name: Run tests
|
||||
run: |
|
||||
. devel/setup.sh
|
||||
CTEST_OUTPUT_ON_FAILURE=1 catkin_make test
|
||||
cd src/rospy_message_converter
|
||||
python3 src/rospy_message_converter/json_message_converter.py
|
||||
python3 src/rospy_message_converter/message_converter.py
|
||||
- name: Run pre-commit hooks
|
||||
run: |
|
||||
cd src/rospy_message_converter
|
||||
pre-commit run -a
|
||||
3
navigations/rospy_message_converter/.gitignore
vendored
Executable file
3
navigations/rospy_message_converter/.gitignore
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
.idea
|
||||
*.iml
|
||||
55
navigations/rospy_message_converter/.pre-commit-config.yaml
Executable file
55
navigations/rospy_message_converter/.pre-commit-config.yaml
Executable file
@@ -0,0 +1,55 @@
|
||||
# To use:
|
||||
#
|
||||
# pre-commit run -a
|
||||
#
|
||||
# Or:
|
||||
#
|
||||
# pre-commit install # (runs every time you commit in git)
|
||||
#
|
||||
# To update this file:
|
||||
#
|
||||
# pre-commit autoupdate
|
||||
#
|
||||
# See https://github.com/pre-commit/pre-commit
|
||||
|
||||
repos:
|
||||
# Standard hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-docstring-first
|
||||
- id: check-merge-conflict
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-symlinks
|
||||
- id: check-vcs-permalinks
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: end-of-file-fixer
|
||||
- id: fix-byte-order-marker
|
||||
- id: mixed-line-ending
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/PyCQA/flake8.git
|
||||
rev: 5.0.4
|
||||
hooks:
|
||||
- id: flake8
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: catkin_lint
|
||||
name: catkin_lint
|
||||
description: Check package.xml and cmake files
|
||||
entry: catkin_lint .
|
||||
language: system
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
args: [ "--strict" ]
|
||||
161
navigations/rospy_message_converter/CHANGELOG.rst
Executable file
161
navigations/rospy_message_converter/CHANGELOG.rst
Executable file
@@ -0,0 +1,161 @@
|
||||
Change Log
|
||||
==========
|
||||
|
||||
0.5.9 (2022-09-12)
|
||||
------------------
|
||||
* Fix flake8 errors
|
||||
* Re-format code using black
|
||||
* package.xml: Add missing build_export_depend
|
||||
* Fix EOF and trailing whitespace
|
||||
* Add pre-commit config
|
||||
* README: Add note about branches
|
||||
* pass down log_level to helper functions (`#60 <https://github.com/uos/rospy_message_converter/issues/60>`_)
|
||||
* Declare file encoding
|
||||
This is necessary on ROS Melodic (Python2), because I have added a
|
||||
non-ASCII character (u umlaut) in my last commit.
|
||||
* Add LICENSE file and license headers
|
||||
* Contributors: Martin Günther, Yuri Rocha
|
||||
|
||||
0.5.8 (2022-03-03)
|
||||
------------------
|
||||
* add option to change log level (`#58 <https://github.com/uos/rospy_message_converter/issues/58>`_)
|
||||
* Contributors: Yuri Rocha
|
||||
|
||||
0.5.7 (2021-09-08)
|
||||
------------------
|
||||
* Handle None values in dictionary
|
||||
* Add tests for None
|
||||
* Dockerfile-kinetic: Add --include-eol-distros
|
||||
* Contributors: Martin Günther
|
||||
|
||||
0.5.6 (2021-03-01)
|
||||
------------------
|
||||
* Propagate strict_mode, check_missing_fields in _convert_to_ros_type
|
||||
Previously, _convert_to_ros_type dropped strict_mode and
|
||||
check_missing_fields in nested messages.
|
||||
* Add NestedUint8ArrayTestService tests
|
||||
* propagate check_types in _convert_to_ros_type (`#51 <https://github.com/uos/rospy_message_converter/issues/51>`_)
|
||||
Co-authored-by: Martin Günther <martin.guenther@dfki.de>
|
||||
* Fix binary_array_as_bytes=False with nested msgs
|
||||
* Add param binary_array_as_bytes
|
||||
Closes `#45 <https://github.com/uos/rospy_message_converter/issues/45>`_.
|
||||
* Contributors: Marc Bosch-Jorge, Martin Günther, Otacon5555
|
||||
|
||||
0.5.5 (2020-11-09)
|
||||
------------------
|
||||
* Decode strings from ROS messages as UTF8
|
||||
This makes the python2 behavior equal to python3.
|
||||
* python3 only: Validate base64 strings
|
||||
* Add bytes to python3 string types
|
||||
This means that `bytes` will now also be base64-decoded, which fixes the following tests on python3:
|
||||
* test_dictionary_with_uint8_array_bytes
|
||||
* test_dictionary_with_uint8_array_bytes_unencoded
|
||||
* test_dictionary_with_3uint8_array_bytes
|
||||
On python2, `bytes` is just an alias for `str`, which is why it worked
|
||||
without this.
|
||||
* Fix and add tests
|
||||
* Contributors: Martin Günther
|
||||
|
||||
0.5.4 (2020-10-13)
|
||||
------------------
|
||||
* Avoid numpy dependency
|
||||
* Contributors: Martin Günther, betaboon
|
||||
|
||||
0.5.3 (2020-08-20)
|
||||
------------------
|
||||
* Add check_types parameter to convert_dictionary_to_ros_message (`#42 <https://github.com/uos/rospy_message_converter/issues/42>`_)
|
||||
* Allow numpy numeric types in numeric fields (`#41 <https://github.com/uos/rospy_message_converter/issues/41>`_)
|
||||
Fixes `#39 <https://github.com/uos/rospy_message_converter/issues/39>`_.
|
||||
* perf: Remove remaining regexes
|
||||
This is only a small speedup of about 1.03x.
|
||||
* perf: Avoid regex in _is_field_type_a_primitive_array
|
||||
This makes the function almost 3x faster.
|
||||
* perf: Reorder type checks
|
||||
Perform the cheaper checks first. This results in a speedup of about
|
||||
1.2x.
|
||||
* perf: Avoid regex in is_ros_binary_type
|
||||
This makes is_ros_binary_type almost 5x faster and as a result the whole
|
||||
convert_ros_message_to_dictionary function almost 2x faster.
|
||||
* Compare types, not type names; improve error message
|
||||
Old error message:
|
||||
TypeError: Wrong type: '1.0' must be float64
|
||||
New error message:
|
||||
TypeError: Field 'x' has wrong type <type 'numpy.float64'> (valid types: [<type 'int'>, <type 'float'>])
|
||||
* Remove unused python_to_ros_type_map
|
||||
* added test for convert_dictionary_to_ros_message with int8 array
|
||||
* python 3 fix for _convert_to_ros_binary
|
||||
* Contributors: Martin Günther, Steffen Rühl
|
||||
|
||||
0.5.2 (2020-07-09)
|
||||
------------------
|
||||
* Check for wrong field types when converting from dict to ros msg
|
||||
* Check for missing fields when converting from dict to ros msg
|
||||
* Contributors: Martin Günther, alecarnevale
|
||||
|
||||
0.5.1 (2020-05-25)
|
||||
------------------
|
||||
* Initial release into Noetic
|
||||
* Decode base64-encoded byte arrays as unicode
|
||||
* Make tests compatible with python3
|
||||
* add check for python3 str serializing `#33 <https://github.com/uos/rospy_message_converter/issues/33>`_ (`#34 <https://github.com/uos/rospy_message_converter/issues/34>`_)
|
||||
* efficient conversion of primitive array to ros type (`#31 <https://github.com/uos/rospy_message_converter/issues/31>`_)
|
||||
* efficient conversion of primitive array
|
||||
* removed unused _convert_from_ros_primitive
|
||||
* optionally ignore extra fields when deserializing (`#29 <https://github.com/uos/rospy_message_converter/issues/29>`_)
|
||||
* Remove EOL distros indigo + lunar from CI
|
||||
* travis CI: Use matrix to split ROS distros
|
||||
* Update README (convert to md, add build status)
|
||||
* Contributors: Martin Günther, George Hartt, Jannik Abbenseth, Omri Rozenzaft
|
||||
|
||||
0.5.0 (2019-01-17)
|
||||
------------------
|
||||
* Initial release into Lunar and Melodic
|
||||
* Remove support for Jade (EOL)
|
||||
* Change maintainer from Brandon Alexander to Martin Günther
|
||||
* Move repo from baalexander to uos
|
||||
* Add serialize_deserialize to unit tests, fix incorrect tests caught by this
|
||||
* Remove dependency on ROS master in tests; all tests are now unit
|
||||
tests (`#18 <https://github.com/uos/rospy_message_converter/issues/18>`_)
|
||||
* Add service request/response support (`#17 <https://github.com/uos/rospy_message_converter/issues/17>`_)
|
||||
* Fix fixed-size uint8 array conversion failure (`#15 <https://github.com/uos/rospy_message_converter/issues/15>`_)
|
||||
* Fix unicode handling in string fields (`#13 <https://github.com/uos/rospy_message_converter/issues/13>`_)
|
||||
* Enable testing only if CATKIN_ENABLE_TESTING is set (`#9 <https://github.com/uos/rospy_message_converter/issues/9>`_)
|
||||
* Contributors: Martin Günther, Brandon Alexander, George Laurent, Jean-Baptiste Doyon, Viktor Schlegel, Rein Appeldoorn, Will Baker, neka-nat
|
||||
|
||||
0.4.0 (2015-12-13)
|
||||
------------------
|
||||
* Adds support for ROS Jade
|
||||
* Removes support for ROS Groovy and Hydro (EOL)
|
||||
* Uses single branch for all ROS versions
|
||||
* Docker support for local development and Travis CI
|
||||
|
||||
0.3.0 (2014-06-03)
|
||||
------------------
|
||||
* Adds support for ROS Indigo
|
||||
|
||||
0.2.0 (2013-07-15)
|
||||
------------------
|
||||
* Updates to ROS Hydro
|
||||
* Builds and runs tests with Travis CI
|
||||
* Adds CHANGELOG
|
||||
|
||||
0.1.4 (2013-04-16)
|
||||
------------------
|
||||
* Documents Python functions
|
||||
* Throws error if invalid JSON or dictionary
|
||||
|
||||
0.1.3 (2013-03-04)
|
||||
------------------
|
||||
* Adds rostest dependency
|
||||
|
||||
0.1.2 (2013-03-04)
|
||||
------------------
|
||||
* Adds missing build_depends and run_depends
|
||||
|
||||
0.1.1 (2013-03-01)
|
||||
------------------
|
||||
* Adds message_generation dependency to fix build
|
||||
|
||||
0.1.0 (2013-02-27)
|
||||
------------------
|
||||
* Initial release of rospy_message_converter
|
||||
37
navigations/rospy_message_converter/CMakeLists.txt
Executable file
37
navigations/rospy_message_converter/CMakeLists.txt
Executable file
@@ -0,0 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
project(rospy_message_converter)
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS message_generation std_msgs)
|
||||
|
||||
# Generate messages for testing (NOINSTALL)
|
||||
add_message_files(
|
||||
FILES
|
||||
NestedUint8ArrayTestMessage.msg
|
||||
TestArray.msg
|
||||
Uint8Array3TestMessage.msg
|
||||
Uint8ArrayTestMessage.msg
|
||||
NOINSTALL
|
||||
)
|
||||
|
||||
add_service_files(
|
||||
FILES
|
||||
NestedUint8ArrayTestService.srv
|
||||
NOINSTALL
|
||||
)
|
||||
|
||||
catkin_python_setup()
|
||||
|
||||
# Generate added messages and services with any dependencies listed here
|
||||
generate_messages(
|
||||
DEPENDENCIES
|
||||
std_msgs
|
||||
)
|
||||
|
||||
catkin_package(CATKIN_DEPENDS message_runtime std_msgs)
|
||||
|
||||
# Testing
|
||||
if(CATKIN_ENABLE_TESTING)
|
||||
catkin_add_nosetests(test/test_json_message_converter.py)
|
||||
catkin_add_nosetests(test/test_message_converter.py)
|
||||
endif()
|
||||
18
navigations/rospy_message_converter/Dockerfile-kinetic
Executable file
18
navigations/rospy_message_converter/Dockerfile-kinetic
Executable file
@@ -0,0 +1,18 @@
|
||||
FROM ros:kinetic-ros-core
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y build-essential python-rosdep cmake \
|
||||
&& rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log} /tmp/* /var/tmp/*
|
||||
|
||||
RUN rosdep init && rosdep update --include-eol-distros
|
||||
|
||||
# Create ROS workspace
|
||||
COPY . /ws/src/rospy_message_converter
|
||||
WORKDIR /ws
|
||||
|
||||
# Install the package and its dependencies
|
||||
RUN rosdep install --from-paths src --ignore-src --rosdistro kinetic -y
|
||||
|
||||
# Set up the development environment
|
||||
RUN /bin/bash -c "source /opt/ros/kinetic/setup.bash && \
|
||||
catkin_make install"
|
||||
18
navigations/rospy_message_converter/Dockerfile-melodic
Executable file
18
navigations/rospy_message_converter/Dockerfile-melodic
Executable file
@@ -0,0 +1,18 @@
|
||||
FROM ros:melodic-ros-core
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y build-essential python-rosdep cmake \
|
||||
&& rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log} /tmp/* /var/tmp/*
|
||||
|
||||
RUN rosdep init && rosdep update
|
||||
|
||||
# Create ROS workspace
|
||||
COPY . /ws/src/rospy_message_converter
|
||||
WORKDIR /ws
|
||||
|
||||
# Install the package and its dependencies
|
||||
RUN rosdep install --from-paths src --ignore-src --rosdistro melodic -y
|
||||
|
||||
# Set up the development environment
|
||||
RUN /bin/bash -c "source /opt/ros/melodic/setup.bash && \
|
||||
catkin_make install"
|
||||
18
navigations/rospy_message_converter/Dockerfile-noetic
Executable file
18
navigations/rospy_message_converter/Dockerfile-noetic
Executable file
@@ -0,0 +1,18 @@
|
||||
FROM ros:noetic-ros-core
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y build-essential python3-rosdep cmake \
|
||||
&& rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log} /tmp/* /var/tmp/*
|
||||
|
||||
RUN rosdep init && rosdep update
|
||||
|
||||
# Create ROS workspace
|
||||
COPY . /ws/src/rospy_message_converter
|
||||
WORKDIR /ws
|
||||
|
||||
# Install the package and its dependencies
|
||||
RUN rosdep install --from-paths src --ignore-src --rosdistro noetic -y
|
||||
|
||||
# Set up the development environment
|
||||
RUN /bin/bash -c "source /opt/ros/noetic/setup.bash && \
|
||||
catkin_make install"
|
||||
28
navigations/rospy_message_converter/LICENSE
Executable file
28
navigations/rospy_message_converter/LICENSE
Executable file
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2019-2022, Martin Günther (DFKI GmbH) and others
|
||||
Copyright (c) 2013-2016, Brandon Alexander
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of this project nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
104
navigations/rospy_message_converter/README.md
Executable file
104
navigations/rospy_message_converter/README.md
Executable file
@@ -0,0 +1,104 @@
|
||||
rospy_message_converter
|
||||
=======================
|
||||
|
||||
Rospy_message_converter is a lightweight ROS package and Python library to
|
||||
convert from Python dictionaries and JSON messages to rospy messages, and vice
|
||||
versa.
|
||||
|
||||
ROS 1 and ROS 2 branches
|
||||
------------------------
|
||||
|
||||
ROS 1 users should use the `master` branch. ROS 2 users should use the appropriate
|
||||
branch for their distro (`foxy`/`galactic`/`humble`/`rolling`/...).
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Convert a dictionary to a ROS message
|
||||
|
||||
```python
|
||||
from rospy_message_converter import message_converter
|
||||
from std_msgs.msg import String
|
||||
dictionary = { 'data': 'Howdy' }
|
||||
message = message_converter.convert_dictionary_to_ros_message('std_msgs/String', dictionary)
|
||||
```
|
||||
|
||||
Convert a ROS message to a dictionary
|
||||
|
||||
```python
|
||||
from rospy_message_converter import message_converter
|
||||
from std_msgs.msg import String
|
||||
message = String(data = 'Howdy')
|
||||
dictionary = message_converter.convert_ros_message_to_dictionary(message)
|
||||
```
|
||||
|
||||
Convert JSON to a ROS message
|
||||
|
||||
```python
|
||||
from rospy_message_converter import json_message_converter
|
||||
from std_msgs.msg import String
|
||||
json_str = '{"data": "Hello"}'
|
||||
message = json_message_converter.convert_json_to_ros_message('std_msgs/String', json_str)
|
||||
```
|
||||
|
||||
Convert a ROS message to JSON
|
||||
|
||||
```python
|
||||
from rospy_message_converter import json_message_converter
|
||||
from std_msgs.msg import String
|
||||
message = String(data = 'Hello')
|
||||
json_str = json_message_converter.convert_ros_message_to_json(message)
|
||||
```
|
||||
|
||||
Test
|
||||
----
|
||||
|
||||
To run the tests:
|
||||
|
||||
```bash
|
||||
catkin_make test
|
||||
```
|
||||
|
||||
pre-commit Formatting Checks
|
||||
----------------------------
|
||||
|
||||
This repo has a [pre-commit](https://pre-commit.com/) check that runs in CI.
|
||||
You can use this locally and set it up to run automatically before you commit
|
||||
something. To install, use pip:
|
||||
|
||||
```bash
|
||||
pip3 install --user pre-commit
|
||||
```
|
||||
|
||||
To run over all the files in the repo manually:
|
||||
|
||||
```bash
|
||||
pre-commit run -a
|
||||
```
|
||||
|
||||
To run pre-commit automatically before committing in the local repo, install the git hooks:
|
||||
|
||||
```bash
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Project is released under the BSD license.
|
||||
|
||||
GitHub actions - Continuous Integration
|
||||
---------------------------------------
|
||||
|
||||
[](https://github.com/DFKI-NI/rospy_message_converter/actions/workflows/github-actions.yml/)
|
||||
|
||||
|
||||
ROS Buildfarm
|
||||
-------------
|
||||
|
||||
| | binary deb | source deb | devel | doc |
|
||||
|-----------|------------|------------|-------|-----|
|
||||
| kinetic | [](http://build.ros.org/job/Kbin_uX64__rospy_message_converter__ubuntu_xenial_amd64__binary/) | [](http://build.ros.org/job/Ksrc_uX__rospy_message_converter__ubuntu_xenial__source/) | [](http://build.ros.org/job/Kdev__rospy_message_converter__ubuntu_xenial_amd64) | [](http://build.ros.org/job/Kdoc__rospy_message_converter__ubuntu_xenial_amd64) |
|
||||
| melodic | [](http://build.ros.org/job/Mbin_uB64__rospy_message_converter__ubuntu_bionic_amd64__binary) | [](http://build.ros.org/job/Msrc_uB__rospy_message_converter__ubuntu_bionic__source/) | [](http://build.ros.org/job/Mdev__rospy_message_converter__ubuntu_bionic_amd64) | [](http://build.ros.org/job/Mdoc__rospy_message_converter__ubuntu_bionic_amd64) |
|
||||
| noetic | [](http://build.ros.org/job/Nbin_uF64__rospy_message_converter__ubuntu_focal_amd64__binary/) | [](http://build.ros.org/job/Nsrc_uF__rospy_message_converter__ubuntu_focal__source/) | [](http://build.ros.org/job/Ndev__rospy_message_converter__ubuntu_focal_amd64/) | [](http://build.ros.org/job/Ndoc__rospy_message_converter__ubuntu_focal_amd64/) |
|
||||
2
navigations/rospy_message_converter/msg/NestedUint8ArrayTestMessage.msg
Executable file
2
navigations/rospy_message_converter/msg/NestedUint8ArrayTestMessage.msg
Executable file
@@ -0,0 +1,2 @@
|
||||
# array of arrays for testing purposes
|
||||
Uint8ArrayTestMessage[] arrays
|
||||
1
navigations/rospy_message_converter/msg/TestArray.msg
Executable file
1
navigations/rospy_message_converter/msg/TestArray.msg
Executable file
@@ -0,0 +1 @@
|
||||
float64[] data
|
||||
2
navigations/rospy_message_converter/msg/Uint8Array3TestMessage.msg
Executable file
2
navigations/rospy_message_converter/msg/Uint8Array3TestMessage.msg
Executable file
@@ -0,0 +1,2 @@
|
||||
# Fixed size uint8 array for testing purposes
|
||||
uint8[3] data
|
||||
2
navigations/rospy_message_converter/msg/Uint8ArrayTestMessage.msg
Executable file
2
navigations/rospy_message_converter/msg/Uint8ArrayTestMessage.msg
Executable file
@@ -0,0 +1,2 @@
|
||||
# Size-agnostic uint8 array for testing purposes
|
||||
uint8[] data
|
||||
28
navigations/rospy_message_converter/package.xml
Executable file
28
navigations/rospy_message_converter/package.xml
Executable file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<package format="3">
|
||||
<name>rospy_message_converter</name>
|
||||
<version>0.5.9</version>
|
||||
<description>Converts between Python dictionaries and JSON to rospy messages.</description>
|
||||
<maintainer email="martin.guenther@dfki.de">Martin Günther</maintainer>
|
||||
<license>BSD</license>
|
||||
<url type="website">http://ros.org/wiki/rospy_message_converter</url>
|
||||
<url type="repository">https://github.com/DFKI-NI/rospy_message_converter</url>
|
||||
<url type="bugtracker">https://github.com/DFKI-NI/rospy_message_converter/issues</url>
|
||||
<author email="baalexander@gmail.com">Brandon Alexander</author>
|
||||
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
|
||||
<build_depend>message_generation</build_depend>
|
||||
<build_depend>std_msgs</build_depend>
|
||||
<build_export_depend>std_msgs</build_export_depend>
|
||||
|
||||
<exec_depend>roslib</exec_depend>
|
||||
<exec_depend>rospy</exec_depend>
|
||||
<exec_depend>message_runtime</exec_depend>
|
||||
<exec_depend>std_msgs</exec_depend>
|
||||
|
||||
<test_depend>rosunit</test_depend>
|
||||
<test_depend>std_srvs</test_depend>
|
||||
<test_depend condition="$ROS_PYTHON_VERSION == 2">python-numpy</test_depend>
|
||||
<test_depend condition="$ROS_PYTHON_VERSION == 3">python3-numpy</test_depend>
|
||||
</package>
|
||||
5
navigations/rospy_message_converter/pyproject.toml
Executable file
5
navigations/rospy_message_converter/pyproject.toml
Executable file
@@ -0,0 +1,5 @@
|
||||
[tool.black]
|
||||
# The line length here has to match the flake8 config in .flake8
|
||||
line-length = 120
|
||||
target-version = ['py38']
|
||||
skip-string-normalization = true
|
||||
11
navigations/rospy_message_converter/setup.py
Executable file
11
navigations/rospy_message_converter/setup.py
Executable file
@@ -0,0 +1,11 @@
|
||||
# ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
|
||||
|
||||
from distutils.core import setup
|
||||
from catkin_pkg.python_setup import generate_distutils_setup
|
||||
|
||||
d = generate_distutils_setup(
|
||||
packages=['rospy_message_converter'],
|
||||
package_dir={'': 'src'},
|
||||
)
|
||||
|
||||
setup(**d)
|
||||
@@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2019-2022, Martin Günther (DFKI GmbH) and others
|
||||
# Copyright (c) 2013-2016, Brandon Alexander
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
# * Neither the name of this project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import json
|
||||
|
||||
from rospy_message_converter import message_converter
|
||||
|
||||
|
||||
def convert_json_to_ros_message(message_type, json_message, strict_mode=True, log_level='error'):
|
||||
"""
|
||||
Takes in the message type and a JSON-formatted string and returns a ROS
|
||||
message.
|
||||
|
||||
:param message_type: The desired ROS message type of the result
|
||||
:type message_type: str
|
||||
:param json_message: A JSON-formatted string
|
||||
:type json_message: str
|
||||
:param strict_mode: If strict_mode is set, an exception will be thrown if the json message contains extra fields.
|
||||
:type strict_mode: bool, optional
|
||||
:param log_level: The log level to be used. Available levels: debug, info, warning, error, critical
|
||||
:type log_level: str, optional
|
||||
:return: A ROS message
|
||||
:rtype: class:`genpy.Message`
|
||||
|
||||
Example:
|
||||
>>> msg_type = "std_msgs/String"
|
||||
>>> json_msg = '{"data": "Hello, Robot"}'
|
||||
>>> convert_json_to_ros_message(msg_type, json_msg)
|
||||
data: "Hello, Robot"
|
||||
"""
|
||||
dictionary = json.loads(json_message)
|
||||
return message_converter.convert_dictionary_to_ros_message(
|
||||
message_type, dictionary, strict_mode=strict_mode, log_level=log_level
|
||||
)
|
||||
|
||||
|
||||
def convert_ros_message_to_json(message, binary_array_as_bytes=True):
|
||||
"""
|
||||
Takes in a ROS message and returns a JSON-formatted string.
|
||||
|
||||
:param message: A ROS message to convert
|
||||
:type message: class:`genpy.Message`
|
||||
:param binary_array_as_bytes: rospy treats `uint8[]` data as a `bytes`, which is the Python representation for byte
|
||||
data. In Python 2, this is the same as `str`. If this parameter is `False`, all `uint8[]` fields will be
|
||||
converted to `list(int)` instead.
|
||||
:type binary_array_as_bytes: bool, optional
|
||||
:return: A JSON-formatted string
|
||||
:rtype: str
|
||||
|
||||
Example:
|
||||
>>> import std_msgs.msg
|
||||
>>> ros_message = std_msgs.msg.String(data="Hello, Robot")
|
||||
>>> convert_ros_message_to_json(ros_message)
|
||||
'{"data": "Hello, Robot"}'
|
||||
"""
|
||||
dictionary = message_converter.convert_ros_message_to_dictionary(message, binary_array_as_bytes)
|
||||
json_message = json.dumps(dictionary)
|
||||
return json_message
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
|
||||
doctest.testmod()
|
||||
@@ -0,0 +1,397 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2019-2022, Martin Günther (DFKI GmbH) and others
|
||||
# Copyright (c) 2013-2016, Brandon Alexander
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
# * Neither the name of this project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import logging
|
||||
import roslib.message
|
||||
import rospy
|
||||
import base64
|
||||
import sys
|
||||
import copy
|
||||
import collections
|
||||
|
||||
python3 = sys.hexversion > 0x03000000
|
||||
|
||||
python_list_types = [list, tuple]
|
||||
|
||||
if python3:
|
||||
python_string_types = [str, bytes]
|
||||
python_int_types = [int]
|
||||
else:
|
||||
python_string_types = [str, unicode] # noqa
|
||||
python_int_types = [int, long] # noqa
|
||||
|
||||
python_float_types = [float]
|
||||
|
||||
ros_to_python_type_map = {
|
||||
'bool': [bool],
|
||||
'float32': copy.deepcopy(python_float_types + python_int_types),
|
||||
'float64': copy.deepcopy(python_float_types + python_int_types),
|
||||
'int8': copy.deepcopy(python_int_types),
|
||||
'int16': copy.deepcopy(python_int_types),
|
||||
'int32': copy.deepcopy(python_int_types),
|
||||
'int64': copy.deepcopy(python_int_types),
|
||||
'uint8': copy.deepcopy(python_int_types),
|
||||
'uint16': copy.deepcopy(python_int_types),
|
||||
'uint32': copy.deepcopy(python_int_types),
|
||||
'uint64': copy.deepcopy(python_int_types),
|
||||
'byte': copy.deepcopy(python_int_types),
|
||||
'char': copy.deepcopy(python_int_types),
|
||||
'string': copy.deepcopy(python_string_types),
|
||||
}
|
||||
|
||||
try:
|
||||
import numpy as np
|
||||
|
||||
_ros_to_numpy_type_map = {
|
||||
'float32': [np.float32, np.int8, np.int16, np.uint8, np.uint16],
|
||||
# don't include int32, because conversion to float may change value:
|
||||
# v = np.iinfo(np.int32).max; np.float32(v) != v
|
||||
'float64': [np.float32, np.float64, np.int8, np.int16, np.int32, np.uint8, np.uint16, np.uint32],
|
||||
'int8': [np.int8],
|
||||
'int16': [np.int8, np.int16, np.uint8],
|
||||
'int32': [np.int8, np.int16, np.int32, np.uint8, np.uint16],
|
||||
'int64': [np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32],
|
||||
'uint8': [np.uint8],
|
||||
'uint16': [np.uint8, np.uint16],
|
||||
'uint32': [np.uint8, np.uint16, np.uint32],
|
||||
'uint64': [np.uint8, np.uint16, np.uint32, np.uint64],
|
||||
'byte': [np.int8],
|
||||
'char': [np.uint8],
|
||||
}
|
||||
|
||||
# merge type_maps
|
||||
merged = collections.defaultdict(list, ros_to_python_type_map)
|
||||
for k, v in _ros_to_numpy_type_map.items():
|
||||
merged[k].extend(v)
|
||||
ros_to_python_type_map = dict(merged)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
ros_time_types = ['time', 'duration']
|
||||
ros_primitive_types = [
|
||||
'bool',
|
||||
'byte',
|
||||
'char',
|
||||
'int8',
|
||||
'uint8',
|
||||
'int16',
|
||||
'uint16',
|
||||
'int32',
|
||||
'uint32',
|
||||
'int64',
|
||||
'uint64',
|
||||
'float32',
|
||||
'float64',
|
||||
'string',
|
||||
]
|
||||
ros_header_types = ['Header', 'std_msgs/Header', 'roslib/Header']
|
||||
|
||||
|
||||
def convert_dictionary_to_ros_message(
|
||||
message_type,
|
||||
dictionary,
|
||||
kind='message',
|
||||
strict_mode=True,
|
||||
check_missing_fields=False,
|
||||
check_types=True,
|
||||
log_level='error',
|
||||
):
|
||||
"""
|
||||
Takes in the message type and a Python dictionary and returns a ROS message.
|
||||
|
||||
Example:
|
||||
>>> msg_type = "std_msgs/String"
|
||||
>>> dict_msg = { "data": "Hello, Robot" }
|
||||
>>> convert_dictionary_to_ros_message(msg_type, dict_msg)
|
||||
data: "Hello, Robot"
|
||||
|
||||
>>> msg_type = "std_srvs/SetBool"
|
||||
>>> dict_msg = { "data": True }
|
||||
>>> kind = "request"
|
||||
>>> convert_dictionary_to_ros_message(msg_type, dict_msg, kind)
|
||||
data: True
|
||||
"""
|
||||
if kind == 'message':
|
||||
message_class = roslib.message.get_message_class(message_type)
|
||||
message = message_class()
|
||||
elif kind == 'request':
|
||||
service_class = roslib.message.get_service_class(message_type)
|
||||
message = service_class._request_class()
|
||||
elif kind == 'response':
|
||||
service_class = roslib.message.get_service_class(message_type)
|
||||
message = service_class._response_class()
|
||||
else:
|
||||
raise ValueError('Unknown kind "%s".' % kind)
|
||||
message_fields = dict(_get_message_fields(message))
|
||||
|
||||
remaining_message_fields = copy.deepcopy(message_fields)
|
||||
|
||||
if dictionary is None:
|
||||
dictionary = {}
|
||||
for field_name, field_value in dictionary.items():
|
||||
if field_name in message_fields:
|
||||
field_type = message_fields[field_name]
|
||||
if field_value is not None:
|
||||
field_value = _convert_to_ros_type(
|
||||
field_name, field_type, field_value, strict_mode, check_missing_fields, check_types, log_level
|
||||
)
|
||||
setattr(message, field_name, field_value)
|
||||
del remaining_message_fields[field_name]
|
||||
else:
|
||||
error_message = 'ROS message type "{0}" has no field named "{1}"'.format(message_type, field_name)
|
||||
if strict_mode:
|
||||
raise ValueError(error_message)
|
||||
else:
|
||||
if log_level not in ["debug", "info", "warning", "error", "critical"]:
|
||||
log_level = "error"
|
||||
logger = logging.getLogger('rosout')
|
||||
log_func = getattr(logger, log_level)
|
||||
|
||||
log_func('{}! It will be ignored.'.format(error_message))
|
||||
|
||||
if check_missing_fields and remaining_message_fields:
|
||||
error_message = 'Missing fields "{0}"'.format(remaining_message_fields)
|
||||
raise ValueError(error_message)
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def _convert_to_ros_type(
|
||||
field_name,
|
||||
field_type,
|
||||
field_value,
|
||||
strict_mode=True,
|
||||
check_missing_fields=False,
|
||||
check_types=True,
|
||||
log_level='error',
|
||||
):
|
||||
if _is_ros_binary_type(field_type):
|
||||
field_value = _convert_to_ros_binary(field_type, field_value)
|
||||
elif field_type in ros_time_types:
|
||||
field_value = _convert_to_ros_time(field_type, field_value)
|
||||
elif field_type in ros_primitive_types:
|
||||
# Note: one could also use genpy.message.check_type() here, but:
|
||||
# 1. check_type is "not designed to run fast and is meant only for error diagnosis"
|
||||
# 2. it doesn't check floats (see ros/genpy#130)
|
||||
# 3. it rejects numpy types, although they can be serialized
|
||||
if check_types and type(field_value) not in ros_to_python_type_map[field_type]:
|
||||
raise TypeError(
|
||||
"Field '{0}' has wrong type {1} (valid types: {2})".format(
|
||||
field_name, type(field_value), ros_to_python_type_map[field_type]
|
||||
)
|
||||
)
|
||||
field_value = _convert_to_ros_primitive(field_type, field_value)
|
||||
elif _is_field_type_a_primitive_array(field_type):
|
||||
field_value = field_value
|
||||
elif _is_field_type_an_array(field_type):
|
||||
field_value = _convert_to_ros_array(
|
||||
field_name, field_type, field_value, strict_mode, check_missing_fields, check_types, log_level
|
||||
)
|
||||
else:
|
||||
field_value = convert_dictionary_to_ros_message(
|
||||
field_type,
|
||||
field_value,
|
||||
strict_mode=strict_mode,
|
||||
check_missing_fields=check_missing_fields,
|
||||
check_types=check_types,
|
||||
log_level=log_level,
|
||||
)
|
||||
return field_value
|
||||
|
||||
|
||||
def _convert_to_ros_binary(field_type, field_value):
|
||||
if type(field_value) in python_string_types:
|
||||
if python3:
|
||||
# base64 in python3 added the `validate` arg:
|
||||
# If field_value is not properly base64 encoded and there are non-base64-alphabet characters in the input,
|
||||
# a binascii.Error will be raised.
|
||||
binary_value_as_string = base64.b64decode(field_value, validate=True)
|
||||
else:
|
||||
# base64 in python2 doesn't have the `validate` arg: characters that are not in the base-64 alphabet are
|
||||
# silently discarded, resulting in garbage output.
|
||||
binary_value_as_string = base64.b64decode(field_value)
|
||||
else:
|
||||
binary_value_as_string = bytes(bytearray(field_value))
|
||||
return binary_value_as_string
|
||||
|
||||
|
||||
def _convert_to_ros_time(field_type, field_value):
|
||||
time = None
|
||||
|
||||
if field_type == 'time' and field_value == 'now':
|
||||
time = rospy.get_rostime()
|
||||
else:
|
||||
if field_type == 'time':
|
||||
time = rospy.rostime.Time()
|
||||
elif field_type == 'duration':
|
||||
time = rospy.rostime.Duration()
|
||||
if 'secs' in field_value and field_value['secs'] is not None:
|
||||
setattr(time, 'secs', field_value['secs'])
|
||||
if 'nsecs' in field_value and field_value['nsecs'] is not None:
|
||||
setattr(time, 'nsecs', field_value['nsecs'])
|
||||
|
||||
return time
|
||||
|
||||
|
||||
def _convert_to_ros_primitive(field_type, field_value):
|
||||
# std_msgs/msg/_String.py always calls encode() on python3, so don't do it here
|
||||
if field_type == "string" and not python3:
|
||||
field_value = field_value.encode('utf-8')
|
||||
return field_value
|
||||
|
||||
|
||||
def _convert_to_ros_array(
|
||||
field_name,
|
||||
field_type,
|
||||
list_value,
|
||||
strict_mode=True,
|
||||
check_missing_fields=False,
|
||||
check_types=True,
|
||||
log_level='error',
|
||||
):
|
||||
# use index to raise ValueError if '[' not present
|
||||
list_type = field_type[: field_type.index('[')]
|
||||
return [
|
||||
_convert_to_ros_type(field_name, list_type, value, strict_mode, check_missing_fields, check_types, log_level)
|
||||
for value in list_value
|
||||
]
|
||||
|
||||
|
||||
def convert_ros_message_to_dictionary(message, binary_array_as_bytes=True):
|
||||
"""
|
||||
Takes in a ROS message and returns a Python dictionary.
|
||||
|
||||
Example:
|
||||
>>> import std_msgs.msg
|
||||
>>> ros_message = std_msgs.msg.UInt32(data=42)
|
||||
>>> convert_ros_message_to_dictionary(ros_message)
|
||||
{'data': 42}
|
||||
"""
|
||||
dictionary = {}
|
||||
message_fields = _get_message_fields(message)
|
||||
for field_name, field_type in message_fields:
|
||||
field_value = getattr(message, field_name)
|
||||
dictionary[field_name] = _convert_from_ros_type(field_type, field_value, binary_array_as_bytes)
|
||||
|
||||
return dictionary
|
||||
|
||||
|
||||
def _convert_from_ros_type(field_type, field_value, binary_array_as_bytes=True):
|
||||
if field_type in ros_primitive_types:
|
||||
field_value = _convert_from_ros_primitive(field_type, field_value)
|
||||
elif field_type in ros_time_types:
|
||||
field_value = _convert_from_ros_time(field_type, field_value)
|
||||
elif _is_ros_binary_type(field_type):
|
||||
if binary_array_as_bytes:
|
||||
field_value = _convert_from_ros_binary(field_type, field_value)
|
||||
elif type(field_value) == str:
|
||||
field_value = [ord(v) for v in field_value]
|
||||
else:
|
||||
field_value = list(field_value)
|
||||
elif _is_field_type_a_primitive_array(field_type):
|
||||
field_value = list(field_value)
|
||||
elif _is_field_type_an_array(field_type):
|
||||
field_value = _convert_from_ros_array(field_type, field_value, binary_array_as_bytes)
|
||||
else:
|
||||
field_value = convert_ros_message_to_dictionary(field_value, binary_array_as_bytes)
|
||||
|
||||
return field_value
|
||||
|
||||
|
||||
def _is_ros_binary_type(field_type):
|
||||
"""Checks if the field is a binary array one, fixed size or not
|
||||
|
||||
>>> _is_ros_binary_type("uint8")
|
||||
False
|
||||
>>> _is_ros_binary_type("uint8[]")
|
||||
True
|
||||
>>> _is_ros_binary_type("uint8[3]")
|
||||
True
|
||||
>>> _is_ros_binary_type("char")
|
||||
False
|
||||
>>> _is_ros_binary_type("char[]")
|
||||
True
|
||||
>>> _is_ros_binary_type("char[3]")
|
||||
True
|
||||
"""
|
||||
return field_type.startswith('uint8[') or field_type.startswith('char[')
|
||||
|
||||
|
||||
def _convert_from_ros_binary(field_type, field_value):
|
||||
field_value = base64.b64encode(field_value).decode('utf-8')
|
||||
return field_value
|
||||
|
||||
|
||||
def _convert_from_ros_time(field_type, field_value):
|
||||
field_value = {'secs': field_value.secs, 'nsecs': field_value.nsecs}
|
||||
return field_value
|
||||
|
||||
|
||||
def _convert_from_ros_primitive(field_type, field_value):
|
||||
# std_msgs/msg/_String.py always calls decode() on python3, so don't do it here
|
||||
if field_type == "string" and not python3:
|
||||
field_value = field_value.decode('utf-8')
|
||||
return field_value
|
||||
|
||||
|
||||
def _convert_from_ros_array(field_type, field_value, binary_array_as_bytes=True):
|
||||
# use index to raise ValueError if '[' not present
|
||||
list_type = field_type[: field_type.index('[')]
|
||||
return [_convert_from_ros_type(list_type, value, binary_array_as_bytes) for value in field_value]
|
||||
|
||||
|
||||
def _get_message_fields(message):
|
||||
return zip(message.__slots__, message._slot_types)
|
||||
|
||||
|
||||
def _is_field_type_an_array(field_type):
|
||||
return field_type.find('[') >= 0
|
||||
|
||||
|
||||
def _is_field_type_a_primitive_array(field_type):
|
||||
bracket_index = field_type.find('[')
|
||||
if bracket_index < 0:
|
||||
return False
|
||||
else:
|
||||
list_type = field_type[:bracket_index]
|
||||
return list_type in ros_primitive_types
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
|
||||
doctest.testmod()
|
||||
6
navigations/rospy_message_converter/srv/NestedUint8ArrayTestService.srv
Executable file
6
navigations/rospy_message_converter/srv/NestedUint8ArrayTestService.srv
Executable file
@@ -0,0 +1,6 @@
|
||||
# service with nested types for testing purposes
|
||||
NestedUint8ArrayTestMessage input
|
||||
|
||||
---
|
||||
|
||||
NestedUint8ArrayTestMessage output
|
||||
0
navigations/rospy_message_converter/test/__init__.py
Executable file
0
navigations/rospy_message_converter/test/__init__.py
Executable file
172
navigations/rospy_message_converter/test/test_json_message_converter.py
Executable file
172
navigations/rospy_message_converter/test/test_json_message_converter.py
Executable file
@@ -0,0 +1,172 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2019-2022, Martin Günther (DFKI GmbH) and others
|
||||
# Copyright (c) 2013-2016, Brandon Alexander
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
# * Neither the name of this project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import unittest
|
||||
import rospy
|
||||
from rospy_message_converter import json_message_converter
|
||||
|
||||
|
||||
class TestJsonMessageConverter(unittest.TestCase):
|
||||
def test_ros_message_with_string(self):
|
||||
from std_msgs.msg import String
|
||||
|
||||
expected_json = '{"data": "Hello"}'
|
||||
message = String(data='Hello')
|
||||
message = serialize_deserialize(message)
|
||||
returned_json = json_message_converter.convert_ros_message_to_json(message)
|
||||
self.assertEqual(returned_json, expected_json)
|
||||
|
||||
def test_ros_message_with_string_unicode(self):
|
||||
from std_msgs.msg import String
|
||||
|
||||
expected_json = '{"data": "Hello \\u00dcnicode"}'
|
||||
message = String(data=u'Hello \u00dcnicode')
|
||||
message = serialize_deserialize(message)
|
||||
returned_json = json_message_converter.convert_ros_message_to_json(message)
|
||||
self.assertEqual(returned_json, expected_json)
|
||||
|
||||
def test_ros_message_with_header(self):
|
||||
from std_msgs.msg import Header
|
||||
from time import time
|
||||
|
||||
now_time = rospy.Time(time())
|
||||
expected_json1 = '{{"stamp": {{"secs": {0}, "nsecs": {1}}}, "frame_id": "my_frame", "seq": 3}}'.format(
|
||||
now_time.secs, now_time.nsecs
|
||||
)
|
||||
expected_json2 = '{{"seq": 3, "stamp": {{"secs": {0}, "nsecs": {1}}}, "frame_id": "my_frame"}}'.format(
|
||||
now_time.secs, now_time.nsecs
|
||||
)
|
||||
expected_json3 = '{{"frame_id": "my_frame", "seq": 3, "stamp": {{"secs": {0}, "nsecs": {1}}}}}'.format(
|
||||
now_time.secs, now_time.nsecs
|
||||
)
|
||||
message = Header(stamp=now_time, frame_id='my_frame', seq=3)
|
||||
message = serialize_deserialize(message)
|
||||
returned_json = json_message_converter.convert_ros_message_to_json(message)
|
||||
self.assertTrue(
|
||||
returned_json == expected_json1 or returned_json == expected_json2 or returned_json == expected_json3
|
||||
)
|
||||
|
||||
def test_ros_message_with_uint8_array(self):
|
||||
from rospy_message_converter.msg import Uint8ArrayTestMessage
|
||||
|
||||
input_data = [97, 98, 99, 100]
|
||||
expected_json = '{"data": "YWJjZA=="}' # base64.b64encode("abcd") is "YWJjZA=="
|
||||
message = Uint8ArrayTestMessage(data=input_data)
|
||||
message = serialize_deserialize(message)
|
||||
returned_json = json_message_converter.convert_ros_message_to_json(message)
|
||||
self.assertEqual(returned_json, expected_json)
|
||||
|
||||
def test_ros_message_with_3uint8_array(self):
|
||||
from rospy_message_converter.msg import Uint8Array3TestMessage
|
||||
|
||||
input_data = [97, 98, 99]
|
||||
expected_json = '{"data": "YWJj"}' # base64.b64encode("abc") is "YWJj"
|
||||
message = Uint8Array3TestMessage(data=input_data)
|
||||
message = serialize_deserialize(message)
|
||||
returned_json = json_message_converter.convert_ros_message_to_json(message)
|
||||
self.assertEqual(returned_json, expected_json)
|
||||
|
||||
def test_json_with_string(self):
|
||||
from std_msgs.msg import String
|
||||
|
||||
expected_message = String(data='Hello')
|
||||
json_str = '{"data": "Hello"}'
|
||||
message = json_message_converter.convert_json_to_ros_message('std_msgs/String', json_str)
|
||||
expected_message = serialize_deserialize(expected_message)
|
||||
self.assertEqual(message, expected_message)
|
||||
|
||||
def test_json_with_string_unicode(self):
|
||||
from std_msgs.msg import String
|
||||
|
||||
expected_message = String(data=u'Hello \u00dcnicode')
|
||||
json_str = '{"data": "Hello \\u00dcnicode"}'
|
||||
message = json_message_converter.convert_json_to_ros_message('std_msgs/String', json_str)
|
||||
expected_message = serialize_deserialize(expected_message)
|
||||
self.assertEqual(message, expected_message)
|
||||
|
||||
def test_json_with_header(self):
|
||||
from std_msgs.msg import Header
|
||||
from time import time
|
||||
|
||||
now_time = rospy.Time(time())
|
||||
expected_message = Header(stamp=now_time, frame_id='my_frame', seq=12)
|
||||
json_str = '{{"stamp": {{"secs": {0}, "nsecs": {1}}}, "frame_id": "my_frame", "seq": 12}}'.format(
|
||||
now_time.secs, now_time.nsecs
|
||||
)
|
||||
message = json_message_converter.convert_json_to_ros_message('std_msgs/Header', json_str)
|
||||
expected_message = serialize_deserialize(expected_message)
|
||||
self.assertEqual(message, expected_message)
|
||||
|
||||
def test_json_with_string_null(self):
|
||||
from std_msgs.msg import String
|
||||
|
||||
expected_message = String(data='')
|
||||
json_str = '{"data": null}'
|
||||
message = json_message_converter.convert_json_to_ros_message('std_msgs/String', json_str)
|
||||
expected_message = serialize_deserialize(expected_message)
|
||||
self.assertEqual(message, expected_message)
|
||||
|
||||
def test_json_with_invalid_message_fields(self):
|
||||
self.assertRaises(
|
||||
ValueError, json_message_converter.convert_json_to_ros_message, 'std_msgs/String', '{"not_data": "Hello"}'
|
||||
)
|
||||
|
||||
|
||||
def serialize_deserialize(message):
|
||||
"""
|
||||
Serialize and then deserialize a message. This simulates sending a message
|
||||
between ROS nodes and makes sure that the ROS messages being tested are
|
||||
actually serializable, and are in the same format as they would be received
|
||||
over the network. In rospy, it is possible to assign an illegal data type
|
||||
to a message field (for example, `message = String(data=42)`), but trying
|
||||
to publish this message will throw `SerializationError: field data must be
|
||||
of type str`. This method will expose such bugs.
|
||||
"""
|
||||
from io import BytesIO
|
||||
|
||||
buff = BytesIO()
|
||||
message.serialize(buff)
|
||||
result = message.__class__() # create new instance of same class as message
|
||||
result.deserialize(buff.getvalue())
|
||||
return result
|
||||
|
||||
|
||||
PKG = 'rospy_message_converter'
|
||||
NAME = 'test_json_message_converter'
|
||||
if __name__ == '__main__':
|
||||
import rosunit
|
||||
|
||||
rosunit.unitrun(PKG, NAME, TestJsonMessageConverter)
|
||||
1188
navigations/rospy_message_converter/test/test_message_converter.py
Executable file
1188
navigations/rospy_message_converter/test/test_message_converter.py
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user