From ffc3c372be68379c3f7da219253ff2b3a8a66e2a Mon Sep 17 00:00:00 2001 From: Javier Moreno Date: Thu, 4 Oct 2018 09:14:22 +0200 Subject: [PATCH] Refs #3459. --- CMakeLists.txt | 185 +++++++++++++++-- bin/Xml_interface_gen.py | 30 +++ bin/Xml_read_default_profiles.py | 26 +++ bin/micro_ros_agent | 45 ----- cmake/micrortps_agent_xml_generator.cmake | 69 ------- micro_ros_agent-extras.cmake.in | 14 -- package.xml | 7 +- {micro_ros_agent => uros_agent}/__init__.py | 212 +++++++++----------- 8 files changed, 327 insertions(+), 261 deletions(-) create mode 100644 bin/Xml_interface_gen.py create mode 100644 bin/Xml_read_default_profiles.py delete mode 100644 bin/micro_ros_agent delete mode 100644 cmake/micrortps_agent_xml_generator.cmake delete mode 100644 micro_ros_agent-extras.cmake.in rename {micro_ros_agent => uros_agent}/__init__.py (54%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3f9f81..782435d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,10 +18,11 @@ cmake_minimum_required(VERSION 3.5) # Set proyect name -project(micro_ros_agent) +project(uros_agent) # Find packages depencences +find_package(rosidl_cmake REQUIRED) find_package(ament_cmake REQUIRED) find_package(fastcdr REQUIRED CONFIG) find_package(fastrtps REQUIRED CONFIG) @@ -35,26 +36,178 @@ ament_export_dependencies(fastrtps) ament_export_dependencies(micrortps_agent) -# Install python files -ament_python_install_package(${PROJECT_NAME}) +# Set variables +set(_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/python") + +set(_XML_INTERFACE_GEN_BIN "${_OUTPUT_PATH}/bin/Xml_interface_gen.py") +normalize_path(_XML_INTERFACE_GEN_BIN "${_XML_INTERFACE_GEN_BIN}") + +set(_XML_DEFAULT_READ_BIN "${_OUTPUT_PATH}/bin/Xml_read_default_profiles.py") +normalize_path(_XML_DEFAULT_READ_BIN "${_XML_DEFAULT_READ_BIN}") + +set(_PYTHON_PKG_TOOL ${PROJECT_NAME}) + +set(_RESOURCE_DIR "${_OUTPUT_PATH}/resource") +normalize_path(_RESOURCE_DIR "${_RESOURCE_DIR}") -# Register package resource in order to be called when a new msg package is generated -ament_index_register_resource("rosidl_typesupport_c") +set(_DEFAULT_FASTRTPS_PROFILES_PATH "${_OUTPUT_PATH}/gen/DEFAULT_FASTRTPS_PROFILES.xml") +normalize_path(_DEFAULT_FASTRTPS_PROFILES_PATH "${_DEFAULT_FASTRTPS_PROFILES_PATH}") + + +# Get colcon call dir +get_filename_component(_COLCON_CALL_DIR "${CMAKE_INSTALL_PREFIX}" DIRECTORY) +get_filename_component(_COLCON_CALL_DIR "${_COLCON_CALL_DIR}" DIRECTORY) + + +# Generate python header +set( +_PYTHON_SCRIPT_HEAD +" +import os\n +import sys\n +sys.path.append('${_OUTPUT_PATH}')\n +from ${_PYTHON_PKG_TOOL} import *\n +" +) + + +# Copy pyton files +file(COPY "bin" "${_PYTHON_PKG_TOOL}" "resource" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/python") + + +# Extract all packages and manifiest paths +execute_process( + COMMAND + "${PYTHON_EXECUTABLE}" + "-c" + "${_PYTHON_SCRIPT_HEAD}for package in GetInterfacePackages(GetPackageList('${_COLCON_CALL_DIR}')): print ('%s,%s' % (package, GetPackageName(package)))" + OUTPUT_VARIABLE _packages + RESULT_VARIABLE _result + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if(NOT _result EQUAL 0) + message(FATAL_ERROR "Error finding xml packages") +endif() + + +# Convert output to list +string(REPLACE "\n" ";" _packages ${_packages}) + + +# Extract all msg from each package (stored in ${package}_MSG_FILES) +set(ALL_MSG_FILES "") +foreach(package ${_packages}) + + # Extract info + string(REPLACE "," ";" package ${package}) + list(GET package 0 package_file) + list(GET package 1 package) + + + # Get all msg files + execute_process( + COMMAND + "${PYTHON_EXECUTABLE}" + "-c" + "${_PYTHON_SCRIPT_HEAD}for msg in GetInterfacePackageMsgs('${package_file}'): print ('%s' % msg)" + OUTPUT_VARIABLE ${package}_MSG_FILES + RESULT_VARIABLE _result + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT _result EQUAL 0) + message(FATAL_ERROR "Error finding .msg files") + endif() + + # Skip if there are no msgs to create + if(${package}_MSG_FILES STREQUAL "") + continue() + endif() + + # Generate list + string(REPLACE "\n" ";" ${package}_MSG_FILES ${${package}_MSG_FILES}) + +endforeach() + + +# Append defaul xml profiles +execute_process( + COMMAND "${PYTHON_EXECUTABLE}" "${_XML_DEFAULT_READ_BIN}" "--default-xml-path" "${_RESOURCE_DIR}" + OUTPUT_VARIABLE _XmlDoc + RESULT_VARIABLE _result + ) +if(NOT _result EQUAL 0) + message(FATAL_ERROR "Error in typesuppor generation") +endif() + + +# Create one xml for each message +foreach(package ${_packages}) + string(REPLACE "," ";" package ${package}) + list(GET package 0 package_file) + list(GET package 1 package) + + + # Skip this generation if there are no msgs to process + if(${package}_MSG_FILES STREQUAL "") + continue() + endif() + + + + # generate script argument file + set(generator_arguments_file "${_OUTPUT_PATH}/XML_ArgFiles/${package}_Args.json") + rosidl_write_generator_arguments( + "${generator_arguments_file}" + PACKAGE_NAME "${package}" + ROS_INTERFACE_FILES "${${package}_MSG_FILES}" + ROS_INTERFACE_DEPENDENCIES "NULL" + OUTPUT_DIR "NULL" + TEMPLATE_DIR "NULL" + TARGET_DEPENDENCIES "NULL" + ADDITIONAL_FILES "" + ) + + + # execute python script + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" "${_XML_INTERFACE_GEN_BIN}" --generator-arguments-file "${generator_arguments_file}" + OUTPUT_VARIABLE _XmlGen + RESULT_VARIABLE _result + ) + if(NOT _result EQUAL 0) + message(FATAL_ERROR "Error in typesuppor generation") + endif() + + + # Strore xml + set(_XmlDoc "${_XmlDoc}${_XmlGen}") + +endforeach() + + +# Close profile +set (_XmlDoc "\n${_XmlDoc}\n") + + +# Save +file(WRITE "${_DEFAULT_FASTRTPS_PROFILES_PATH}" "${_XmlDoc}") + # Install the package.xml file, and generate code for ``find_package`` so that other packages can get information about this package. -ament_package( - CONFIG_EXTRAS "micro_ros_agent-extras.cmake.in" +ament_package() + + +#Install +install( + FILES "${_DEFAULT_FASTRTPS_PROFILES_PATH}" + DESTINATION "../micrortps_agent/bin" ) -install( - PROGRAMS bin/micro_ros_agent - DESTINATION lib/micro_ros_agent -) - -install( - DIRECTORY cmake resource - DESTINATION share/${PROJECT_NAME} -) \ No newline at end of file +# Install package +#install( +# DIRECTORY cmake resource +# DESTINATION share/${PROJECT_NAME} +#) \ No newline at end of file diff --git a/bin/Xml_interface_gen.py b/bin/Xml_interface_gen.py new file mode 100644 index 0000000..07fdb7d --- /dev/null +++ b/bin/Xml_interface_gen.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import argparse +import os +import sys + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)),'..')) + +from rosidl_cmake import read_generator_arguments +from rosidl_parser import UnknownMessageType +from uros_agent import generate_XML + + +def main(argv=sys.argv[1:]): + parser = argparse.ArgumentParser( + description='Generate the C interfaces for Micro RTPS.', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + '--generator-arguments-file', + required=True, + help='The location of the file containing the generator arguments') + args = parser.parse_args(argv) + + generator_args = read_generator_arguments(args.generator_arguments_file) + rc = generate_XML(generator_args) + return rc + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/bin/Xml_read_default_profiles.py b/bin/Xml_read_default_profiles.py new file mode 100644 index 0000000..001d256 --- /dev/null +++ b/bin/Xml_read_default_profiles.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse +import xml.etree.ElementTree + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)),'..')) + +from uros_agent import * + +def main(argv=sys.argv[1:]): + parser = argparse.ArgumentParser( + description='Import default xml files', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + '--default-xml-path', + required=True, + help='The location of the default profiles') + args = parser.parse_args(argv) + ReadDefaultXMLs(args.default_xml_path) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/bin/micro_ros_agent b/bin/micro_ros_agent deleted file mode 100644 index a717221..0000000 --- a/bin/micro_ros_agent +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import sys - -from rosidl_cmake import read_generator_arguments -from rosidl_parser import UnknownMessageType -from micro_ros_agent import generate_micro_ros_agent_xml_support - -def is_valid_file(parser, file_name): - if not os.path.exists(file_name): - parser.error("File does not exist: '{0}'".format(file_name)) - file_name_abs = os.path.abspath(file_name) - if not os.path.isfile(file_name_abs): - parser.error("Path exists but is not a file: '{0}'".format(file_name_abs)) - return file_name - - -def main(argv=sys.argv[1:]): - parser = argparse.ArgumentParser( - description='Generate xml files for micrortps agent.', - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument( - '--generator-arguments-file', - required=True, - help='The location of the file containing the generator arguments') - args = parser.parse_args(argv) - - generator_args = read_generator_arguments(args.generator_arguments_file) - - try: - rc = generate_micro_ros_agent_xml_support(generator_args) - - return 0 - except UnknownMessageType as e: - print(str(e), file=sys.stderr) - return 1 - if rc: - return rc - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/cmake/micrortps_agent_xml_generator.cmake b/cmake/micrortps_agent_xml_generator.cmake deleted file mode 100644 index 6f269f0..0000000 --- a/cmake/micrortps_agent_xml_generator.cmake +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2016-2018 Proyectos y Sistemas de Mantenimiento SL (eProsima). -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -# list msg files -set(_ros_idl_files "") -foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) - get_filename_component(_extension "${_idl_file}" EXT) - # Skip .srv files - if(_extension STREQUAL ".msg") - list(APPEND _ros_idl_files "${_idl_file}") - endif() -endforeach() - - -# Set output dir -set(_output_path "${CMAKE_CURRENT_BINARY_DIR}/../micro_ros_agent/xml_gen") - - -# check if all templates exits -set(target_dependencies - "${micro_ros_agent_BIN}" - "${micro_ros_agent_GENERATOR_FILES}" - ${rosidl_generate_interfaces_IDL_FILES} - ${_dependency_files}) -foreach(dep ${target_dependencies}) - if(NOT EXISTS "${dep}") - message(FATAL_ERROR "Target dependency '${dep}' does not exist") - endif() -endforeach() - - -# generate script argument file -set(generator_arguments_file "${CMAKE_CURRENT_BINARY_DIR}/micro_ros_agent__arguments.json") -rosidl_write_generator_arguments( - "${generator_arguments_file}" - PACKAGE_NAME "${PROJECT_NAME}" - ROS_INTERFACE_FILES "${rosidl_generate_interfaces_IDL_FILES}" - ROS_INTERFACE_DEPENDENCIES "${_dependencies}" - OUTPUT_DIR "${_output_path}" - TEMPLATE_DIR "${micro_ros_agent_DEFAULT_PROFILES_DIR}" - TARGET_DEPENDENCIES ${target_dependencies} - ADDITIONAL_FILES ${_dds_idl_files} -) - - -# Execute python script -execute_process( - COMMAND ${PYTHON_EXECUTABLE} ${micro_ros_agent_BIN} - --generator-arguments-file "${generator_arguments_file}" - ) - - -#Install -install( - DIRECTORY "${_output_path}/" - DESTINATION "../micrortps_agent/bin" -) \ No newline at end of file diff --git a/micro_ros_agent-extras.cmake.in b/micro_ros_agent-extras.cmake.in deleted file mode 100644 index aa4e047..0000000 --- a/micro_ros_agent-extras.cmake.in +++ /dev/null @@ -1,14 +0,0 @@ -find_package(ament_cmake_core QUIET REQUIRED) -ament_register_extension( - "rosidl_generate_interfaces" - "micro_ros_agent" - "micrortps_agent_xml_generator.cmake") - -set(micro_ros_agent_BIN "${micro_ros_agent_DIR}/../../../lib/micro_ros_agent/micro_ros_agent") -normalize_path(micro_ros_agent_BIN "${micro_ros_agent_BIN}") - -set(micro_ros_agent_GENERATOR_FILES "${micro_ros_agent_DIR}/../../../@PYTHON_INSTALL_DIR@/micro_ros_agent/__init__.py") -normalize_path(micro_ros_agent_GENERATOR_FILES "${micro_ros_agent_GENERATOR_FILES}") - -set(micro_ros_agent_DEFAULT_PROFILES_DIR "${micro_ros_agent_DIR}/../resource") -normalize_path(micro_ros_agent_DEFAULT_PROFILES_DIR "${micro_ros_agent_DEFAULT_PROFILES_DIR}") \ No newline at end of file diff --git a/package.xml b/package.xml index 461ed8a..f895d2a 100644 --- a/package.xml +++ b/package.xml @@ -1,8 +1,8 @@ - micro_ros_agent - 0.0.1 + uros_agent + 0.0.2 DDS-XCRE agent implementation Javier Moreno Apache License 2.0 @@ -15,11 +15,12 @@ fastcdr fastrtps micrortps_agent + rosidl_cmake ament_lint_auto ament_lint_common - rosidl_typesupport_c_packages + rosidl_interface_packages ament_cmake diff --git a/micro_ros_agent/__init__.py b/uros_agent/__init__.py similarity index 54% rename from micro_ros_agent/__init__.py rename to uros_agent/__init__.py index 34ad0b0..a785b15 100644 --- a/micro_ros_agent/__init__.py +++ b/uros_agent/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2016-2018 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,19 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import struct import os import sys - -if os.name == 'nt': - import win32con - import win32file - import pywintypes - __overlapped = pywintypes.OVERLAPPED() -elif os.name == 'posix': - import fcntl -else: - raise RuntimeError('PortaLocker only defined for nt and posix platforms') +import xml.etree.ElementTree from rosidl_cmake import convert_camel_case_to_lower_case_underscore from rosidl_cmake import expand_template @@ -33,14 +23,103 @@ from rosidl_cmake import get_newest_modification_time from rosidl_parser import parse_message_file from rosidl_parser import parse_service_file from rosidl_parser import validate_field_types -from shutil import copyfile -from pathlib import Path -def generate_micro_ros_agent_xml_support(args): +def GetPackage(Dir): + + # ignore? + for f in os.listdir(Dir): + full_path = os.path.join(Dir, f) + if os.path.isfile(full_path): + if f == "COLCON_IGNORE": + return "COLCON_IGNORE" + + # found package + found_package_path = "" + for f in os.listdir(Dir): + if f == "package.xml": + found_package_path = os.path.join(Dir, f) + found_package_path = os.path.abspath(found_package_path) + break + + + return found_package_path + + +def GetPackageList(Dir): + package_list = [] + package_path = GetPackage(Dir) + + if package_path == "": + for f in os.listdir(Dir): + full_path = os.path.join(Dir, f) + if os.path.isdir(full_path): + for l in GetPackageList(full_path): + package_list.append(l) + elif package_path != "COLCON_IGNORE": + package_list.append(package_path) + + return package_list + + +def GetInterfacePackages(packages_list): + package_interface_list = [] + for package in packages_list: + xml_root = xml.etree.ElementTree.parse(package).getroot() + for element in xml_root.findall('member_of_group'): + if element.text == "rosidl_interface_packages": + package_interface_list.append(package) + return package_interface_list + + +def GetPackageName(package_path): + xml_root = xml.etree.ElementTree.parse(package_path).getroot() + xml_name_elements = xml_root.findall('name') + if len(xml_name_elements) != 1: + return "" + else: + return xml_name_elements[0].text + + +def GetInterfacePackageMsgs(package_path): + msg_list = [] + package_dir = os.path.dirname(package_path) + for root, dirs, files in os.walk(package_dir): + for file in files: + if file.endswith(".msg"): + full_path = os.path.join(root, file) + msg_list.append(full_path) + + return msg_list + +def GetInterfacePackageSrvs(package_path): + msg_list = [] + package_dir = os.path.dirname(package_path) + for root, dirs, files in os.walk(package_dir): + for file in files: + if file.endswith(".srv"): + full_path = os.path.join(root, file) + msg_list.append(full_path) + + return msg_list + + +def ReadDefaultXMLs(Path): + for f in os.listdir(Path): + full_path = os.path.join(Path, f) + if os.path.isfile(full_path) and f.endswith(".xml"): + #xml_root = xml.etree.ElementTree.parse(full_path).getroot() + #if xml_root.iselement(): + fd = open(full_path) + print ("%s" % fd.read()) + fd.close() + + + +def generate_XML(args): pkg_name = args['package_name'] - known_msg_types = extract_message_types( - pkg_name, args['ros_interface_files'], args.get('ros_interface_dependencies', [])) + #known_msg_types = extract_message_types( + # pkg_name, args['ros_interface_files'], args.get('ros_interface_dependencies', [])) functions = { 'get_header_filename_from_msg_name': convert_camel_case_to_lower_case_underscore, @@ -48,7 +127,6 @@ def generate_micro_ros_agent_xml_support(args): # Set file format - file_format = ".xml" ros2_prefix = "rt/" @@ -63,14 +141,6 @@ def generate_micro_ros_agent_xml_support(args): if not os.path.exists(srcs_dir): os.makedirs(srcs_dir) - - # Copy all included xml files - for filename in os.listdir(args['template_dir']): - if filename.endswith(file_format): - template_src_path = os.path.join(args['template_dir'], filename) - template_dest_path = os.path.join(srcs_dir, filename) - if not os.path.isfile(template_dest_path): - copyfile(template_src_path, template_dest_path) # Iterate throw all msgs/srvs @@ -78,7 +148,7 @@ def generate_micro_ros_agent_xml_support(args): extension = os.path.splitext(idl_file)[1] if extension == '.msg': spec = parse_message_file(pkg_name, idl_file) - validate_field_types(spec, known_msg_types) + #validate_field_types(spec, known_msg_types) subfolder = os.path.basename(os.path.dirname(idl_file)) data = { @@ -137,95 +207,9 @@ def generate_micro_ros_agent_xml_support(args): # Write file content - fd1 = open(src_file, "w") - if os.name == 'nt': - # Lock - hfile1 = win32file._get_osfhandle(fd1.fileno()) - win32file.LockFileEx(hfile1, win32con.LOCKFILE_EXCLUSIVE_LOCK, 0, -0x10000, __overlapped) - - # Write - fd1.write(file_content) - - # Unlock - win32file.UnlockFileEx(hfile1, 0, -0x10000, __overlapped) - elif os.name == 'posix': - # Lock - fcntl.flock(fd1.fileno(), fcntl.LOCK_EX) - - # Write - fd1.write(file_content) - - # Unlock - fcntl.flock(fd1.fileno(), fcntl.LOCK_UN) - fd1.close() + print ("%s" % file_content) - # Open collect file - collec_file = os.path.join(args['output_dir'], "DEFAULT_FASTRTPS_PROFILES.xml") - fd2 = open(collec_file, "w") - if os.name == 'nt': - # Lock - hfile2 = win32file._get_osfhandle(fd2.fileno()) - win32file.LockFileEx(hfile2, win32con.LOCKFILE_EXCLUSIVE_LOCK, 0, -0x10000, __overlapped) - - # Generate head - fd2.write("\n") - - # Append all files contents - for filename in os.listdir(srcs_dir): - if filename.endswith(".xml"): - # Open - fd3 = open(os.path.join(srcs_dir, filename), "r+") - - # Lock - hfile3 = win32file._get_osfhandle(fd3.fileno()) - win32file.LockFileEx(hfile3, win32con.LOCKFILE_EXCLUSIVE_LOCK, 0, -0x10000, __overlapped) - - # Write - fd2.write(fd3.read()) - - # Unlock - win32file.UnlockFileEx(hfile3, 0, -0x10000, __overlapped) - fd3.close() - - # Generate tail - fd2.write("\n") - - # UnLock - win32file.UnlockFileEx(hfile2, 0, -0x10000, __overlapped) - elif os.name == 'posix': - # Lock - fcntl.flock(fd2.fileno(), fcntl.LOCK_EX) - - # Generate head - fd2.write("\n") - - # Append all files contents - for filename in os.listdir(srcs_dir): - if filename.endswith(".xml"): - # Open - fd3 = open(os.path.join(srcs_dir, filename), "r+") - - # Lock - fcntl.flock(fd3, fcntl.LOCK_EX) - - # Write - fd2.write(fd3.read()) - - # Unlock - fcntl.flock(fd3.fileno(), fcntl.LOCK_UN) - - # Close - fd3.close() - - # Generate tail - fd2.write("\n") - - # UnLock - fcntl.flock(fd2.fileno(), fcntl.LOCK_UN) - # Close file - fd2.close() - #elif extension == '.srv':