Commit 5b91a754 authored by Alexander David Hellwig's avatar Alexander David Hellwig
Browse files

Merge branch 'MsgProjectGeneration' into 'master'

Msg project generation

See merge request !8
parents 86ac6853 55b65b9c
Pipeline #118296 passed with stages
in 2 minutes and 41 seconds
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<groupId>de.monticore.lang.monticar</groupId> <groupId>de.monticore.lang.monticar</groupId>
<artifactId>embedded-montiarc-math-rosmsg-generator</artifactId> <artifactId>embedded-montiarc-math-rosmsg-generator</artifactId>
<version>0.1.2-SNAPSHOT</version> <version>0.1.3-SNAPSHOT</version>
<!-- == PROJECT DEPENDENCIES ============================================= --> <!-- == PROJECT DEPENDENCIES ============================================= -->
......
package de.monticore.lang.monticar.generator.rosmsg; package de.monticore.lang.monticar.generator.rosmsg;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAPortInstanceSymbol;
import de.monticore.lang.monticar.common2._ast.ASTCommonMatrixType; import de.monticore.lang.monticar.common2._ast.ASTCommonMatrixType;
import de.monticore.lang.monticar.generator.rosmsg.util.CMakeListsViewModel;
import de.monticore.lang.monticar.generator.rosmsg.util.FileContent; import de.monticore.lang.monticar.generator.rosmsg.util.FileContent;
import de.monticore.lang.monticar.generator.rosmsg.util.RosMsgTemplates;
import de.monticore.lang.monticar.struct._symboltable.StructFieldDefinitionSymbol; import de.monticore.lang.monticar.struct._symboltable.StructFieldDefinitionSymbol;
import de.monticore.lang.monticar.struct._symboltable.StructSymbol; import de.monticore.lang.monticar.struct._symboltable.StructSymbol;
import de.monticore.lang.monticar.ts.MCASTTypeSymbol; import de.monticore.lang.monticar.ts.MCASTTypeSymbol;
import de.monticore.lang.monticar.ts.MCTypeSymbol; import de.monticore.lang.monticar.ts.MCTypeSymbol;
import de.monticore.lang.monticar.ts.MontiCarTypeSymbol; import de.monticore.lang.monticar.ts.MontiCarTypeSymbol;
import de.monticore.lang.monticar.ts.references.MCTypeReference; import de.monticore.lang.monticar.ts.references.MCTypeReference;
import de.monticore.symboltable.references.SymbolReference;
import de.se_rwth.commons.logging.Log; import de.se_rwth.commons.logging.Log;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
...@@ -15,6 +20,7 @@ import java.io.File; ...@@ -15,6 +20,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GeneratorRosMsg { public class GeneratorRosMsg {
private String path; private String path;
...@@ -53,7 +59,7 @@ public class GeneratorRosMsg { ...@@ -53,7 +59,7 @@ public class GeneratorRosMsg {
public List<FileContent> generateStrings(MCTypeReference<? extends MCTypeSymbol> typeReference) { public List<FileContent> generateStrings(MCTypeReference<? extends MCTypeSymbol> typeReference) {
//is typeReference basic type or struct? //is typeReference basic type or struct?
if (getRosType(currentPackageName, typeReference) != null) { if (getRosType(currentPackageName, typeReference, ros2mode) != null) {
MCTypeSymbol type = typeReference.getReferencedSymbol(); MCTypeSymbol type = typeReference.getReferencedSymbol();
List<FileContent> res = new ArrayList<>(); List<FileContent> res = new ArrayList<>();
if (type instanceof StructSymbol) { if (type instanceof StructSymbol) {
...@@ -77,7 +83,7 @@ public class GeneratorRosMsg { ...@@ -77,7 +83,7 @@ public class GeneratorRosMsg {
String definition = structSymbol.getStructFieldDefinitions().stream() String definition = structSymbol.getStructFieldDefinitions().stream()
.filter(sfds -> sfds.getType().existsReferencedSymbol()) .filter(sfds -> sfds.getType().existsReferencedSymbol())
.map(sfds -> getInMsgRosType(currentPackageName, sfds.getType().getReferencedSymbol(), ros2mode) + " " + sfds.getName()) .map(sfds -> getInMsgRosType(currentPackageName, sfds.getType().getReferencedSymbol(), ros2mode) + " " + getFieldName(sfds, ros2mode))
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));
FileContent fc = new FileContent(); FileContent fc = new FileContent();
...@@ -114,10 +120,9 @@ public class GeneratorRosMsg { ...@@ -114,10 +120,9 @@ public class GeneratorRosMsg {
} }
private static String getFieldName(StructFieldDefinitionSymbol sfds, boolean ros2mode) { private static String getFieldName(StructFieldDefinitionSymbol sfds, boolean ros2mode) {
if (ros2mode){ if (ros2mode) {
return sfds.getName().toLowerCase(); return sfds.getName().toLowerCase();
} } else {
else{
return sfds.getName(); return sfds.getName();
} }
...@@ -229,13 +234,12 @@ public class GeneratorRosMsg { ...@@ -229,13 +234,12 @@ public class GeneratorRosMsg {
} }
private static String getTargetName(StructSymbol structSymbol, boolean ros2mode) { private static String getTargetName(StructSymbol structSymbol, boolean ros2mode) {
if (ros2mode){ if (ros2mode) {
return Arrays.stream(structSymbol.getFullName() return Arrays.stream(structSymbol.getFullName()
.split("\\.")) .split("\\."))
.map(fn -> fn.substring(0, 1).toUpperCase() + fn.substring(1)) .map(fn -> fn.substring(0, 1).toUpperCase() + fn.substring(1))
.collect(Collectors.joining()); .collect(Collectors.joining());
} } else {
else{
return structSymbol.getFullName().replace(".", "_"); return structSymbol.getFullName().replace(".", "_");
} }
} }
...@@ -243,4 +247,61 @@ public class GeneratorRosMsg { ...@@ -243,4 +247,61 @@ public class GeneratorRosMsg {
private static String getFullTargetName(String packageName, StructSymbol structSymbol, boolean ros2mode) { private static String getFullTargetName(String packageName, StructSymbol structSymbol, boolean ros2mode) {
return ros2mode ? packageName + "/msg/" + getTargetName(structSymbol, ros2mode) : packageName + "/" + getTargetName(structSymbol, ros2mode); return ros2mode ? packageName + "/msg/" + getTargetName(structSymbol, ros2mode) : packageName + "/" + getTargetName(structSymbol, ros2mode);
} }
public List<FileContent> generateProjectStrings(List<MCTypeReference<? extends MCTypeSymbol>> typeReferences) {
ArrayList<FileContent> res = new ArrayList<>();
// .msg files
for (MCTypeReference<? extends MCTypeSymbol> tr : typeReferences) {
for (FileContent fc : generateStrings(tr)) {
fc.setFileName("msg/" + fc.getFileName());
res.add(fc);
}
}
if (!ros2mode) {
res.add(getRosCMakeLists(res));
} else {
res.add(getRos2CMakeLists(res));
res.add(getRos2PackageXml());
}
return res;
}
private FileContent getRos2CMakeLists(ArrayList<FileContent> res) {
return new FileContent("CMakeLists.txt", RosMsgTemplates.generateRos2CMakeLists(new CMakeListsViewModel(res)));
}
private FileContent getRos2PackageXml() {
return new FileContent("package.xml", RosMsgTemplates.generateRos2Package());
}
private FileContent getRosCMakeLists(ArrayList<FileContent> res) {
return new FileContent("CMakeLists.txt", RosMsgTemplates.generateRosCMakeLists(new CMakeListsViewModel(res)));
}
public List<File> generateProject(List<MCTypeReference<? extends MCTypeSymbol>> typeReferences) throws IOException {
List<File> res = new ArrayList<>();
List<FileContent> fileContents = generateProjectStrings(typeReferences);
for (FileContent fileContent : fileContents) {
File file = new File(path + "/" + fileContent.getFileName());
FileUtils.write(file, fileContent.getFileContent());
res.add(file);
}
return res;
}
public List<File> generateProject(EMAComponentInstanceSymbol component) throws IOException {
Stream<EMAPortInstanceSymbol> p = component.getPortInstanceList().stream();
Stream<EMAPortInstanceSymbol> subp = component.getSubComponents().stream().flatMap(sc -> sc.getPortInstanceList().stream());
List<MCTypeReference<? extends MCTypeSymbol>> typeReferences = Stream.concat(p, subp)
.map(EMAPortInstanceSymbol::getTypeReference)
.filter(SymbolReference::existsReferencedSymbol)
.filter(mcTypeReference -> mcTypeReference.getReferencedSymbol() instanceof StructSymbol)
.collect(Collectors.toList());
return generateProject(typeReferences);
}
} }
package de.monticore.lang.monticar.generator.rosmsg.util;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CMakeListsViewModel{
public List<String> getFileNamesWithPath() {
return fileContents.stream()
.map(FileContent::getFileName)
.collect(Collectors.toList());
}
public List<String> getFileNamesOnly() {
return fileContents.stream()
.map(FileContent::getFileName)
.map(fn -> Paths.get(fn).getFileName().toString())
.collect(Collectors.toList());
}
private List<FileContent> fileContents;
public CMakeListsViewModel(List<FileContent> fileContents) {
this.fileContents = fileContents;
}
}
...@@ -4,6 +4,14 @@ public class FileContent { ...@@ -4,6 +4,14 @@ public class FileContent {
private String fileName; private String fileName;
private String fileContent; private String fileContent;
public FileContent() {
}
public FileContent(String fileName, String fileContent) {
this.fileName = fileName;
this.fileContent = fileContent;
}
public String getFileName() { public String getFileName() {
return fileName; return fileName;
} }
......
package de.monticore.lang.monticar.generator.rosmsg.util;
import de.se_rwth.commons.logging.Log;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
public class RosMsgTemplates {
private RosMsgTemplates(){
}
private static final Template ROS2_CMAKELISTS;
private static final Template ROS2_PACKAGE_XML;
private static final Template ROS_CMAKELISTS;
static {
Configuration conf = new Configuration(Configuration.VERSION_2_3_23);
conf.setDefaultEncoding("UTF-8");
conf.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
conf.setLogTemplateExceptions(false);
conf.setClassForTemplateLoading(RosMsgTemplates.class, "/templates");
try {
ROS2_CMAKELISTS = conf.getTemplate("ros2.MsgGen.CMakeLists.ftl");
ROS_CMAKELISTS = conf.getTemplate("ros.MsgGen.CMakeLists.ftl");
ROS2_PACKAGE_XML = conf.getTemplate("ros2.MsgGen.Package.ftl");
} catch (IOException e) {
String msg = "could not load templates";
Log.error(msg, e);
throw new RuntimeException(msg, e);
}
}
public static String generateRos2CMakeLists(CMakeListsViewModel viewModel){
HashMap<String, Object> data = new HashMap<>();
data.put("viewModel", viewModel);
return generate(ROS2_CMAKELISTS, data);
}
public static String generateRosCMakeLists(CMakeListsViewModel viewModel){
HashMap<String, Object> data = new HashMap<>();
data.put("viewModel", viewModel);
return generate(ROS_CMAKELISTS, data);
}
public static String generateRos2Package(){
return generate(ROS2_PACKAGE_XML, new HashMap<>());
}
private static String generate(Template template, Object dataForTemplate) {
Log.errorIfNull(template);
Log.errorIfNull(dataForTemplate);
StringWriter sw = new StringWriter();
try {
template.process(dataForTemplate, sw);
} catch (TemplateException | IOException e) {
Log.error("template generation failed, template: " + template.getName(), e);
}
return sw.toString();
}
}
# generated by ros.MsgGen.CMakeLists.ftl
cmake_minimum_required(VERSION 2.8.12)
project(struct_msgs)
find_package(genmsg REQUIRED)
<#list viewModel.getFileNamesOnly() as fileOnly>
add_message_files(FILES ${fileOnly})
</#list>
<#noparse>
set(CATKIN_MESSAGE_GENERATORS gencpp)
generate_messages()
set(struct_msgs_INCLUDE_DIRS ${struct_msgs_INCLUDE_DIRS} PARENT_SCOPE)
</#noparse>
\ No newline at end of file
# generated by ros2.MsgGen.CMakeLists.ftl
cmake_minimum_required(VERSION 3.5)
project(struct_msgs)
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
# declare the message files to generate code for
<#list viewModel.getFileNamesWithPath() as fileWithPath>
LIST(APPEND msg_files "${fileWithPath}")
</#list>
<#noparse>
rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files})
ament_export_dependencies(rosidl_default_runtime)
ament_package()
set(struct_msgs_LIBRARIES struct_msgs__rosidl_typesupport_cpp struct_msgs__rosidl_typesupport_fastrtps_cpp struct_msgs__rosidl_typesupport_introspection_cpp PARENT_SCOPE)
export(TARGETS struct_msgs__rosidl_typesupport_cpp struct_msgs__rosidl_typesupport_fastrtps_cpp struct_msgs__rosidl_typesupport_introspection_cpp FILE struct_msgs.cmake)
</#noparse>
\ No newline at end of file
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<!--Generated by ros2.MsgGen.Package.ftl-->
<package format="3">
<name>struct_msgs</name>
<version>0.0.0</version>
<description>Generated Messages from Struct</description>
<maintainer email="unknown@unknown.com">Unknown</maintainer>
<license>Unknown</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
<member_of_group>rosidl_interface_packages</member_of_group>
</package>
\ No newline at end of file
...@@ -17,6 +17,7 @@ import de.monticore.lang.tagging._symboltable.TaggingResolver; ...@@ -17,6 +17,7 @@ import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.monticore.symboltable.GlobalScope; import de.monticore.symboltable.GlobalScope;
import de.monticore.symboltable.Scope; import de.monticore.symboltable.Scope;
import de.se_rwth.commons.logging.Log; import de.se_rwth.commons.logging.Log;
import org.apache.commons.io.FilenameUtils;
import org.junit.Assert; import org.junit.Assert;
import java.io.File; import java.io.File;
......
package de.monticore.lang.monticar.generator.rosmsg;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.symboltable.Scope;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import static org.junit.Assert.assertNotNull;
public class ProjectTest extends AbstractSymtabTest{
@Test
public void testRosProjectGeneration() throws IOException {
Scope symtab = createSymTab("src/test/resources/");
EMAComponentInstanceSymbol component = symtab.<EMAComponentInstanceSymbol>resolve("tests.basicStructComp", EMAComponentInstanceSymbol.KIND).orElse(null);
assertNotNull(component);
GeneratorRosMsg generatorRosMsg = new GeneratorRosMsg();
generatorRosMsg.setTarget("target/projects/ros/basicStructComp","struct_msgs");
List<File> files = generatorRosMsg.generateProject(component);
checkFilesAreEqual(files, Paths.get("target/projects/ros/basicStructComp"),"projects/ros/basicStructComp/");
}
@Test
public void testRos2ProjectGeneration() throws IOException {
Scope symtab = createSymTab("src/test/resources/");
EMAComponentInstanceSymbol component = symtab.<EMAComponentInstanceSymbol>resolve("tests.basicStructComp", EMAComponentInstanceSymbol.KIND).orElse(null);
assertNotNull(component);
GeneratorRosMsg generatorRosMsg = new GeneratorRosMsg();
generatorRosMsg.setTarget("target/projects/ros2/basicStructComp","struct_msgs");
generatorRosMsg.setRos2mode(true);
List<File> files = generatorRosMsg.generateProject(component);
checkFilesAreEqual(files, Paths.get("target/projects/ros2/basicStructComp"),"projects/ros2/basicStructComp/");
}
}
cmake_minimum_required(VERSION 3.5)
project(multinested)
find_package(genmsg REQUIRED)
FILE(GLOB MSG_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.msg)
add_message_files(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} FILES ${MSG_FILES})
generate_messages()
\ No newline at end of file
# generated by ros.MsgGen.CMakeLists.ftl
cmake_minimum_required(VERSION 2.8.12)
project(struct_msgs)
find_package(genmsg REQUIRED)
add_message_files(FILES structs_BasicStruct.msg)
set(CATKIN_MESSAGE_GENERATORS gencpp)
generate_messages()
set(struct_msgs_INCLUDE_DIRS ${struct_msgs_INCLUDE_DIRS} PARENT_SCOPE)
float64 fieldQ1
float64 fieldQ2
int32 fieldZ1
int32 fieldZ2
bool fieldB1
\ No newline at end of file
# generated by ros2.MsgGen.CMakeLists.ftl
cmake_minimum_required(VERSION 3.5)
project(struct_msgs)
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
# declare the message files to generate code for
LIST(APPEND msg_files "msg/StructsBasicStruct.msg")
rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files})
ament_export_dependencies(rosidl_default_runtime)
ament_package()
set(struct_msgs_LIBRARIES struct_msgs__rosidl_typesupport_cpp struct_msgs__rosidl_typesupport_fastrtps_cpp struct_msgs__rosidl_typesupport_introspection_cpp PARENT_SCOPE)
export(TARGETS struct_msgs__rosidl_typesupport_cpp struct_msgs__rosidl_typesupport_fastrtps_cpp struct_msgs__rosidl_typesupport_introspection_cpp FILE struct_msgs.cmake)
float64 fieldq1
float64 fieldq2
int32 fieldz1
int32 fieldz2
bool fieldb1
\ No newline at end of file
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<!--Generated by ros2.MsgGen.Package.ftl-->
<package format="3">
<name>struct_msgs</name>
<version>0.0.0</version>
<description>Generated Messages from Struct</description>
<maintainer email="unknown@unknown.com">Unknown</maintainer>
<license>Unknown</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
<member_of_group>rosidl_interface_packages</member_of_group>
</package>
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment