You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

405 lines
19 KiB

#!/usr/bin/env python
################################################################################
#
# Copyright 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
# 2018 PX4 Pro 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
import px_generate_uorb_topic_helper
from uorb_rtps_classifier import Classifier
import subprocess
import glob
import errno
try:
import yaml
except ImportError:
raise ImportError(
"Failed to import yaml. You may need to install it with 'sudo pip install pyyaml'")
def get_absolute_path(arg_parse_dir):
"""
Get absolute path from 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
def check_rtps_id_uniqueness(classifier):
"""
Checks if there are no ID's for different msgs repeated on the map
"""
repeated_ids = dict()
# check if there are repeated ID's on the messages to send
for key, value in classifier.msgs_to_send.items():
if sys.version_info[0] < 3:
if classifier.msgs_to_send.values().count(value) > 1:
repeated_ids.update({key: value})
else:
if list(classifier.msgs_to_send.values()).count(value) > 1:
repeated_ids.update({key: value})
# check if there are repeated ID's on the messages to receive
for key, value in classifier.msgs_to_receive.items():
if sys.version_info[0] < 3:
if classifier.msgs_to_receive.values().count(value) > 1:
repeated_ids.update({key: value})
else:
if list(classifier.msgs_to_receive.values()).count(value) > 1:
repeated_ids.update({key: value})
# check if there are repeated ID's on the messages to ignore
for key, value in classifier.msgs_to_ignore.items():
if sys.version_info[0] < 3:
if classifier.msgs_to_ignore.values().count(value) > 1:
repeated_ids.update({key: value})
else:
if list(classifier.msgs_to_ignore.values()).count(value) > 1:
repeated_ids.update({key: value})
# check if there are repeated IDs between classfied and unclassified msgs
# check send and ignore lists
send_ignore_common_ids = list(set(classifier.msgs_to_ignore.values(
)).intersection(classifier.msgs_to_send.values()))
for item in zip(classifier.msgs_to_send.items(), classifier.msgs_to_ignore.items()):
for repeated in send_ignore_common_ids:
if item[1] == repeated:
repeated_ids.update({item[0]: item[1]})
for item in classifier.msgs_to_ignore.items():
for repeated in send_ignore_common_ids:
if item[1] == repeated:
repeated_ids.update({item[0]: item[1]})
# check receive and ignore lists
receive_ignore_common_ids = list(set(classifier.msgs_to_ignore.values(
)).intersection(classifier.msgs_to_receive.values()))
for item in classifier.msgs_to_receive.items():
for repeated in receive_ignore_common_ids:
if item[1] == repeated:
repeated_ids.update({item[0]: item[1]})
for item in classifier.msgs_to_ignore.items():
for repeated in receive_ignore_common_ids:
if item[1] == repeated:
repeated_ids.update({item[0]: item[1]})
all_msgs = classifier.msgs_to_send
all_msgs.update(classifier.msgs_to_receive)
all_msgs.update(classifier.msgs_to_ignore)
all_ids = list()
if sys.version_info[0] < 3:
all_ids = all_msgs.values()
else:
all_ids = list(all_msgs.values())
all_ids.sort()
if not repeated_ids:
print("All good. RTPS ID's are unique")
else:
raise AssertionError(", ".join('%s' % msgs for msgs in repeated_ids.keys()) +
" have their ID's repeated. Please choose from the following pool:\n" +
", ".join('%d' % id for id in px_generate_uorb_topic_helper.check_available_ids(all_ids)))
default_client_out = get_absolute_path(
"src/modules/micrortps_bridge/micrortps_client")
default_agent_out = get_absolute_path(
"src/modules/micrortps_bridge/micrortps_agent")
default_uorb_templates_dir = "templates/uorb_microcdr"
default_urtps_templates_dir = "templates/urtps"
default_rtps_id_file = "tools/uorb_rtps_message_ids.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 dir, by default msg/", default="msg")
parser.add_argument("-b", "--uorb-templates-dir", dest='uorb_templates', type=str,
help="uORB templates dir, by default msgdir/templates/uorb_microcdr", default=default_uorb_templates_dir)
parser.add_argument("-q", "--urtps-templates-dir", dest='urtps_templates', type=str,
help="uRTPS templates dir, by default msgdir/templates/urtps", default=default_urtps_templates_dir)
parser.add_argument("-y", "--rtps-ids-file", dest='yaml_file', type=str,
help="RTPS msg IDs definition file, relative to the msgdir, by default tools/uorb_rtps_message_ids.yaml", default=default_rtps_id_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, 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("-g", "--fastrtpsgen-include", dest='fastrtpsgen_include', type=str, nargs='?',
help="directory(ies) to add to preprocessor include paths of fastrtpsgen, 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)
package = args.package
agent = args.agent
client = args.client
mkdir_build = args.mkdir_build
cmakelists = args.cmakelists
del_tree = args.del_tree
px_generate_uorb_topic_files.append_to_include_path(
{msg_folder}, px_generate_uorb_topic_files.INCL_DEFAULT, package)
agent_out_dir = get_absolute_path(args.agentdir)
client_out_dir = get_absolute_path(
args.clientdir)
gen_idl = args.gen_idl
idl_dir = args.idl_dir
if idl_dir != '':
idl_dir = get_absolute_path(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'
else:
# Path to fastrtpsgen is explicitly specified
fastrtpsgen_path = os.path.join(
get_absolute_path(args.fastrtpsgen), 'fastrtpsgen')
fastrtpsgen_include = args.fastrtpsgen_include
if fastrtpsgen_include is not None and fastrtpsgen_include != '':
fastrtpsgen_include = "-I " + \
get_absolute_path(
args.fastrtpsgen_include) + " "
# 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(os.path.join(agent_out_dir, "idl")):
shutil.rmtree(os.path.join(agent_out_dir, "idl"))
uorb_templates_dir = os.path.join(msg_folder, args.uorb_templates)
urtps_templates_dir = os.path.join(msg_folder, args.urtps_templates)
# parse yaml file into a map of ids
classifier = Classifier(os.path.join(msg_folder, args.yaml_file), msg_folder)
# check if there are no ID's repeated
check_rtps_id_uniqueness(classifier)
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_CMAKELISTS_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 classifier.msg_files_send:
for msg_file in classifier.msg_files_send:
if gen_idl:
if out_dir != agent_out_dir:
px_generate_uorb_topic_files.generate_idl_file(msg_file, os.path.join(out_dir, "/idl"), urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map)
else:
px_generate_uorb_topic_files.generate_idl_file(msg_file, idl_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map)
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, uRTPS_PUBLISHER_SRC_TEMPL_FILE)
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, uRTPS_PUBLISHER_H_TEMPL_FILE)
if classifier.msg_files_receive:
for msg_file in classifier.msg_files_receive:
if gen_idl:
if out_dir != agent_out_dir:
px_generate_uorb_topic_files.generate_idl_file(msg_file, os.path.join(out_dir, "/idl"), urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map)
else:
px_generate_uorb_topic_files.generate_idl_file(msg_file, idl_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map)
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, uRTPS_SUBSCRIBER_SRC_TEMPL_FILE)
px_generate_uorb_topic_files.generate_topic_file(msg_file, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, uRTPS_SUBSCRIBER_H_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msg_files_send, classifier.msg_files_receive, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, uRTPS_AGENT_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msg_files_send, classifier.msg_files_receive, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, uRTPS_AGENT_TOPICS_H_TEMPL_FILE)
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msg_files_send, classifier.msg_files_receive, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, uRTPS_AGENT_TOPICS_SRC_TEMPL_FILE)
if cmakelists:
px_generate_uorb_topic_files.generate_uRTPS_general(classifier.msg_files_send, classifier.msg_files_receive, out_dir, urtps_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, 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)
for idl_file in glob.glob(os.path.join(idl_dir, "*.idl")):
ret = subprocess.call(fastrtpsgen_path + " -d " + out_dir +
"/fastrtpsgen -example x64Linux2.6gcc " + fastrtpsgen_include + idl_file, shell=True)
if ret:
raise Exception(
"fastrtpsgen not found. Specify the location of fastrtpsgen with the -f flag")
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(out_dir, "microRTPS_agent_CMakeLists.txt"),
os.path.join(out_dir, "CMakeLists.txt"))
if (mkdir_build):
mkdir_p(os.path.join(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):
# 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.msg_files_send, classifier.msg_files_receive, out_dir, uorb_templates_dir,
package, px_generate_uorb_topic_files.INCL_DEFAULT, classifier.msg_id_map, 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)