mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-18 01:03:33 +08:00
uORB generation move to msg/
This commit is contained in:
Submodule
+1
Submodule msg/tools/gencpp added at 26a86f04bc
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
|
||||
#
|
||||
# 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, os, argparse, shutil
|
||||
import px_generate_uorb_topic_files
|
||||
import subprocess, glob
|
||||
import errno
|
||||
|
||||
def get_absolute_path(arg_parse_dir):
|
||||
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
if isinstance(arg_parse_dir, list):
|
||||
dir = arg_parse_dir[0]
|
||||
else:
|
||||
dir = arg_parse_dir
|
||||
|
||||
if dir[0] != '/':
|
||||
dir = root_path + "/" + dir
|
||||
|
||||
return dir
|
||||
|
||||
default_client_out = get_absolute_path("src/modules/micrortps_bridge/micrortps_client")
|
||||
default_agent_out = get_absolute_path("src/modules/micrortps_bridge/micrortps_agent")
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-s", "--send", dest='send', metavar='*.msg', type=str, nargs='+', help="Topics to be sended")
|
||||
parser.add_argument("-r", "--receive", dest='receive', metavar='*.msg', type=str, nargs='+', help="Topics to be received")
|
||||
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("-t", "--topic-msg-dir", dest='msgdir', type=str, nargs=1, help="Topics message dir, by default msg/", default="msg")
|
||||
parser.add_argument("-o", "--agent-outdir", dest='agentdir', type=str, nargs=1, help="Agent output dir, by default src/modules/micrortps_bridge/micrortps_agent", default=default_agent_out)
|
||||
parser.add_argument("-u", "--client-outdir", dest='clientdir', type=str, nargs=1, help="Client output dir, by default 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("--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()
|
||||
msg_folder = get_absolute_path(args.msgdir)
|
||||
msg_files_send = []
|
||||
if args.send:
|
||||
msg_files_send = [get_absolute_path(msg) for msg in args.send]
|
||||
else:
|
||||
msg_files_send = []
|
||||
if args.receive:
|
||||
msg_files_receive = [get_absolute_path(msg) for msg in args.receive]
|
||||
else:
|
||||
msg_files_receive = []
|
||||
agent = args.agent
|
||||
client = args.client
|
||||
del_tree = args.del_tree
|
||||
px_generate_uorb_topic_files.append_to_include_path({msg_folder}, px_generate_uorb_topic_files.INCL_DEFAULT)
|
||||
agent_out_dir = get_absolute_path(args.agentdir)
|
||||
client_out_dir = get_absolute_path(args.clientdir)
|
||||
|
||||
if args.fastrtpsgen is None or args.fastrtpsgen == "":
|
||||
# Assume fastrtpsgen is in PATH
|
||||
fastrtpsgen_path = "fastrtpsgen"
|
||||
else:
|
||||
# Path to fastrtpsgen is explicitly specified
|
||||
fastrtpsgen_path = get_absolute_path(args.fastrtpsgen) + "/fastrtpsgen"
|
||||
|
||||
# If nothing specified it's generated both
|
||||
if agent == False and client == False:
|
||||
agent = True
|
||||
client = True
|
||||
|
||||
if del_tree:
|
||||
if agent:
|
||||
_continue = str(raw_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(raw_input("\nFiles in " + client_out_dir + " will be erased, continue?[Y/n]\n"))
|
||||
if _continue == "N" or _continue == "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(agent_out_dir + "/idl"):
|
||||
shutil.rmtree(agent_out_dir + "/idl")
|
||||
|
||||
uorb_templates_dir = msg_folder + "/templates/uorb"
|
||||
urtps_templates_dir = msg_folder + "/templates/urtps"
|
||||
|
||||
uRTPS_CLIENT_TEMPL_FILE = 'microRTPS_client.cpp.template'
|
||||
uRTPS_AGENT_TOPICS_H_TEMPL_FILE = 'RtpsTopics.h.template'
|
||||
uRTPS_AGENT_TOPICS_SRC_TEMPL_FILE = 'RtpsTopics.cpp.template'
|
||||
uRTPS_AGENT_TEMPL_FILE = 'microRTPS_agent.cpp.template'
|
||||
uRTPS_AGENT_CMAKELIST_TEMPL_FILE = 'microRTPS_agent_CMakeLists.txt.template'
|
||||
uRTPS_PUBLISHER_SRC_TEMPL_FILE = 'Publisher.cpp.template'
|
||||
uRTPS_PUBLISHER_H_TEMPL_FILE = 'Publisher.h.template'
|
||||
uRTPS_SUBSCRIBER_SRC_TEMPL_FILE = 'Subscriber.cpp.template'
|
||||
uRTPS_SUBSCRIBER_H_TEMPL_FILE = 'Subscriber.h.template'
|
||||
|
||||
def generate_agent(out_dir):
|
||||
|
||||
if msg_files_send:
|
||||
for msg_file in msg_files_send:
|
||||
px_generate_uorb_topic_files.generate_idl_file(msg_file, out_dir + "/idl", urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT)
|
||||
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_PUBLISHER_SRC_TEMPL_FILE)
|
||||
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_PUBLISHER_H_TEMPL_FILE)
|
||||
|
||||
if msg_files_receive:
|
||||
for msg_file in msg_files_receive:
|
||||
px_generate_uorb_topic_files.generate_idl_file(msg_file, out_dir + "/idl", urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT)
|
||||
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_SUBSCRIBER_SRC_TEMPL_FILE)
|
||||
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_SUBSCRIBER_H_TEMPL_FILE)
|
||||
|
||||
px_generate_uorb_topic_files.generate_uRTPS_general(msg_files_send, msg_files_receive, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_AGENT_TEMPL_FILE)
|
||||
px_generate_uorb_topic_files.generate_uRTPS_general(msg_files_send, msg_files_receive, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_AGENT_TOPICS_H_TEMPL_FILE)
|
||||
px_generate_uorb_topic_files.generate_uRTPS_general(msg_files_send, msg_files_receive, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_AGENT_TOPICS_SRC_TEMPL_FILE)
|
||||
px_generate_uorb_topic_files.generate_uRTPS_general(msg_files_send, msg_files_receive, out_dir, urtps_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_AGENT_CMAKELIST_TEMPL_FILE)
|
||||
|
||||
# Final steps to install agent
|
||||
mkdir_p(agent_out_dir + "/fastrtpsgen")
|
||||
os.chdir(agent_out_dir + "/fastrtpsgen")
|
||||
for idl_file in glob.glob(agent_out_dir + "/idl/*.idl"):
|
||||
ret = subprocess.call(fastrtpsgen_path + " -d " + agent_out_dir + "/fastrtpsgen -example x64Linux2.6gcc " + idl_file, shell=True)
|
||||
if ret:
|
||||
raise Exception("fastrtpsgen not found. Specify the location of fastrtpsgen with the -f flag")
|
||||
rm_wildcard(agent_out_dir + "/fastrtpsgen/*PubSubMain*")
|
||||
rm_wildcard(agent_out_dir + "/fastrtpsgen/makefile*")
|
||||
rm_wildcard(agent_out_dir + "/fastrtpsgen/*Publisher*")
|
||||
rm_wildcard(agent_out_dir + "/fastrtpsgen/*Subscriber*")
|
||||
for f in glob.glob(agent_out_dir + "/fastrtpsgen/*.cxx"):
|
||||
os.rename(f, f.replace(".cxx", ".cpp"))
|
||||
cp_wildcard(agent_out_dir + "/fastrtpsgen/*", agent_out_dir)
|
||||
if os.path.isdir(agent_out_dir + "/fastrtpsgen"):
|
||||
shutil.rmtree(agent_out_dir + "/fastrtpsgen")
|
||||
cp_wildcard(urtps_templates_dir + "/microRTPS_transport.*", agent_out_dir)
|
||||
os.rename(agent_out_dir + "/microRTPS_agent_CMakeLists.txt", agent_out_dir + "/CMakeLists.txt")
|
||||
mkdir_p(agent_out_dir + "/build")
|
||||
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):
|
||||
|
||||
# Rename work in the default path
|
||||
if default_client_out != out_dir:
|
||||
def_file = default_client_out + "/microRTPS_client.cpp"
|
||||
if os.path.isfile(def_file):
|
||||
os.rename(def_file, def_file.replace(".cpp", ".cpp_"))
|
||||
def_file = default_client_out + "/microRTPS_transport.cpp"
|
||||
if os.path.isfile(def_file):
|
||||
os.rename(def_file, def_file.replace(".cpp", ".cpp_"))
|
||||
def_file = 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(msg_files_send, msg_files_receive, out_dir, uorb_templates_dir,
|
||||
px_generate_uorb_topic_files.INCL_DEFAULT, uRTPS_CLIENT_TEMPL_FILE)
|
||||
|
||||
# Final steps to install client
|
||||
cp_wildcard(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)
|
||||
Submodule
+1
Submodule msg/tools/genmsg added at 72f0383f0e
Executable
+440
@@ -0,0 +1,440 @@
|
||||
#!/usr/bin/env python
|
||||
#############################################################################
|
||||
#
|
||||
# Copyright (C) 2013-2016 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 PX4 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.
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
"""
|
||||
px_generate_uorb_topics.py
|
||||
Generates c/cpp header/source files for uorb topics from .msg (ROS syntax)
|
||||
message files
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import shutil
|
||||
import filecmp
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
px4_tools_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.append(px4_tools_dir + "/genmsg/src")
|
||||
sys.path.append(px4_tools_dir + "/gencpp/src")
|
||||
px4_msg_dir = os.path.join(px4_tools_dir, "..")
|
||||
|
||||
try:
|
||||
import em
|
||||
import genmsg.template_tools
|
||||
except ImportError as e:
|
||||
print("python import error: ", e)
|
||||
print('''
|
||||
Required python packages not installed.
|
||||
|
||||
On a Debian/Ubuntu system please run:
|
||||
|
||||
sudo apt-get install python-empy
|
||||
sudo pip install catkin_pkg
|
||||
|
||||
On MacOS please run:
|
||||
sudo pip install empy catkin_pkg
|
||||
|
||||
On Windows please run:
|
||||
easy_install empy catkin_pkg
|
||||
''')
|
||||
exit(1)
|
||||
|
||||
__author__ = "Sergey Belash, Thomas Gubler, Beat Kueng"
|
||||
__copyright__ = "Copyright (C) 2013-2016 PX4 Development Team."
|
||||
__license__ = "BSD"
|
||||
__email__ = "thomasgubler@gmail.com"
|
||||
|
||||
|
||||
TEMPLATE_FILE = ['msg.h.template', 'msg.cpp.template']
|
||||
TOPICS_LIST_TEMPLATE_FILE = 'uORBTopics.cpp.template'
|
||||
OUTPUT_FILE_EXT = ['.h', '.cpp']
|
||||
INCL_DEFAULT = ['std_msgs:./msg/std_msgs']
|
||||
PACKAGE = 'px4'
|
||||
TOPICS_TOKEN = '# TOPICS '
|
||||
IDL_TEMPLATE_FILE = 'msg.idl.template'
|
||||
|
||||
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, 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)
|
||||
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 = {
|
||||
"file_name_in": filename,
|
||||
"md5sum": md5sum,
|
||||
"search_path": search_path,
|
||||
"msg_context": msg_context,
|
||||
"spec": spec,
|
||||
"topics": topics
|
||||
}
|
||||
|
||||
# Make sure output directory exists:
|
||||
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])
|
||||
|
||||
return generate_by_template(output_file, template_file, em_globals)
|
||||
|
||||
def generate_idl_file(filename_msg, outputdir, templatedir, includepath):
|
||||
"""
|
||||
Generates an .idl from .msg file
|
||||
"""
|
||||
em_globals = get_em_globals(filename_msg, includepath, 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)
|
||||
output_file = os.path.join(outputdir, IDL_TEMPLATE_FILE.replace("msg.idl.template", str(spec_short_name + "_.idl")))
|
||||
|
||||
return generate_by_template(output_file, template_file, em_globals)
|
||||
|
||||
def generate_uRTPS_general(filename_send_msgs, filename_received_msgs,
|
||||
outputdir, templatedir, includepath, template_name):
|
||||
"""
|
||||
Generates source file by msg content
|
||||
"""
|
||||
em_globals_list = []
|
||||
if filename_send_msgs:
|
||||
em_globals_list.extend([get_em_globals(f, includepath, MsgScope.SEND) for f in filename_send_msgs])
|
||||
|
||||
if filename_received_msgs:
|
||||
em_globals_list.extend([get_em_globals(f, includepath, MsgScope.RECEIVE) for f in filename_received_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(".template", ""))
|
||||
|
||||
return generate_by_template(output_file, template_file, merged_em_globals)
|
||||
|
||||
def generate_topic_file(filename_msg, outputdir, templatedir, includepath, template_name):
|
||||
"""
|
||||
Generates an .idl from .msg file
|
||||
"""
|
||||
em_globals = get_em_globals(filename_msg, includepath, 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, template_name)
|
||||
output_file = os.path.join(outputdir, spec_short_name + "_" + template_name.replace(".template", ""))
|
||||
|
||||
return generate_by_template(output_file, template_file, em_globals)
|
||||
|
||||
def get_em_globals(filename_msg, includepath, 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)
|
||||
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,
|
||||
"scope": scope
|
||||
}
|
||||
|
||||
return em_globals
|
||||
|
||||
def merge_em_globals_list(em_globals_list):
|
||||
"""
|
||||
Merges a list of em_globals to a single dictionary where each attribute is a list
|
||||
"""
|
||||
if len(em_globals_list) < 1:
|
||||
return {}
|
||||
|
||||
merged_em_globals = {}
|
||||
for name in em_globals_list[0]:
|
||||
merged_em_globals[name] = [em_globals[name] for em_globals in em_globals_list]
|
||||
|
||||
return merged_em_globals
|
||||
|
||||
|
||||
|
||||
def generate_by_template(output_file, template_file, em_globals):
|
||||
"""
|
||||
Invokes empy intepreter to geneate output_file by the
|
||||
given template_file and predefined em_globals dict
|
||||
"""
|
||||
# check if folder exists:
|
||||
folder_name = os.path.dirname(output_file)
|
||||
if not os.path.exists(folder_name):
|
||||
os.makedirs(folder_name)
|
||||
|
||||
ofile = open(output_file, 'w')
|
||||
# todo, reuse interpreter
|
||||
interpreter = em.Interpreter(output=ofile, globals=em_globals, options={em.RAW_OPT:True,em.BUFFERED_OPT:True})
|
||||
if not os.path.isfile(template_file):
|
||||
ofile.close()
|
||||
os.remove(output_file)
|
||||
raise RuntimeError("Template file %s not found" % (template_file))
|
||||
interpreter.file(open(template_file)) #todo try
|
||||
interpreter.shutdown()
|
||||
ofile.close()
|
||||
return True
|
||||
|
||||
|
||||
def convert_dir(format_idx, inputdir, outputdir, 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, 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, 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, templatedir)
|
||||
if generate_idx == 1:
|
||||
generate_topics_list_file(inputdir, temporarydir, templatedir)
|
||||
# Copy changed headers from temporary dir to output dir
|
||||
copy_changed(temporarydir, outputdir, prefix, quiet)
|
||||
|
||||
def generate_topics_list_file(msgdir, outputdir, 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, TOPICS_LIST_TEMPLATE_FILE)
|
||||
tl_out_file = os.path.join(outputdir, TOPICS_LIST_TEMPLATE_FILE.replace(".template", ""))
|
||||
generate_by_template(tl_out_file, tl_template_file, tl_globals)
|
||||
|
||||
def generate_topics_list_file_from_files(files, outputdir, 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, TOPICS_LIST_TEMPLATE_FILE)
|
||||
tl_out_file = os.path.join(outputdir, TOPICS_LIST_TEMPLATE_FILE.replace(".template", ""))
|
||||
generate_by_template(tl_out_file, tl_template_file, tl_globals)
|
||||
|
||||
def append_to_include_path(path_to_append, curr_include):
|
||||
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.add_argument('-f', dest='file',
|
||||
help="files to convert (use only without -d)",
|
||||
nargs="+")
|
||||
parser.add_argument('-i', dest="include_paths",
|
||||
help='Additional Include Paths', nargs="*",
|
||||
default=None)
|
||||
parser.add_argument('-e', dest='templatedir',
|
||||
help='directory with template files',)
|
||||
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')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.include_paths:
|
||||
append_to_include_path(args.include_paths, INCL_DEFAULT)
|
||||
|
||||
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.templatedir, INCL_DEFAULT)
|
||||
if generate_idx == 1:
|
||||
generate_topics_list_file_from_files(args.file, args.outputdir, 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.templatedir,
|
||||
args.temporarydir,
|
||||
args.prefix,
|
||||
args.quiet)
|
||||
@@ -0,0 +1,201 @@
|
||||
|
||||
# helper methods & common code for the uorb message templates msg.{cpp,h}.template
|
||||
|
||||
# Another positive effect of having the code here, is that this file will get
|
||||
# precompiled and thus message generation will be much faster
|
||||
|
||||
|
||||
import genmsg.msgs
|
||||
import gencpp
|
||||
|
||||
type_map = {
|
||||
'int8': 'int8_t',
|
||||
'int16': 'int16_t',
|
||||
'int32': 'int32_t',
|
||||
'int64': 'int64_t',
|
||||
'uint8': 'uint8_t',
|
||||
'uint16': 'uint16_t',
|
||||
'uint32': 'uint32_t',
|
||||
'uint64': 'uint64_t',
|
||||
'float32': 'float',
|
||||
'float64': 'double',
|
||||
'bool': 'bool',
|
||||
'char': 'char',
|
||||
}
|
||||
|
||||
type_serialize_map = {
|
||||
'int8': 'SignedChar',
|
||||
'int16': 'Short',
|
||||
'int32': 'Int',
|
||||
'int64': 'Long',
|
||||
'uint8': 'UnsignedChar',
|
||||
'uint16': 'UnsignedShort',
|
||||
'uint32': 'UnsignedInt',
|
||||
'uint64': 'UnsignedLong',
|
||||
'float32': 'Float',
|
||||
'float64': 'Double',
|
||||
'bool': 'Bool',
|
||||
'char': 'Char',
|
||||
}
|
||||
|
||||
type_idl_map = {
|
||||
'int8': 'octet',
|
||||
'int16': 'short',
|
||||
'int32': 'long',
|
||||
'int64': 'long long',
|
||||
'uint8': 'octet',
|
||||
'uint16': 'unsigned short',
|
||||
'uint32': 'unsigned long',
|
||||
'uint64': 'unsigned long long',
|
||||
'float32': 'float',
|
||||
'float64': 'double',
|
||||
'bool': 'boolean',
|
||||
'char': 'char',
|
||||
}
|
||||
|
||||
msgtype_size_map = {
|
||||
'int8': 1,
|
||||
'int16': 2,
|
||||
'int32': 4,
|
||||
'int64': 8,
|
||||
'uint8': 1,
|
||||
'uint16': 2,
|
||||
'uint32': 4,
|
||||
'uint64': 8,
|
||||
'float32': 4,
|
||||
'float64': 8,
|
||||
'bool': 1,
|
||||
'char': 1,
|
||||
}
|
||||
|
||||
|
||||
def bare_name(msg_type):
|
||||
"""
|
||||
Get bare_name from <dir>/<bare_name>[x] format
|
||||
"""
|
||||
bare = msg_type
|
||||
if '/' in msg_type:
|
||||
# removing prefix
|
||||
bare = (msg_type.split('/'))[1]
|
||||
# removing suffix
|
||||
return bare.split('[')[0]
|
||||
|
||||
|
||||
def sizeof_field_type(field):
|
||||
"""
|
||||
Get size of a field, used for sorting
|
||||
"""
|
||||
bare_name_str = bare_name(field.type)
|
||||
if bare_name_str in msgtype_size_map:
|
||||
return msgtype_size_map[bare_name_str]
|
||||
return 0 # this is for non-builtin types: sort them at the end
|
||||
|
||||
def get_children_fields(base_type, search_path):
|
||||
(package, name) = genmsg.names.package_resource_name(base_type)
|
||||
tmp_msg_context = genmsg.msg_loader.MsgContext.create_default()
|
||||
spec_temp = genmsg.msg_loader.load_msg_by_type(tmp_msg_context, '%s/%s' %(package, name), search_path)
|
||||
sorted_fields = sorted(spec_temp.parsed_fields(), key=sizeof_field_type, reverse=True)
|
||||
return sorted_fields
|
||||
|
||||
def add_padding_bytes(fields, search_path):
|
||||
"""
|
||||
Add padding fields before the embedded types, at the end and calculate the
|
||||
struct size
|
||||
returns a tuple with the struct size and padding at the end
|
||||
"""
|
||||
struct_size = 8 # account for the timestamp
|
||||
align_to = 8 # this is always 8, because of the 64bit timestamp
|
||||
i = 0
|
||||
padding_idx = 0
|
||||
while i < len(fields):
|
||||
field = fields[i]
|
||||
if not field.is_header:
|
||||
a_pos = field.type.find('[')
|
||||
array_size = 1
|
||||
if field.is_array:
|
||||
array_size = field.array_len
|
||||
if field.is_builtin:
|
||||
field.sizeof_field_type = sizeof_field_type(field)
|
||||
else:
|
||||
# embedded type: may need to add padding
|
||||
num_padding_bytes = align_to - (struct_size % align_to)
|
||||
if num_padding_bytes != align_to:
|
||||
padding_field = genmsg.Field('_padding'+str(padding_idx),
|
||||
'uint8['+str(num_padding_bytes)+']')
|
||||
padding_idx += 1
|
||||
padding_field.sizeof_field_type = 1
|
||||
struct_size += num_padding_bytes
|
||||
fields.insert(i, padding_field)
|
||||
i += 1
|
||||
children_fields = get_children_fields(field.base_type, search_path)
|
||||
field.sizeof_field_type, unused = add_padding_bytes(children_fields,
|
||||
search_path)
|
||||
struct_size += field.sizeof_field_type * array_size
|
||||
i += 1
|
||||
|
||||
# add padding at the end (necessary for embedded types)
|
||||
num_padding_bytes = align_to - (struct_size % align_to)
|
||||
if num_padding_bytes == align_to:
|
||||
num_padding_bytes = 0
|
||||
else:
|
||||
padding_field = genmsg.Field('_padding'+str(padding_idx),
|
||||
'uint8['+str(num_padding_bytes)+']')
|
||||
padding_idx += 1
|
||||
padding_field.sizeof_field_type = 1
|
||||
struct_size += num_padding_bytes
|
||||
fields.append(padding_field)
|
||||
return (struct_size, num_padding_bytes)
|
||||
|
||||
|
||||
def convert_type(spec_type):
|
||||
"""
|
||||
Convert from msg type to C type
|
||||
"""
|
||||
bare_type = spec_type
|
||||
if '/' in spec_type:
|
||||
# removing prefix
|
||||
bare_type = (spec_type.split('/'))[1]
|
||||
|
||||
msg_type, is_array, array_length = genmsg.msgs.parse_type(bare_type)
|
||||
c_type = msg_type
|
||||
if msg_type in type_map:
|
||||
c_type = type_map[msg_type]
|
||||
if is_array:
|
||||
return c_type + "[" + str(array_length) + "]"
|
||||
return c_type
|
||||
|
||||
|
||||
def print_field_def(field):
|
||||
"""
|
||||
Print the C type from a field
|
||||
"""
|
||||
type_name = field.type
|
||||
# detect embedded types
|
||||
sl_pos = type_name.find('/')
|
||||
type_appendix = ''
|
||||
type_prefix = ''
|
||||
if (sl_pos >= 0):
|
||||
type_name = type_name[sl_pos + 1:]
|
||||
type_prefix = 'struct '
|
||||
type_appendix = '_s'
|
||||
|
||||
# detect arrays
|
||||
a_pos = type_name.find('[')
|
||||
array_size = ''
|
||||
if (a_pos >= 0):
|
||||
# field is array
|
||||
array_size = type_name[a_pos:]
|
||||
type_name = type_name[:a_pos]
|
||||
|
||||
if type_name in type_map:
|
||||
# need to add _t: int8 --> int8_t
|
||||
type_px4 = type_map[type_name]
|
||||
else:
|
||||
type_px4 = type_name
|
||||
|
||||
comment = ''
|
||||
if field.name.startswith('_padding'):
|
||||
comment = ' // required for logger'
|
||||
|
||||
print('\t%s%s%s %s%s;%s'%(type_prefix, type_px4, type_appendix, field.name,
|
||||
array_size, comment))
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
# Message identification constants
|
||||
|
||||
|
||||
msg_id_map = {
|
||||
'actuator_armed': 1,
|
||||
'actuator_controls': 2,
|
||||
'actuator_direct': 3,
|
||||
'actuator_outputs': 4,
|
||||
'adc_report': 5,
|
||||
'airspeed': 6,
|
||||
'att_pos_mocap': 7,
|
||||
'battery_status': 8,
|
||||
'camera_capture': 9,
|
||||
'camera_trigger': 10,
|
||||
'collision_report': 11,
|
||||
'commander_state': 12,
|
||||
'control_state': 13,
|
||||
'cpuload': 14,
|
||||
'debug_key_value': 15,
|
||||
'differential_pressure': 16,
|
||||
'distance_sensor': 17,
|
||||
'ekf2_innovations': 18,
|
||||
|
||||
'ekf2_timestamps': 20,
|
||||
'esc_report': 21,
|
||||
'esc_status': 22,
|
||||
'estimator_status': 23,
|
||||
'fence': 24,
|
||||
'fence_vertex': 25,
|
||||
'filtered_bottom_flow': 26,
|
||||
'follow_target': 27,
|
||||
'fw_pos_ctrl_status': 28,
|
||||
'geofence_result': 29,
|
||||
'gps_dump': 30,
|
||||
'gps_inject_data': 31,
|
||||
'hil_sensor': 32,
|
||||
'home_position': 33,
|
||||
'input_rc': 34,
|
||||
'led_control': 35,
|
||||
'log_message': 36,
|
||||
'manual_control_setpoint': 37,
|
||||
'mavlink_log': 38,
|
||||
'mc_att_ctrl_status': 39,
|
||||
'mission': 40,
|
||||
'mission_result': 41,
|
||||
'mount_orientation': 42,
|
||||
'multirotor_motor_limits': 43,
|
||||
'offboard_control_mode': 44,
|
||||
'optical_flow': 45,
|
||||
'output_pwm': 46,
|
||||
'parameter_update': 47,
|
||||
'position_setpoint': 48,
|
||||
'position_setpoint_triplet': 49,
|
||||
'pwm_input': 50,
|
||||
'qshell_req': 51,
|
||||
'rc_channels': 52,
|
||||
'rc_parameter_map': 53,
|
||||
'safety': 54,
|
||||
'satellite_info': 55,
|
||||
'sensor_accel': 56,
|
||||
'sensor_baro': 57,
|
||||
'sensor_combined': 58,
|
||||
'sensor_correction': 59,
|
||||
'sensor_gyro': 60,
|
||||
'sensor_mag': 61,
|
||||
'sensor_preflight': 62,
|
||||
'sensor_selection': 63,
|
||||
'servorail_status': 64,
|
||||
'subsystem_info': 65,
|
||||
'system_power': 66,
|
||||
'task_stack_info': 67,
|
||||
'tecs_status': 68,
|
||||
'telemetry_status': 69,
|
||||
'test_motor': 70,
|
||||
'time_offset': 71,
|
||||
'transponder_report': 72,
|
||||
'uavcan_parameter_request': 73,
|
||||
'uavcan_parameter_value': 74,
|
||||
'ulog_stream_ack': 75,
|
||||
'ulog_stream': 76,
|
||||
'vehicle_attitude': 77,
|
||||
'vehicle_attitude_setpoint': 78,
|
||||
'vehicle_command_ack': 79,
|
||||
'vehicle_command': 80,
|
||||
'vehicle_control_mode': 81,
|
||||
'vehicle_force_setpoint': 82,
|
||||
'vehicle_global_position': 83,
|
||||
'vehicle_global_velocity_setpoint': 84,
|
||||
'vehicle_gps_position': 85,
|
||||
'vehicle_land_detected': 86,
|
||||
'vehicle_local_position': 87,
|
||||
'vehicle_local_position_setpoint': 88,
|
||||
'vehicle_rates_setpoint': 89,
|
||||
'vehicle_roi': 90,
|
||||
'vehicle_status_flags': 91,
|
||||
'vehicle_status': 92,
|
||||
'vtol_vehicle_status': 93,
|
||||
'wind_estimate': 94,
|
||||
}
|
||||
|
||||
def message_id(message):
|
||||
"""
|
||||
Get id of a message
|
||||
"""
|
||||
if message in msg_id_map:
|
||||
return msg_id_map[message]
|
||||
return 0
|
||||
Reference in New Issue
Block a user