PX4 ROS2 msg conformity and explicit topics

- .msg files are PascalCase
 - topics are still either per msg or explicitly listed in TOPICS
 - compatible structs are still generated (eg struct msg_s), but ROS2 style px4::msg::Msg is also available
This commit is contained in:
Daniel Agar
2022-03-07 10:36:27 -05:00
parent 5affa693f2
commit 9ee8fa21cd
481 changed files with 5491 additions and 8399 deletions
View File
-444
View File
@@ -1,444 +0,0 @@
#!/usr/bin/env python3
################################################################################
#
# Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
# Copyright (c) 2018-2021 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. Neither the name of the copyright holder 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 HOLDER 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.
#
################################################################################
# This script can generate the client and agent code based on a set of topics
# to sent and set to receive. It uses fastrtpsgen to generate the code from the
# IDL for the topic messages. The PX4 msg definitions are used to create the IDL
# used by fastrtpsgen using templates.
import sys
import os
import argparse
import shutil
import px_generate_uorb_topic_files
from uorb_rtps_classifier import Classifier
import subprocess
import glob
import errno
import re
try:
from six.moves import input
except ImportError as e:
print("Failed to import six: " + e)
print("")
print("You may need to install it using:")
print(" pip3 install --user six")
print("")
sys.exit(1)
try:
from packaging import version
except ImportError as e:
print("Failed to import packaging: " + str(e))
print("")
print("You may need to install it using:")
print(" pip3 install --user packaging")
print("")
sys.exit(1)
default_client_out = "src/modules/micrortps_bridge/micrortps_client"
default_agent_out = "src/modules/micrortps_bridge/micrortps_agent"
default_uorb_templates_dir = "templates/uorb_microcdr"
default_urtps_templates_dir = "templates/urtps"
default_urtps_topics_file = "tools/urtps_bridge_topics.yaml"
default_package_name = px_generate_uorb_topic_files.PACKAGE
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--agent", dest='agent', action="store_true",
help="Flag for generate the agent, by default is true if -c is not specified")
parser.add_argument("-c", "--client", dest='client', action="store_true",
help="Flag for generate the client, by default is true if -a is not specified")
parser.add_argument("-i", "--generate-idl", dest='gen_idl',
action="store_true", help="Flag for generate idl files for each msg")
parser.add_argument("-j", "--idl-dir", dest='idl_dir',
type=str, help="IDL files dir", default='')
parser.add_argument("-m", "--mkdir-build", dest='mkdir_build',
action="store_true", help="Flag to create 'build' dir")
parser.add_argument("-l", "--generate-cmakelists", dest='cmakelists',
action="store_true", help="Flag to generate a CMakeLists.txt file for the micro-RTPS agent")
parser.add_argument("-t", "--topic-msg-dir", dest='msgdir', type=str,
help="Topics message, by default using relative path 'msg/'", default="msg")
parser.add_argument("-b", "--uorb-templates-dir", dest='uorb_templates', type=str,
help="uORB templates, by default using relative path to msgdir 'templates/uorb_microcdr'", default=default_uorb_templates_dir)
parser.add_argument("-q", "--urtps-templates-dir", dest='urtps_templates', type=str,
help="uRTPS templates, by default using relative path to msgdir 'templates/urtps'", default=default_urtps_templates_dir)
parser.add_argument("-y", "--rtps-ids-file", dest='yaml_file', type=str,
help="Setup uRTPS bridge topics file path, by default using relative path to msgdir 'tools/urtps_bridge_topics.yaml'", default=default_urtps_topics_file)
parser.add_argument("-p", "--package", dest='package', type=str,
help="Msg package naming, by default px4", default=default_package_name)
parser.add_argument("-o", "--agent-outdir", dest='agentdir', type=str,
help="Agent output dir, by default using relative path 'src/modules/micrortps_bridge/micrortps_agent'", default=default_agent_out)
parser.add_argument("-u", "--client-outdir", dest='clientdir', type=str,
help="Client output dir, by default using relative path 'src/modules/micrortps_bridge/micrortps_client'", default=default_client_out)
parser.add_argument("-f", "--fastrtpsgen-dir", dest='fastrtpsgen', type=str, nargs='?',
help="fastrtpsgen installation dir, only needed if fastrtpsgen is not in PATH, by default empty", default="")
parser.add_argument("-g", "--fastrtpsgen-include", dest='fastrtpsgen_include', type=str,
help="directory(ies) to add to preprocessor include paths of fastrtpsgen, by default empty", default="")
parser.add_argument("-r", "--ros2-distro", dest='ros2_distro', type=str, nargs='?',
help="ROS2 distro, only required if generating the agent for usage with ROS2 nodes, by default empty", default="")
parser.add_argument("--delete-tree", dest='del_tree',
action="store_true", help="Delete dir tree output dir(s)")
if len(sys.argv) <= 1:
parser.print_usage()
exit(-1)
# Parse arguments
args = parser.parse_args()
agent = args.agent
client = args.client
cmakelists = args.cmakelists
del_tree = args.del_tree
gen_idl = args.gen_idl
mkdir_build = args.mkdir_build
package = args.package
# Msg files path
msg_dir = os.path.abspath(args.msgdir)
px_generate_uorb_topic_files.append_to_include_path(
{msg_dir}, px_generate_uorb_topic_files.INCL_DEFAULT, package)
# Agent files output path
agent_out_dir = os.path.abspath(args.agentdir)
# Client files output path
client_out_dir = os.path.abspath(args.clientdir)
# IDL files path
idl_dir = args.idl_dir
if idl_dir != '':
idl_dir = os.path.abspath(args.idl_dir)
else:
idl_dir = os.path.join(agent_out_dir, "idl")
if args.fastrtpsgen is None or args.fastrtpsgen == '':
# Assume fastrtpsgen is in PATH
fastrtpsgen_path = 'fastrtpsgen'
for dirname in os.environ['PATH'].split(':'):
candidate = os.path.join(dirname, 'fastrtpsgen')
if os.path.isfile(candidate):
fastrtpsgen_path = candidate
else:
# Path to fastrtpsgen is explicitly specified
if os.path.isdir(args.fastrtpsgen):
fastrtpsgen_path = os.path.join(
os.path.abspath(args.fastrtpsgen), 'fastrtpsgen')
else:
fastrtpsgen_path = args.fastrtpsgen
fastrtpsgen_include = args.fastrtpsgen_include
if fastrtpsgen_include is not None and fastrtpsgen_include != '':
fastrtpsgen_include = "-I " + \
os.path.abspath(
args.fastrtpsgen_include) + " "
# get FastRTPSGen version
# .. note:: since Fast-RTPS 1.8.0 release, FastRTPSGen is a separated repository
# and not included in the Fast-RTPS project.
# The starting version since this separation is 1.0.0, which follows its own
# versioning
fastrtpsgen_version = version.Version("1.0.0")
if(os.path.exists(fastrtpsgen_path)):
try:
fastrtpsgen_version_out = subprocess.check_output(
[fastrtpsgen_path, "-version"]).decode("utf-8").strip()[-5:]
except OSError:
raise
try:
fastrtpsgen_version = version.parse(fastrtpsgen_version_out)
except version.InvalidVersion:
raise Exception(
"'fastrtpsgen -version' returned None or an invalid version")
else:
raise Exception(
"FastRTPSGen not found. Specify the location of fastrtpsgen with the -f flag")
# get ROS 2 version, if exists
ros2_distro = ''
ros_version = os.environ.get('ROS_VERSION')
if ros_version == '2':
if args.ros2_distro != '':
ros2_distro = args.ros2_distro
else:
ros2_distro = os.environ.get('ROS_DISTRO')
# get FastRTPS version
fastrtps_version = ''
if not ros2_distro:
# grab the version installed system wise
fastrtps_version = subprocess.check_output(
"ldconfig -v 2>/dev/null | grep libfastrtps", shell=True).decode("utf-8").strip().split('so.')[-1]
else:
# grab the version of the ros-<ros_distro>-fastrtps package
fastrtps_version = re.search(r'Version:\s*([\dd.]+)', subprocess.check_output(
"dpkg -s ros-" + ros2_distro + "-fastrtps 2>/dev/null | grep -i version", shell=True).decode("utf-8").strip()).group(1)
# If nothing specified it's generated both
if agent == False and client == False:
agent = True
client = True
if del_tree:
if agent:
_continue = str(input("\nFiles in " + agent_out_dir +
" will be erased, continue?[Y/n]\n"))
if _continue == "N" or _continue == "n":
print("Aborting execution...")
exit(-1)
else:
if agent and os.path.isdir(agent_out_dir):
shutil.rmtree(agent_out_dir)
if client:
_continue = str(input(
"\nFiles in " + client_out_dir + " will be erased, continue?[Y/n]\n"))
if _continue.strip() in ("N", "n"):
print("Aborting execution...")
exit(-1)
else:
if client and os.path.isdir(client_out_dir):
shutil.rmtree(client_out_dir)
if agent and os.path.isdir(os.path.join(agent_out_dir, "idl")):
shutil.rmtree(os.path.join(agent_out_dir, "idl"))
# uORB templates path
uorb_templates_dir = (args.uorb_templates if os.path.isabs(args.uorb_templates)
else os.path.join(msg_dir, args.uorb_templates))
# uRTPS templates path
urtps_templates_dir = (args.urtps_templates if os.path.isabs(args.urtps_templates)
else os.path.join(msg_dir, args.urtps_templates))
# parse yaml file into a map of ids and messages to send and receive
classifier = (Classifier(os.path.abspath(args.yaml_file), msg_dir) if os.path.isabs(args.yaml_file)
else Classifier(os.path.join(msg_dir, args.yaml_file), msg_dir))
uRTPS_CLIENT_TEMPL_FILE = 'microRTPS_client.cpp.em'
uRTPS_AGENT_TOPICS_H_TEMPL_FILE = 'RtpsTopics.h.em'
uRTPS_AGENT_TOPICS_SRC_TEMPL_FILE = 'RtpsTopics.cpp.em'
uRTPS_AGENT_TEMPL_FILE = 'microRTPS_agent.cpp.em'
uRTPS_TIMESYNC_CPP_TEMPL_FILE = 'microRTPS_timesync.cpp.em'
uRTPS_TIMESYNC_H_TEMPL_FILE = 'microRTPS_timesync.h.em'
uRTPS_AGENT_CMAKELISTS_TEMPL_FILE = 'microRTPS_agent_CMakeLists.txt.em'
uRTPS_PUBLISHER_SRC_TEMPL_FILE = 'Publisher.cpp.em'
uRTPS_PUBLISHER_H_TEMPL_FILE = 'Publisher.h.em'
uRTPS_SUBSCRIBER_SRC_TEMPL_FILE = 'Subscriber.cpp.em'
uRTPS_SUBSCRIBER_H_TEMPL_FILE = 'Subscriber.h.em'
def generate_agent(out_dir):
global fastrtps_version
if classifier.msgs_to_send:
for msg_file in classifier.msgs_to_send:
if gen_idl:
if out_dir != agent_out_dir:
px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", os.path.join(out_dir, "/idl"), urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
else:
px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", idl_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_SRC_TEMPL_FILE)
px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_H_TEMPL_FILE)
if classifier.alias_msgs_to_send:
for msg_file in classifier.alias_msgs_to_send:
msg_alias = msg_file[0]
msg_name = msg_file[1]
if gen_idl:
if out_dir != agent_out_dir:
px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, os.path.join(out_dir, "/idl"), urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
else:
px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, idl_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_SRC_TEMPL_FILE)
px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_PUBLISHER_H_TEMPL_FILE)
if classifier.msgs_to_receive:
for msg_file in classifier.msgs_to_receive:
if gen_idl:
if out_dir != agent_out_dir:
px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", os.path.join(out_dir, "/idl"), urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
else:
px_generate_uorb_topic_files.generate_idl_file(msg_file, msg_dir, "", idl_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_SRC_TEMPL_FILE)
px_generate_uorb_topic_files.generate_topic_file(msg_file, msg_dir, "", out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_H_TEMPL_FILE)
if classifier.alias_msgs_to_receive:
for msg_file in classifier.alias_msgs_to_receive:
msg_alias = msg_file[0]
msg_name = msg_file[1]
if gen_idl:
if out_dir != agent_out_dir:
px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, os.path.join(out_dir, "/idl"), urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
else:
px_generate_uorb_topic_files.generate_idl_file(msg_name, msg_dir, msg_alias, idl_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, fastrtps_version, ros2_distro, classifier.msg_list)
px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_SRC_TEMPL_FILE)
px_generate_uorb_topic_files.generate_topic_file(msg_name, msg_dir, msg_alias, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_SUBSCRIBER_H_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir,
urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir,
urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_TIMESYNC_CPP_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir,
urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_TIMESYNC_H_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir,
urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_TOPICS_H_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, out_dir,
urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_TOPICS_SRC_TEMPL_FILE)
if cmakelists:
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir, os.path.dirname(out_dir),
urtps_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_AGENT_CMAKELISTS_TEMPL_FILE)
# Final steps to install agent
mkdir_p(os.path.join(out_dir, "fastrtpsgen"))
prev_cwd_path = os.getcwd()
os.chdir(os.path.join(out_dir, "fastrtpsgen"))
if not glob.glob(os.path.join(idl_dir, "*.idl")):
raise Exception("No IDL files found in %s" % idl_dir)
# If it is generating the bridge code for interfacing with ROS2, then set
# the '-typeros2' option in fastrtpsgen.
# .. note:: This is only available in FastRTPSGen 1.0.4 and above
gen_ros2_typename = ""
if ros2_distro and ros2_distro in ['dashing', 'eloquent', 'foxy', 'galactic', 'rolling'] and fastrtpsgen_version >= version.Version("1.0.4"):
gen_ros2_typename = "-typeros2 "
idl_files = []
for idl_file in glob.glob(os.path.join(idl_dir, "*.idl")):
# Only run the generator for the topics that are actually marked to be
# used by the bridge
if os.path.splitext(os.path.basename(idl_file))[0] in classifier.msg_list:
idl_files.append(idl_file)
try:
ret = subprocess.check_call(fastrtpsgen_path + " -d " + out_dir +
"/fastrtpsgen -example x64Linux2.6gcc " + gen_ros2_typename + fastrtpsgen_include + " ".join(str(idl_file) for idl_file in idl_files), shell=True)
except OSError:
raise
rm_wildcard(os.path.join(out_dir, "fastrtpsgen/*PubSubMain*"))
rm_wildcard(os.path.join(out_dir, "fastrtpsgen/makefile*"))
rm_wildcard(os.path.join(out_dir, "fastrtpsgen/*Publisher*"))
rm_wildcard(os.path.join(out_dir, "fastrtpsgen/*Subscriber*"))
for f in glob.glob(os.path.join(out_dir, "fastrtpsgen/*.cxx")):
os.rename(f, f.replace(".cxx", ".cpp"))
cp_wildcard(os.path.join(out_dir, "fastrtpsgen/*"), out_dir)
if os.path.isdir(os.path.join(out_dir, "fastrtpsgen")):
shutil.rmtree(os.path.join(out_dir, "fastrtpsgen"))
cp_wildcard(os.path.join(urtps_templates_dir,
"microRTPS_transport.*"), agent_out_dir)
if cmakelists:
os.rename(os.path.join(os.path.dirname(out_dir), "microRTPS_agent_CMakeLists.txt"),
os.path.join(os.path.dirname(out_dir), "CMakeLists.txt"))
if (mkdir_build):
mkdir_p(os.path.join(os.path.dirname(out_dir), "build"))
os.chdir(prev_cwd_path)
return 0
def rm_wildcard(pattern):
for f in glob.glob(pattern):
os.remove(f)
def cp_wildcard(pattern, destdir):
for f in glob.glob(pattern):
shutil.copy(f, destdir)
def mkdir_p(dirpath):
try:
os.makedirs(dirpath)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(dirpath):
pass
else:
raise
def generate_client(out_dir):
global fastrtps_version
# Rename work in the default path
if default_client_out != out_dir:
def_file = os.path.join(default_client_out, "microRTPS_client.cpp")
if os.path.isfile(def_file):
os.rename(def_file, def_file.replace(".cpp", ".cpp_"))
def_file = os.path.join(default_client_out, "microRTPS_transport.cpp")
if os.path.isfile(def_file):
os.rename(def_file, def_file.replace(".cpp", ".cpp_"))
def_file = os.path.join(default_client_out, "microRTPS_transport.h")
if os.path.isfile(def_file):
os.rename(def_file, def_file.replace(".h", ".h_"))
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msgs_to_send, classifier.alias_msgs_to_send, classifier.msgs_to_receive, classifier.alias_msgs_to_receive, msg_dir,
out_dir, uorb_templates_dir, package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_list, fastrtps_version, ros2_distro, uRTPS_CLIENT_TEMPL_FILE)
# Final steps to install client
cp_wildcard(os.path.join(urtps_templates_dir,
"microRTPS_transport.*"), out_dir)
return 0
if agent:
generate_agent(agent_out_dir)
print(("\nAgent created in: " + agent_out_dir))
if client:
generate_client(client_out_dir)
print(("\nClient created in: " + client_out_dir))
+1 -4
View File
@@ -66,7 +66,7 @@ if __name__ == "__main__":
with open(msg_filename, 'r') as source_file:
msg_contents = source_file.read()
#Format markdown using msg name, comment, url, contents.
#Format markdown using msg name, comment, url, contents.
markdown_output="""# %s (UORB message)
%s
@@ -93,9 +93,6 @@ if __name__ == "__main__":
This list is [auto-generated](https://github.com/PX4/PX4-Autopilot/blob/master/msg/tools/generate_msg_docs.py) from the source code.
:::
This topic lists the UORB messages available in PX4 (some of which may be may be shared by the [PX4-ROS 2 Bridge](../ros/ros2_comm.md)).
Graphs showing how these are used [can be found here](../middleware/uorb_graph.md).
%s
""" % (filelist_in_markdown)
readme_file = os.path.join(output_dir, 'README.md')
+36 -309
View File
@@ -34,7 +34,7 @@
"""
px_generate_uorb_topic_files.py
Generates c/cpp header/source files for uorb topics from .msg (ROS syntax)
Generates c/cpp header/source files for uorb topics from .msg
message files
"""
@@ -42,6 +42,7 @@ import os
import shutil
import filecmp
import argparse
import re
import sys
try:
@@ -76,93 +77,54 @@ except ImportError as e:
__author__ = "Sergey Belash, Thomas Gubler, Beat Kueng"
__copyright__ = "Copyright (C) 2013-2016 PX4 Development Team."
__copyright__ = "Copyright (C) 2013-2021 PX4 Development Team."
__license__ = "BSD"
__email__ = "thomasgubler@gmail.com"
TEMPLATE_FILE = ['msg.h.em', 'msg.cpp.em']
TOPICS_LIST_TEMPLATE_FILE = ['uORBTopics.hpp.em', 'uORBTopics.cpp.em']
OUTPUT_FILE_EXT = ['.h', '.cpp']
TEMPLATE_FILE = ['msg.h.em']
OUTPUT_FILE_EXT = ['.h']
INCL_DEFAULT = ['std_msgs:./msg/std_msgs']
PACKAGE = 'px4'
TOPICS_TOKEN = '# TOPICS '
IDL_TEMPLATE_FILE = 'msg.idl.em'
CONSTRAINED_FLASH = False
class MsgScope:
NONE = 0
SEND = 1
RECEIVE = 2
def get_multi_topics(filename):
"""
Get TOPICS names from a "# TOPICS" line
"""
ofile = open(filename, 'r')
text = ofile.read()
result = []
for each_line in text.split('\n'):
if each_line.startswith(TOPICS_TOKEN):
topic_names_str = each_line.strip()
topic_names_str = topic_names_str.replace(TOPICS_TOKEN, "")
result.extend(topic_names_str.split(" "))
ofile.close()
return result
def get_msgs_list(msgdir):
"""
Makes list of msg files in the given directory
"""
return [fn for fn in os.listdir(msgdir) if fn.endswith(".msg")]
def generate_output_from_file(format_idx, filename, outputdir, package, templatedir, includepath):
def generate_output_from_file(filename, outputdir, package, templatedir, includepath):
"""
Converts a single .msg file to an uorb header/source file
"""
msg_context = genmsg.msg_loader.MsgContext.create_default()
full_type_name = genmsg.gentools.compute_full_type_name(
package, os.path.basename(filename))
spec = genmsg.msg_loader.load_msg_from_file(
msg_context, filename, full_type_name)
full_type_name = genmsg.gentools.compute_full_type_name(package, os.path.basename(filename))
file_base_name = os.path.basename(filename).replace(".msg", "")
full_type_name_snake = re.sub(r'(?<!^)(?=[A-Z])', '_', file_base_name).lower()
spec = genmsg.msg_loader.load_msg_from_file(msg_context, filename, full_type_name)
field_name_and_type = {}
for field in spec.parsed_fields():
field_name_and_type.update({field.name: field.type})
# assert if the timestamp field exists
try:
assert 'timestamp' in field_name_and_type
except AssertionError:
print("[ERROR] uORB topic files generator:\n\tgenerate_output_from_file:\tNo 'timestamp' field found in " +
spec.short_name + " msg definition!")
exit(1)
# assert if the timestamp field is of type uint64
try:
assert field_name_and_type.get('timestamp') == 'uint64'
except AssertionError:
print("[ERROR] uORB topic files generator:\n\tgenerate_output_from_file:\t'timestamp' field in " + spec.short_name +
" msg definition is not of type uint64 but rather of type " + field_name_and_type.get('timestamp') + "!")
exit(1)
topics = get_multi_topics(filename)
if includepath:
search_path = genmsg.command_line.includepath_to_dict(includepath)
else:
search_path = {}
genmsg.msg_loader.load_depends(msg_context, spec, search_path)
md5sum = genmsg.gentools.compute_md5(msg_context, spec)
if len(topics) == 0:
topics.append(spec.short_name)
em_globals = {
"name_snake_case": full_type_name_snake,
"file_name_in": filename,
"md5sum": md5sum,
"search_path": search_path,
"msg_context": msg_context,
"spec": spec,
"topics": topics,
"constrained_flash": CONSTRAINED_FLASH
}
@@ -170,90 +132,12 @@ def generate_output_from_file(format_idx, filename, outputdir, package, template
if not os.path.isdir(outputdir):
os.makedirs(outputdir)
template_file = os.path.join(templatedir, TEMPLATE_FILE[format_idx])
output_file = os.path.join(outputdir, spec.short_name +
OUTPUT_FILE_EXT[format_idx])
template_file = os.path.join(templatedir, TEMPLATE_FILE[0])
output_file = os.path.join(outputdir, full_type_name_snake + OUTPUT_FILE_EXT[0])
return generate_by_template(output_file, template_file, em_globals)
def generate_idl_file(filename_msg, msg_dir, alias, outputdir, templatedir, package, includepath, fastrtps_version, ros2_distro, msgs):
"""
Generates an .idl from .msg file
"""
msg = os.path.join(msg_dir, filename_msg + ".msg")
if (alias != ""):
em_globals = get_em_globals(
msg, alias, package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.NONE)
spec_short_name = alias
else:
em_globals = get_em_globals(
msg, "", package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.NONE)
spec_short_name = em_globals["spec"].short_name
# Make sure output directory exists:
if not os.path.isdir(outputdir):
os.makedirs(outputdir)
template_file = os.path.join(templatedir, IDL_TEMPLATE_FILE)
if version.parse(fastrtps_version) <= version.parse('1.7.2'):
output_file = os.path.join(outputdir, IDL_TEMPLATE_FILE.replace(
"msg.idl.em", str(spec_short_name + "_.idl")))
else:
output_file = os.path.join(outputdir, IDL_TEMPLATE_FILE.replace(
"msg.idl.em", str(spec_short_name + ".idl")))
return generate_by_template(output_file, template_file, em_globals)
def generate_uRTPS_general(filename_send_msgs, filename_alias_send_msgs, filename_receive_msgs, filename_alias_receive_msgs,
msg_dir, outputdir, templatedir, package, includepath, msgs, fastrtps_version, ros2_distro, template_name):
"""
Generates source file by msg content
"""
send_msgs = list(os.path.join(msg_dir, msg + ".msg")
for msg in filename_send_msgs)
receive_msgs = list(os.path.join(msg_dir, msg + ".msg")
for msg in filename_receive_msgs)
alias_send_msgs = list([os.path.join(
msg_dir, msg[1] + ".msg"), msg[0]] for msg in filename_alias_send_msgs)
alias_receive_msgs = list([os.path.join(
msg_dir, msg[1] + ".msg"), msg[0]] for msg in filename_alias_receive_msgs)
em_globals_list = []
if send_msgs:
em_globals_list.extend([get_em_globals(
f, "", package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.SEND) for f in send_msgs])
if alias_send_msgs:
em_globals_list.extend([get_em_globals(
f[0], f[1], package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.SEND) for f in alias_send_msgs])
if receive_msgs:
em_globals_list.extend([get_em_globals(
f, "", package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.RECEIVE) for f in receive_msgs])
if alias_receive_msgs:
em_globals_list.extend([get_em_globals(
f[0], f[1], package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.RECEIVE) for f in alias_receive_msgs])
merged_em_globals = merge_em_globals_list(em_globals_list)
# Make sure output directory exists:
if not os.path.isdir(outputdir):
os.makedirs(outputdir)
template_file = os.path.join(templatedir, template_name)
output_file = os.path.join(
outputdir, template_name.replace(".em", ""))
return generate_by_template(output_file, template_file, merged_em_globals)
def generate_topic_file(filename_msg, msg_dir, alias, outputdir, templatedir, package, includepath, msgs, fastrtps_version, ros2_distro, template_name):
def generate_topic_file(filename_msg, msg_dir, alias, outputdir, templatedir, package, includepath, msgs, template_name):
"""
Generates a sources and headers from .msg file
"""
@@ -261,11 +145,11 @@ def generate_topic_file(filename_msg, msg_dir, alias, outputdir, templatedir, pa
if (alias):
em_globals = get_em_globals(
msg, alias, package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.NONE)
msg, alias, package, includepath, msgs, MsgScope.NONE)
spec_short_name = alias
else:
em_globals = get_em_globals(
msg, "", package, includepath, msgs, fastrtps_version, ros2_distro, MsgScope.NONE)
msg, "", package, includepath, msgs, MsgScope.NONE)
spec_short_name = em_globals["spec"].short_name
# Make sure output directory exists:
@@ -279,37 +163,34 @@ def generate_topic_file(filename_msg, msg_dir, alias, outputdir, templatedir, pa
return generate_by_template(output_file, template_file, em_globals)
def get_em_globals(filename_msg, alias, package, includepath, msgs, fastrtps_version, ros2_distro, scope):
def get_em_globals(filename_msg, alias, package, includepath, msgs, scope):
"""
Generates em globals dictionary
"""
msg_context = genmsg.msg_loader.MsgContext.create_default()
full_type_name = genmsg.gentools.compute_full_type_name(
package, os.path.basename(filename_msg))
spec = genmsg.msg_loader.load_msg_from_file(
msg_context, filename_msg, full_type_name)
topics = get_multi_topics(filename_msg)
full_type_name = genmsg.gentools.compute_full_type_name(package, os.path.basename(filename_msg))
spec = genmsg.msg_loader.load_msg_from_file(msg_context, filename_msg, full_type_name)
if includepath:
search_path = genmsg.command_line.includepath_to_dict(includepath)
else:
search_path = {}
genmsg.msg_loader.load_depends(msg_context, spec, search_path)
md5sum = genmsg.gentools.compute_md5(msg_context, spec)
if len(topics) == 0:
topics.append(spec.short_name)
em_globals = {
"file_name_in": filename_msg,
"md5sum": md5sum,
"search_path": search_path,
"msg_context": msg_context,
"spec": spec,
"topics": topics,
"msgs": msgs,
"scope": scope,
"package": package,
"alias": alias,
"fastrtps_version": fastrtps_version,
"ros2_distro": ros2_distro
"package": package
}
return em_globals
@@ -324,8 +205,7 @@ def merge_em_globals_list(em_globals_list):
merged_em_globals = {}
for name in em_globals_list[0]:
merged_em_globals[name] = [em_globals[name]
for em_globals in em_globals_list]
merged_em_globals[name] = [em_globals[name] for em_globals in em_globals_list]
return merged_em_globals
@@ -354,140 +234,13 @@ def generate_by_template(output_file, template_file, em_globals):
ofile.close()
return True
def convert_dir(format_idx, inputdir, outputdir, package, templatedir):
"""
Converts all .msg files in inputdir to uORB header/source files
"""
# Find the most recent modification time in input dir
maxinputtime = 0
for f in os.listdir(inputdir):
fni = os.path.join(inputdir, f)
if os.path.isfile(fni):
it = os.path.getmtime(fni)
if it > maxinputtime:
maxinputtime = it
# Find the most recent modification time in output dir
maxouttime = 0
if os.path.isdir(outputdir):
for f in os.listdir(outputdir):
fni = os.path.join(outputdir, f)
if os.path.isfile(fni):
it = os.path.getmtime(fni)
if it > maxouttime:
maxouttime = it
# Do not generate if nothing changed on the input
if (maxinputtime != 0 and maxouttime != 0 and maxinputtime < maxouttime):
return False
includepath = INCL_DEFAULT + [':'.join([package, inputdir])]
for f in os.listdir(inputdir):
# Ignore hidden files
if f.startswith("."):
continue
fn = os.path.join(inputdir, f)
# Only look at actual files
if not os.path.isfile(fn):
continue
if fn[-4:].lower() != '.msg':
continue
generate_output_from_file(
format_idx, fn, outputdir, package, templatedir, includepath)
return True
def copy_changed(inputdir, outputdir, prefix='', quiet=False):
"""
Copies files from inputdir to outputdir if they don't exist in
ouputdir or if their content changed
"""
# Make sure output directory exists:
if not os.path.isdir(outputdir):
os.makedirs(outputdir)
for input_file in os.listdir(inputdir):
fni = os.path.join(inputdir, input_file)
if os.path.isfile(fni):
# Check if input_file exists in outpoutdir, copy the file if not
fno = os.path.join(outputdir, prefix + input_file)
if not os.path.isfile(fno):
shutil.copy(fni, fno)
if not quiet:
print("{0}: new header file".format(fno))
continue
if os.path.getmtime(fni) > os.path.getmtime(fno):
# The file exists in inputdir and outputdir
# only copy if contents do not match
if not filecmp.cmp(fni, fno):
shutil.copy(fni, fno)
if not quiet:
print("{0}: updated".format(input_file))
continue
if not quiet:
print("{0}: unchanged".format(input_file))
def convert_dir_save(format_idx, inputdir, outputdir, package, templatedir, temporarydir, prefix, quiet=False):
"""
Converts all .msg files in inputdir to uORB header files
Unchanged existing files are not overwritten.
"""
# Create new headers in temporary output directory
convert_dir(format_idx, inputdir, temporarydir, package, templatedir)
if generate_idx == 1:
generate_topics_list_file(inputdir, temporarydir, TOPICS_LIST_TEMPLATE_FILE[1], templatedir)
# Copy changed headers from temporary dir to output dir
copy_changed(temporarydir, outputdir, prefix, quiet)
def generate_topics_list_file(msgdir, outputdir, template_filename, templatedir):
# generate cpp file with topics list
msgs = get_msgs_list(msgdir)
multi_topics = []
for msg in msgs:
msg_filename = os.path.join(msgdir, msg)
multi_topics.extend(get_multi_topics(msg_filename))
tl_globals = {"msgs": msgs, "multi_topics": multi_topics}
tl_template_file = os.path.join(templatedir, template_filename)
tl_out_file = os.path.join(outputdir, template_filename.replace(".em", ""))
generate_by_template(tl_out_file, tl_template_file, tl_globals)
def generate_topics_list_file_from_files(files, outputdir, template_filename, templatedir):
# generate cpp file with topics list
filenames = [os.path.basename(
p) for p in files if os.path.basename(p).endswith(".msg")]
multi_topics = []
for msg_filename in files:
multi_topics.extend(get_multi_topics(msg_filename))
tl_globals = {"msgs": filenames, "multi_topics": multi_topics}
tl_template_file = os.path.join(templatedir, template_filename)
tl_out_file = os.path.join(outputdir, template_filename.replace(".em", ""))
generate_by_template(tl_out_file, tl_template_file, tl_globals)
def append_to_include_path(path_to_append, curr_include, package):
for p in path_to_append:
curr_include.append('%s:%s' % (package, p))
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Convert msg files to uorb headers/sources')
parser.add_argument('--headers', help='Generate header files',
action='store_true')
parser.add_argument('--sources', help='Generate source files',
action='store_true')
parser.add_argument('-d', dest='dir', help='directory with msg files')
parser = argparse.ArgumentParser(description='Convert msg files to uorb headers')
parser.add_argument('-f', dest='file',
help="files to convert (use only without -d)",
nargs="+")
@@ -500,14 +253,9 @@ if __name__ == "__main__":
help='package name')
parser.add_argument('-o', dest='outputdir',
help='output directory for header files')
parser.add_argument('-t', dest='temporarydir',
help='temporary directory')
parser.add_argument('-p', dest='prefix', default='',
help='string added as prefix to the output file '
' name when converting directories')
parser.add_argument('-q', dest='quiet', default=False, action='store_true',
help='string added as prefix to the output file '
' name when converting directories')
parser.add_argument('--constrained-flash', dest='constrained_flash', default=False, action='store_true',
help='set to save flash space')
args = parser.parse_args()
@@ -517,27 +265,6 @@ if __name__ == "__main__":
CONSTRAINED_FLASH = args.constrained_flash
if args.headers:
generate_idx = 0
elif args.sources:
generate_idx = 1
else:
print('Error: either --headers or --sources must be specified')
exit(-1)
if args.file is not None:
for f in args.file:
generate_output_from_file(
generate_idx, f, args.temporarydir, args.package, args.templatedir, INCL_DEFAULT)
generate_topics_list_file_from_files(args.file, args.outputdir, TOPICS_LIST_TEMPLATE_FILE[generate_idx], args.templatedir)
copy_changed(args.temporarydir, args.outputdir, args.prefix, args.quiet)
elif args.dir is not None:
convert_dir_save(
generate_idx,
args.dir,
args.outputdir,
args.package,
args.templatedir,
args.temporarydir,
args.prefix,
args.quiet)
generate_output_from_file(f, args.outputdir, args.package, args.templatedir, INCL_DEFAULT)
@@ -41,6 +41,7 @@ precompiled and thus message generation will be much faster
import os
import errno
import re
import genmsg.msgs
@@ -257,6 +258,7 @@ def print_field_def(field):
type_prefix = ''
if (sl_pos >= 0):
type_name = type_name[sl_pos + 1:]
type_name = re.sub(r'(?<!^)(?=[A-Z])', '_', type_name).lower() # PascalCase to snake_case
type_prefix = 'struct '
type_appendix = '_s'
-186
View File
@@ -1,186 +0,0 @@
#!/usr/bin/env python3
################################################################################
#
# Copyright (c) 2018-2021 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. Neither the name of the copyright holder 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 HOLDER 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 argparse
import difflib
import errno
import os
from typing import Dict, List, Tuple
import yaml
class Classifier():
"""Class to classify RTPS msgs as to send, receive and set their IDs."""
def __init__(self, yaml_file, msg_folder) -> None:
self.msg_folder = msg_folder
self.msg_map = self.parse_yaml_msgs_file(yaml_file)
# Check if base types are defined correctly
self.check_base_type()
# Get messages to send and to receive
self.msgs_to_send: Dict[str, int] = dict()
self.msgs_to_receive: Dict[str, int] = dict()
self.alias_msgs_to_send: List[Tuple[str, str]] = []
self.alias_msgs_to_receive: List[Tuple[str, str]] = []
self.msg_list: List[str] = []
# Create message map
self.setup_msg_map()
self.msg_files_send = self.set_msg_files_send()
self.msg_files_receive = self.set_msg_files_receive()
def setup_msg_map(self) -> None:
"""Setup dictionary with an ID map for the messages."""
for topic in self.msg_map['rtps']:
if 'send' in list(topic.keys()):
if 'base' in list(topic.keys()):
self.alias_msgs_to_send.append(
(topic['msg'], topic['base']))
else:
self.msgs_to_send.update({topic['msg']: 0})
if 'receive' in list(topic.keys()):
if 'base' in list(topic.keys()):
self.alias_msgs_to_receive.append(
(topic['msg'], topic['base']))
else:
self.msgs_to_receive.update({topic['msg']: 0})
self.msg_list.append(topic['msg'])
def set_msg_files_send(self) -> list:
"""
Append the path to the files which messages are marked to
be sent.
"""
return [os.path.join(self.msg_folder, msg + ".msg")
for msg in list(self.msgs_to_send.keys())]
def set_msg_files_receive(self) -> list:
"""
Append the path to the files which messages are marked to
be received.
"""
return [os.path.join(self.msg_folder, msg + ".msg")
for msg in list(self.msgs_to_receive.keys())]
def check_base_type(self) -> None:
"""Check if alias message has correct base type."""
registered_alias_msgs = list(
topic['base'] for topic in self.msg_map['rtps'] if 'base' in list(topic.keys()))
base_types = []
for topic in self.msg_map['rtps']:
if 'base' not in list(topic.keys()):
base_types.append(topic['msg'])
incorrect_base_types = list(
set(registered_alias_msgs) - set(base_types))
base_types_suggestion = {}
for incorrect in incorrect_base_types:
base_types_suggestion.update({incorrect: difflib.get_close_matches(
incorrect, base_types, n=1, cutoff=0.6)})
if len(base_types_suggestion) > 0:
raise AssertionError(
('\n' + '\n'.join('\t- The multi-topic message base type \'{}\' does not exist.{}'.format(k, (' Did you mean \'' + v[0] + '\'?' if v else '')) for k, v in list(base_types_suggestion.items()))))
@staticmethod
def parse_yaml_msgs_file(yaml_file) -> dict:
"""Parses a yaml file into a dict."""
try:
with open(yaml_file, 'r') as file:
return yaml.safe_load(file)
except OSError as err:
if err.errno == errno.ENOENT:
raise IOError(errno.ENOENT, os.strerror(
errno.ENOENT), yaml_file)
raise
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--send", dest='send',
action="store_true", help="Get topics to be sent")
parser.add_argument("-a", "--alias", dest='alias',
action="store_true", help="Get alias topics")
parser.add_argument("-r", "--receive", dest='receive',
action="store_true", help="Get topics to be received")
parser.add_argument("-i", "--ignore", dest='ignore',
action="store_true", help="Get topics to be ignored")
parser.add_argument("-p", "--path", dest='path',
action="store_true", help="Get msgs and its paths")
parser.add_argument("-m", "--topic-msg-dir", dest='msgdir', type=str,
help="Topics message dir, by default msg/", default="msg")
parser.add_argument("-y", "--rtps-ids-file", dest='yaml_file', type=str,
help="RTPS msg IDs definition file absolute path, by default use relative path to msg, tools/urtps_bridge_topics.yaml",
default='tools/urtps_bridge_topics.yaml')
# Parse arguments
args = parser.parse_args()
msg_dir = args.msgdir
if args.msgdir == 'msg':
msg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
else:
msg_dir = os.path.abspath(args.msgdir)
classifier = (Classifier(os.path.abspath(args.yaml_file), msg_dir) if os.path.isabs(args.yaml_file)
else Classifier(os.path.join(msg_dir, args.yaml_file), msg_dir))
if args.send:
if args.path:
print(('send files: ' + ', '.join(str(msg_file)
for msg_file in classifier.msg_files_send) + '\n'))
else:
if args.alias:
print((', '.join(str(msg)
for msg in sorted(classifier.msgs_to_send)) + (' alias ' + ', '.join(msg[0]
for msg in classifier.alias_msgs_to_send) if len(classifier.alias_msgs_to_send) > 0 else '') + '\n'))
else:
print((', '.join(str(msg)
for msg in sorted(classifier.msgs_to_send))))
if args.receive:
if args.path:
print(('receive files: ' + ', '.join(str(msg_file)
for msg_file in classifier.msg_files_receive) + '\n'))
else:
if args.alias:
print((', '.join(str(msg)
for msg in sorted(classifier.msgs_to_receive)) + (' alias ' + ', '.join(msg[0]
for msg in classifier.alias_msgs_to_receive) if len(classifier.alias_msgs_to_receive) > 0 else '') + '\n'))
else:
print((', '.join(str(msg)
for msg in sorted(classifier.msgs_to_receive))))
-127
View File
@@ -1,127 +0,0 @@
#!/usr/bin/env python3
"""
Script to parse uORB message format to ROS msg format
Adapted from https://github.com/eProsima/px4_to_ros/blob/master/px4_to_ros2_PoC/px4_msgs/scripts/copy_and_rename.py
"""
import os
import re
import sys
from shutil import copyfile
__author__ = 'PX4 Development Team'
__copyright__ = \
'''
'
' Copyright (c) 2018 PX4 Development Team. All rights reserved.
'
' Redistribution and use in source and binary forms, or without
' modification, permitted provided that the following conditions
' are met:
'
' 1. Redistributions of source code must retain the above copyright
' notice, list of conditions and the following disclaimer.
' 2. Redistributions in binary form must reproduce the above copyright
' notice, list of conditions and the following disclaimer in
' the documentation and/or other materials provided with the
' distribution.
' 3. Neither the name PX4 nor the names of its contributors may be
' used to endorse or promote products derived from self 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, NOT
' LIMITED TO, 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, CONSEQUENTIAL DAMAGES (INCLUDING,
' BUT NOT LIMITED TO, OF SUBSTITUTE GOODS OR SERVICES; LOSS
' OF USE, DATA, PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
' AND ON ANY THEORY OF LIABILITY, IN CONTRACT, STRICT
' LIABILITY, TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
' ANY WAY OUT OF THE USE OF THIS SOFTWARE, IF ADVISED OF THE
' POSSIBILITY OF SUCH DAMAGE.
'
'''
__credits__ = ['Nuno Marques <nuno.marques@dronesolution.io>']
__license__ = 'BSD-3-Clause'
__version__ = '0.1.0'
__maintainer__ = 'Nuno Marques'
__email__ = 'nuno.marques@dronesolution.io'
__status__ = 'Development'
input_dir = sys.argv[1]
output_dir = sys.argv[2]
def main():
print("----------------------- \033[1mmicroRTPS bridge uORB to ROS messages\033[0m -----------------------")
print("-------------------------------------------------------------------------------------------------------")
if not os.path.exists(os.path.abspath(output_dir)):
os.mkdir(os.path.abspath(output_dir))
else:
ros_msg_dir = os.path.abspath(output_dir)
msg_files = os.listdir(ros_msg_dir)
for msg in msg_files:
if msg.endswith(".msg"):
os.remove(os.path.join(ros_msg_dir, msg))
msg_list = list()
for filename in os.listdir(input_dir):
if '.msg' in filename:
msg_list.append(filename.rstrip('.msg'))
input_file = input_dir + filename
output_file = output_dir + \
filename.partition(".")[0].title().replace('_', '') + ".msg"
copyfile(input_file, output_file)
for filename in os.listdir(output_dir):
if '.msg' in filename:
input_file = output_dir + filename
fileUpdated = False
with open(input_file, 'r') as f:
lines = f.readlines()
newlines = []
alias_msgs = []
alias_msg_files = []
for line in lines:
for msg_type in msg_list:
if ('px4/' + msg_type + ' ') in line:
fileUpdated = True
line = line.replace(('px4/' + msg_type),
msg_type.partition(".")[0].title().replace('_', ''))
if re.findall('^' + msg_type + '[\s\[]', line.partition('#')[0]):
fileUpdated = True
line = line.replace(msg_type,
msg_type.partition(".")[0].title().replace('_', ''))
if '# TOPICS' in line:
fileUpdated = True
alias_msgs += line.split()
alias_msgs.remove('#')
alias_msgs.remove('TOPICS')
line = line.replace(line, '')
newlines.append(line)
for msg_file in alias_msgs:
with open(output_dir + msg_file.partition(".")[0].title().replace('_', '') + ".msg", 'w+') as f:
for line in newlines:
f.write(line)
if fileUpdated:
with open(input_file, 'w+') as f:
for line in newlines:
f.write(line)
print("--\t\t- Generated {} ROS message files in '{}'".format(len(os.listdir(output_dir)), os.path.abspath(output_dir)))
print("-------------------------------------------------------------------------------------------------------")
if __name__ == '__main__':
main()
-160
View File
@@ -1,160 +0,0 @@
#!/usr/bin/env python3
"""
Script to read an yaml file containing the microRTPS topics and update the naming convention to PascalCase
"""
import argparse
import errno
from pathlib import Path
import os
import six
import yaml
__author__ = 'PX4 Development Team'
__copyright__ = \
'''
'
' Copyright (c) 2018-2021 PX4 Development Team. All rights reserved.
'
' Redistribution and use in source and binary forms, or without
' modification, permitted provided that the following conditions
' are met:
'
' 1. Redistributions of source code must retain the above copyright
' notice, list of conditions and the following disclaimer.
' 2. Redistributions in binary form must reproduce the above copyright
' notice, list of conditions and the following disclaimer in
' the documentation and/or other materials provided with the
' distribution.
' 3. Neither the name PX4 nor the names of its contributors may be
' used to endorse or promote products derived from self 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, NOT
' LIMITED TO, 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, CONSEQUENTIAL DAMAGES (INCLUDING,
' BUT NOT LIMITED TO, OF SUBSTITUTE GOODS OR SERVICES; LOSS
' OF USE, DATA, PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
' AND ON ANY THEORY OF LIABILITY, IN CONTRACT, STRICT
' LIABILITY, TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
' ANY WAY OUT OF THE USE OF THIS SOFTWARE, IF ADVISED OF THE
' POSSIBILITY OF SUCH DAMAGE.
'
'''
__credits__ = ['Nuno Marques <nuno.marques@dronesolution.io>']
__license__ = 'BSD-3-Clause'
__version__ = '0.1.0'
__maintainer__ = 'Nuno Marques'
__email__ = 'nuno.marques@dronesolution.io'
__status__ = 'Development'
verbose = False
class IndenterDumper(yaml.Dumper):
""" Custom dumper for yaml files that apply the correct indentation """
def increase_indent(self, flow=False, indentless=False):
return super(IndenterDumper, self).increase_indent(flow, False)
def load_yaml_file(file):
"""
Open yaml file and parse the data into a list of dict
:param file: the yaml file to load
:returns: the list of dictionaries that represent the topics to send and receive
:raises IOError: raises and error when the file is not found
"""
try:
with open(file, 'r') as f:
if verbose:
print(("--\t\t- '%s' file loaded" % file))
return yaml.safe_load(f)
except OSError as e:
if e.errno == errno.ENOENT:
raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), file)
else:
raise
def update_dict(list):
"""
Update the message naming on the dictionary to fit the PascalCase convention
:param file: the list of dicts to be updated
"""
if verbose:
num_of_msgs = 0
for i, dictionary in enumerate(list["rtps"]):
list["rtps"][i] = {k: v.title().replace('_', '') if isinstance(
v, six.string_types) else v for k, v in six.iteritems(dictionary)}
if verbose:
num_of_msgs += 1
if verbose:
print(("--\t\t- %d ROS message file names updated" % num_of_msgs))
def update_yaml_file(list, in_file, out_file):
"""
Open the yaml file to dump the new list of dict toself.
:param list: the list of updated dicts
:param file: the yaml file to load and write the new data
:raises IOError: raises and error when the file is not found
"""
try:
with open(out_file, 'w') as f:
f.write("# AUTOGENERATED-FILE! DO NOT MODIFY IT DIRECTLY.\n#"
" Edit instead the same file under PX4-Autopilot/msg/tools and"
" use the \n# PX4-Autopilot/msg/tools/uorb_to_ros_urtps_topics.py"
" to regenerate this file.\n")
yaml.dump(list, f, Dumper=IndenterDumper, default_flow_style=False)
if verbose:
if in_file == out_file:
print(("--\t\t- '%s' updated" % in_file))
else:
print(("--\t\t- '%s' created" % out_file))
except OSError as err:
if err.errno == errno.ENOENT:
raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), out_file)
raise
def main():
parser = argparse.ArgumentParser(
description='Read an yaml file containing the microRTPS topics and update the naming convention to PascalCase')
optional = parser._action_groups.pop()
required = parser.add_argument_group('Required')
required.add_argument("-i", "--input-file", dest="input_file",
help="Yaml file to read", metavar="INFILE")
optional.add_argument("-o", "--output-file", dest="output_file",
help="Yaml file to dump. If not set, it is the same as the input",
metavar="OUTFILE", default="")
optional.add_argument("-q", "--quiet", action="store_false", dest="verbose",
default=True, help="Don't print status messages to stdout")
args = parser.parse_args()
global verbose
verbose = args.verbose
in_file = Path(args.input_file)
out_file = Path(args.output_file) if (
Path(args.output_file) != in_file and Path(args.output_file) != "") else in_file
if verbose:
print("---------------------- \033[1mmicroRTPS bridge yaml PX4 to ROS topics\033[0m ----------------------")
print("-------------------------------------------------------------------------------------------------------")
list = load_yaml_file(in_file)
update_dict(list)
update_yaml_file(list, in_file, out_file)
if verbose:
print("-------------------------------------------------------------------------------------------------------")
if __name__ == "__main__":
main()
-91
View File
@@ -1,91 +0,0 @@
#####
#
# This file maps all the topics that are to be used on the microRTPS bridge.
# When one wants to add a new topic to the bridge, it should add it to this file
# and mark it to be sent or received from the link.
# For alias/multi-topic messages (i.e. the ones found on the '#TOPICS' of the
# uORB messages), these can be also added, requiring an extra entry ('base') to
# define the base message.
#
# IMPORTANT NOTICE: The IDs of the messages sent on the bridge get generated
# according to the order of the messages in this file. To keep consistency and
# backwards compatibility, it is recommended that any new message that one wants
# to be streamed in the bridge gets added to the end of the list. Any changes
# in the middle of the list (additions, removals, replacements) will change also
# the current message IDS, which might result with incompatibilities with
# previous PX4 versions (where the list with this format got introduced or
# subsisted).
#
# Any updates to this file should be mirrored in both sides of the bridge (i.e.,
# PX4 and px4_ros_com), when using it with ROS2. That can be easily done using
# the 'msg/tools/uorb_to_ros_urtps_topics.py' script to regenerate this same
# file under 'px4_ros_com/templates/''. The same is not applicable/required if
# using this bridge with "raw" RTPS/DDS applications, since the microRTPS agent
# to be used and stored in 'build/<px4_target>/src/modules/micrortps_bridge/''
# gets generated using this same list.
#
#####
rtps:
# topic ID 1
- msg: debug_array
receive: true
# topic ID 2
- msg: debug_key_value
receive: true
# topic ID 3
- msg: debug_value
receive: true
# ...
- msg: debug_vect
receive: true
- msg: offboard_control_mode
receive: true
- msg: optical_flow
receive: true
- msg: position_setpoint
receive: true
- msg: position_setpoint_triplet
receive: true
- msg: telemetry_status
receive: true
- msg: timesync
receive: true
send: true
- msg: trajectory_waypoint
send: true
- msg: vehicle_command
receive: true
- msg: vehicle_control_mode
send: true
- msg: vehicle_local_position_setpoint
receive: true
- msg: trajectory_setpoint # multi-topic / alias of vehicle_local_position_setpoint
base: vehicle_local_position_setpoint
receive: true
- msg: vehicle_odometry
send: true
- msg: vehicle_mocap_odometry # multi-topic / alias of vehicle_odometry
base: vehicle_odometry
receive: true
- msg: vehicle_visual_odometry # multi-topic / alias of vehicle_odometry
base: vehicle_odometry
receive: true
- msg: vehicle_status
send: true
- msg: vehicle_trajectory_waypoint
receive: true
- msg: vehicle_trajectory_waypoint_desired # multi-topic / alias of vehicle_trajectory_waypoint
base: vehicle_trajectory_waypoint
send: true
- msg: collision_constraints
send: true
- msg: onboard_computer_status
receive: true
- msg: trajectory_bezier
receive: true
- msg: vehicle_trajectory_bezier
receive: true
- msg: timesync_status
send: true
- msg: sensor_combined
send: true