diff --git a/plugins/de.fraunhofer.ipa.ros.feature/feature.xml b/plugins/de.fraunhofer.ipa.ros.feature/feature.xml index 8d90d570..ba0aafbe 100644 --- a/plugins/de.fraunhofer.ipa.ros.feature/feature.xml +++ b/plugins/de.fraunhofer.ipa.ros.feature/feature.xml @@ -197,4 +197,11 @@ POSSIBILITY OF SUCH DAMAGE. version="3.0.0.qualifier" unpack="false"/> + + diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/.classpath b/plugins/de.fraunhofer.ipa.roscode.generator/.classpath new file mode 100644 index 00000000..45427fef --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/.project b/plugins/de.fraunhofer.ipa.roscode.generator/.project new file mode 100644 index 00000000..87835c34 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/.project @@ -0,0 +1,34 @@ + + + de.fraunhofer.ipa.roscode.generator + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.xtext.ui.shared.xtextNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + + diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/.settings/org.eclipse.core.resources.prefs b/plugins/de.fraunhofer.ipa.roscode.generator/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..99f26c02 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/.settings/org.eclipse.jdt.core.prefs b/plugins/de.fraunhofer.ipa.roscode.generator/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..49cf78d8 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=19 +org.eclipse.jdt.core.compiler.compliance=19 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=19 diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/META-INF/MANIFEST.MF b/plugins/de.fraunhofer.ipa.roscode.generator/META-INF/MANIFEST.MF new file mode 100644 index 00000000..f5b6b455 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/META-INF/MANIFEST.MF @@ -0,0 +1,24 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: de.fraunhofer.ipa.roscode.generator;singleton:=true +Bundle-Version: 3.0.0.qualifier +Bundle-Vendor: %providerName +Export-Package: de.fraunhofer.ipa.roscode.generator +Bundle-RequiredExecutionEnvironment: JavaSE-19 +Import-Package: javax.inject;version="1.0.0", + org.eclipse.ui.handlers, + org.eclipse.xtext.ui.resource +Bundle-ActivationPolicy: lazy +Bundle-Activator: de.fraunhofer.ipa.roscode.generator.Activator +Require-Bundle: org.eclipse.xtext.builder, + org.eclipse.xtext.ui, + de.fraunhofer.ipa.ros2.xtext;bundle-version="3.0.0", + de.fraunhofer.ipa.ros2.xtext.ide;bundle-version="3.0.0", + de.fraunhofer.ipa.ros2.xtext.ui;bundle-version="3.0.0", + de.fraunhofer.ipa.ros.xtext;bundle-version="3.0.0", + de.fraunhofer.ipa.ros.xtext.ide;bundle-version="3.0.0", + de.fraunhofer.ipa.ros.xtext.ui;bundle-version="3.0.0", + de.fraunhofer.ipa.ros;bundle-version="3.0.0", + de.fraunhofer.ipa.ros.edit;bundle-version="3.0.0", + de.fraunhofer.ipa.ros.editor;bundle-version="3.0.0" diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/build.properties b/plugins/de.fraunhofer.ipa.roscode.generator/build.properties new file mode 100644 index 00000000..41a2e90a --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/build.properties @@ -0,0 +1,17 @@ +source.. = src/,\ + xtend-gen/ +bin.includes = .,\ + META-INF/,\ + plugin.xml +bin.excludes = **/*.mwe2,\ + **/*.xtend +additional.bundles = org.eclipse.xtext.xbase,\ + org.eclipse.xtext.common.types,\ + org.eclipse.xtext.xtext.generator,\ + org.eclipse.emf.codegen.ecore,\ + org.eclipse.emf.mwe.utils,\ + org.eclipse.emf.mwe2.lib,\ + org.objectweb.asm,\ + org.apache.commons.logging,\ + org.apache.log4j,\ + com.ibm.icu \ No newline at end of file diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/plugin.xml b/plugins/de.fraunhofer.ipa.roscode.generator/plugin.xml new file mode 100644 index 00000000..90ab3c4e --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/plugin.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/pom.xml b/plugins/de.fraunhofer.ipa.roscode.generator/pom.xml new file mode 100644 index 00000000..bf60bf56 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + de.fraunhofer.ipa.ros + de.fraunhofer.ipa.ros.parent + 2.0.0-SNAPSHOT + ../de.fraunhofer.ipa.ros.parent/pom.xml + + de.fraunhofer.ipa.roscode.generator + eclipse-plugin + + + + + org.eclipse.xtend + xtend-maven-plugin + + + org.apache.maven.plugins + maven-clean-plugin + + + xtend-gen-clean + clean + + + + + + diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Activator.java b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Activator.java new file mode 100644 index 00000000..d50e613d --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Activator.java @@ -0,0 +1,7 @@ +package de.fraunhofer.ipa.roscode.generator; + +import de.fraunhofer.ipa.ros2.xtext.ui.internal.XtextActivator; + +public class Activator extends XtextActivator { + +} diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationHandler.java b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationHandler.java new file mode 100644 index 00000000..db86d9aa --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationHandler.java @@ -0,0 +1,82 @@ +package de.fraunhofer.ipa.roscode.generator; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.xtext.builder.EclipseResourceFileSystemAccess2; +import org.eclipse.xtext.generator.GeneratorContext; +import org.eclipse.xtext.generator.IOutputConfigurationProvider; +import org.eclipse.xtext.generator.OutputConfiguration; +import org.eclipse.xtext.resource.IResourceDescriptions; +import org.eclipse.xtext.ui.resource.IResourceSetProvider; + +import com.google.inject.Inject; +import com.google.inject.Provider; + +import de.fraunhofer.ipa.roscode.generator.RosCodeGenerator;; + +public class GenerationHandler extends AbstractHandler implements IHandler { + + @Inject + private Provider fileAccessProvider; + + @Inject + IResourceDescriptions resourceDescriptions; + + @Inject + IResourceSetProvider resourceSetProvider; + + static Map getOutputConfigurationsAsMap(IOutputConfigurationProvider provider) { + Map outputs = new HashMap(); + for(OutputConfiguration c: provider.getOutputConfigurations()) { + outputs.put(c.getName(), c); + } + return outputs; + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + Object firstElement = structuredSelection.getFirstElement(); + if (firstElement instanceof IFile) { + IFile file = (IFile) firstElement; + IProject project = file.getProject(); + + final EclipseResourceFileSystemAccess2 fsa = fileAccessProvider.get(); + fsa.setProject(project); + fsa.setOutputConfigurations(getOutputConfigurationsAsMap(new CustomOutputProvider())); + fsa.setMonitor(new NullProgressMonitor()); + + URI uri = URI.createPlatformResourceURI(file.getFullPath().toString(), true); + ResourceSet rs = resourceSetProvider.get(project); + Resource r = rs.getResource(uri, true); + + RosCodeGenerator generator = new RosCodeGenerator(); + generator.doGenerate(r, fsa, new GeneratorContext()); + + } + } + return null; + } + + @Override + public boolean isEnabled() { + return true; + } + } diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationRos2CppHandler.java b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationRos2CppHandler.java new file mode 100644 index 00000000..658695dd --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationRos2CppHandler.java @@ -0,0 +1,82 @@ +package de.fraunhofer.ipa.roscode.generator; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.xtext.builder.EclipseResourceFileSystemAccess2; +import org.eclipse.xtext.generator.GeneratorContext; +import org.eclipse.xtext.generator.IOutputConfigurationProvider; +import org.eclipse.xtext.generator.OutputConfiguration; +import org.eclipse.xtext.resource.IResourceDescriptions; +import org.eclipse.xtext.ui.resource.IResourceSetProvider; + +import com.google.inject.Inject; +import com.google.inject.Provider; + +import de.fraunhofer.ipa.roscode.generator.RosCodeGenerator;; + +public class GenerationRos2CppHandler extends AbstractHandler implements IHandler { + + @Inject + private Provider fileAccessProvider; + + @Inject + IResourceDescriptions resourceDescriptions; + + @Inject + IResourceSetProvider resourceSetProvider; + + static Map getOutputConfigurationsAsMap(IOutputConfigurationProvider provider) { + Map outputs = new HashMap(); + for(OutputConfiguration c: provider.getOutputConfigurations()) { + outputs.put(c.getName(), c); + } + return outputs; + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + Object firstElement = structuredSelection.getFirstElement(); + if (firstElement instanceof IFile) { + IFile file = (IFile) firstElement; + IProject project = file.getProject(); + + final EclipseResourceFileSystemAccess2 fsa = fileAccessProvider.get(); + fsa.setProject(project); + fsa.setOutputConfigurations(getOutputConfigurationsAsMap(new CustomOutputProvider())); + fsa.setMonitor(new NullProgressMonitor()); + + URI uri = URI.createPlatformResourceURI(file.getFullPath().toString(), true); + ResourceSet rs = resourceSetProvider.get(project); + Resource r = rs.getResource(uri, true); + + Ros2CppCodeGenerator generator = new Ros2CppCodeGenerator(); + generator.doGenerate(r, fsa, new GeneratorContext()); + + } + } + return null; + } + + @Override + public boolean isEnabled() { + return true; + } + } \ No newline at end of file diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationRos2PythonHandler.java b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationRos2PythonHandler.java new file mode 100644 index 00000000..af69301c --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/GenerationRos2PythonHandler.java @@ -0,0 +1,82 @@ +package de.fraunhofer.ipa.roscode.generator; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.xtext.builder.EclipseResourceFileSystemAccess2; +import org.eclipse.xtext.generator.GeneratorContext; +import org.eclipse.xtext.generator.IOutputConfigurationProvider; +import org.eclipse.xtext.generator.OutputConfiguration; +import org.eclipse.xtext.resource.IResourceDescriptions; +import org.eclipse.xtext.ui.resource.IResourceSetProvider; + +import com.google.inject.Inject; +import com.google.inject.Provider; + +import de.fraunhofer.ipa.roscode.generator.RosCodeGenerator;; + +public class GenerationRos2PythonHandler extends AbstractHandler implements IHandler { + + @Inject + private Provider fileAccessProvider; + + @Inject + IResourceDescriptions resourceDescriptions; + + @Inject + IResourceSetProvider resourceSetProvider; + + static Map getOutputConfigurationsAsMap(IOutputConfigurationProvider provider) { + Map outputs = new HashMap(); + for(OutputConfiguration c: provider.getOutputConfigurations()) { + outputs.put(c.getName(), c); + } + return outputs; + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + Object firstElement = structuredSelection.getFirstElement(); + if (firstElement instanceof IFile) { + IFile file = (IFile) firstElement; + IProject project = file.getProject(); + + final EclipseResourceFileSystemAccess2 fsa = fileAccessProvider.get(); + fsa.setProject(project); + fsa.setOutputConfigurations(getOutputConfigurationsAsMap(new CustomOutputProvider())); + fsa.setMonitor(new NullProgressMonitor()); + + URI uri = URI.createPlatformResourceURI(file.getFullPath().toString(), true); + ResourceSet rs = resourceSetProvider.get(project); + Resource r = rs.getResource(uri, true); + + Ros2PythonCodeGenerator generator = new Ros2PythonCodeGenerator(); + generator.doGenerate(r, fsa, new GeneratorContext()); + + } + } + return null; + } + + @Override + public boolean isEnabled() { + return true; + } + } \ No newline at end of file diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/ParameterGeneratorHelpers.xtend b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/ParameterGeneratorHelpers.xtend new file mode 100644 index 00000000..77c75ac1 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/ParameterGeneratorHelpers.xtend @@ -0,0 +1,95 @@ +package de.fraunhofer.ipa.roscode.generator + +import ros.impl.ParameterStringTypeImpl +import ros.impl.ParameterStringImpl +import ros.impl.ParameterIntegerTypeImpl +import ros.impl.ParameterIntegerImpl +import ros.impl.ParameterDoubleTypeImpl +import ros.impl.ParameterDoubleImpl +import ros.impl.ParameterBooleanTypeImpl +import ros.impl.ParameterBooleanImpl +import ros.impl.ParameterBase64TypeImpl +import ros.impl.ParameterBase64Impl +import ros.impl.ParameterListTypeImpl +import org.eclipse.emf.common.util.EList +import ros.ParameterType +import ros.impl.ParameterStructTypeImpl +import ros.impl.ParameterArrayTypeImpl + +abstract class ParameterGeneratorHelpers { + + def String get_param_declaration_str(String param_type, String param_name, String delim, Boolean has_value) + + def Boolean is_array(EList list) { + var t = list.get(0).class + for (type : list) { + if (!(type.class == t)) { + return false; + } + } + return true; + } + + def Pair compile_struct(ParameterStructTypeImpl struct, String name) { + var struct_str = ""; + var struct_type_str = ""; + for (elem : struct.getParameterstructypetmember()) { + if (elem.getType() instanceof ParameterStructTypeImpl) { + var elem_pair = compile_struct(elem.getType() as ParameterStructTypeImpl, name + "." + elem.getName()); + struct_str += elem_pair.getKey(); + struct_type_str += elem_pair.getValue(); + } else { + var param_pair = get_param_type(elem.getType()); + var elem_name = name + "." + elem.getName(); + struct_str += get_param_declaration_str(param_pair.getKey(), elem_name, ".", (param_pair.getValue != "")); + + struct_type_str += param_pair.getKey() + " " + elem_name.replace(".", "_") + "_;\n"; + } + } + return new Pair(struct_str, struct_type_str); + } + + def Pair get_param_type(ParameterType type) { + var has_value = type.eContents.length > 0; + var param_val = "" + if (type instanceof ParameterStringTypeImpl) { + if (has_value) { + param_val = ", \"" + (type.eContents.get(0) as ParameterStringImpl).getValue() + "\""; + } + return new Pair("std::string", param_val); + } else if (type instanceof ParameterIntegerTypeImpl) { + if (has_value) { + param_val = ", " + (type.eContents.get(0) as ParameterIntegerImpl).getValue(); + } + return new Pair("int", param_val); + } else if (type instanceof ParameterDoubleTypeImpl) { + if (has_value) { + param_val = ", " + (type.eContents.get(0) as ParameterDoubleImpl).getValue(); + } + return new Pair("double", param_val); + } else if (type instanceof ParameterBooleanTypeImpl) { + if (has_value) { + param_val = ", " + (type.eContents.get(0) as ParameterBooleanImpl).isValue(); + } + return new Pair("bool", param_val); + /*} else if (type instanceof ParameterBase64TypeImpl) { + if (has_value) { + param_val = ", " + (type.eContents.get(0) as ParameterBase64Impl).getValue(); + } + return new Pair("uint64_t", param_val); // not sure about this*/ + } else if (type instanceof ParameterListTypeImpl) { + var list = (type as ParameterListTypeImpl).getSequence(); + if (!list.is_array()) { + return new Pair("", ""); + } + var list_type = get_param_type(list.get(0)); + return new Pair("std::vector<" + list_type.getKey() + ">", ""); + } else if (type instanceof ParameterArrayTypeImpl) { + var array = type as ParameterArrayTypeImpl; + return new Pair("std::vector<" + get_param_type(array.getType()).getKey() + ">", ""); + } + + return new Pair("", ""); + } + +} diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2CppCodeGenerator.xtend b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2CppCodeGenerator.xtend new file mode 100644 index 00000000..5d5e626c --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2CppCodeGenerator.xtend @@ -0,0 +1,291 @@ +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 ros.Package +import ros.impl.ParameterStructTypeImpl +import ros.Node + +/** + * Generates code from your model files on save. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation + */ +class Ros2CppCodeGenerator extends AbstractGenerator { + + String resourcepath + String import_msgs + int char_i + Node node + List PkgsList + Set set + ParameterGeneratorHelpers parameter_helper = new ParameterGeneratorHelpers() { + + override get_param_declaration_str(String param_type, String param_name, String delim, Boolean has_value) { + var struct_str = ""; + struct_str += "this->declare_parameter"; + if (has_value) { + struct_str += "<" + param_type + ">"; + } + struct_str += "(\"" + param_name + "\");\n"; + struct_str += "this->get_parameter(\"" + param_name + "\", " + param_name.replace(delim, "_") + "_);\n\n"; + + return struct_str; + } + + }; + + override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { + resourcepath = resource.URI.toString(); + if (! resourcepath.contains("/ros-input")) { + for (pkg : resource.allContents.toIterable.filter(Package)){ + fsa.generateFile(pkg.getName().toLowerCase+"/package.xml",pkg.compile_package_xml) + 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) + + } + } + } + } + + +def compile_package_xml(Package pkg)''' + + + + «pkg.name» + 0.0.0 + This package contains the implementation of the node «pkg.artifact.get(0).node.name» + Jane Doe + Jane Doe + Apache 2.0 + + ament_cmake + + boost + rclcpp + «FOR depend_pkg:pkg.getPkgDependencies» + «depend_pkg» + «ENDFOR» + + ament_lint_auto + ament_lint_common + + + ament_cmake + + +''' + +def compile_CMakeLists(Package pkg)''' +cmake_minimum_required(VERSION 3.5) +project(«pkg.name») + +# 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) + «FOR depend_pkg:pkg.getPkgDependencies» +find_package(«depend_pkg» REQUIRED) + «ENDFOR» + +«FOR art:pkg.artifact» +add_executable(«art.name» src/«art.node.name».cpp) +ament_target_dependencies(«art.name» rclcpp «FOR depend_pkg:pkg.getPkgDependencies»«depend_pkg» «ENDFOR») + +install(TARGETS + «art.name» + DESTINATION lib/${PROJECT_NAME}) +«ENDFOR» + +ament_package() +''' + +def compile_node(Node node) ''' +#include +#include +#include +#include +#include + +#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» + «FOR sub : node.subscriber» +#include <«sub.message.package.name»/msg/«check_message_include(sub.message.name)».hpp> + «ENDFOR» + «FOR srvserver : node.serviceserver» +#include <«srvserver.service.package.name»/srv/«check_message_include(srvserver.service.name)».hpp> + «ENDFOR» + «FOR srvclient : node.serviceclient» +#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"); +} + +class «node.name» : public rclcpp::Node { + public: + «node.name»() : Node("«node.name»") { + «FOR param : node.parameter» + «IF (param.type instanceof ParameterStructTypeImpl)» + «parameter_helper.compile_struct(param.type as ParameterStructTypeImpl, param.name).getKey()» + «ELSE» +«var param_pair = parameter_helper.get_param_type(param.type)» + «IF !(param_pair.getKey().empty)» + this->declare_parameter«IF !param_pair.getValue().empty»<«param_pair.getKey()»>«ENDIF»("«param.name»"«param_pair.getValue()»); + this->get_parameter("«param.name»", «param.name»_); + «ENDIF» + «ENDIF» + «ENDFOR» + «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» + } + + private: + «FOR param : node.parameter» + «IF (param.type instanceof ParameterStructTypeImpl)» + «parameter_helper.compile_struct(param.type as ParameterStructTypeImpl, param.name).getValue()» + «ELSE» + «var param_pair = parameter_helper.get_param_type(param.type)» + «IF !(param_pair.getKey().empty)» + «param_pair.getKey()» «param.name»_; + «ENDIF» + «ENDIF» + «ENDFOR» + + «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", msg); + } + + 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» + } + rclcpp::TimerBase::SharedPtr timer_; + «ENDIF» + + «FOR client : node.serviceclient»rclcpp::Client<«client.service.package.name»::srv::«client.service.name»>::SharedPtr «check_name(client.name)»_; + «ENDFOR» + + «FOR service : node.serviceserver» + // Service Handler + void «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; + (void)request_; + (void)response_; + RCLCPP_INFO( this->get_logger(), "trigger service '%s'","«service.name»"); + } + rclcpp::Service<«service.service.package.name»::srv::«service.service.name»>::SharedPtr «check_name(service.name)»_; + «ENDFOR» +}; + +int main(int argc, char * argv[]) +{ + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared<«node.name»>()); + rclcpp::shutdown(); + return 0; +} + ''' + + def List getPkgDependencies(Package pkg){ + set=new HashSet() + PkgsList = new ArrayList() + for (art:pkg.artifact){ + node=art.node + for (pub:node.publisher){set.add(pub.message.package.name)} + for (sub:node.subscriber){set.add(sub.message.package.name)} + for (srvserver:node.serviceserver){set.add(srvserver.service.package.name)} + for (srvclient:node.serviceclient){set.add(srvclient.service.package.name)} + } + PkgsList.addAll(set) + return PkgsList + } + + def String check_message_include(String message_name){ + import_msgs = message_name.toFirstLower; + for (char_i =0; char_i < import_msgs.length; char_i++ ){ + if (Character.isUpperCase(import_msgs.charAt(char_i))){ + import_msgs = import_msgs.substring(0,char_i)+"_"+Character.toLowerCase(import_msgs.charAt(char_i))+import_msgs.substring(char_i+1); + } + } + return import_msgs; + } + + def String check_name(String interface_name){ + return interface_name.replace("/",""); + } + +} diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2PythonCodeGenerator.xtend b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2PythonCodeGenerator.xtend new file mode 100644 index 00000000..c933dbf1 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/Ros2PythonCodeGenerator.xtend @@ -0,0 +1,189 @@ +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 ros.Package +import ros.impl.ParameterStructTypeImpl +import ros.Node + +/** + * Generates code from your model files on save. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation + */ +class Ros2PythonCodeGenerator extends AbstractGenerator { + + String resourcepath + String import_msgs + int char_i + Node node + List PkgsList + Set set + ParameterGeneratorHelpers parameter_helper = new ParameterGeneratorHelpers() { + + override get_param_declaration_str(String param_type, String param_name, String delim, Boolean has_value) { + var struct_str = ""; + struct_str += "this->declare_parameter"; + if (has_value) { + struct_str += "<" + param_type + ">"; + } + struct_str += "(\"" + param_name + "\");\n"; + struct_str += "this->get_parameter(\"" + param_name + "\", " + param_name.replace(delim, "_") + "_);\n\n"; + + return struct_str; + } + + }; + + override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { + resourcepath = resource.URI.toString(); + if (! resourcepath.contains("/ros-input")) { + for (pkg : resource.allContents.toIterable.filter(Package)){ + fsa.generateFile(pkg.getName().toLowerCase+"/package.xml",pkg.compile_package_xml) + fsa.generateFile(pkg.getName().toLowerCase+"/"+pkg.getName().toLowerCase+"/__init__.py","") + fsa.generateFile(pkg.getName().toLowerCase+"/resource/"+pkg.getName().toLowerCase,"") + fsa.generateFile(pkg.getName().toLowerCase+"/setup.cfg",pkg.compile_setup_cfg) + fsa.generateFile(pkg.getName().toLowerCase+"/setup.py",pkg.compile_setup_py) + + + for (art : pkg.artifact){ + node = art.node + fsa.generateFile(pkg.getName().toLowerCase+"/"+pkg.getName().toLowerCase+"/"+node.name+".py",node.compile_node) + } + } + } + } + + +def compile_package_xml(Package pkg)''' + + + + «pkg.name» + 0.0.0 + This package contains the implementation of the node «pkg.artifact.get(0).node.name» + Name + Name + Apache 2.0 + + ament_cmake + + boost + rclpy + «FOR depend_pkg:pkg.getPkgDependencies» + «depend_pkg» + «ENDFOR» + + ament_lint_auto + ament_lint_common + + + ament_cmake + + +''' + +def compile_setup_cfg(Package pkg)''' +[develop] +script-dir=$base/lib/«pkg.getName().toLowerCase» +[install] +install-scripts=$base/lib/«pkg.getName().toLowerCase» +''' + +def compile_setup_py(Package pkg)''' +from setuptools import setup + +package_name = '«pkg.getName().toLowerCase»' + +setup( + name=package_name, + version='0.0.0', + packages=[package_name], + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + ('share/' + package_name, ['package.xml']), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Name', + maintainer_email='your@email.com', + description='TODO: Package description', + license='TODO: License declaration', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + ], + }, +) +''' + +def compile_node(Node node) ''' +import rclpy +from rclpy.node import Node + + «FOR pub : node.publisher» +from «pub.message.package.name».msg import «check_message_include(pub.message.name)» + «ENDFOR» + «FOR sub : node.subscriber» +from «sub.message.package.name».msg import «check_message_include(sub.message.name)» + «ENDFOR» + «FOR srvserver : node.serviceserver» +from «srvserver.service.package.name».srv import «check_message_include(srvserver.service.name)» + «ENDFOR» + «FOR srvclient : node.serviceclient» +from «srvclient.service.package.name».srv import «check_message_include(srvclient.service.name)» + «ENDFOR» + + +class «node.name»(Node): + def __init__(self): + super().__init__("«node.name»") + self.get_logger().info("This node just says 'Hello'") +def main(args=None): + rclpy.init(args=args) + node = «node.name»() + rclpy.spin(node) + node.destroy_node() + rclpy.shutdown() +if __name__ == "__main__": + main() + ''' + + def List getPkgDependencies(Package pkg){ + set=new HashSet() + PkgsList = new ArrayList() + for (art:pkg.artifact){ + node=art.node + for (pub:node.publisher){ if (pub.message.package !== null) set.add(pub.message.package.name)} + for (sub:node.subscriber){ if (sub.message.package !== null) set.add(sub.message.package.name)} + for (srvserver:node.serviceserver){if (srvserver.service.package !== null) set.add(srvserver.service.package.name)} + for (srvclient:node.serviceclient){if (srvclient.service.package !== null) set.add(srvclient.service.package.name)} + } + PkgsList.addAll(set) + return PkgsList + } + + def String check_message_include(String message_name){ + import_msgs = message_name.toFirstLower; + for (char_i =0; char_i < import_msgs.length; char_i++ ){ + if (Character.isUpperCase(import_msgs.charAt(char_i))){ + import_msgs = import_msgs.substring(0,char_i)+"_"+Character.toLowerCase(import_msgs.charAt(char_i))+import_msgs.substring(char_i+1); + } + } + return import_msgs; + } + + def String check_name(String interface_name){ + return interface_name.replace("/",""); + } + +} diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/RosCodeGenerator.xtend b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/RosCodeGenerator.xtend new file mode 100644 index 00000000..93751f77 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/RosCodeGenerator.xtend @@ -0,0 +1,211 @@ +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 ros.Node +import ros.Package +import ros.Publisher +import ros.ServiceClient +import ros.ServiceServer +import ros.Subscriber +import ros.impl.ParameterStructTypeImpl + +class CustomOutputProvider implements IOutputConfigurationProvider { + public final static String DEFAULT_OUTPUT = "DEFAULT_OUTPUT" + + + override Set getOutputConfigurations() { + var OutputConfiguration default_config = new OutputConfiguration(DEFAULT_OUTPUT) + default_config.setDescription("DEFAULT_OUTPUT"); + default_config.setOutputDirectory("./src-gen/"); + default_config.setOverrideExistingResources(true); + default_config.setCreateOutputDirectory(true); + default_config.setCleanUpDerivedResources(true); + default_config.setSetDerivedProperty(true); + return newHashSet(default_config) + } +} + +/** + * Generates code from your model files on save. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation + */ +class RosCodeGenerator extends AbstractGenerator { + + + String resourcepath + Node node + List PkgsList + Set set + ParameterGeneratorHelpers parameter_helper = new ParameterGeneratorHelpers() { + + override get_param_declaration_str(String param_type, String param_name, String delim, Boolean has_value) { + var struct_str = ""; + struct_str += param_type + " " + param_name.replace(delim, "_") + "_;\n"; + struct_str += "n.param(\"" + param_name.replace(delim, "/") + "\", " + param_name.replace(delim, "_") + "_);\n\n"; + + return struct_str; + } + + }; + + override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { + resourcepath = resource.URI.toString(); + if (! resourcepath.contains("/ros-input")) { + for (pkg : resource.allContents.toIterable.filter(Package)){ + fsa.generateFile(pkg.getName().toLowerCase+"/package.xml",pkg.compile_package_xml) + 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) + + } + } + } + } + +def compile_package_xml(Package pkg)''' + + + + «pkg.name» + 0.0.0 + This package contains the implementation of the node «pkg.artifact.get(0).node.name» + Jane Doe + Jane Doe + Apache 2.0 + + catkin + + boost + roscpp + «FOR depend_pkg:pkg.getPkgDependencies» + «depend_pkg» + «ENDFOR» + + +''' + +def compile_CMakeLists(Package pkg)''' +cmake_minimum_required(VERSION 3.0.2) +project(«pkg.name») + +find_package(catkin REQUIRED COMPONENTS roscpp «FOR depend_pkg:pkg.getPkgDependencies»«depend_pkg» «ENDFOR») + +catkin_package( + CATKIN_DEPENDS roscpp «FOR depend_pkg:pkg.getPkgDependencies»«depend_pkg» «ENDFOR» +) + +### Build ### + +include_directories(${catkin_INCLUDE_DIRS}) + +«FOR art:pkg.artifact» +add_executable(«art.name» src/«art.node.name».cpp) +add_dependencies(«art.name» ${catkin_EXPORTED_TARGETS}) +target_link_libraries(«art.name» ${catkin_LIBRARIES}) + +«ENDFOR» +### Install ### +install(TARGETS «FOR art:pkg.artifact»«art.name»«ENDFOR» + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) +''' + +def compile_node(Node node) ''' +#include + «FOR pub : node.publisher» +#include <«pub.message.package.name»/«pub.message.name».h> + «ENDFOR» + «FOR sub : node.subscriber» +#include <«sub.message.package.name»/«sub.message.name».h> + «ENDFOR» + «FOR srvserver : node.serviceserver» +#include <«srvserver.service.package.name»/«srvserver.service.name».h> + «ENDFOR» + «FOR srvclient : node.serviceclient» +#include <«srvclient.service.package.name»/«srvclient.service.name».h> + «ENDFOR» + + «FOR srvserver : node.serviceserver» +bool «srvserver.name»_cb («srvserver.service.package.name»::«srvserver.service.name»::Request &req, «srvserver.service.package.name»::«srvserver.service.name»::Response &res){ + return true; +} + «ENDFOR» + «FOR sub : node.subscriber» +void «sub.name»_cb (const «sub.message.package.name»::«sub.message.name» msg){} + «ENDFOR» + + +int main(int argc, char **argv) +{ + ros::init(argc, argv, "«node.name»"); + ros::NodeHandle n; + «FOR param : node.parameter» + «IF (param.type instanceof ParameterStructTypeImpl)» + «parameter_helper.compile_struct(param.type as ParameterStructTypeImpl, param.name).getKey()» + «ELSE» + «var param_pair = parameter_helper.get_param_type(param.type)» + «IF !(param_pair.getKey().empty)» + «param_pair.getKey()» «param.name»_; + n.param«IF!(param_pair.getValue().empty)»<«param_pair.getKey()»>«ENDIF»("«param.name»", «param.name»_«param_pair.getValue()»); + «ENDIF» + «ENDIF» +«ENDFOR» +«FOR pub : node.publisher» +«pub.compile» +«ENDFOR» +«FOR sub : node.subscriber» +«sub.compile» +«ENDFOR» +«FOR srvserver : node.serviceserver» +«srvserver.compile» +«ENDFOR» +«FOR srvclient : node.serviceclient» +«srvclient.compile» +«ENDFOR» + + ros::spin(); + + return 0; +} + ''' + +def compile(Publisher pub) +''' ros::Publisher «pub.name»_pub = n.advertise<«pub.message.package.name»::«pub.message.name»>("«pub.name»", 10);''' +def compile(Subscriber sub) +''' ros::Subscriber «sub.name» = n.subscribe("«sub.name»", 10, «sub.name»_cb);''' +def compile(ServiceServer srvserver) +''' ros::ServiceServer «srvserver.name» = n.advertiseService("«srvserver.name»", «srvserver.name»_cb);''' +def compile(ServiceClient srvclient) +''' ros::ServiceClient «srvclient.name» = n.serviceClient<«srvclient.service.package.name»::«srvclient.service.name»>("«srvclient.name»");''' + + + def List getPkgDependencies(Package pkg){ + set=new HashSet() + PkgsList = new ArrayList() + for (art:pkg.artifact){ + node=art.node + for (pub:node.publisher){set.add(pub.message.package.name)} + for (sub:node.subscriber){set.add(sub.message.package.name)} + for (srvserver:node.serviceserver){set.add(srvserver.service.package.name)} + for (srvclient:node.serviceclient){set.add(srvclient.service.package.name)} + } + PkgsList.addAll(set) + return PkgsList + } + +} diff --git a/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/RosCodeGeneratorExecutableExtensionFactory.java b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/RosCodeGeneratorExecutableExtensionFactory.java new file mode 100644 index 00000000..66bc4a47 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.roscode.generator/src/de/fraunhofer/ipa/roscode/generator/RosCodeGeneratorExecutableExtensionFactory.java @@ -0,0 +1,19 @@ +package de.fraunhofer.ipa.roscode.generator; + +import org.eclipse.xtext.ui.guice.AbstractGuiceAwareExecutableExtensionFactory; +import org.osgi.framework.Bundle; + +import com.google.inject.Injector; + +public class RosCodeGeneratorExecutableExtensionFactory extends AbstractGuiceAwareExecutableExtensionFactory { + + @Override + protected Bundle getBundle() { + return Activator.getInstance().getBundle(); + } + + @Override + protected Injector getInjector() { + return Activator.getInstance().getInjector(Activator.DE_FRAUNHOFER_IPA_ROS2_ROS2); + } + }