Commit bd15dd7c authored by Alexander David Hellwig's avatar Alexander David Hellwig
Browse files

Merge branch 'refactoring' into 'master'

Refactoring

See merge request !12
parents 5f1dc200 707d2d57
Pipeline #118298 passed with stages
in 1 minute and 48 seconds
......@@ -9,7 +9,7 @@
<groupId>de.monticore.lang.monticar</groupId>
<artifactId>embedded-montiarc-math-roscpp-generator</artifactId>
<version>0.1.3-SNAPSHOT</version>
<version>0.1.4-SNAPSHOT</version>
<!-- == PROJECT DEPENDENCIES ============================================= -->
......@@ -18,13 +18,12 @@
<!-- .. SE-Libraries .................................................. -->
<se-commons.version>1.7.7</se-commons.version>
<Embedded-MontiArc-Math.version>0.1.7-SNAPSHOT</Embedded-MontiArc-Math.version>
<Embedded-montiarc-math-rosmsg-generator.version>0.1.2-SNAPSHOT</Embedded-montiarc-math-rosmsg-generator.version>
<Embedded-montiarc-math-rosmsg-generator.version>0.1.3-SNAPSHOT</Embedded-montiarc-math-rosmsg-generator.version>
<!-- .. Libraries .................................................. -->
<guava.version>18.0</guava.version>
<junit.version>4.12</junit.version>
<logback.version>1.1.2</logback.version>
<yamlbeans.version>1.12</yamlbeans.version>
<!-- .. Plugins ....................................................... -->
<assembly.plugin>2.5.4</assembly.plugin>
......@@ -59,12 +58,6 @@
<version>${Embedded-montiarc-math-rosmsg-generator.version}</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware.yamlbeans</groupId>
<artifactId>yamlbeans</artifactId>
<version>${yamlbeans.version}</version>
</dependency>
<!-- MontiCore Dependencies -->
<dependency>
......@@ -193,6 +186,7 @@
<maxmem>256m</maxmem>
<!-- aggregated reports for multi-module projects -->
<aggregate>true</aggregate>
<check/>
</configuration>
</plugin>
</plugins>
......
package de.monticar.lang.monticar.generator.python;
import java.util.HashMap;
//TODO: replace with dependency: EMAM2PythonRos!
public class RosInterface {
public String type = "";
public String topic = "";
public HashMap<String, String> ports = new HashMap<>();
}
\ No newline at end of file
package de.monticar.lang.monticar.generator.python;
import java.util.ArrayList;
//TODO: replace with dependency: EMAM2PythonRos!
//TODO: rename: not used as a Tag
public class RosTag {
public String component = "";
public ArrayList<RosInterface> subscriber = new ArrayList<>();
public ArrayList<RosInterface> publisher = new ArrayList<>();
}
package de.monticar.lang.monticar.generator.python;
import com.esotericsoftware.yamlbeans.YamlException;
import com.esotericsoftware.yamlbeans.YamlReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
//TODO: replace with dependency: EMAM2PythonRos!
public class TagReader<T> {
public List<T> readYAML(String path) {
List<T> components;
try {
YamlReader reader = new YamlReader(new FileReader(path));
components = (List<T>) reader.read();
} catch (FileNotFoundException e) {
return new ArrayList<>();
} catch (YamlException e) {
return new ArrayList<>();
}
return components;
}
}
\ No newline at end of file
package de.monticore.lang.monticar.generator.roscpp;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncModel.EMAPortSymbol;
import de.monticore.lang.monticar.generator.roscpp.helper.NameHelper;
public class DirectMsgConverter implements MsgConverter {
private String msgField;
private boolean isMsgToPort;
public DirectMsgConverter(String msgField, boolean isMsgToPort) {
this.isMsgToPort = isMsgToPort;
this.msgField = msgField;
}
@Override
public boolean isMsgToPort() {
return isMsgToPort;
}
@Override
public String getConversion(EMAPortSymbol portSymbol) {
return !isMsgToPort ? "." + msgField + " = component->" + NameHelper.getPortNameTargetLanguage(portSymbol) : "msg->" + msgField;
}
}
package de.monticore.lang.monticar.generator.roscpp;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncModel.EMAPortSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosConnectionSymbol;
import de.monticore.lang.monticar.generator.roscpp.helper.*;
import de.monticore.lang.monticar.generator.roscpp.template.RosAdapterModel;
import de.monticore.lang.monticar.generator.roscpp.template.RosCppCMakeListsModel;
import de.monticore.lang.monticar.generator.roscpp.template.RosCppTemplates;
import de.monticore.lang.monticar.generator.roscpp.util.RosInterface;
import de.monticore.lang.monticar.generator.roscpp.util.RosPublisher;
import de.monticore.lang.monticar.generator.roscpp.util.RosSubscriber;
import de.monticore.lang.monticar.generator.rosmsg.util.FileContent;
import de.monticore.lang.monticar.generator.roscpp.util.BluePrintCPP;
import de.monticore.lang.monticar.generator.roscpp.helper.FormatHelper;
import de.monticore.lang.monticar.generator.roscpp.helper.NameHelper;
import de.monticore.lang.monticar.generator.roscpp.helper.PrinterHelper;
import de.monticore.lang.monticar.generator.rosmsg.GeneratorRosMsg;
import de.monticore.lang.monticar.generator.rosmsg.RosMsg;
import de.monticore.lang.monticar.ts.MCTypeSymbol;
import de.monticore.lang.monticar.ts.references.MCTypeReference;
import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class GeneratorRosCpp {
......@@ -43,39 +43,20 @@ public class GeneratorRosCpp {
}
public void setGenerationTargetPath(String generationTargetPath) {
this.generationTargetPath = generationTargetPath;
this.generationTargetPath = generationTargetPath.endsWith("/") ? generationTargetPath : generationTargetPath + "/";
}
public List<File> generateFiles(EMAComponentInstanceSymbol component, TaggingResolver symtab) throws IOException {
List<FileContent> fileContents = generateStrings(component);
if (getGenerationTargetPath().charAt(getGenerationTargetPath().length() - 1) != '/') {
setGenerationTargetPath(getGenerationTargetPath() + "/");
}
List<File> files = new ArrayList<>();
for (FileContent fileContent : fileContents) {
files.add(generateFile(fileContent));
files.add(PrinterHelper.generateFile(fileContent, getGenerationTargetPath()));
}
return files;
}
public File generateFile(FileContent fileContent) throws IOException {
File f = new File(getGenerationTargetPath() + fileContent.getFileName());
Log.info(f.getName(), "FileCreation:");
if (!f.exists()) {
f.getParentFile().mkdirs();
if (!f.createNewFile()) {
Log.error("File could not be created");
}
}
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(f));
bufferedWriter.write(fileContent.getFileContent(), 0, fileContent.getFileContent().length());
bufferedWriter.close();
return f;
}
public List<FileContent> generateStrings(EMAComponentInstanceSymbol component) {
List<FileContent> fileContents = new ArrayList<>();
fileContents.addAll(generateRosAdapter(component));
......@@ -88,54 +69,70 @@ public class GeneratorRosCpp {
private List<FileContent> generateRosAdapter(EMAComponentInstanceSymbol component) {
List<FileContent> res = new ArrayList<>();
FileContent apdapter = new FileContent();
LanguageUnitRosCppAdapter languageUnitRosCppAdapter = new LanguageUnitRosCppAdapter();
languageUnitRosCppAdapter.setRos2Mode(this.isRos2Mode());
Optional<BluePrintCPP> currentBluePrint = languageUnitRosCppAdapter.generateBluePrint(component);
if(currentBluePrint.isPresent()) {
String classname = currentBluePrint.get().getName();
apdapter.setFileName(classname + ".h");
String nameTargetLanguage = NameHelper.getComponentNameTargetLanguage(component.getFullName());
apdapter.setFileContent(PrinterHelper.printClass(currentBluePrint.get(), ": public IAdapter_" + nameTargetLanguage));
GeneratorRosMsg generatorRosMsg = new GeneratorRosMsg();
generatorRosMsg.setRos2mode(ros2Mode);
for (Map.Entry<RosMsg, MCTypeReference<? extends MCTypeSymbol>> entry : languageUnitRosCppAdapter.getUsedRosMsgs().entrySet()) {
String packageName = Arrays.stream(entry.getKey().getName().split("/")).findFirst().get();
if (packageName.equals("struct_msgs")) {
String ros2extra = isRos2Mode() ? "msg/" : "";
generatorRosMsg.setTarget(generationTargetPath + "/" + ros2extra + packageName, packageName);
List<FileContent> tmpFileContents = generatorRosMsg.generateStrings(entry.getValue());
tmpFileContents.forEach(fc -> fc.setFileName(packageName + "/" + ros2extra + fc.getFileName()));
if(ros2Mode){
lowercaseMsgFieldNames(tmpFileContents);
}
res.addAll(tmpFileContents);
}
}
if (TagHelper.rosConnectionsValid(component, ros2Mode)) {
List<EMAPortSymbol> rosPorts = component.getPortInstanceList().stream()
.filter(EMAPortSymbol::isRosPort)
.collect(Collectors.toList());
if (generateCMake) {
LanguageUnitRosCMake languageUnitRosCMake = new LanguageUnitRosCMake();
List<RosSubscriber> rosSubscribers = rosPorts.stream()
.filter(EMAPortSymbol::isIncoming)
.map(RosSubscriber::new)
.collect(Collectors.toList());
List<RosPublisher> rosPublishers = rosPorts.stream()
.filter(EMAPortSymbol::isOutgoing)
.map(RosPublisher::new)
.collect(Collectors.toList());
List<RosMsg> rosMsgs = new ArrayList<>(languageUnitRosCppAdapter.getUsedRosMsgs().keySet());
res.addAll(languageUnitRosCMake.generate(component, languageUnitRosCppAdapter.getAdditionalPackages(), rosMsgs, isRos2Mode()));
List<RosInterface> rosInterfaces = new ArrayList<>();
rosInterfaces.addAll(rosSubscribers);
rosInterfaces.addAll(rosPublishers);
if (generateCMake) {
res.addAll(generateCMakeFiles(component, rosInterfaces, isRos2Mode()));
}
res.add(apdapter);
RosAdapterModel model = new RosAdapterModel(ros2Mode, NameHelper.getComponentNameTargetLanguage(component.getFullName()));
model.addPublishers(rosPublishers);
model.addSubscribers(rosSubscribers);
model.addGenerics(GenericsHelper.getGenericsDefinition(component));
if(ros2Mode) {
rosSubscribers.forEach(s -> model.addInclude(s.getRos2Include()));
rosPublishers.forEach(s -> model.addInclude(s.getRos2Include()));
}else{
rosSubscribers.forEach(s -> model.addInclude(s.getRosInclude()));
rosPublishers.forEach(s -> model.addInclude(s.getRosInclude()));
}
String s = RosCppTemplates.generateRosAdapter(model);
res.add(new FileContent("RosAdapter_" + NameHelper.getComponentNameTargetLanguage(component.getFullName()) + ".h",s));
}
return res;
}
private void lowercaseMsgFieldNames(List<FileContent> tmpFileContents) {
tmpFileContents.forEach(fc ->{
String fixedFileContent = Arrays.stream(fc.getFileContent().split("\\r?\\n"))
.map(line -> line.substring(0,line.indexOf(" ")) + line.substring(line.indexOf(" ")).toLowerCase())
.collect(Collectors.joining("\n"));
fc.setFileContent(fixedFileContent);
});
public List<FileContent> generateCMakeFiles(EMAComponentInstanceSymbol component, List<RosInterface> rosInterfaces, boolean ros2Mode) {
String compName = NameHelper.getComponentNameTargetLanguage(component.getFullName());
String name = NameHelper.getAdapterName(component);
RosCppCMakeListsModel model = new RosCppCMakeListsModel(name, compName);
List<String> allPackages = new ArrayList<>();
allPackages.add(ros2Mode ? "rclcpp" : "roscpp");
rosInterfaces.stream()
.map(RosInterface::getRosConnectionSymbol)
.map(RosConnectionSymbol::getTopicType)
.map(Optional::get)
.map(n -> n.split("/")[0])
.forEach(allPackages::add);
allPackages.forEach(model::addPackage);
if(!ros2Mode){
model.addExcludeFindPackage("struct_msgs");
}
List<FileContent> result = new ArrayList<>();
result.add(new FileContent("CMakeLists.txt", RosCppTemplates.generateRosCMakeLists(model)));
result.add(new FileContent(name + ".cpp","#include \"" + name + ".h\""));
return result;
}
}
package de.monticore.lang.monticar.generator.roscpp;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.monticar.generator.roscpp.helper.NameHelper;
import de.monticore.lang.monticar.generator.roscpp.helper.TemplateHelper;
import de.monticore.lang.monticar.generator.rosmsg.RosField;
import de.monticore.lang.monticar.generator.rosmsg.RosMsg;
import de.monticore.lang.monticar.generator.rosmsg.util.FileContent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class LanguageUnitRosCMake {
private String genDepTemplate = "add_dependencies(<name> <deps>)";
//TODO: msg->h for ROS2
//.msg files must start with A-Z and must not contain _
//field names must all be lowercase
private List<RosMsg> getAllMsgs(List<RosMsg> rosMsgs){
List<RosMsg> result = new ArrayList<>();
for (RosMsg rosMsg : rosMsgs) {
result.add(rosMsg);
result.addAll(getAllMsgs(rosMsg.getFields().stream()
.map(RosField::getType)
.filter(t -> t instanceof RosMsg)
.map(t -> (RosMsg) t)
.collect(Collectors.toList())));
}
return result;
}
public List<FileContent> generate(EMAComponentInstanceSymbol componentInstanceSymbol, List<String> additionalPackages, List<RosMsg> rosMsgs, boolean isRos2Mode) {
List<RosMsg> structMsgs = getStructMsgs(rosMsgs);
FileContent fileContent = new FileContent();
fileContent.setFileName("CMakeLists.txt");
List<String> allPackages = new ArrayList<>();
allPackages.addAll(additionalPackages);
if(!isRos2Mode) {
allPackages.add("roscpp");
}else{
allPackages.add("rclcpp");
}
List<String> distinctSortedPackages = allPackages.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
String compName = NameHelper.getComponentNameTargetLanguage(componentInstanceSymbol.getFullName());
String name = NameHelper.getAdapterName(componentInstanceSymbol);
String packages = distinctSortedPackages.stream()
.filter(p -> !p.equals("struct_msgs"))
.map(p -> "find_package(" + p + " REQUIRED)")
.collect(Collectors.joining("\n"));
String libraries = distinctSortedPackages.stream()
.filter(p -> !p.equals("struct_msgs"))
.map(p -> "${" + p + "_LIBRARIES}")
.collect(Collectors.joining(" "));
String include_dirs = distinctSortedPackages.stream()
.filter(p -> !p.equals("struct_msgs"))
.map(p -> "${" + p + "_INCLUDE_DIRS}")
.collect(Collectors.joining(" "));
String dependency = "";
// if (distinctSortedPackages.stream().filter(pack -> pack.startsWith("struct_msgs")).count() > 0) {
// dependency = "add_dependencies(<name> struct_msgs_generate_messages)".replace("<name>", name);
// }
String content = TemplateHelper.getCMakeListsTemplate()
.replace("<name>", name)
.replace("<compName>", compName)
.replace("<packages>", packages)
.replace("<libraries>", libraries)
.replace("<include_dirs>", include_dirs)
.replace("<dependency>", dependency);
List<FileContent> result = new ArrayList<>();
String genMsgString = null;
if(!isRos2Mode) {
genMsgString = getRosMsgGenerationString(structMsgs, name);
}else{
genMsgString = getRos2MsgGenerationString(name);
}
if(structMsgs.size() > 0){
content = content + "\n\n" + genMsgString;
if(isRos2Mode){
result.add(genRos2GenerationSettings(structMsgs));
result.add(genRos2GenerateMsgsPy());
}
}
fileContent.setFileContent(content);
result.add(fileContent);
FileContent cppFile = new FileContent();
cppFile.setFileName(name + ".cpp");
cppFile.setFileContent("#include \""+ name +".h\"");
result.add(cppFile);
return result;
}
private List<RosMsg> getStructMsgs(List<RosMsg> rosMsgs) {
List<RosMsg> struct_msgs = getAllMsgs(rosMsgs).stream()
.filter(rosMsg -> rosMsg.getName().startsWith("struct_msgs/"))
.collect(Collectors.toList());
Set<String> names = new HashSet<>();
return struct_msgs.stream()
.filter(m -> !names.contains(m.getName()))
.peek(m -> names.add(m.getName()))
.collect(Collectors.toList());
}
private FileContent genRos2GenerateMsgsPy() {
FileContent res = new FileContent();
res.setFileName("generateMsgs.py");
res.setFileContent(TemplateHelper.getGenerateMsgsPyTemplate());
return res;
}
private String getRos2MsgGenerationString(String name) {
String res = "";
res += TemplateHelper.getRos2MsgGenTemplate();
res = res
.replace("<struct_replaced>", "all_structs")
.replace("<name>",name);
res += "\n\n";
res += genDepTemplate
.replace("<deps>", "gen_<name>_all_structs")
.replace("<name>",name);
return res;
}
private FileContent genRos2GenerationSettings(List<RosMsg> structMsgs){
FileContent res = new FileContent();
res.setFileName("rclcpp_msg_gen.json");
String msgFiles = structMsgs.stream().map(msg -> "'<cur_dir>/" + msg.getName() + ".msg'").collect(Collectors.joining(", "));
String content = "{\n";
content += "\t'template_dir':'<ros_base>/share/rosidl_generator_cpp/resource/',\n";
content += "\t'target_dependencies':[" + msgFiles +"],\n";
content += "\t'ros_interface_files':["+ msgFiles +"],\n";
content += "\t'package_name':'struct_msgs',\n";
content += "\t'output_dir':'<cur_dir>/struct_msgs/'\n";
content += "}";
res.setFileContent(content.replace("'","\""));
return res;
}
private String getRosMsgGenerationString(List<RosMsg> structMsgs, String name) {
String genCommands = structMsgs.stream()
.map(msg -> TemplateHelper.getMsgGenTemplate()
.replace("<struct_replaced>",msg.getName().replace("/","_"))
.replace("<struct>",msg.getName())
.replace("<name>",name))
.collect(Collectors.joining("\n\n"));
String genDeps = structMsgs.stream()
.map(msg -> "gen_" + name + "_" + msg.getName().replace("/","_"))
.collect(Collectors.joining(" "));
String genDependenies = genDepTemplate
.replace("<name>",name)
.replace("<deps>", genDeps);
return genCommands + "\n\n" + genDependenies;
}
}
package de.monticore.lang.monticar.generator.roscpp;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncModel.EMAPortSymbol;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosConnectionSymbol;
import de.monticore.lang.monticar.generator.roscpp.helper.InitHelper;
import de.monticore.lang.monticar.generator.roscpp.util.*;
import de.monticore.lang.monticar.generator.roscpp.helper.NameHelper;
import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper;
import de.monticore.lang.monticar.generator.roscpp.instructions.*;
import de.monticore.lang.monticar.generator.rosmsg.GeneratorRosMsg;
import de.monticore.lang.monticar.generator.rosmsg.RosMsg;
import de.monticore.lang.monticar.ts.MCTypeSymbol;
import de.monticore.lang.monticar.ts.references.MCTypeReference;
import java.util.*;
import java.util.stream.Collectors;
public class LanguageUnitRosCppAdapter {
private Map<Variable, RosConnectionSymbol> subscribers = new HashMap<>();
private Map<Variable, RosConnectionSymbol> publishers = new HashMap<>();
private List<Method> publishMethods = new ArrayList<>();
private List<MsgConverter> msgConverts = new ArrayList<>();
private List<String> additionalPackages = new ArrayList<>();
private Map<RosMsg, MCTypeReference<? extends MCTypeSymbol>> usedRosMsgs = new HashMap<>();
private boolean ros2Mode = false;
public boolean isRos2Mode() {
return ros2Mode;
}
public void setRos2Mode(boolean ros2Mode) {
this.ros2Mode = ros2Mode;
}
public List<String> getAdditionalPackages() {
return additionalPackages;
}
public Optional<BluePrintCPP> generateBluePrint(EMAComponentInstanceSymbol componentSymbol) {
BluePrintCPP currentBluePrint = null;
if (TagHelper.rosConnectionsValid(componentSymbol)) {
String name = NameHelper.getAdapterName(componentSymbol);
currentBluePrint = new BluePrintCPP(name);
List<EMAPortSymbol> rosPorts = componentSymbol.getPortInstanceList().stream()
.filter(EMAPortSymbol::isRosPort)
.collect(Collectors.toList());
generateFields(componentSymbol, rosPorts, currentBluePrint);
generateCallbacks(rosPorts, currentBluePrint);
generateConstructor(name, currentBluePrint);
generateInit(name, NameHelper.getComponentNameTargetLanguage(componentSymbol.getFullName()), currentBluePrint);
generatePublishMethods(rosPorts, currentBluePrint);
generateTick(currentBluePrint);