diff --git a/build.gradle b/build.gradle index 4f57507f7e9f39a2fd8fb84a7820360bd9d4fdcb..5f86f7d094af70ac9fc2241e78dbd8df18c53c4d 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,6 @@ subprojects { dependencies { implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.2' implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.2' - testImplementation group: 'junit', name: 'junit', version: '4.12' testImplementation group: 'org.hamcrest', name: 'hamcrest-junit', version: '2.0.0.0' } diff --git a/ros2rag.base/build.gradle b/ros2rag.base/build.gradle index 9e0bde27ffc2908022aa1a0148b6dc8acec2be35..a6a7882eeee6db3cc34887d5b8cce3fe267905d5 100644 --- a/ros2rag.base/build.gradle +++ b/ros2rag.base/build.gradle @@ -17,10 +17,9 @@ buildscript { } dependencies { - implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' -// api 'org.jastadd:jastadd:2.3.4' - runtime 'org.jastadd:jastadd:2.3.4' + implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: '0.9.6' + implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '2.11.2' + runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4' api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' } @@ -62,6 +61,7 @@ task relast(type: JavaExec) { "../libs/relast.jar", "../relast.preprocessor/src/main/jastadd/RelAst.relast", "./src/main/jastadd/Ros2Rag.relast", + "./src/main/jastadd/MustacheNodes.relast", "--listClass=java.util.ArrayList", "--jastAddList=JastAddList", "--useJastAddNames", @@ -72,11 +72,12 @@ task relast(type: JavaExec) { inputs.files file("../libs/relast.jar"), file("../relast.preprocessor/src/main/jastadd/RelAST.relast"), - file("src/main/jastadd/Ros2Rag.relast") + file("./src/main/jastadd/Ros2Rag.relast") + file("./src/main/jastadd/MustacheNodes.relast") outputs.files file("./src/gen/jastadd/Ros2Rag.ast"), - file("src/gen/jastadd/Ros2Rag.jadd"), - file("src/gen/jastadd/Ros2RagRefResolver.jadd"), - file('src/gen/jastadd/Ros2RagResolverStubs.jrag') + file("./src/gen/jastadd/Ros2Rag.jadd"), + file("./src/gen/jastadd/Ros2RagRefResolver.jadd"), + file('./src/gen/jastadd/Ros2RagResolverStubs.jrag') } jastadd { diff --git a/ros2rag.base/src/main/jastadd/MustacheNodes.relast b/ros2rag.base/src/main/jastadd/MustacheNodes.relast new file mode 100644 index 0000000000000000000000000000000000000000..69d4280f9d9038c838e83767c6e72be9a8d6d5d8 --- /dev/null +++ b/ros2rag.base/src/main/jastadd/MustacheNodes.relast @@ -0,0 +1,22 @@ +//TypeComponentMustache ; +//rel TypeComponentMustache.TypeComponent -> TypeComponent ; + +MRos2Rag ::= ReadDefinition:MReadDefinition* WriteDefinition:MWriteDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*; +abstract MUpdateDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*; +MReadDefinition : MUpdateDefinition; +MWriteDefinition : MUpdateDefinition; +MMappingDefinition; +MInnerMappingDefinition; +MDependencyDefinition; +MTypeComponent; +MTokenComponent; + +rel MRos2Rag.Ros2Rag -> Ros2Rag; +rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition; +rel MReadDefinition.ReadFromMqttDefinition -> ReadFromMqttDefinition; +rel MWriteDefinition.WriteToMqttDefinition -> WriteToMqttDefinition; +rel MMappingDefinition.MappingDefinition -> MappingDefinition; +rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition; +rel MTypeComponent.TypeComponent -> TypeComponent; +rel MTokenComponent.TokenComponent -> TokenComponent; +rel MTokenComponent.DependencyDefinition* -> MDependencyDefinition; diff --git a/ros2rag.base/src/main/jastadd/Navigation.jrag b/ros2rag.base/src/main/jastadd/Navigation.jrag index f56d99825c376248ce8f9a2dae1388ed3898ea64..a4f21e6e8d79c549a63d54474792382457bf74fd 100644 --- a/ros2rag.base/src/main/jastadd/Navigation.jrag +++ b/ros2rag.base/src/main/jastadd/Navigation.jrag @@ -2,10 +2,12 @@ aspect Navigation { // --- program --- eq Ros2Rag.getChild().program() = getProgram(); + eq MRos2Rag.getChild().program() = getRos2Rag().program(); - // --- ros2rag + // --- ros2rag --- inh Ros2Rag ASTNode.ros2rag(); eq Ros2Rag.getChild().ros2rag() = this; + eq MRos2Rag.getChild().ros2rag() = getRos2Rag(); // --- containedFile (first equation should be in preprocessor) --- eq Program.getChild().containedFile() = null; @@ -32,6 +34,7 @@ aspect Navigation { eq GrammarFile.getChild().containedFileName() = getFileName(); eq Ros2Rag.getChild().containedFileName() = getFileName(); eq Program.getChild().containedFileName() = null; + eq MRos2Rag.getChild().containedFileName() = null; // --- isTokenUpdateDefinition --- syn boolean UpdateDefinition.isTokenUpdateDefinition() = false; @@ -49,6 +52,10 @@ aspect Navigation { syn WriteToMqttDefinition UpdateDefinition.asWriteToMqttDefinition() = null; eq WriteToMqttDefinition.asWriteToMqttDefinition() = this; + // --- asReadFromMqttDefinition --- + syn ReadFromMqttDefinition UpdateDefinition.asReadFromMqttDefinition() = null; + eq ReadFromMqttDefinition.asReadFromMqttDefinition() = this; + // --- targetUpdateDefinition --- syn WriteToMqttDefinition DependencyDefinition.targetUpdateDefinition() { // resolve definition in here, as we do not need resolveMethod in any other place (yet) diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.relast b/ros2rag.base/src/main/jastadd/Ros2Rag.relast index a5499702f44facd5b649a811a958b890d4565d0a..e1a84e8beac44fcbc23d09fc56ed536586bde4e8 100644 --- a/ros2rag.base/src/main/jastadd/Ros2Rag.relast +++ b/ros2rag.base/src/main/jastadd/Ros2Rag.relast @@ -10,7 +10,6 @@ rel TokenUpdateDefinition.Token -> TokenComponent; ReadFromMqttDefinition : TokenUpdateDefinition; WriteToMqttDefinition : TokenUpdateDefinition; -// example: RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 DependencyDefinition ::= <ID>; rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; rel DependencyDefinition.Target -> TokenComponent; diff --git a/ros2rag.base/src/main/jastadd/backend/Configuration.jadd b/ros2rag.base/src/main/jastadd/backend/Configuration.jadd index 711ba2511d1e0c8535007cacccac99872128e58f..d4f5b43f27cb329f1ff82318c6f383f0f69ebbd6 100644 --- a/ros2rag.base/src/main/jastadd/backend/Configuration.jadd +++ b/ros2rag.base/src/main/jastadd/backend/Configuration.jadd @@ -1,4 +1,5 @@ aspect Configuration { public static boolean ASTNode.loggingEnabledForReads = false; public static boolean ASTNode.loggingEnabledForWrites = false; + public static TypeDecl ASTNode.rootNode; } diff --git a/ros2rag.base/src/main/jastadd/backend/Generation.jadd b/ros2rag.base/src/main/jastadd/backend/Generation.jadd index 1b6dfff3386d079e75163c4725681a87904f6786..7aa915a421b4034ffa7f20406b22a041a0868bb9 100644 --- a/ros2rag.base/src/main/jastadd/backend/Generation.jadd +++ b/ros2rag.base/src/main/jastadd/backend/Generation.jadd @@ -21,11 +21,188 @@ aspect GenerationUtils { } } +/* Open questions +- Should all string constants be defined on the normal AST, or on the special mustache AST? +*/ + +aspect AttributesForMustache { + // --- MRos2Rag --- + eq MRos2Rag.getChild().mqttUpdaterAttribute() = mqttUpdaterAttribute(); + eq MRos2Rag.getChild().mqttUpdaterField() = mqttUpdaterField(); + + syn String MRos2Rag.mqttUpdaterAttribute() = getRos2Rag().mqttUpdaterAttribute(); + syn String MRos2Rag.mqttUpdaterField() = getRos2Rag().mqttUpdaterField(); + syn String MRos2Rag.mqttSetHostMethod() = getRos2Rag().mqttSetHostMethod(); + syn String MRos2Rag.mqttWaitUntilReadyMethod() = getRos2Rag().mqttWaitUntilReadyMethod(); + syn String MRos2Rag.mqttCloseMethod() = getRos2Rag().mqttCloseMethod(); + + // --- MUpdateDefinition --- + syn String MUpdateDefinition.preemptiveExpectedValue(); + syn String MUpdateDefinition.preemptiveReturn(); + syn TokenUpdateDefinition MUpdateDefinition.updateDef(); + syn String MUpdateDefinition.firstInputVarName(); + + eq MUpdateDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1; + eq MUpdateDefinition.getInnerMappingDefinition().resultVarPrefix() = resultVarPrefix(); + eq MUpdateDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : resultVarPrefix() + getInnerMappingDefinition(i - 1).getMappingDefinition().methodName(); + + inh String MUpdateDefinition.mqttUpdaterAttribute(); + + syn String MUpdateDefinition.connectMethod() = updateDef().connectMethod(); + syn TokenComponent MUpdateDefinition.token() = updateDef().getToken(); + syn boolean MUpdateDefinition.alwaysApply() = updateDef().getAlwaysApply(); + syn String MUpdateDefinition.resultVarPrefix() = "result"; // we do not need "_" here, because methodName begins with one + syn String MUpdateDefinition.parentTypeName() = token().containingTypeDecl().getName(); + syn String MUpdateDefinition.tokenName() = token().getName(); + syn MInnerMappingDefinition MUpdateDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1); + syn String MUpdateDefinition.lastDefinitionToType() = lastDefinition().ToType(); + syn String MUpdateDefinition.lastDefinitionName() = lastDefinition().methodName(); + syn String MUpdateDefinition.lastResult() = resultVarPrefix() + lastDefinitionName(); + syn String MUpdateDefinition.condition() { + if (lastDefinition().getMappingDefinition().getToType().isArray()) { + return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")"; + } + if (token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) { + return preemptiveExpectedValue() + " == " + lastResult(); + } + if (lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) { + return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")"; + } + return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null"; + } + + // --- MInnerMappingDefinition --- + inh boolean MInnerMappingDefinition.isLast(); + inh String MInnerMappingDefinition.resultVarPrefix(); + syn String MInnerMappingDefinition.ToType() = getMappingDefinition().getToType().prettyPrint(); + syn String MInnerMappingDefinition.methodName() = getMappingDefinition().methodName(); + inh String MInnerMappingDefinition.inputVarName(); + + // --- MReadDefinition --- + eq MReadDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()"; + eq MReadDefinition.preemptiveReturn() = "return;"; + eq MReadDefinition.updateDef() = getReadFromMqttDefinition(); + eq MReadDefinition.firstInputVarName() = "message"; + + // --- MWriteDefinition --- + eq MWriteDefinition.preemptiveExpectedValue() = lastValue(); + eq MWriteDefinition.preemptiveReturn() = "return false;"; + eq MWriteDefinition.updateDef() = getWriteToMqttDefinition(); + eq MWriteDefinition.firstInputVarName() = "get" + tokenName() + "()"; + + syn String MWriteDefinition.writeTopic() = getWriteToMqttDefinition().writeTopic(); + syn String MWriteDefinition.lastValue() = getWriteToMqttDefinition().lastValue(); + syn String MWriteDefinition.updateMethod() = getWriteToMqttDefinition().updateMethod(); + syn String MWriteDefinition.writeMethod() = getWriteToMqttDefinition().writeMethod(); + syn String MWriteDefinition.tokenResetMethod() = getWriteToMqttDefinition().tokenResetMethod(); + + // --- MMappingDefinition --- + syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint(); + syn String MMappingDefinition.methodName() = getMappingDefinition().methodName(); + syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint(); + syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName(); + syn String MMappingDefinition.content() = getMappingDefinition().getContent(); + + // --- MDependencyDefinition --- + syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName(); + syn String MDependencyDefinition.dependencyMethod() = getDependencyDefinition().dependencyMethod(); + syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName(); + syn String MDependencyDefinition.internalRelationPrefix() = getDependencyDefinition().internalRelationPrefix(); + syn nta MUpdateDefinition MDependencyDefinition.targetUpdateDefinition() { + return getDependencyDefinition().targetUpdateDefinition().toMustache(); + } + + // --- MTypeComponent --- + syn String MTypeComponent.name() = getTypeComponent().getName(); + inh String MTypeComponent.mqttUpdaterAttribute(); + inh String MTypeComponent.mqttUpdaterField(); + + // --- MTokenComponent --- + syn String MTokenComponent.parentTypeName() = getTokenComponent().containingTypeDecl().getName(); + syn String MTokenComponent.name() = getTokenComponent().getName(); + syn String MTokenComponent.javaType() = getTokenComponent().getJavaTypeUse().prettyPrint(); + syn String MTokenComponent.internalName() = getTokenComponent().internalName(); + + // --- toMustache --- + syn lazy MRos2Rag Ros2Rag.toMustache() { + MRos2Rag result = new MRos2Rag(); + result.setRos2Rag(this); + for (UpdateDefinition def : getUpdateDefinitionList()) { + if (def.isWriteToMqttDefinition()) { + result.addWriteDefinition(def.asWriteToMqttDefinition().toMustache()); + } else { + result.addReadDefinition(def.asReadFromMqttDefinition().toMustache()); + } + } + for (MappingDefinition def : allMappingDefinitions()) { + result.addMappingDefinition(def.toMustache()); + } + for (DependencyDefinition def : getDependencyDefinitionList()) { + result.addDependencyDefinition(def.toMustache()); + } + for (TokenComponent token : getProgram().allTokenComponents()) { + if (!token.getDependencySourceDefinitionList().isEmpty()) { + result.addTokenComponent(token.toMustache()); + } + } + for (Component child : rootNode.getComponentList()) { + if (child.isTypeComponent()) { + result.addRootTypeComponent(child.asTypeComponent().toMustache()); + } + } + return result; + } + +//MInnerMappingDefinition.MappingDefinition -> MappingDefinition; + protected void MUpdateDefinition.addInnerMappings() { + for (MappingDefinition def : updateDef().effectiveMappings()) { + MInnerMappingDefinition inner = new MInnerMappingDefinition(); + inner.setMappingDefinition(def); + addInnerMappingDefinition(inner); + } + } + syn lazy MReadDefinition ReadFromMqttDefinition.toMustache() { + MReadDefinition result = new MReadDefinition(); + result.setReadFromMqttDefinition(this); + result.addInnerMappings(); + return result; + } + syn lazy MWriteDefinition WriteToMqttDefinition.toMustache() { + MWriteDefinition result = new MWriteDefinition(); + result.setWriteToMqttDefinition(this); + result.addInnerMappings(); + return result; + } + syn lazy MMappingDefinition MappingDefinition.toMustache() { + MMappingDefinition result = new MMappingDefinition(); + result.setMappingDefinition(this); + return result; + } + syn lazy MDependencyDefinition DependencyDefinition.toMustache() { + MDependencyDefinition result = new MDependencyDefinition(); + result.setDependencyDefinition(this); + return result; + } + syn lazy MTypeComponent TypeComponent.toMustache() { + MTypeComponent result = new MTypeComponent(); + result.setTypeComponent(this); + return result; + } + syn lazy MTokenComponent TokenComponent.toMustache() { + MTokenComponent result = new MTokenComponent(); + result.setTokenComponent(this); + for (DependencyDefinition def : getDependencySourceDefinitionList()) { + result.addDependencyDefinition(def.toMustache()); + } + return result; + } +} + aspect AspectGeneration { + // naming convention attributes syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName(); syn String TokenComponent.externalName() = getName(); - // naming convention attributes syn String TokenUpdateDefinition.connectMethod() = "connect" + getToken().getName(); syn String WriteToMqttDefinition.writeTopic() = "_topic_" + getToken().getName(); syn String WriteToMqttDefinition.lastValue() = "_lastValue" + getToken().getName(); @@ -39,10 +216,6 @@ aspect AspectGeneration { syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID(); syn String DependencyDefinition.internalTokenName() = getSource().internalName(); - inh String UpdateDefinition.mqttUpdaterAttribute(); - inh String MappingDefinition.mqttUpdaterAttribute(); - inh String DependencyDefinition.mqttUpdaterAttribute(); - eq Ros2Rag.getChild().mqttUpdaterAttribute() = mqttUpdaterAttribute(); syn String Ros2Rag.mqttUpdaterAttribute() = "_mqttUpdater"; syn String Ros2Rag.mqttUpdaterField() = "_mqttUpdater"; @@ -50,247 +223,43 @@ aspect AspectGeneration { syn String Ros2Rag.mqttWaitUntilReadyMethod() = "MqttWaitUntilReady"; syn String Ros2Rag.mqttCloseMethod() = "MqttCloseConnections"; - public String Ros2Rag.generateAspect(String rootNodeName) { - StringBuilder sb = new StringBuilder(); - TypeDecl rootNode = getProgram().resolveTypeDecl(rootNodeName); - generateMqttAspect(sb, rootNode); - generateGrammarExtension(sb); - return sb.toString(); - } - - public void Ros2Rag.generateMqttAspect(StringBuilder sb, TypeDecl rootNode) { - String rootNodeName = rootNode.getName(); - sb.append("aspect MQTT {\n"); - sb.append(ind(1)).append("private MqttUpdater ").append(rootNodeName) - .append(".").append(mqttUpdaterField()).append(" = new MqttUpdater();\n"); - - // mqttSetHost(String host) - sb.append(ind(1)).append("public void ").append(rootNodeName).append(".") - .append(mqttSetHostMethod()).append("(String host) throws java.io.IOException {\n"); - sb.append(ind(2)).append(mqttUpdaterField()).append(".setHost(host);\n"); - sb.append(ind(1)).append("}\n"); - - // mqttSetHost(String host, int port) - sb.append(ind(1)).append("public void ").append(rootNodeName).append(".") - .append(mqttSetHostMethod()).append("(String host, int port) throws java.io.IOException {\n"); - sb.append(ind(2)).append(mqttUpdaterField()).append(".setHost(host, port);\n"); - sb.append(ind(1)).append("}\n\n"); - - // mqttWaitUntilReady - sb.append(ind(1)).append("public boolean ").append(rootNodeName).append(".") - .append(mqttWaitUntilReadyMethod()).append("(long time, java.util.concurrent.TimeUnit unit) {\n"); - sb.append(ind(2)).append("return ").append(mqttUpdaterField()).append(".waitUntilReady(time, unit);\n"); - sb.append(ind(1)).append("}\n\n"); - - // mqttClose - sb.append(ind(1)).append("public void ").append(rootNodeName).append(".") - .append(mqttCloseMethod()).append("() {\n"); - sb.append(ind(2)).append(mqttUpdaterField()).append(".close();\n"); - sb.append(ind(1)).append("}\n\n"); - - // mqttUpdater - sb.append(ind(1)).append("inh MqttUpdater ASTNode.").append(mqttUpdaterAttribute()).append("();\n"); - for (Component child : rootNode.getComponentList()) { - if (child.isTypeComponent()) { - sb.append(ind(1)).append("eq ").append(rootNodeName) - .append(".get").append(child.getName()).append("().") - .append(mqttUpdaterAttribute()).append("() = ").append(mqttUpdaterField()).append(";\n"); - } - } - sb.append("}\n\n"); - } - - public void Ros2Rag.generateGrammarExtension(StringBuilder sb) { - sb.append("aspect ROS2RAG {\n"); - - for (UpdateDefinition def : getUpdateDefinitionList()) { - def.generateAspect(sb); - } - for (MappingDefinition def : allMappingDefinitions()) { - def.generateAspect(sb); - } - for (DependencyDefinition def : getDependencyDefinitionList()) { - def.generateAspect(sb); - } - for (TokenComponent token : getProgram().allTokenComponents()) { - token.generateAspect(sb); - } - - sb.append("}\n"); - } - - abstract void UpdateDefinition.generateAspect(StringBuilder sb); - - String TokenUpdateDefinition.generateMappingApplication(StringBuilder sb, int indent, - String initialInputVariableName) { - final String resultVariablePrefix = "result"; // we do not need "_" here, because methodName begins with one - String inputVariableName = initialInputVariableName; - // last variable need to be declared before begin of try - MappingDefinition lastDefinition = effectiveMappings().get(effectiveMappings().size() - 1); - sb.append(ind(indent)).append(lastDefinition.getToType().prettyPrint()).append(" ") - .append(resultVariablePrefix).append(lastDefinition.methodName()).append(";\n"); - sb.append(ind(indent)).append("try {\n"); - for (MappingDefinition mappingDefinition : effectiveMappings()) { - String resultVariableName = resultVariablePrefix + mappingDefinition.methodName(); - sb.append(ind(indent + 1)); - if (mappingDefinition != lastDefinition) { - sb.append(mappingDefinition.getToType().prettyPrint()).append(" "); - } - sb.append(resultVariablePrefix).append(mappingDefinition.methodName()) - .append(" = ").append(mappingDefinition.methodName()).append("(") - .append(inputVariableName).append(");\n"); - inputVariableName = resultVariableName; - } - sb.append(ind(indent)).append("} catch (Exception e) {\n"); - sb.append(ind(indent + 1)).append("e.printStackTrace();\n"); - sb.append(ind(indent + 1)).append(preemptiveReturnStatement()).append("\n"); - sb.append(ind(indent)).append("}\n"); - if (!getAlwaysApply()) { - MappingDefinition lastMapping = effectiveMappings().get(effectiveMappings().size() - 1); - sb.append(ind(indent)).append("if ("); - if (lastMapping.getToType().isArray()) { - sb.append("java.util.Arrays.equals(").append(preemptiveExpectedValue()) - .append(", ").append(inputVariableName).append(")"); - } else { - sb.append(preemptiveExpectedValue()); - if (getToken().isPrimitiveType() && lastMapping.getToType().isPrimitiveType()) { - sb.append(" == ").append(inputVariableName); - } else if (lastMapping.isDefaultMappingDefinition()) { - sb.append(" != null && ").append(preemptiveExpectedValue()).append(".equals(") - .append(inputVariableName).append(")"); - } else { - sb.append(" != null ? ").append(preemptiveExpectedValue()).append(".equals(") - .append(inputVariableName).append(")").append(" : ") - .append(inputVariableName).append(" == null"); - } - } - sb.append(") { ").append(preemptiveReturnStatement()).append(" }\n"); - } - return inputVariableName; - } - - syn String TokenUpdateDefinition.preemptiveExpectedValue(); - eq ReadFromMqttDefinition.preemptiveExpectedValue() = "get" + getToken().getName() + "()"; - eq WriteToMqttDefinition.preemptiveExpectedValue() = lastValue(); - - syn String TokenUpdateDefinition.preemptiveReturnStatement(); - eq ReadFromMqttDefinition.preemptiveReturnStatement() = "return;"; - eq WriteToMqttDefinition.preemptiveReturnStatement() = "return false;"; - - @Override - void ReadFromMqttDefinition.generateAspect(StringBuilder sb) { - sb.append(ind(1)).append("public void ").append(getToken().containingTypeDecl().getName()).append(".") - .append(connectMethod()).append("(String topic) {\n"); - sb.append(ind(2)).append(mqttUpdaterAttribute()).append("().newConnection(topic, message -> {\n"); - String lastResult = generateMappingApplication(sb, 3, "message"); - if (loggingEnabledForReads) { - sb.append(ind(3)).append("System.out.println(\"[Read] \" + topic + \" -> ") - .append(getToken().getName()).append(" = \" + ").append(lastResult) - .append(");\n"); - } - sb.append(ind(3)).append("set").append(getToken().getName()).append("(").append(lastResult).append(");\n"); - sb.append(ind(2)).append("});\n"); - sb.append(ind(1)).append("}\n\n"); - } - - @Override - void WriteToMqttDefinition.generateAspect(StringBuilder sb) { - String parentTypeName = getToken().containingTypeDecl().getName(); - // fields - sb.append(ind(1)).append("private String ").append(parentTypeName).append(".") - .append(writeTopic()).append(" = null;\n"); - sb.append(ind(1)).append("private byte[] ").append(parentTypeName).append(".") - .append(lastValue()).append(" = null;\n"); - - // connect method - sb.append(ind(1)).append("public void ").append(parentTypeName).append(".") - .append(connectMethod()).append("(String topic, boolean writeCurrentValue) {\n"); - sb.append(ind(2)).append(writeTopic()).append(" = topic;\n"); - sb.append(ind(2)).append(updateMethod()).append("();\n"); - sb.append(ind(2)).append("if (writeCurrentValue) {\n"); - sb.append(ind(3)).append(writeMethod()).append("();\n"); - sb.append(ind(2)).append("}\n"); - sb.append(ind(1)).append("}\n\n"); + // naming copy attributes + // --- mqttUpdaterAttribute --- + inh String UpdateDefinition.mqttUpdaterAttribute(); + inh String MappingDefinition.mqttUpdaterAttribute(); + inh String DependencyDefinition.mqttUpdaterAttribute(); + eq Ros2Rag.getChild().mqttUpdaterAttribute() = mqttUpdaterAttribute(); - // update method - sb.append(ind(1)).append("protected boolean ").append(parentTypeName).append(".") - .append(updateMethod()).append("() {\n"); - sb.append(ind(2)).append(tokenResetMethod()).append("();\n"); - String lastResult = generateMappingApplication(sb, 2, "get" + getToken().getName() + "()"); - sb.append(ind(2)).append(lastValue()).append(" = ").append(lastResult).append(";\n"); - sb.append(ind(2)).append("return true;\n"); - sb.append(ind(1)).append("}\n\n"); + // --- rootNodeName --- + syn String ASTNode.rootNodeName() = rootNode.getName(); - // write method - sb.append(ind(1)).append("protected void ").append(parentTypeName).append(".") - .append(writeMethod()).append("() {\n"); - if (loggingEnabledForWrites) { - sb.append(ind(2)).append("System.out.println(\"[Write] ").append(getToken().getName()) - .append(" = \" + ") - .append("get").append(getToken().getName()).append("() + \" -> \" + ") - .append(writeTopic()).append(");\n"); - } - // _mqttUpdater().publish(${writeTopic()}, ${lastValue()}); - sb.append(ind(2)).append(mqttUpdaterAttribute()).append("().publish(") - .append(writeTopic()).append(", ").append(lastValue()).append(");\n"); - sb.append(ind(1)).append("}\n\n"); + public String Ros2Rag.generateAspect(String rootNodeName) { + rootNode = getProgram().resolveTypeDecl(rootNodeName); + return toMustache().generateAspect(); } - void MappingDefinition.generateAspect(StringBuilder sb) { - sb.append(ind(1)).append("protected static ").append(getToType().prettyPrint()) - .append(" ASTNode.").append(methodName()).append("(") - .append(getFromType().prettyPrint()).append(" ").append(getFromVariableName()) - .append(") throws Exception {\n"); - for (String line : getContent().split("\n")) { - if (!line.trim().isEmpty()) { - sb.append(ind(2)).append(line).append("\n"); + public String MRos2Rag.generateAspect() { + StringBuilder sb = new StringBuilder(); + com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() { + @Override + public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) { + return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) { + @Override + protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) { + com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes); + if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) { + throw new com.github.mustachejava.MustacheException(name + " not found in " + tc); + } + return wrapper; + } + }; } - } - sb.append(ind(1)).append("}\n\n"); - } - - void DependencyDefinition.generateAspect(StringBuilder sb) { - String targetParentTypeName = getTarget().containingTypeDecl().getName(); - String sourceParentTypeName = getSource().containingTypeDecl().getName(); - - // dependency method - sb.append(ind(1)).append("public void ").append(targetParentTypeName).append(".") - .append(dependencyMethod()).append("(").append(sourceParentTypeName).append(" source) {\n"); - sb.append(ind(2)).append("add").append(internalRelationPrefix()).append("Source(source);\n"); - sb.append(ind(1)).append("}\n\n"); - } - - void TokenComponent.generateAspect(StringBuilder sb) { - if (getDependencySourceDefinitionList().isEmpty()) { return; } - - String parentTypeName = containingTypeDecl().getName(); - // virtual setter - sb.append(ind(1)).append("public ").append(parentTypeName).append(" ") - .append(parentTypeName).append(".set").append(getName()).append("(") - .append(getJavaTypeUse().prettyPrint()).append(" value) {\n"); - sb.append(ind(2)).append("set").append(internalName()).append("(value);\n"); - - for (DependencyDefinition dependencyDefinition : getDependencySourceDefinitionList()) { - String targetParentTypeName = dependencyDefinition.getTarget().containingTypeDecl().getName(); - sb.append(ind(2)).append("for (").append(targetParentTypeName).append(" target : get") - .append(dependencyDefinition.internalRelationPrefix()).append("TargetList()) {\n"); - sb.append(ind(3)).append("if (target.") - .append(dependencyDefinition.targetUpdateDefinition().updateMethod()) - .append("()) {\n"); - sb.append(ind(4)).append("target.") - .append(dependencyDefinition.targetUpdateDefinition().writeMethod()) - .append("();\n"); - sb.append(ind(3)).append("}\n"); - sb.append(ind(2)).append("}\n"); - } - sb.append(ind(2)).append("return this;\n"); - sb.append(ind(1)).append("}\n\n"); - - // virtual getter - sb.append(ind(1)).append("public ").append(getJavaTypeUse().prettyPrint()) - .append(" ").append(parentTypeName).append(".get").append(getName()).append("() {\n"); - sb.append(ind(2)).append("return get").append(internalName()).append("();\n"); - sb.append(ind(1)).append("}\n\n"); + }; + com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); + mf.setObjectHandler(roh); + com.github.mustachejava.Mustache m = mf.compile("ros2rag.mustache"); + m.execute(new java.io.PrintWriter(new org.jastadd.ros2rag.compiler.AppendableWriter(sb)), this); + return sb.toString(); } } diff --git a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/AppendableWriter.java b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/AppendableWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..97680b18dc4fd1d8af3df75f35221e059355e19e --- /dev/null +++ b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/AppendableWriter.java @@ -0,0 +1,37 @@ +package org.jastadd.ros2rag.compiler; + +import java.io.IOException; +import java.io.Writer; + +/** + * Writer appending to a StringBuilder. + * + * @author rschoene - Initial contribution + */ +public class AppendableWriter extends Writer { + private final StringBuilder sb; + + public AppendableWriter(StringBuilder sb) { + this.sb = sb; + } + + @Override + public void write(char[] chars, int off, int len) throws IOException { + sb.append(chars, off, len); + } + + @Override + public void write(String str) throws IOException { + sb.append(str); + } + + @Override + public void flush() { + + } + + @Override + public void close() { + + } +} diff --git a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java index ee575b9495f53eca9925c6828ba96225734fe31a..8428bf27961070afa6410a349b3412e7bff729e6 100644 --- a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java +++ b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java @@ -37,6 +37,8 @@ public class Compiler { } public void run(String[] args) throws CommandLineException, CompilerException { + System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); + System.setProperty("mustache.debug", "true"); options = new ArrayList<>(); addOptions(); commandLine = new CommandLine(options); diff --git a/ros2rag.base/src/main/resources/dependencyDefinition.mustache b/ros2rag.base/src/main/resources/dependencyDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..b8d74ffe8f8aa5aaa8979682e4442215af1974e8 --- /dev/null +++ b/ros2rag.base/src/main/resources/dependencyDefinition.mustache @@ -0,0 +1,3 @@ + public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) { + add{{internalRelationPrefix}}Source(source); + } diff --git a/ros2rag.base/src/main/resources/mappingApplication.mustache b/ros2rag.base/src/main/resources/mappingApplication.mustache new file mode 100644 index 0000000000000000000000000000000000000000..95ce2fe29329d87f880497b37697d50c8c0687be --- /dev/null +++ b/ros2rag.base/src/main/resources/mappingApplication.mustache @@ -0,0 +1,14 @@ +{{lastDefinitionToType}} {{resultVarPrefix}}{{lastDefinitionName}}; +try { + {{#InnerMappingDefinitions}} + {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});{{!inputVarName has to be computed beforehand}} + {{/InnerMappingDefinitions}} +} catch (Exception e) { + e.printStackTrace(); + {{preemptiveReturn}} +} +{{^alwaysApply}} +if ({{{condition}}}) { + {{preemptiveReturn}} +} +{{/alwaysApply}} diff --git a/ros2rag.base/src/main/resources/mappingDefinition.mustache b/ros2rag.base/src/main/resources/mappingDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..920c5a610b0ce0cff1687122c97b6e18ce63bf7f --- /dev/null +++ b/ros2rag.base/src/main/resources/mappingDefinition.mustache @@ -0,0 +1,3 @@ + protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception { + {{{content}}}{{!maybe print line by line to get better indentation}} + } diff --git a/ros2rag.base/src/main/resources/mqtt.mustache b/ros2rag.base/src/main/resources/mqtt.mustache new file mode 100644 index 0000000000000000000000000000000000000000..2474e16525f6058540c3d73191369f4d2d056c4f --- /dev/null +++ b/ros2rag.base/src/main/resources/mqtt.mustache @@ -0,0 +1,22 @@ +aspect MQTT { + private MqttUpdater {{rootNodeName}}.{{mqttUpdaterField}} = new MqttUpdater(); + public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host) throws java.io.IOException { + {{mqttUpdaterField}}.setHost(host); + } + public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host, int port) throws java.io.IOException { + {{mqttUpdaterField}}.setHost(host, port); + } + + public boolean {{rootNodeName}}.{{mqttWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) { + return {{mqttUpdaterField}}.waitUntilReady(time, unit); + } + + public void {{rootNodeName}}.{{mqttCloseMethod}}() { + {{mqttUpdaterField}}.close(); + } + + inh MqttUpdater ASTNode.{{mqttUpdaterAttribute}}(); + {{#getRootTypeComponents}} + eq {{rootNodeName}}.get{{name}}().{{mqttUpdaterAttribute}}() = {{mqttUpdaterField}}; + {{/getRootTypeComponents}} +} diff --git a/ros2rag.base/src/main/resources/readDefinition.mustache b/ros2rag.base/src/main/resources/readDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..f908ef166db8eb781d7b7d46e7bae1719ea5fb1d --- /dev/null +++ b/ros2rag.base/src/main/resources/readDefinition.mustache @@ -0,0 +1,9 @@ + public void {{parentTypeName}}.{{connectMethod}}(String topic) { + {{mqttUpdaterAttribute}}().newConnection(topic, message -> { + {{> mappingApplication}} + {{#loggingEnabledForReads}} + System.out.println("[Read] " + topic + " -> {{tokenName}} = " + {{lastResult}});{{!lastResult has to be a new attribute}} + {{/loggingEnabledForReads}} + set{{tokenName}}({{lastResult}}); + }); + } diff --git a/ros2rag.base/src/main/resources/ros2rag.mustache b/ros2rag.base/src/main/resources/ros2rag.mustache new file mode 100644 index 0000000000000000000000000000000000000000..dd5e73cf554e35cc20da014b2d83f0db1b88521b --- /dev/null +++ b/ros2rag.base/src/main/resources/ros2rag.mustache @@ -0,0 +1,22 @@ +{{> mqtt}} +aspect ROS2RAG { + {{#ReadDefinitions}} + {{> readDefinition}} + {{/ReadDefinitions}} + + {{#WriteDefinitions}} + {{> writeDefinition}} + {{/WriteDefinitions}} + + {{#MappingDefinitions}} + {{> mappingDefinition}} + {{/MappingDefinitions}} + + {{#DependencyDefinitions}} + {{> dependencyDefinition}} + {{/DependencyDefinitions}} + + {{#TokenComponents}} + {{> tokenComponent}} + {{/TokenComponents}} +} diff --git a/ros2rag.base/src/main/resources/tokenComponent.mustache b/ros2rag.base/src/main/resources/tokenComponent.mustache new file mode 100644 index 0000000000000000000000000000000000000000..a3615c4526e5725bdd49121b16a87a011562f0cd --- /dev/null +++ b/ros2rag.base/src/main/resources/tokenComponent.mustache @@ -0,0 +1,17 @@ + public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) { + set{{internalName}}(value); + {{#DependencyDefinitions}} + for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) { + {{#targetUpdateDefinition}} + if (target.{{updateMethod}}()) { + target.{{writeMethod}}(); + } + {{/targetUpdateDefinition}} + } + {{/DependencyDefinitions}} + return this; + } + + public {{javaType}} {{parentTypeName}}.get{{name}}() { + return get{{internalName}}(); + } diff --git a/ros2rag.base/src/main/resources/writeDefinition.mustache b/ros2rag.base/src/main/resources/writeDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..3d8aab888ed0a39131f5cf00b20cfa2e9ade9048 --- /dev/null +++ b/ros2rag.base/src/main/resources/writeDefinition.mustache @@ -0,0 +1,24 @@ + private String {{parentTypeName}}.{{writeTopic}} = null; + private byte[] {{parentTypeName}}.{{lastValue}} = null; + + public void {{parentTypeName}}.{{connectMethod}}(String topic, boolean writeCurrentValue) { + {{writeTopic}} = topic; + {{updateMethod}}(); + if (writeCurrentValue) { + {{writeMethod}}(); + } + } + + protected boolean {{parentTypeName}}.{{updateMethod}}() { + {{tokenResetMethod}}(); + {{> mappingApplication}} + {{lastValue}} = {{lastResult}}; + return true; + } + + protected void {{parentTypeName}}.{{writeMethod}}() { + {{#loggingEnabledForWrites}} + System.out.println("[Write] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{writeTopic}}); + {{/loggingEnabledForWrites}} + {{mqttUpdaterAttribute}}().publish({{writeTopic}}, {{lastValue}}); + } diff --git a/ros2rag.receiverstub/build.gradle b/ros2rag.receiverstub/build.gradle index 0fe6ce127a31540f68c2a16f6f0a7bdf6326c249..59f2bcc96d6aa72f86c9a96f0221ada6a7c65139 100644 --- a/ros2rag.receiverstub/build.gradle +++ b/ros2rag.receiverstub/build.gradle @@ -25,8 +25,8 @@ dependencies { implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: "${jackson_version}" implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" implementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' - compile 'com.google.protobuf:protobuf-java:3.0.0' - compile group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0' + implementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' protobuf files("$projectDir/../ros2rag.example/src/main/proto") } diff --git a/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java b/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java index f954120848dad5a83d498f91c6c33bdceb32969f..f4a635989356bb01b2940e3ca93d76bd034e98ba 100644 --- a/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java +++ b/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java @@ -4,9 +4,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.protobuf.InvalidProtocolBufferException; import config.Dataconfig.DataConfig; import config.Robotconfig.RobotConfig; +import de.tudresden.inf.st.ros2rag.starter.Util; import de.tudresden.inf.st.ros2rag.starter.ast.MqttUpdater; import de.tudresden.inf.st.ros2rag.starter.data.DataConfiguration; -import de.tudresden.inf.st.ros2rag.starter.data.DataJoint; +import de.tudresden.inf.st.ros2rag.starter.data.DataConfiguration.ActualConfiguration; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,6 +17,7 @@ import java.io.File; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; public class Main { @@ -27,23 +29,24 @@ public class Main { ObjectMapper mapper = new ObjectMapper(); File configFile = new File(args[0]); System.out.println("Using config file: " + configFile.getAbsolutePath()); - DataConfiguration config = mapper.readValue(configFile, DataConfiguration.class); + ActualConfiguration config = mapper.readValue(configFile, DataConfiguration.class).panda_mqtt_connector; main.run(config); } - private void run(DataConfiguration config) throws IOException, InterruptedException { + private void run(ActualConfiguration config) throws IOException, InterruptedException { final CountDownLatch finish = new CountDownLatch(1); - int topicMaxLength = 0; + AtomicInteger topicMaxLength = new AtomicInteger(); MqttUpdater receiver = new MqttUpdater("receiver stub"); - receiver.setHost(config.mqttHost); + Util.setMqttHost(receiver, config); receiver.waitUntilReady(2, TimeUnit.SECONDS); - receiver.newConnection(config.robotConfigTopic, this::printRobotConfig); - receiver.newConnection(config.dataConfigTopic, this::printDataConfig); - for (DataJoint joint : config.joints) { - receiver.newConnection(joint.topic, this::printPandaLinkState); - topicMaxLength = Math.max(topicMaxLength, joint.topic.length()); - } + receiver.newConnection(config.topics.robotConfig, this::printRobotConfig); + receiver.newConnection(config.topics.dataConfig, this::printDataConfig); + + Util.iterateLinks((isEndEffector, topic, name) -> { + receiver.newConnection(topic, this::printPandaLinkState); + topicMaxLength.set(Math.max(topicMaxLength.get(), topic.length())); + }, config); this.topicPattern = "%" + topicMaxLength + "s"; receiver.newConnection("components", bytes -> { @@ -59,11 +62,7 @@ public class Main { }); Runtime.getRuntime().addShutdownHook(new Thread(receiver::close)); - if (config.exitAfterSeconds > 0) { - finish.await(config.exitAfterSeconds, TimeUnit.SECONDS); - } else { - finish.await(); - } + finish.await(); receiver.close(); } diff --git a/ros2rag.senderstub/build.gradle b/ros2rag.senderstub/build.gradle index 362a729af0aa2f6b7335b4cbd6a1164c22fe2dbf..a7900a991d5a4b44ebb52e8e7b9d81f4d0e11fe2 100644 --- a/ros2rag.senderstub/build.gradle +++ b/ros2rag.senderstub/build.gradle @@ -25,8 +25,8 @@ dependencies { implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: "${jackson_version}" implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" implementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' - compile 'com.google.protobuf:protobuf-java:3.0.0' - compile group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0' + implementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' protobuf files("$projectDir/../ros2rag.example/src/main/proto") } diff --git a/ros2rag.starter/build.gradle b/ros2rag.starter/build.gradle index 42aa7ecc0de7c5201e7847f41d607a79e8cbc1e6..b4aec98a67e1ddc6657e293bb90d4d9dfdf5ec29 100644 --- a/ros2rag.starter/build.gradle +++ b/ros2rag.starter/build.gradle @@ -30,9 +30,10 @@ dependencies { baseRuntimeClasspath project (':ros2rag.base') implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: "${jackson_version}" implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: "${jackson_version}" implementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' - compile 'com.google.protobuf:protobuf-java:3.0.0' - compile group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0' + implementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' jastadd2 "org.jastadd:jastadd:2.3.4" } diff --git a/ros2rag.starter/src/main/jastadd/Computation.jrag b/ros2rag.starter/src/main/jastadd/Computation.jrag index bddac5a4db5245259f19cfb72c86b9533fbf3fa7..d7c003c29cffe8d885430c929c0ffa89ccc514b3 100644 --- a/ros2rag.starter/src/main/jastadd/Computation.jrag +++ b/ros2rag.starter/src/main/jastadd/Computation.jrag @@ -1,8 +1,8 @@ aspect Computation { syn boolean RobotArm.isInSafetyZone() { System.out.println("isInSafetyZone()"); - for (Joint joint : getJointList()) { - if (model().getZoneModel().isInSafetyZone(joint.getCurrentPosition())) { + for (Link link : getLinkList()) { + if (model().getZoneModel().isInSafetyZone(link.getCurrentPosition())) { return true; } } diff --git a/ros2rag.starter/src/main/jastadd/Definitions.ros2rag b/ros2rag.starter/src/main/jastadd/Definitions.ros2rag index 94c7aa11611efe3a594bf5b1a8652ffdccd8cc57..99aec4416578d7af313846eb2ed33645b58cc358 100644 --- a/ros2rag.starter/src/main/jastadd/Definitions.ros2rag +++ b/ros2rag.starter/src/main/jastadd/Definitions.ros2rag @@ -2,11 +2,11 @@ * Version 2020-05-28 */ // --- update definitions --- -read Joint.CurrentPosition using ParseLinkState, LinkStateToIntPosition ; +read Link.CurrentPosition using ParseLinkState, LinkStateToIntPosition ; write RobotArm.AppropriateSpeed using CreateSpeedMessage, SerializeRobotConfig ; // --- dependency definitions --- -RobotArm.AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ; +RobotArm.AppropriateSpeed canDependOn Link.CurrentPosition as dependency1 ; // --- mapping definitions --- ParseLinkState maps byte[] bytes to panda.Linkstate.PandaLinkState {: diff --git a/ros2rag.starter/src/main/jastadd/Navigation.jrag b/ros2rag.starter/src/main/jastadd/Navigation.jrag index 855a2d87d6bbb1374c5ae23569435fe2a9b329ac..db53288f4c4b61f0c5d315f8a18ab3f5bac9b720 100644 --- a/ros2rag.starter/src/main/jastadd/Navigation.jrag +++ b/ros2rag.starter/src/main/jastadd/Navigation.jrag @@ -2,7 +2,7 @@ aspect Navigation { inh Model RobotArm.model(); eq Model.getRobotArm().model() = this; - inh RobotArm Joint.containingRobotArm(); - eq RobotArm.getJoint().containingRobotArm() = this; + inh RobotArm Link.containingRobotArm(); + eq RobotArm.getLink().containingRobotArm() = this; eq RobotArm.getEndEffector().containingRobotArm() = this; } diff --git a/ros2rag.starter/src/main/jastadd/RobotModel.relast b/ros2rag.starter/src/main/jastadd/RobotModel.relast index 415d3e5e25b8d03fa1d53bc6f95c05ad5e1987d8..b8d932ae470e4bf3dc433f8610eec1f05016e595 100644 --- a/ros2rag.starter/src/main/jastadd/RobotModel.relast +++ b/ros2rag.starter/src/main/jastadd/RobotModel.relast @@ -4,10 +4,10 @@ ZoneModel ::= SafetyZone:Zone* ; Zone ::= Coordinate* ; -RobotArm ::= Joint* EndEffector /<AppropriateSpeed:double>/ ; +RobotArm ::= Link* EndEffector /<AppropriateSpeed:double>/ ; -Joint ::= <Name:String> <CurrentPosition:IntPosition> ; +Link ::= <Name:String> <CurrentPosition:IntPosition> ; -EndEffector : Joint; +EndEffector : Link; Coordinate ::= <Position:IntPosition> ; diff --git a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java index 66a48f60b39cb7c5d48af9993eeca7159dc92e51..570b219e35c9f6defbe5d6041eece66a21af508e 100644 --- a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java +++ b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java @@ -1,15 +1,18 @@ package de.tudresden.inf.st.ros2rag.starter; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import config.Dataconfig; import de.tudresden.inf.st.ros2rag.starter.ast.*; import de.tudresden.inf.st.ros2rag.starter.data.DataConfiguration; -import de.tudresden.inf.st.ros2rag.starter.data.DataJoint; +import de.tudresden.inf.st.ros2rag.starter.data.DataConfiguration.ActualConfiguration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.File; import java.io.IOException; +import java.util.Map.Entry; +import java.util.SortedMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -25,26 +28,26 @@ public class StarterMain { private Model model; public void run(String[] args) throws IOException, InterruptedException { - File configFile = new File(args[0]); - - final int[][] safetyZoneCoordinates = { - {1, 1, 0}, - {-1, -1, 1} - }; + File configFile = new File(args.length == 0 ? "./src/main/resources/config.yaml" : args[0]); // --- No configuration below this line --- - ObjectMapper mapper = new ObjectMapper(); + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); System.out.println("Using config file: " + configFile.getAbsolutePath()); - DataConfiguration config = mapper.readValue(configFile, DataConfiguration.class); + ActualConfiguration config = mapper.readValue(configFile, DataConfiguration.class).panda_mqtt_connector; model = new Model(); - model.MqttSetHost(config.mqttHost); + Util.setMqttHost(model::MqttSetHost, config); ZoneModel zoneModel = new ZoneModel(); Zone safetyZone = new Zone(); - for (int[] coordinate : safetyZoneCoordinates) { + for (String zone : config.zones) { + int[] coordinate = {0, 0, 0}; + String[] zoneSplit = zone.split(" "); + for (int i = 0; i < zoneSplit.length; i++) { + coordinate[i] = Integer.parseInt(zoneSplit[i]); + } safetyZone.addCoordinate(new Coordinate( IntPosition.of(coordinate[0], coordinate[1], coordinate[2]))); } @@ -55,24 +58,23 @@ public class StarterMain { RobotArm robotArm = new RobotArm(); model.setRobotArm(robotArm); - for (DataJoint dataJoint : config.joints) { - final Joint jointOrEndEffector; - if (dataJoint.isEndEffector) { + Util.iterateLinks((isEndEffector, topic, name) -> { + Link link; + if (isEndEffector) { EndEffector endEffector = new EndEffector(); robotArm.setEndEffector(endEffector); - jointOrEndEffector = endEffector; + link = endEffector; } else { - Joint joint = new Joint(); - robotArm.addJoint(joint); - jointOrEndEffector = joint; + link = new Link(); + robotArm.addLink(link); } - jointOrEndEffector.setName(dataJoint.name); - jointOrEndEffector.setCurrentPosition(makePosition(0, 0, 0)); - robotArm.addDependency1(jointOrEndEffector); - jointOrEndEffector.connectCurrentPosition(dataJoint.topic); - } + link.setName(name); + link.setCurrentPosition(makePosition(0, 0, 0)); + link.containingRobotArm().addDependency1(link); + link.connectCurrentPosition(topic); + }, config); - robotArm.connectAppropriateSpeed(config.robotConfigTopic, true); + robotArm.connectAppropriateSpeed(config.topics.robotConfig, true); logStatus("Start", robotArm); CountDownLatch exitCondition = new CountDownLatch(1); @@ -81,12 +83,12 @@ public class StarterMain { logger.info("To exit the system cleanly, send a message to the topic 'exit', or use Ctrl+C."); mainHandler = new MqttUpdater("mainHandler"); - mainHandler.setHost(config.mqttHost); + Util.setMqttHost(mainHandler, config); mainHandler.waitUntilReady(2, TimeUnit.SECONDS); mainHandler.newConnection("exit", bytes -> exitCondition.countDown()); mainHandler.newConnection("model", bytes -> logStatus(new String(bytes), robotArm)); - sendInitialDataConfig(mainHandler, config.dataConfigTopic); + sendInitialDataConfig(mainHandler, config.topics.dataConfig); Runtime.getRuntime().addShutdownHook(new Thread(this::close)); @@ -110,7 +112,7 @@ public class StarterMain { StringBuilder sb = new StringBuilder(prefix).append("\n") .append("robotArm.isInSafetyZone: ").append(robotArm.isInSafetyZone()) .append(", robotArm.getAppropriateSpeed = ").append(robotArm.getAppropriateSpeed()).append("\n"); - for (Joint joint : robotArm.getJointList()) { + for (Link joint : robotArm.getLinkList()) { sb.append(joint.getName()).append(": ").append(joint.getCurrentPosition()).append("\n"); } sb.append("endEffector ").append(robotArm.getEndEffector().getName()).append(": ") diff --git a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/Util.java b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/Util.java new file mode 100644 index 0000000000000000000000000000000000000000..22a27e5cfbfac0f1cec983238a34ffec14501aad --- /dev/null +++ b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/Util.java @@ -0,0 +1,67 @@ +package de.tudresden.inf.st.ros2rag.starter; + +import de.tudresden.inf.st.ros2rag.starter.ast.Link; +import de.tudresden.inf.st.ros2rag.starter.ast.MqttUpdater; +import de.tudresden.inf.st.ros2rag.starter.data.DataConfiguration; +import de.tudresden.inf.st.ros2rag.starter.data.DataConfiguration.ActualConfiguration; + +import java.io.IOException; +import java.util.Map; +import java.util.SortedMap; + +/** + * Helper method dealing with config. + * + * @author rschoene - Initial contribution + */ +public class Util { + public static void setMqttHost(RootElement model, ActualConfiguration config) throws IOException { + HostAndPort hostAndPort = split(config.server); + model.MqttSetHost(hostAndPort.host, hostAndPort.port); + } + + public static void setMqttHost(MqttUpdater handler, ActualConfiguration config) throws IOException { + HostAndPort hostAndPort = split(config.server); + handler.setHost(hostAndPort.host, hostAndPort.port); + } + + public static void iterateLinks(HandleLink callback, ActualConfiguration config) { + for (Map.Entry<String, SortedMap<String, String>> dataRobot : config.parts.entrySet()) { + String topicPrefix = dataRobot.getKey() + "/"; + for (Map.Entry<String, String> dataLink : dataRobot.getValue().entrySet()) { + String name = dataLink.getKey(); + callback.handle(false, topicPrefix + name, name); + } + } + for (Map.Entry<String, SortedMap<String, String>> dataRobot : config.end_effectors.entrySet()) { + String topicPrefix = dataRobot.getKey() + "/"; + for (Map.Entry<String, String> dataLink : dataRobot.getValue().entrySet()) { + String name = dataLink.getKey(); + callback.handle(true, topicPrefix + name, name); + } + } + } + + private static HostAndPort split(String serverString) { + HostAndPort result = new HostAndPort(); + String[] serverTokens = serverString.replace("tcp://", "").split(":"); + result.host = serverTokens[0]; + result.port = Integer.parseInt(serverTokens[1]); + return result; + } + + private static class HostAndPort { + String host; + int port; + } + + @FunctionalInterface + public interface RootElement { + void MqttSetHost(String host, int port) throws IOException; + } + + @FunctionalInterface + public interface HandleLink { + void handle(boolean isEndEffector, String topic, String name); + } +} diff --git a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/data/DataConfiguration.java b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/data/DataConfiguration.java index b2ec3639dc485971c58a91b0cd4916b37e79c0b2..092d498d6fcb7535eb016d06efeffeb54a8b9a52 100644 --- a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/data/DataConfiguration.java +++ b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/data/DataConfiguration.java @@ -1,7 +1,9 @@ package de.tudresden.inf.st.ros2rag.starter.data; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.SortedMap; /** * Data class for initial configuration. @@ -9,9 +11,17 @@ import java.util.List; * @author rschoene - Initial contribution */ public class DataConfiguration { - public List<DataJoint> joints = new ArrayList<>(); - public String robotConfigTopic; - public String dataConfigTopic; - public int exitAfterSeconds = 0; - public String mqttHost = "localhost"; + public ActualConfiguration panda_mqtt_connector; + public static class ActualConfiguration { + public String server = "tcp://localhost:1883"; + public DataTopics topics; + public int zone_size; + public List<String> zones; + public Map<String, SortedMap<String, String>> parts; + public Map<String, SortedMap<String, String>> end_effectors; + } + public static class DataTopics { + public String robotConfig; + public String dataConfig; + } } diff --git a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/data/DataJoint.java b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/data/DataJoint.java deleted file mode 100644 index 2431827d30a9742f0366283c39d1dc156b876da2..0000000000000000000000000000000000000000 --- a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/data/DataJoint.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.tudresden.inf.st.ros2rag.starter.data; - -/** - * Data class to describe a joint. - * - * @author rschoene - Initial contribution - */ -public class DataJoint { - public String topic; - public String name; - public boolean isEndEffector = false; -} diff --git a/ros2rag.starter/src/main/resources/config.yaml b/ros2rag.starter/src/main/resources/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a18ba849e3714314ed726f9e2780839dc1cae3ae --- /dev/null +++ b/ros2rag.starter/src/main/resources/config.yaml @@ -0,0 +1,23 @@ +panda_mqtt_connector: + server: "tcp://localhost:1883" + topics: + robotConfig: "robotconfig" + dataConfig: "dataconfig" + zone_size: 0.5 + zones: + - "1 1" + - "-1 -1 1" + parts: + panda: + Link0: "panda::panda_link0" + Link1: "panda::panda_link1" + Link2: "panda::panda_link2" + Link3: "panda::panda_link3" + Link4: "panda::panda_link4" + Link5: "panda::panda_link5" + Link6: "panda::panda_link6" + LeftFinger: "panda::panda_leftfinger" + RightFinger: "panda::panda_rightfinger" + end_effectors: + panda: + EndEffector: "panda::panda_link7" diff --git a/ros2rag.tests/build.gradle b/ros2rag.tests/build.gradle index 568f441a595ac78644f2d216c850ab92ef8660d9..add5c201afc959a8054b2f196b4ad59ca959b9a6 100644 --- a/ros2rag.tests/build.gradle +++ b/ros2rag.tests/build.gradle @@ -19,14 +19,15 @@ buildscript { } dependencies { - runtime 'org.jastadd:jastadd:2.3.4' implementation project(':ros2rag.base') - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.0' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.0' - testImplementation 'org.assertj:assertj-core:3.12.1' + + runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1' testImplementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' testImplementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' - testImplementation 'com.google.protobuf:protobuf-java:3.0.0' + testImplementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0' } test {