Commit 434a145a authored by Evgeny Kusmenko's avatar Evgeny Kusmenko

Merge branch 'RosAndAdaptable' into 'master'

CLI, Model updates, assembly of jar with dependencies

See merge request !4
parents 158b1fd9 2186a834
Pipeline #57458 passed with stage
in 2 minutes and 35 seconds
# EMAM2Middleware
## Problems
This generator needs versions of other EMAM libraries that are not yet distributed via Maven.
Workaround: Download these branches and build each via 'mvn clean install -U -s settings.xml'
* EmbeddedMontiArc/RosPort: https://github.com/EmbeddedMontiArc/EmbeddedMontiArc/tree/RosPort
* EmbeddedMontiArcMath/RosPort: https://github.com/EmbeddedMontiArc/EmbeddedMontiArcMath/tree/RosPort
* EMAM2RosMsg: https://github.com/EmbeddedMontiArc/EMAM2RosMsg
* EMAM2RosCpp: https://github.com/EmbeddedMontiArc/EMAM2RosCpp
* EMAM2Cpp/hellwig: https://github.com/EmbeddedMontiArc/EMAM2Cpp/tree/hellwig
or use this script:
```bash
#!/bin/bash
#fail if any of the commands fail
set -e
git clone https://github.com/EmbeddedMontiArc/EmbeddedMontiArc
git clone https://github.com/EmbeddedMontiArc/EmbeddedMontiArcMath
git clone https://github.com/EmbeddedMontiArc/EMAM2RosMsg
git clone https://github.com/EmbeddedMontiArc/EMAM2RosCpp
git clone https://github.com/EmbeddedMontiArc/EMAM2Cpp
cd EmbeddedMontiArc
git checkout RosPort
mvn clean install -U -s settings.xml
cd ..
cd EmbeddedMontiArcMath
git checkout RosPort
mvn clean install -U -s settings.xml
cd ..
cd EMAM2RosMsg
mvn clean install -U -s settings.xml
cd ..
cd EMAM2RosCpp
mvn clean install -U -s settings.xml
cd ..
cd EMAM2Cpp
git checkout hellwig
mvn clean install -U -s settings.xml
cd ..
```
## Usage
### Defining the connection between a component and the middleware
The connection between middleware and the component is defined as tags on Ports in .tag files.
......@@ -65,9 +20,8 @@ Look at GenerationTest::testDistributedTargetGenerator. The component is defined
1. install needed software:
* ROS Kinetic(http://wiki.ros.org/kinetic/Installation)
* CMake(https://cmake.org/)
* Armadillo( www.arma.sourceforge.net)
* Ubuntu package: libarmadillo-dev
* creating a copy of the library named armadillo.h might be necessary. Ubuntu location: /usr/include/armadillo
* Armadillo 8 or higher( www.arma.sourceforge.net)
* creating a copy of the library named armadillo.h might be necessary.
1. source your ros environment(http://wiki.ros.org/ROS/Tutorials/InstallingandConfiguringROSEnvironment , 2.)
1. a) run src/test/resources/TargetCompilation.sh from **this project's** root
1. or b) compile a single project by
......
......@@ -9,7 +9,7 @@
<groupId>de.monticore.lang.monticar</groupId>
<artifactId>embedded-montiarc-math-middleware-generator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>0.0.2-SNAPSHOT</version>
<!-- == PROJECT DEPENDENCIES ============================================= -->
......@@ -305,6 +305,30 @@
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>jar-with-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>de.monticore.lang.monticar.generator.middleware.DistributedTargetGeneratorCli</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<!-- Source Jar Configuration -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
......
package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosToEmamTagSchema;
import de.monticore.lang.monticar.generator.middleware.impls.CPPGenImpl;
import de.monticore.lang.monticar.generator.middleware.impls.RosCppGenImpl;
import de.monticore.lang.monticar.generator.order.simulator.AbstractSymtab;
import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper;
import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log;
import org.apache.commons.cli.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* java -cp target/embedded-montiarc-math-middleware-generator-0.0.1-SNAPSHOT-jar-with-dependencies.jar \
* de.monticore.lang.monticar.generator.middleware.DistributedTargetGeneratorCli \
* --models-dir=src/test/resources/ \
* --root-model=tests.a.addComp \
* --output-dir=target/cli-test/src \
* --generators=cpp,roscpp
*/
public final class DistributedTargetGeneratorCli {
private static final Option OPTION_MODELS_PATH = Option.builder("m")
.longOpt("models-dir")
.desc("full path to directory with EMAM models")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_ROOT_MODEL = Option.builder("r")
.longOpt("root-model")
.desc("fully qualified name of the root model")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_OUTPUT_PATH = Option.builder("o")
.longOpt("output-dir")
.desc("full path to output directory for tests")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_GENERATORS = Option.builder("g")
.longOpt("generators")
.desc("identifiers for the generators that should be used")
.hasArg(true)
.required(true)
.numberOfArgs(10)
.valueSeparator(',')
.build();
public static final String GENERATOR_CPP = "cpp";
public static final String GENERATOR_ROSCPP = "roscpp";
private DistributedTargetGeneratorCli() {}
public static void main(String[] args) {
System.out.println(Arrays.toString(args));
Options options = getOptions();
CommandLineParser parser = new DefaultParser();
CommandLine cliArgs = parseArgs(options, parser, args);
if (cliArgs != null) {
runGenerator(cliArgs);
}
}
private static Options getOptions() {
Options options = new Options();
options.addOption(OPTION_MODELS_PATH);
options.addOption(OPTION_ROOT_MODEL);
options.addOption(OPTION_OUTPUT_PATH);
options.addOption(OPTION_GENERATORS);
return options;
}
private static Set<String> getGeneratorNames() {
HashSet<String> res = new HashSet<>();
res.add(GENERATOR_CPP);
res.add(GENERATOR_ROSCPP);
return res;
}
private static CommandLine parseArgs(Options options, CommandLineParser parser, String[] args) {
CommandLine cliArgs;
try {
cliArgs = parser.parse(options, args);
} catch (ParseException e) {
Log.error("0x9A1AC: Argument parsing exception", e);
return null;
}
return cliArgs;
}
private static void runGenerator(CommandLine cliArgs) {
String modelsDirPath = expandHomeDir(cliArgs.getOptionValue(OPTION_MODELS_PATH.getOpt()));
if(!Files.isDirectory(Paths.get(modelsDirPath))){
Log.error("0x6444B: Models dir does not exist: " + modelsDirPath);
return;
}
String outputPath = expandHomeDir(cliArgs.getOptionValue(OPTION_OUTPUT_PATH.getOpt()));
String rootModelName = cliArgs.getOptionValue(OPTION_ROOT_MODEL.getOpt());
Set<String> generators = Arrays.stream(cliArgs.getOptionValues(OPTION_GENERATORS.getOpt())).collect(Collectors.toSet());
TaggingResolver taggingResolver = AbstractSymtab.createSymTabAndTaggingResolver(modelsDirPath);
DistributedTargetGenerator generator = new DistributedTargetGenerator();
generator.setGenerationTargetPath(outputPath);
Set<String> validGenNames = getGeneratorNames();
generators.forEach(genName -> {
if (validGenNames.contains(genName)) {
Log.warn("Using generator " + genName);
} else {
Log.error("0xE28B6: Not a valid generator Name:" + genName +".");
}
});
ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.<ExpandedComponentInstanceSymbol>resolve(rootModelName, ExpandedComponentInstanceSymbol.KIND).orElse(null);
if (componentInstanceSymbol == null) {
Log.error("0x5FFAE: The given component cannot be resolved.");
return;
}
if (generators.contains(GENERATOR_CPP)) {
generator.add(new CPPGenImpl(), "cpp");
}
if (generators.contains(GENERATOR_ROSCPP)) {
generator.add(new RosCppGenImpl(), "roscpp");
RosToEmamTagSchema.registerTagTypes(taggingResolver);
TagHelper.resolveTags(taggingResolver, componentInstanceSymbol);
}
try {
generator.generate(componentInstanceSymbol, taggingResolver);
} catch (IOException e) {
Log.error("Error generating files", e);
}
}
private static String expandHomeDir(String path) {
if (path.startsWith("~")) {
return System.getProperty("user.home") + path.substring(1);
}
return path;
}
}
......@@ -51,6 +51,7 @@ public class MiddlewareGenerator extends CMakeGenerator {
.filter(fn -> fn.matches("(\\w+(Adapter_))?(" + name + "\\.h)"))
.filter(fn -> !fn.startsWith("IAdapter"))
.map(fn -> "#include \"" + fn + "\"")
.sorted()
.collect(Collectors.joining("\n"));
String addAdapters = filesNames.stream()
......@@ -58,6 +59,7 @@ public class MiddlewareGenerator extends CMakeGenerator {
.filter(fn -> !fn.startsWith("IAdapter"))
.map(fn -> fn.substring(0, fn.length() - 2))
.map(fn -> " adapters.push_back(new " + fn + "());")
.sorted()
.collect(Collectors.joining("\n"));
String initParams = componentInstanceSymbol.getArguments().stream()
......@@ -87,6 +89,7 @@ public class MiddlewareGenerator extends CMakeGenerator {
.filter(fn -> fn.matches("(\\w+(Adapter_))?(" + name + "\\.h)"))
.filter(fn -> !fn.startsWith("IAdapter"))
.map(fn -> fn.substring(0, fn.length() - 2))
.sorted()
.collect(Collectors.joining(" "));
String content = TemplateHelper.getCoordinatorCmakeListsTemplate()
......
package de.monticore.lang.monticar.generator.middleware.helpers;
import de.se_rwth.commons.logging.Log;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
......@@ -16,12 +15,13 @@ public class TemplateHelper {
}
String tmpStr = "";
String fullFileName = "src/main/resources/de/monticore/lang/monticar/generator/middleware/" + fileName;
String resourceFileName = "/de/monticore/lang/monticar/generator/middleware/" + fileName;
try {
tmpStr = FileUtils.readFileToString(new File(fullFileName));
tmpStr = IOUtils.toString(TemplateHelper.class.getResourceAsStream(resourceFileName));
} catch (Exception e) {
//Not recoverable
Log.error("Template file not found: " + fullFileName);
Log.error("Template file not found: " + resourceFileName);
}
cache.put(fileName, tmpStr);
......
......@@ -9,6 +9,11 @@ import de.monticore.lang.tagging._symboltable.TaggingResolver;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
......@@ -34,6 +39,8 @@ public class CPPGenImpl implements GeneratorImpl {
files.add(generatorCPP.generateFile(generateCMake(componentInstanceSymbol)));
files.addAll(generatorCPP.generateFiles(componentInstanceSymbol, taggingResolver));
fixKnownErrors(files);
return files;
}
......@@ -49,4 +56,21 @@ public class CPPGenImpl implements GeneratorImpl {
cmake.setFileContent(TemplateHelper.getCmakeCppTemplate().replace("${compName}", name));
return cmake;
}
private void fixKnownErrors(List<File> files) throws IOException {
for (File f : files) {
Path path = Paths.get(f.getAbsolutePath());
Charset charset = StandardCharsets.UTF_8;
String content = new String(Files.readAllBytes(path), charset);
content = content
//Matrix access problems
.replace("int curIndex = indexLookup(i);","int curIndex = indexLookup(i-1);")
.replace("-1-1", "-1") //not present in emam2cpp master
.replace("] ;","];")
;
Files.write(path, content.getBytes(charset));
}
}
}
......@@ -4,6 +4,7 @@ import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.Expanded
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol;
import de.monticore.lang.monticar.generator.roscpp.GeneratorRosCpp;
import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log;
import java.io.File;
import java.io.IOException;
......@@ -36,8 +37,13 @@ public class RosCppGenImpl implements GeneratorImpl {
@Override
public boolean willAccept(ExpandedComponentInstanceSymbol componentInstanceSymbol) {
return componentInstanceSymbol.getPortsList().stream()
boolean result = componentInstanceSymbol.getPortsList().stream()
.filter(PortSymbol::isRosPort)
.count() > 0;
if(!result){
Log.warn("GeneratorRosCpp: No ROS Ports found! Ignoring component " + componentInstanceSymbol.getName());
}
return result;
}
}
package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.LogConfig;
import de.se_rwth.commons.logging.Finding;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.Assert.assertTrue;
public class CliTest{
private static final String VALID_MODELS_DIR_OPTION = "--models-dir=src/test/resources/";
private static final String VALID_ROOT_MODEL_OPTION = "--root-model=tests.a.addComp";
private static final String VALID_GENERATOR_CPP_OPTION = "--generators=cpp";
private static final String VALID_GENERATOR_ALL_OPTION = "--generators=cpp,roscpp";
private static final String INVALID_MODELS_DIR_OPTION = "--models-dir=src/invalid/resources/";
private static final String INVALID_ROOT_MODEL_OPTION = "--root-model=invalid.invalid.addComp";
private static final String INVALID_GENERATOR_OPTION = "--generators=invalid";
private static final String INVALID_GENERATOR_EMPTY_OPTION = "--generators=";
@BeforeClass
public static void initLog(){
LogConfig.init();
LogConfig.enableFailQuick(false);
}
@Before
public void clearFindings(){
LogConfig.getFindings().clear();
}
@AfterClass
public static void resetLog(){
LogConfig.getFindings().clear();
LogConfig.enableFailQuick(true);
}
@Test
public void testParserError(){
String[] args = {
"invalidParsingArg"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(LogConfig.getFindings().stream().map(Finding::getMsg).anyMatch(msg -> msg.contains("0x9A1AC")));
}
@Test
public void testNoGenerators(){
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
INVALID_GENERATOR_EMPTY_OPTION,
"--output-dir=target/cliTest/NoGenerators/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(LogConfig.getFindings().stream().map(Finding::getMsg).anyMatch(msg -> msg.contains("0xE28B6")));
}
@Test
public void testInvalidGenerator(){
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
INVALID_GENERATOR_OPTION,
"--output-dir=target/cliTest/InvalidGenerators/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(logContains("0xE28B6"));
}
@Test
public void testOneGenerator(){
String targetDir = "target/cliTest/OneGenerator/";
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_CPP_OPTION,
"--output-dir=" + targetDir};
DistributedTargetGeneratorCli.main(args);
String[] positiveFileNames = {
"CMakeLists.txt",
"tests_a_addComp/cpp/tests_a_addComp.h",
"tests_a_addComp/cpp/CMakeLists.txt",
"tests_a_addComp/coordinator/CMakeLists.txt",
"tests_a_addComp/coordinator/Coordinator_tests_a_addComp.cpp"
};
for (String positiveFileName : positiveFileNames) {
assertTrue(Files.exists(Paths.get(targetDir + positiveFileName)));
}
}
@Test
public void testAllGenerators(){
String targetDir = "target/cliTest/AllGenerators/";
String[] args = {
VALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_ALL_OPTION,
"--output-dir=" + targetDir};
DistributedTargetGeneratorCli.main(args);
String[] positiveFileNames = {
"CMakeLists.txt",
"tests_a_addComp/cpp/tests_a_addComp.h",
"tests_a_addComp/cpp/CMakeLists.txt",
"tests_a_addComp/coordinator/CMakeLists.txt",
"tests_a_addComp/coordinator/Coordinator_tests_a_addComp.cpp",
"tests_a_addComp/roscpp/RosAdapter_tests_a_addComp.h",
"tests_a_addComp/roscpp/CMakeLists.txt",
};
for (String positiveFileName : positiveFileNames) {
assertTrue(Files.exists(Paths.get(targetDir + positiveFileName)));
}
}
@Test
public void testInvalidRootModelError(){
String[] args = {
VALID_MODELS_DIR_OPTION,
INVALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_CPP_OPTION,
"--output-dir=target/cliTest/InvalidRootModel/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(logContains("0x5FFAE"));
}
@Test
public void testInvalidModelsDirError(){
String[] args = {
INVALID_MODELS_DIR_OPTION,
VALID_ROOT_MODEL_OPTION,
VALID_GENERATOR_CPP_OPTION,
"--output-dir=target/cliTest/InvalidModelsDir/"};
DistributedTargetGeneratorCli.main(args);
assertTrue(LogConfig.getErrorCount() == 1);
assertTrue(logContains("0x6444B"));
}
private boolean logContains(String errorCode){
return LogConfig.getFindings().stream().map(Finding::getMsg).anyMatch(msg -> msg.contains(errorCode));
}
}
package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTComponent;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ComponentSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarcmath.cocos.EmbeddedMontiArcMathCoCos;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosConnectionSymbol;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosToEmamTagSchema;
import de.monticore.lang.monticar.generator.cpp.GeneratorCPP;
......@@ -66,7 +69,7 @@ public class GenerationTest extends AbstractSymtabTest {
}
@Test
public void testCppOnlyMiddlewareGeneration() throws IOException{
public void testCppOnlyMiddlewareGeneration() throws IOException {
TaggingResolver taggingResolver = createSymTabAndTaggingResolver("src/test/resources/");
ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.<ExpandedComponentInstanceSymbol>resolve("tests.a.addComp", ExpandedComponentInstanceSymbol.KIND).orElse(null);
assertNotNull(componentInstanceSymbol);
......@@ -74,11 +77,37 @@ public class GenerationTest extends AbstractSymtabTest {
MiddlewareGenerator middlewareGenerator = new MiddlewareGenerator();
String generationTargetPath = "./target/generated-sources-cmake/CMakeCppOnly/src/";
middlewareGenerator.setGenerationTargetPath(generationTargetPath);
middlewareGenerator.add(new CPPGenImpl(),"cpp");
middlewareGenerator.add(new CPPGenImpl(), "cpp");
List<File> files = middlewareGenerator.generate(componentInstanceSymbol, taggingResolver);
testFilesAreEqual(files,"CMakeCppOnly/src/",generationTargetPath);
testFilesAreEqual(files, "CMakeCppOnly/src/", generationTargetPath);
}
@Test
public void testBaSystem() throws IOException {
TaggingResolver taggingResolver = createSymTabAndTaggingResolver("src/test/resources/");
RosToEmamTagSchema.registerTagTypes(taggingResolver);
ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.<ExpandedComponentInstanceSymbol>resolve("ba.system", ExpandedComponentInstanceSymbol.KIND).orElse(null);
assertNotNull(componentInstanceSymbol);
ComponentSymbol componentSymbol = taggingResolver.<ComponentSymbol>resolve("ba.System", ComponentSymbol.KIND).orElse(null);
EmbeddedMontiArcMathCoCos.createChecker().checkAll((ASTComponent) componentSymbol.getAstNode().orElse(null));
TagHelper.resolveTags(taggingResolver, componentInstanceSymbol);
DistributedTargetGenerator distributedTargetGenerator = new DistributedTargetGenerator();
String generationTargetPath = "./target/generated-sources-cmake/system/src/";
distributedTargetGenerator.setGenerationTargetPath(generationTargetPath);
//distributedTargetGenerator.setGenDebug(true);
distributedTargetGenerator.add(new CPPGenImpl(), "cpp");
distributedTargetGenerator.add(new RosCppGenImpl(), "roscpp");
List<File> files = distributedTargetGenerator.generate(componentInstanceSymbol, taggingResolver);
testFilesAreEqual(files, "system/src/", generationTargetPath);
}
@Test
......@@ -142,53 +171,6 @@ public class GenerationTest extends AbstractSymtabTest {
distributedTargetGenerator.generate(componentInstanceSymbol, taggingResolver);
}
@Test
public void testIntersectionGeneration() throws IOException {
TaggingResolver taggingResolver = createSymTabAndTaggingResolver("src/test/resources/");
RosToEmamTagSchema.registerTagTypes(taggingResolver);
ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.<ExpandedComponentInstanceSymbol>resolve("ba.intersection.intersectionController", ExpandedComponentInstanceSymbol.KIND).orElse(null);
assertNotNull(componentInstanceSymbol);
//Map<PortSymbol, RosConnectionSymbol> tags = TagHelper.resolveTags(taggingResolver, componentInstanceSymbol);
componentInstanceSymbol.getConnectors().stream()