diff --git a/pom.xml b/pom.xml index a3702d7bd69871fec8ba5e184fc5ab1018fb5f36..37c9f6a0fa4ccf999046f6d931a4342246457f75 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,11 @@ 0.0.1c-SNAPSHOT + + de.monticore.lang.monticar + embedded-montiarc-math-rosmsg-generator + 0.0.1-SNAPSHOT + com.esotericsoftware.yamlbeans diff --git a/src/main/java/de/monticore/lang/monticar/generator/master/DistributedTargetGenerator.java b/src/main/java/de/monticore/lang/monticar/generator/master/DistributedTargetGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..8aec6eaeba9f89a89c9d68e34186e34f58dafeff --- /dev/null +++ b/src/main/java/de/monticore/lang/monticar/generator/master/DistributedTargetGenerator.java @@ -0,0 +1,159 @@ +package de.monticore.lang.monticar.generator.master; + +import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ConnectorSymbol; +import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol; +import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol; +import de.monticore.lang.embeddedmontiarc.tagging.MiddlewareSymbol; +import de.monticore.lang.embeddedmontiarc.tagging.RosConnectionSymbol; +import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper; +import de.monticore.lang.monticar.generator.rosmsg.RosMsg; +import de.monticore.lang.tagging._symboltable.TaggingResolver; +import de.se_rwth.commons.logging.Log; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class DistributedTargetGenerator extends CMakeMasterGenerator { + private RosMsgImpl rosMsgImpl; + + public DistributedTargetGenerator(String generationTargetPath) { + setGenerationTargetPath(generationTargetPath); + rosMsgImpl = new RosMsgImpl("rosmsg"); + //this.add(rosMsgImpl,"rosmsg/"); + } + + @Override + public List generate(ExpandedComponentInstanceSymbol componentInstanceSymbol, TaggingResolver taggingResolver) throws IOException { + Map resolvedTags = TagHelper.resolveTags(taggingResolver, componentInstanceSymbol); + + Map generatorMap = new HashMap<>(); + + fixComponentInstance(componentInstanceSymbol); + Collection subComps = getSubComponentInstances(componentInstanceSymbol); + + //TODO: cluster non mw subcomps together with mw subcomps + + boolean allSubsMwOnly = subComps.stream() + .flatMap(comp -> comp.getPorts().stream()) + .allMatch(p -> p.getMiddlewareSymbol().isPresent()); + + if (allSubsMwOnly) { + subComps.forEach(comp -> + generatorMap.put(comp, createFullGenerator(comp.getFullName().replace(".", "_"))) + ); + } else { + generatorMap.put(componentInstanceSymbol, createFullGenerator(componentInstanceSymbol.getFullName().replace(".", "_"))); + } + + List files = new ArrayList<>(); + + CMakeMasterGenerator cmakeListsGenerator = new CMakeMasterGenerator(); + cmakeListsGenerator.setGenerationTargetPath(generationTargetPath); + for (ExpandedComponentInstanceSymbol comp : generatorMap.keySet()) { + files.addAll(generatorMap.get(comp).generate(comp, taggingResolver)); + //add empty generator to cmakeListsGenerator so that CMakeLists.txt will be generated + cmakeListsGenerator.add(new GeneratorImpl() { + }, comp.getFullName().replace(".", "_")); + } + + files.addAll(cmakeListsGenerator.generate(componentInstanceSymbol, taggingResolver)); + return files; + } + + private GeneratorImpl createFullGenerator(String subdir) { + MiddlewareMasterGenerator res = new MiddlewareMasterGenerator(); + res.setGenerationTargetPath(generationTargetPath + (subdir.endsWith("/") ? subdir : subdir + "/")); + + this.getGeneratorImpls().forEach(gen -> res.add(gen, this.getImplSubfolder(gen))); + + return res; + } + + private void fixComponentInstance(ExpandedComponentInstanceSymbol componentInstanceSymbol) { + fixRosTopics(componentInstanceSymbol); + } + + private void fixRosTopics(ExpandedComponentInstanceSymbol componentInstanceSymbol) { + componentInstanceSymbol.getConnectors().stream() + .filter(connectorSymbol -> connectorSymbol.getSourcePort().isRosPort() && connectorSymbol.getTargetPort().isRosPort()) + .forEach(connectorSymbol -> { + if (Objects.equals(connectorSymbol.getSourcePort().getComponentInstance().orElse(null), componentInstanceSymbol)) { + //In port of supercomp + inferRosConnectionIfPossible(connectorSymbol.getSourcePort(), connectorSymbol.getTargetPort()); + } else if (Objects.equals(connectorSymbol.getTargetPort().getComponentInstance().orElse(null), componentInstanceSymbol)) { + //out port of supercomp + inferRosConnectionIfPossible(connectorSymbol.getTargetPort(), connectorSymbol.getSourcePort()); + } else { + //In between subcomps + generateRosConnectionIfPossible(connectorSymbol); + } + + }); + } + + private void generateRosConnectionIfPossible(ConnectorSymbol connectorSymbol) { + MiddlewareSymbol sourceTag = connectorSymbol.getSourcePort().getMiddlewareSymbol().orElse(null); + MiddlewareSymbol targetTag = connectorSymbol.getTargetPort().getMiddlewareSymbol().orElse(null); + if (sourceTag == null || targetTag == null || !sourceTag.isKindOf(RosConnectionSymbol.KIND) || !targetTag.isKindOf(RosConnectionSymbol.KIND)) { + Log.debug("Both sourcePort and targetPort need to have a RosConnectionSymbol", "RosConnectionSymbol"); + return; + } + RosConnectionSymbol rosConnectionA = (RosConnectionSymbol) sourceTag; + RosConnectionSymbol rosConnectionB = (RosConnectionSymbol) targetTag; + RosConnectionSymbol emptyRosConnection = new RosConnectionSymbol(); + if (!rosConnectionA.equals(emptyRosConnection) || !rosConnectionB.equals(emptyRosConnection)) { + Log.debug("Will not override rosConnections that are not empty!", "RosConnectionSymbol"); + return; + } + + //target port name is unique: each in port can only have one connection! + String topicName = connectorSymbol.getTargetPort().getFullName().replace(".", "_"); + RosMsg rosTypeA = rosMsgImpl.getRosType(connectorSymbol.getTargetPort().getTypeReference()); + RosMsg rosTypeB = rosMsgImpl.getRosType(connectorSymbol.getSourcePort().getTypeReference()); + if (!rosTypeA.equals(rosTypeB)) { + Log.error("topicType mismatch! " + + connectorSymbol.getSourcePort().getFullName() + " has " + rosTypeB + " and " + + connectorSymbol.getTargetPort().getFullName() + " has " + rosTypeA); + return; + } + + rosMsgImpl.addRosTypeToGenerate(connectorSymbol.getTargetPort().getTypeReference()); + if (rosTypeA.getFields().size() == 1) { + connectorSymbol.getSourcePort().setMiddlewareSymbol(new RosConnectionSymbol(topicName, rosTypeB.getName(), rosTypeB.getFields().get(0).getName())); + connectorSymbol.getTargetPort().setMiddlewareSymbol(new RosConnectionSymbol(topicName, rosTypeA.getName(), rosTypeA.getFields().get(0).getName())); + } else { + connectorSymbol.getSourcePort().setMiddlewareSymbol(new RosConnectionSymbol(topicName, rosTypeB.getName())); + connectorSymbol.getTargetPort().setMiddlewareSymbol(new RosConnectionSymbol(topicName, rosTypeA.getName())); + } + } + + private void inferRosConnectionIfPossible(PortSymbol sourcePort, PortSymbol targetPort) { + MiddlewareSymbol sourceTag = sourcePort.getMiddlewareSymbol().orElse(null); + MiddlewareSymbol targetTag = targetPort.getMiddlewareSymbol().orElse(null); + if (sourceTag == null || targetTag == null || !sourceTag.isKindOf(RosConnectionSymbol.KIND) || !targetTag.isKindOf(RosConnectionSymbol.KIND)) { + Log.debug("Both sourcePort and targetPort need to have a RosConnectionSymbol", "RosConnectionSymbol"); + return; + } + + RosConnectionSymbol sourceRosConnection = (RosConnectionSymbol) sourceTag; + RosConnectionSymbol targetRosConnection = (RosConnectionSymbol) targetTag; + + if (sourceRosConnection.getTopicName().isPresent() && !targetRosConnection.getTopicName().isPresent()) + targetRosConnection.setTopicName(sourceRosConnection.getTopicName().get()); + + if (sourceRosConnection.getTopicType().isPresent() && !targetRosConnection.getTopicType().isPresent()) + targetRosConnection.setTopicType(sourceRosConnection.getTopicType().get()); + + if (sourceRosConnection.getMsgField().isPresent() && !targetRosConnection.getMsgField().isPresent()) + targetRosConnection.setMsgField(sourceRosConnection.getMsgField().get()); + } + + private Collection getSubComponentInstances(ExpandedComponentInstanceSymbol componentInstanceSymbol) { + //TODO: check which subcomponents are mw only + //TODO: (build clusters?) + return componentInstanceSymbol.getSubComponents(); + } + + +} diff --git a/src/main/java/de/monticore/lang/monticar/generator/master/RosCppImpl.java b/src/main/java/de/monticore/lang/monticar/generator/master/RosCppImpl.java index d7ab516db278287cbbfbb1011d94317679716bc7..e94bec147a10915a67bd2581e53a1df52bf7404e 100644 --- a/src/main/java/de/monticore/lang/monticar/generator/master/RosCppImpl.java +++ b/src/main/java/de/monticore/lang/monticar/generator/master/RosCppImpl.java @@ -2,7 +2,6 @@ package de.monticore.lang.monticar.generator.master; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol; import de.monticore.lang.monticar.generator.roscpp.GeneratorRosCpp; -import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper; import de.monticore.lang.tagging._symboltable.TaggingResolver; import java.io.File; @@ -17,7 +16,7 @@ public class RosCppImpl implements GeneratorImpl { GeneratorRosCpp generatorRosCpp = new GeneratorRosCpp(); generatorRosCpp.setGenerateCMake(true); generatorRosCpp.setGenerationTargetPath(generationTargetPath); - return TagHelper.generate(generatorRosCpp, taggingResolver, componentInstanceSymbol); + return generatorRosCpp.generateFiles(componentInstanceSymbol, taggingResolver); } @Override diff --git a/src/main/java/de/monticore/lang/monticar/generator/master/RosMsgImpl.java b/src/main/java/de/monticore/lang/monticar/generator/master/RosMsgImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..5170da83363919cbfbe1dfb209b3e9b0c0895ba6 --- /dev/null +++ b/src/main/java/de/monticore/lang/monticar/generator/master/RosMsgImpl.java @@ -0,0 +1,46 @@ +package de.monticore.lang.monticar.generator.master; + +import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol; +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 java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class RosMsgImpl implements GeneratorImpl { + private GeneratorRosMsg generatorRosMsg; + private List> rosTypesToGenerate = new ArrayList<>(); + private String packageName; + + public RosMsgImpl(String packageName) { + this.packageName = packageName; + generatorRosMsg = new GeneratorRosMsg(); + } + + @Override + public List generate(ExpandedComponentInstanceSymbol componentInstanceSymbol, TaggingResolver taggingResolver) throws IOException { + List files = new ArrayList<>(); + for (MCTypeReference type : rosTypesToGenerate) { + files.addAll(generatorRosMsg.generate(type)); + } + return files; + } + + @Override + public void setGenerationTargetPath(String path) { + generatorRosMsg.setTarget(path, packageName); + } + + public void addRosTypeToGenerate(MCTypeReference typeReference) { + rosTypesToGenerate.add(typeReference); + } + + public RosMsg getRosType(MCTypeReference typeReference) { + return generatorRosMsg.getRosType(typeReference); + } +} diff --git a/src/test/java/de/monticore/lang/monticar/generator/master/GenerationTest.java b/src/test/java/de/monticore/lang/monticar/generator/master/GenerationTest.java index 777f651e5d4ecc8a15cfb7ac439c4e6b12d0b628..8872eab5d3a221599d9784b6fea2f0cb084f10e6 100644 --- a/src/test/java/de/monticore/lang/monticar/generator/master/GenerationTest.java +++ b/src/test/java/de/monticore/lang/monticar/generator/master/GenerationTest.java @@ -2,7 +2,9 @@ package de.monticore.lang.monticar.generator.master; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol; import de.monticore.lang.embeddedmontiarc.tagging.RosToEmamTagSchema; +import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper; import de.monticore.lang.tagging._symboltable.TaggingResolver; +import org.junit.Before; import org.junit.Test; import java.io.IOException; @@ -11,6 +13,11 @@ import static org.junit.Assert.assertNotNull; public class GenerationTest extends AbstractSymtabTest { + @Before + public void initTest() { + TagHelper.reset(); + } + @Test public void testBasicGeneration() throws IOException { TaggingResolver taggingResolver = createSymTabAndTaggingResolver("src/test/resources/"); @@ -18,6 +25,7 @@ public class GenerationTest extends AbstractSymtabTest { ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.resolve("tests.a.compA", ExpandedComponentInstanceSymbol.KIND).orElse(null); assertNotNull(componentInstanceSymbol); + TagHelper.resolveTags(taggingResolver, componentInstanceSymbol); MasterGenerator masterGenerator = new MasterGenerator(); masterGenerator.setGenerationTargetPath("./target/generated-sources/basicGeneration/"); @@ -34,6 +42,7 @@ public class GenerationTest extends AbstractSymtabTest { ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.resolve("tests.a.compA", ExpandedComponentInstanceSymbol.KIND).orElse(null); assertNotNull(componentInstanceSymbol); + TagHelper.resolveTags(taggingResolver, componentInstanceSymbol); MasterGenerator masterGenerator = new CMakeMasterGenerator(); masterGenerator.setGenerationTargetPath("./target/generated-sources/CMakeGeneration/"); @@ -50,6 +59,7 @@ public class GenerationTest extends AbstractSymtabTest { ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.resolve("tests.a.addComp", ExpandedComponentInstanceSymbol.KIND).orElse(null); assertNotNull(componentInstanceSymbol); + TagHelper.resolveTags(taggingResolver, componentInstanceSymbol); MasterGenerator masterGenerator = new MiddlewareMasterGenerator(); masterGenerator.setGenerationTargetPath("./target/generated-sources/middlewareMasterGenerator/src/"); @@ -58,7 +68,23 @@ public class GenerationTest extends AbstractSymtabTest { masterGenerator.add(new DummyMiddlewareGenerator(), "dummy"); masterGenerator.generate(componentInstanceSymbol, taggingResolver); + } + + @Test + public void testDistributedTargetGenerator() throws IOException { + TaggingResolver taggingResolver = createSymTabAndTaggingResolver("src/test/resources/"); + RosToEmamTagSchema.registerTagTypes(taggingResolver); + + ExpandedComponentInstanceSymbol componentInstanceSymbol = taggingResolver.resolve("tests.dist.distComp", ExpandedComponentInstanceSymbol.KIND).orElse(null); + assertNotNull(componentInstanceSymbol); + TagHelper.resolveTags(taggingResolver, componentInstanceSymbol); + MasterGenerator masterGenerator = new DistributedTargetGenerator("./target/generated-sources/distributed/src/"); + + masterGenerator.add(new CPPImpl(), "cpp"); + masterGenerator.add(new RosCppImpl(), "roscpp"); + + masterGenerator.generate(componentInstanceSymbol, taggingResolver); } } diff --git a/src/test/resources/tests/dist/DistComp.emam b/src/test/resources/tests/dist/DistComp.emam new file mode 100644 index 0000000000000000000000000000000000000000..68271d7efaa324638ff9b93ea06d03b525039acc --- /dev/null +++ b/src/test/resources/tests/dist/DistComp.emam @@ -0,0 +1,19 @@ +package tests.dist; + +component DistComp{ + component SubComp1{ + ports out Q out1, + out Q out2; + } + + component SubComp2{ + ports in Q in1, + in Q in2; + } + + instance SubComp1 sub1; + instance SubComp2 sub2; + + connect sub1.out1 -> sub2.in1; + connect sub1.out2 -> sub2.in2; +} \ No newline at end of file diff --git a/src/test/resources/tests/dist/SimpleDist.tag b/src/test/resources/tests/dist/SimpleDist.tag new file mode 100644 index 0000000000000000000000000000000000000000..8499a3966b79a4e326798b043c1d23033fc4d9c0 --- /dev/null +++ b/src/test/resources/tests/dist/SimpleDist.tag @@ -0,0 +1,9 @@ +package tests.dist; +conforms to de.monticore.lang.monticar.generator.roscpp.RosToEmamTagSchema; + +tags SimpleDist{ +tag distComp.sub1.out1 with RosConnection = {topic = (topicIn1, std_msgs/Float64), msgField = data}; +tag distComp.sub1.out2 with RosConnection = {topic = (topicIn2, std_msgs/Float64), msgField = data}; +tag distComp.sub2.in1 with RosConnection = {topic = (topicIn1, std_msgs/Float64), msgField = data}; +tag distComp.sub2.in2 with RosConnection = {topic = (topicIn2, std_msgs/Float64), msgField = data}; +} \ No newline at end of file