diff --git a/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/CMakeLists.txt b/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/CMakeLists.txt new file mode 100644 index 000000000..f640b3ee8 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5) +project(test_pkg) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(Boost REQUIRED) +find_package(rclcpp REQUIRED) +find_package(std_srvs REQUIRED) +find_package(sensor_msgs REQUIRED) + +add_executable(test_node src/test_node.cpp) +ament_target_dependencies(test_node std_srvs sensor_msgs ) + +install(TARGETS + test_node + DESTINATION lib/${PROJECT_NAME}) + +ament_package() diff --git a/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/package.xml b/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/package.xml new file mode 100644 index 000000000..fdc193692 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/package.xml @@ -0,0 +1,26 @@ + + + + test_pkg + 0.0.0 + This package contains the implementation of the node test_node + Jane Doe + Jane Doe + Apache 2.0 + + ament_cmake + + boost + rclcpp + std_srvs + sensor_msgs + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_node.cpp b/plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/src/test_node.cpp similarity index 100% rename from plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_node.cpp rename to plugins/de.fraunhofer.ipa.ros.xtext.tests/resources/ros2generator/test_pkg/src/test_node.cpp diff --git a/plugins/de.fraunhofer.ipa.ros.xtext.tests/src/de/fraunhofer/ipa/ros/tests/RosGeneratorTest.xtend b/plugins/de.fraunhofer.ipa.ros.xtext.tests/src/de/fraunhofer/ipa/ros/tests/RosGeneratorTest.xtend index cfa7a4f23..af47365dd 100644 --- a/plugins/de.fraunhofer.ipa.ros.xtext.tests/src/de/fraunhofer/ipa/ros/tests/RosGeneratorTest.xtend +++ b/plugins/de.fraunhofer.ipa.ros.xtext.tests/src/de/fraunhofer/ipa/ros/tests/RosGeneratorTest.xtend @@ -63,10 +63,16 @@ class RosGeneratorTest { val fsa = new InMemoryFileSystemAccess ros2Generator.doGenerate(model.eResource, fsa, new GeneratorContext) - Assert.assertTrue(fsa.textFiles.containsKey(CustomOutputProvider::DEFAULT_OUTPUT + "test_node.cpp")) + Assert.assertTrue(fsa.textFiles.containsKey(CustomOutputProvider::DEFAULT_OUTPUT + "test_pkg/package.xml")) Assert.assertEquals( - new String(Files.readAllBytes(Paths.get(RESOURCES_BASE_DIR, 'ros2generator', 'test_node.cpp'))).trim, - fsa.textFiles.get(CustomOutputProvider::DEFAULT_OUTPUT + "test_node.cpp").toString.trim) + new String(Files.readAllBytes(Paths.get(RESOURCES_BASE_DIR, 'ros2generator/test_pkg/src/','test_node.cpp'))).trim, + fsa.textFiles.get(CustomOutputProvider::DEFAULT_OUTPUT + "test_pkg/src/test_node.cpp").toString.trim) + Assert.assertEquals( + new String(Files.readAllBytes(Paths.get(RESOURCES_BASE_DIR, 'ros2generator/test_pkg/','CMakeLists.txt'))).trim, + fsa.textFiles.get(CustomOutputProvider::DEFAULT_OUTPUT + "test_pkg/CMakeLists.txt").toString.trim) + Assert.assertEquals( + new String(Files.readAllBytes(Paths.get(RESOURCES_BASE_DIR, 'ros2generator/test_pkg/','package.xml'))).trim, + fsa.textFiles.get(CustomOutputProvider::DEFAULT_OUTPUT + "test_pkg/package.xml").toString.trim) } @Test diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2CodeGenerator.xtend b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2CodeGenerator.xtend index 1e7f98458..42ae46692 100644 --- a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2CodeGenerator.xtend +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2CodeGenerator.xtend @@ -1,16 +1,15 @@ package de.fraunhofer.ipa.roscode.generator +import java.util.ArrayList +import java.util.HashSet +import java.util.List +import java.util.Set import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.AbstractGenerator import org.eclipse.xtext.generator.IFileSystemAccess2 import org.eclipse.xtext.generator.IGeneratorContext -import org.eclipse.xtext.generator.IOutputConfigurationProvider -import org.eclipse.xtext.generator.OutputConfiguration -import java.util.Set -import ros.* -import java.util.List -import java.util.ArrayList -import java.util.HashSet +import ros.Node +import ros.Package /** * Generates code from your model files on save. @@ -23,6 +22,7 @@ class Ros2CodeGenerator extends AbstractGenerator { String import_msgs int char_i Node node + String pkg_name List PkgsList Set set @@ -35,8 +35,9 @@ class Ros2CodeGenerator extends AbstractGenerator { fsa.generateFile(pkg.getName().toLowerCase+"/CMakeLists.txt",pkg.compile_CMakeLists) for (art : pkg.artifact){ node = art.node - fsa.generateFile(pkg.getName().toLowerCase+"/src/"+node.name+".cpp",node.compile_node) - + pkg_name = pkg.getName().toLowerCase + fsa.generateFile(pkg_name+"/src/"+node.name+"_component.cpp",node.compile_node(pkg_name)) + fsa.generateFile(pkg_name+"/include/"+pkg_name+"/"+node.name+"_component.hpp",node.compile_node_header(pkg_name)) } } } @@ -60,6 +61,7 @@ def compile_package_xml(Package pkg)''' boost rclcpp + rclcpp_components «FOR depend_pkg:pkg.getPkgDependencies» «depend_pkg» «ENDFOR» @@ -89,31 +91,45 @@ endif() find_package(ament_cmake REQUIRED) find_package(Boost REQUIRED) find_package(rclcpp REQUIRED) +find_package(rclcpp_components REQUIRED) «FOR depend_pkg:pkg.getPkgDependencies» find_package(«depend_pkg» REQUIRED) «ENDFOR» +include_directories(include) + +# create ament index resource which references the libraries in the binary dir +set(node_plugins "") + «FOR art:pkg.artifact» -add_executable(«art.name» src/«art.node.name».cpp) -ament_target_dependencies(«art.name» «FOR depend_pkg:pkg.getPkgDependencies»«depend_pkg» «ENDFOR») +add_library(«art.node.name»_component SHARED + src/«art.node.name»_component.cpp) +ament_target_dependencies(«art.node.name»_component + rclcpp + rclcpp_components + «FOR depend_pkg:pkg.getPkgDependencies» + «depend_pkg» +«ENDFOR») +rclcpp_components_register_nodes(«art.node.name»_component "«pkg.name»::«art.node.name»") +set(node_plugins "${node_plugins}«pkg.name»::«art.node.name»;$\n") + «ENDFOR» install(TARGETS - «art.name» - DESTINATION lib/${PROJECT_NAME}) -«ENDFOR» + «FOR art:pkg.artifact» + «art.node.name»_component + «ENDFOR» + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) ament_package() ''' -def compile_node(Node node) ''' -#include -#include -#include -#include -#include +def compile_node_header(Node node, String pkg_name) ''' +#ifndef «pkg_name.toUpperCase»__«node.name.toUpperCase»_COMPONENT_HPP_ +#define «pkg_name.toUpperCase»__«node.name.toUpperCase»_COMPONENT_HPP_ #include "rclcpp/rclcpp.hpp" -#include "rcutils/cmdline_parser.h" «FOR pub : node.publisher» #include <«pub.message.package.name»/msg/«check_message_include(pub.message.name)».hpp> «ENDFOR» @@ -127,74 +143,25 @@ def compile_node(Node node) ''' #include <«srvclient.service.package.name»/srv/«check_message_include(srvclient.service.name)».hpp> «ENDFOR» -using namespace std::chrono_literals; -using std::placeholders::_1; -using std::placeholders::_2; -using std::placeholders::_3; - -void print_usage() -{ - printf("Usage for «node.name» app:\n"); - printf("..... \n"); - printf("..... \n"); - printf("..... \n"); -} +namespace «pkg_name» { - class «node.name» : public rclcpp::Node { + class «node.name» : public rclcpp::Node { public: - «node.name»() : Node("«node.name»") { - «FOR pub : node.publisher» - «check_name(pub.name)»_ = this->create_publisher<«pub.message.package.name»::msg::«pub.message.name»>("«pub.name»",10); - «ENDFOR» - «FOR sub : node.subscriber» - «check_name(sub.name)»_ = this->create_subscription<«sub.message.package.name»::msg::«sub.message.name»>("«sub.name»", 10, std::bind(&«node.name»::«check_name(sub.name)»_callback, this, _1)); - «ENDFOR» - «FOR client : node.serviceclient» - «check_name(client.name)»_ = this->create_client<«client.service.package.name»::srv::«client.service.name»>("«client.name»"); - «ENDFOR» - «FOR service : node.serviceserver» - «check_name(service.name)»_ = this->create_service<«service.service.package.name»::srv::«service.service.name»>("«service.name»", std::bind(&«node.name»::«check_name(service.name)»_handle, this, _1, _2, _3)); - «ENDFOR» - - «IF node.publisher.length > 0» - timer_ = this->create_wall_timer(500ms, std::bind(&«node.name»::timer_callback, this)); - «ENDIF» - - «FOR client : node.serviceclient» - // Service client - while (!«check_name(client.name)»_->wait_for_service(std::chrono::seconds(10))){ - RCLCPP_ERROR(this->get_logger(), "Client interrupted while waiting for service '%s' to appear.", "«client.name»"); - } - auto request = std::make_shared<«client.service.package.name»::srv::«client.service.name»::Request>(); - // request-> ... = ....; - auto result_future = «check_name(client.name)»_->async_send_request(request); - auto result = result_future.get(); - RCLCPP_INFO(this->get_logger(), "Service called, service: '%s'", "«client.name»"); - «ENDFOR» - } + «node.name»(rclcpp::NodeOptions options); private: «FOR sub : node.subscriber» // Subscriber callback - void «check_name(sub.name)»_callback(const «sub.message.package.name»::msg::«sub.message.name»::SharedPtr msg) const { - RCLCPP_INFO(this->get_logger(), "«sub.name» topic got a message"); - } - + void «check_name(sub.name)»_callback(const «sub.message.package.name»::msg::«sub.message.name»::SharedPtr msg) const; rclcpp::Subscription<«sub.message.package.name»::msg::«sub.message.name»>::SharedPtr «check_name(sub.name)»_ ; + «ENDFOR» «FOR pub : node.publisher» rclcpp::Publisher<«pub.message.package.name»::msg::«pub.message.name»>::SharedPtr «check_name(pub.name)»_; «ENDFOR» «IF node.publisher.length > 0» // Timer Callback - void timer_callback(){ - «FOR pub : node.publisher» - auto «check_name(pub.name)»_msg = «pub.message.package.name»::msg::«pub.message.name»(); - //«check_name(pub.name)»_msg = ... - «check_name(pub.name)»_->publish(«check_name(pub.name)»_msg); - RCLCPP_INFO(this->get_logger(), "«pub.name» publisher active"); - «ENDFOR» - } + void timer_callback(); rclcpp::TimerBase::SharedPtr timer_; «ENDIF» @@ -213,13 +180,101 @@ void print_usage() «ENDFOR» }; -int main(int argc, char * argv[]) +} // namespace «pkg_name» + +#endif // «pkg_name.toUpperCase»__«node.name.toUpperCase»_COMPONENT_HPP_ +''' + +def compile_node(Node node, String pkg_name) ''' +#include +#include +#include +#include +#include + +#include "«pkg_name»/«node.name»_component.hpp" + +using namespace std::chrono_literals; +using std::placeholders::_1; +using std::placeholders::_2; +using std::placeholders::_3; + +void print_usage() { - rclcpp::init(argc, argv); - rclcpp::spin(std::make_shared<«node.name»>()); - rclcpp::shutdown(); - return 0; + printf("Usage for «node.name» app:\n"); + printf("..... \n"); + printf("..... \n"); + printf("..... \n"); } + +namespace «pkg_name» { + +«node.name»::«node.name»(rclcpp::NodeOptions options) : Node("«node.name»", options) { + «FOR pub : node.publisher» + «check_name(pub.name)»_ = this->create_publisher<«pub.message.package.name»::msg::«pub.message.name»>("«pub.name»",10); + «ENDFOR» + «FOR sub : node.subscriber» + «check_name(sub.name)»_ = this->create_subscription<«sub.message.package.name»::msg::«sub.message.name»>("«sub.name»", 10, std::bind(&«node.name»::«check_name(sub.name)»_callback, this, _1)); + «ENDFOR» + «FOR client : node.serviceclient» + «check_name(client.name)»_ = this->create_client<«client.service.package.name»::srv::«client.service.name»>("«client.name»"); + «ENDFOR» + «FOR service : node.serviceserver» + «check_name(service.name)»_ = this->create_service<«service.service.package.name»::srv::«service.service.name»>("«service.name»", std::bind(&«node.name»::«check_name(service.name)»_handle, this, _1, _2, _3)); + «ENDFOR» + + «IF node.publisher.length > 0» + timer_ = this->create_wall_timer(500ms, std::bind(&«node.name»::timer_callback, this)); + «ENDIF» + + «FOR client : node.serviceclient» + // Service client + while (!«check_name(client.name)»_->wait_for_service(std::chrono::seconds(10))){ + RCLCPP_ERROR(this->get_logger(), "Client interrupted while waiting for service '%s' to appear.", "«client.name»"); + } + auto request = std::make_shared<«client.service.package.name»::srv::«client.service.name»::Request>(); + // request-> ... = ....; + auto result_future = «check_name(client.name)»_->async_send_request(request); + auto result = result_future.get(); + RCLCPP_INFO(this->get_logger(), "Service called, service: '%s'", "«client.name»"); +«ENDFOR» +} + +«FOR sub : node.subscriber» +// Subscriber callback +void «node.name»::«check_name(sub.name)»_callback(const «sub.message.package.name»::msg::«sub.message.name»::SharedPtr msg) const { + (void)msg; // supress warnings for unused variables + RCLCPP_INFO(this->get_logger(), "«sub.name» topic got a message"); +} + +«ENDFOR» +«IF node.publisher.length > 0» +// Timer Callback +void «node.name»::timer_callback(){ + «FOR pub : node.publisher» + auto «check_name(pub.name)»_msg = «pub.message.package.name»::msg::«pub.message.name»(); + //«check_name(pub.name)»_msg = ... + «check_name(pub.name)»_->publish(«check_name(pub.name)»_msg); + RCLCPP_INFO(this->get_logger(), "«pub.name» publisher active"); +«ENDFOR» +} +«ENDIF» + +«FOR service : node.serviceserver» +// Service Handler +void «node.name»::«check_name(service.name)»_handle( const std::shared_ptr request_header, + const std::shared_ptr<«service.service.package.name»::srv::«service.service.name»::Request> request_, + const std::shared_ptr<«service.service.package.name»::srv::«service.service.name»::Response> response_){ + (void)request_header; + RCLCPP_INFO( this->get_logger(), "trigger service '%s'","«service.name»"); +} +«ENDFOR» + +} // namespace «pkg_name» + +#include "rclcpp_components/register_node_macro.hpp" + +RCLCPP_COMPONENTS_REGISTER_NODE(«pkg_name»::«node.name») ''' def List getPkgDependencies(Package pkg){