diff --git a/.gitignore b/.gitignore index a78076875b36829500e0d58bf05998ee5ade757a..78aba4bb88ae15cc77fd1bb1b454f37c145e3839 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .classpath .idea/ .gradle/ +build/ diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle index 7bfebcfe84f3d6f53a6ceb997eb0108610135e31..c986f2a61adf610a9c2e5fdc7fd20eb193a9a46a 100644 --- a/ragconnect.base/build.gradle +++ b/ragconnect.base/build.gradle @@ -69,19 +69,12 @@ task relast(type: JavaExec) { group = 'Build' main = "-jar" - doFirst { - delete "src/gen/jastadd/*.ast" - delete "src/gen/jastadd/RagConnect.jadd" - delete "src/gen/jastadd/RagConnectRefResolver.jadd" - delete "src/gen/jastadd/RagConnectResolverStubs.jrag" - mkdir "src/gen/jastadd/" - } - args = [ "../libs/relast.jar", "../relast.preprocessor/src/main/jastadd/RelAst.relast", "./src/main/jastadd/RagConnect.relast", "./src/main/jastadd/MustacheNodes.relast", + "./src/main/jastadd/YAML.relast", "--listClass=java.util.ArrayList", "--jastAddList=JastAddList", "--useJastAddNames", @@ -90,14 +83,15 @@ task relast(type: JavaExec) { "--grammarName=./src/gen/jastadd/RagConnect" ] - inputs.files file("../libs/relast.jar"), - file("../relast.preprocessor/src/main/jastadd/RelAST.relast"), - file("./src/main/jastadd/RagConnect.relast") - file("./src/main/jastadd/MustacheNodes.relast") - outputs.files file("./src/gen/jastadd/RagConnect.ast"), - file("./src/gen/jastadd/RagConnect.jadd"), - file("./src/gen/jastadd/RagConnectRefResolver.jadd"), - file('./src/gen/jastadd/RagConnectResolverStubs.jrag') + inputs.files("../libs/relast.jar", + "../relast.preprocessor/src/main/jastadd/RelAst.relast", + "./src/main/jastadd/RagConnect.relast", + "./src/main/jastadd/MustacheNodes.relast", + "./src/main/jastadd/YAML.relast") + outputs.files("./src/gen/jastadd/RagConnect.ast", + "./src/gen/jastadd/RagConnect.jadd", + "./src/gen/jastadd/RagConnectRefResolver.jadd", + './src/gen/jastadd/RagConnectResolverStubs.jrag') } jastadd { diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag index 623a3f0ad8c0457d8d0606bf70dea0adfea89ea4..b62399960f34a4b8c23a8d23762ec195491ce4e3 100644 --- a/ragconnect.base/src/main/jastadd/Analysis.jrag +++ b/ragconnect.base/src/main/jastadd/Analysis.jrag @@ -1,13 +1,15 @@ aspect Analysis { // --- lookupTokenEndpointDefinition --- - inh TokenEndpointDefinition TokenEndpointDefinition.lookupTokenEndpointDefinition(TokenComponent token); - eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinition(TokenComponent token) { + inh java.util.List<TokenEndpointDefinition> TokenEndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token); + eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token); + syn java.util.List<TokenEndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) { + java.util.List<TokenEndpointDefinition> result = new java.util.ArrayList<>(); for (EndpointDefinition def : getEndpointDefinitionList()) { if (def.isTokenEndpointDefinition() && def.asTokenEndpointDefinition().getToken().equals(token)) { - return def.asTokenEndpointDefinition(); + result.add(def.asTokenEndpointDefinition()); } } - return null; + return result; } // --- lookupDependencyDefinition --- @@ -22,6 +24,34 @@ aspect Analysis { } // --- isAlreadyDefined --- - syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinition(getToken()) != this; + syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinitions(getToken()).size() > 1; syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this; + + syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target); + eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target); + eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) { + if (!target.getName().endsWith("[]")) { return false; } + return getType().assignableTo(new SimpleJavaTypeUse(target.getName().replace("[]", ""))); + } + syn boolean JavaTypeUse.assignableTo(JavaTypeUse target) { + // target var = this; + return target.primitivePrettyPrint().equals(this.primitivePrettyPrint()); + } + syn String JavaTypeUse.primitivePrettyPrint() { + switch(getName()) { + case "int": + case "Integer": return "int"; + case "short": + case "Short": return "short"; + case "long": + case "Long": return "long"; + case "float": + case "Float": return "float"; + case "double": + case "Double": return "double"; + case "char": + case "Character": return "char"; + default: return getName(); + } + } } diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag index bfee2545d4bee28223b04ce931ccd6a6429cf13f..fe2fcd59efd365174ca2803affebc34819cf3d7e 100644 --- a/ragconnect.base/src/main/jastadd/Errors.jrag +++ b/ragconnect.base/src/main/jastadd/Errors.jrag @@ -7,32 +7,32 @@ aspect Errors { [new TreeSet<ErrorMessage>()] root RagConnect; - ReceiveFromMqttDefinition contributes error("Receive definition already defined for " + getToken().getName()) + ReceiveTokenEndpointDefinition contributes error("Receive definition already defined for " + getToken().getName()) when isAlreadyDefined() to RagConnect.errors(); - ReceiveFromMqttDefinition contributes error("Receiving target token must not be an NTA token!") + ReceiveTokenEndpointDefinition contributes error("Receiving target token must not be an NTA token!") when getToken().getNTA() to RagConnect.errors(); // if first mapping is null, then suitableDefaultMapping() == null - ReceiveFromMqttDefinition contributes error("No suitable default mapping found for type " + + ReceiveTokenEndpointDefinition contributes error("No suitable default mapping found for type " + ((getMappingList().isEmpty()) - ? getToken().getJavaTypeUse().prettyPrint() + ? getToken().effectiveJavaTypeUse().prettyPrint() : getMappingList().get(0).getFromType().prettyPrint())) when effectiveMappings().get(0) == null to RagConnect.errors(); - ReceiveFromMqttDefinition contributes error("to-type of last mapping must be type of the Token!") - when getToken().getJavaTypeUse().prettyPrint().equals( - effectiveMappings().get(effectiveMappings().size() - 1)) + ReceiveTokenEndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") not assignable to type of the Token (" + getToken().effectiveJavaTypeUse().prettyPrint() + ")!") + when !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo( + getToken().effectiveJavaTypeUse()) to RagConnect.errors(); - SendToMqttDefinition contributes error("Sending target token must be an NTA token!") + SendTokenEndpointDefinition contributes error("Sending target token must be an NTA token!") when !getToken().getNTA() to RagConnect.errors(); - SendToMqttDefinition contributes error("Send definition already defined for " + getToken().getName()) + SendTokenEndpointDefinition contributes error("Send definition already defined for " + getToken().getName()) when isAlreadyDefined() to RagConnect.errors(); @@ -44,7 +44,7 @@ aspect Errors { when isAlreadyDefinedAsList() to RagConnect.errors(); - DependencyDefinition contributes error("There must be a send update definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID()) + DependencyDefinition contributes error("There must be a send endpoint definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID()) when targetEndpointDefinition() == null to RagConnect.errors(); } diff --git a/ragconnect.base/src/main/jastadd/MustacheNodes.relast b/ragconnect.base/src/main/jastadd/MustacheNodes.relast index ce85caba554d1f582079b299f457d7675ab12dfa..62ecdb6166a783e125cefef2826d550b73ab9e1b 100644 --- a/ragconnect.base/src/main/jastadd/MustacheNodes.relast +++ b/ragconnect.base/src/main/jastadd/MustacheNodes.relast @@ -1,6 +1,3 @@ -//TypeComponentMustache ; -//rel TypeComponentMustache.TypeComponent -> TypeComponent ; - MRagConnect ::= ReceiveDefinition:MReceiveDefinition* SendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*; abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*; MReceiveDefinition : MEndpointDefinition; @@ -12,9 +9,9 @@ MTypeComponent; MTokenComponent; rel MRagConnect.RagConnect -> RagConnect; -rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition; -rel MReceiveDefinition.ReceiveFromMqttDefinition -> ReceiveFromMqttDefinition; -rel MSendDefinition.SendToMqttDefinition -> SendToMqttDefinition; +rel MInnerMappingDefinition.MMappingDefinition -> MMappingDefinition; +rel MReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition; +rel MSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition; rel MMappingDefinition.MappingDefinition -> MappingDefinition; rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition; rel MTypeComponent.TypeComponent -> TypeComponent; diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag index 2314386eb9f74ecb5699d6fba91956e80f75fa63..752fc36aea4c59348874c1a08134696a9543b6cb 100644 --- a/ragconnect.base/src/main/jastadd/Navigation.jrag +++ b/ragconnect.base/src/main/jastadd/Navigation.jrag @@ -10,9 +10,12 @@ aspect Navigation { eq MRagConnect.getChild().ragconnect() = getRagConnect(); // --- containedFile + eq Grammar.getChild().containedFile() = null; eq RagConnect.getChild().containedFile() = null; + eq MRagConnect.getChild().containedFile() = null; // --- containedFileName --- + eq Grammar.getChild().containedFileName() = null; // should be in PP eq RagConnect.getChild().containedFileName() = getFileName(); eq MRagConnect.getChild().containedFileName() = null; @@ -24,30 +27,33 @@ aspect Navigation { syn TokenEndpointDefinition EndpointDefinition.asTokenEndpointDefinition() = null; eq TokenEndpointDefinition.asTokenEndpointDefinition() = this; - // --- isSendToMqttDefinition --- - syn boolean EndpointDefinition.isSendToMqttDefinition() = false; - eq SendToMqttDefinition.isSendToMqttDefinition() = true; + // --- isSendTokenEndpointDefinition --- + syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false; + eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true; - // --- asSendToMqttDefinition --- - syn SendToMqttDefinition EndpointDefinition.asSendToMqttDefinition() = null; - eq SendToMqttDefinition.asSendToMqttDefinition() = this; + // --- asSendTokenEndpointDefinition --- + syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null; + eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this; - // --- asReceiveFromMqttDefinition --- - syn ReceiveFromMqttDefinition EndpointDefinition.asReceiveFromMqttDefinition() = null; - eq ReceiveFromMqttDefinition.asReceiveFromMqttDefinition() = this; + // --- asReceiveTokenEndpointDefinition --- + syn ReceiveTokenEndpointDefinition EndpointDefinition.asReceiveTokenEndpointDefinition() = null; + eq ReceiveTokenEndpointDefinition.asReceiveTokenEndpointDefinition() = this; // --- targetEndpointDefinition --- - syn SendToMqttDefinition DependencyDefinition.targetEndpointDefinition() { + syn SendTokenEndpointDefinition DependencyDefinition.targetEndpointDefinition() { // resolve definition in here, as we do not need resolveMethod in any other place (yet) for (EndpointDefinition endpointDefinition : ragconnect().getEndpointDefinitionList()) { - if (endpointDefinition.isSendToMqttDefinition() && - endpointDefinition.asSendToMqttDefinition().getToken().equals(this.getTarget())) { - return endpointDefinition.asSendToMqttDefinition(); + if (endpointDefinition.isSendTokenEndpointDefinition() && + endpointDefinition.asSendTokenEndpointDefinition().getToken().equals(this.getTarget())) { + return endpointDefinition.asSendTokenEndpointDefinition(); } } return null; } + // --- effectiveJavaTypeUse (should be in preprocessor) --- + syn lazy JavaTypeUse TokenComponent.effectiveJavaTypeUse() = hasJavaTypeUse() ? getJavaTypeUse() : new SimpleJavaTypeUse("String"); + // --- isDefaultMappingDefinition --- syn boolean MappingDefinition.isDefaultMappingDefinition() = false; eq DefaultMappingDefinition.isDefaultMappingDefinition() = true; diff --git a/ragconnect.base/src/main/jastadd/Printing.jrag b/ragconnect.base/src/main/jastadd/Printing.jrag new file mode 100644 index 0000000000000000000000000000000000000000..74607aeb7737e1e43c9565b799693889a648a9df --- /dev/null +++ b/ragconnect.base/src/main/jastadd/Printing.jrag @@ -0,0 +1,75 @@ +aspect Printing { + String ASTNode.PRINT_INDENT = " "; + + syn String MappingDefinitionType.prettyPrint(); + eq JavaMappingDefinitionType.prettyPrint() = getType().getName(); + eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]"; + + syn String JavaTypeUse.prettyPrint() { + StringBuilder sb = new StringBuilder(); + generateAbstractGrammar(sb); + return sb.toString(); + } + + syn String Document.prettyPrint() { + StringBuilder sb = new StringBuilder(); + sb.append("# RagConnect created at ").append(java.time.Instant.now()).append("\n"); + for (ComplexElement element : getComplexElementList()) { + element.prettyPrint(sb, false, ""); + } + if (sb.charAt(sb.length() - 1) != '\n') { + sb.append("\n"); + } + return sb.toString(); + } + syn StringBuilder Element.prettyPrint(StringBuilder sb, boolean printIndent, String indent); + eq ValueElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + sb.append(getValue()); + return sb; + } + eq StringElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + sb.append("\"").append(getValue()).append("\""); + return sb; + } + eq ListElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + if (isEmpty()) { + sb.append("[]"); + } else { + for (Element element : getElementList()) { + sb.append(indent).append("- "); + element.prettyPrint(sb, false, indent + PRINT_INDENT); + sb.append("\n"); + } + // delete last newline + sb.deleteCharAt(sb.length() - 1); + } + return sb; + } + eq KeyValuePair.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + if (printIndent) sb.append(indent); + sb.append(getKey()).append(": "); + if (getValue().isComplex() && !getValue().isEmpty()) { + sb.append("\n"); + getValue().prettyPrint(sb, true, indent + PRINT_INDENT); //); + } else { + getValue().prettyPrint(sb, false, indent); + } + return sb; + } + eq MappingElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + if (isEmpty()) { + sb.append("{}"); + } else { + boolean first = true; + for (KeyValuePair pair : getKeyValuePairList()) { + if (!first || printIndent) sb.append(indent); + first = false; + pair.prettyPrint(sb, false, indent); // + PRINT_INDENT + sb.append("\n"); + } + // delete last newline + sb.deleteCharAt(sb.length() - 1); + } + return sb; + } +} diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast index d30116efae1a6735e88922e31f42f99911c6532a..accc617714ff6bdd24a22831ea84e5d93e6a15b8 100644 --- a/ragconnect.base/src/main/jastadd/RagConnect.relast +++ b/ragconnect.base/src/main/jastadd/RagConnect.relast @@ -2,13 +2,13 @@ RagConnect ::= EndpointDefinition* DependencyDefinition* MappingDefinition* Prog abstract EndpointDefinition ::= <AlwaysApply:boolean> ; -rel EndpointDefinition.Mapping* -> MappingDefinition; +rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*; abstract TokenEndpointDefinition : EndpointDefinition; rel TokenEndpointDefinition.Token -> TokenComponent; -ReceiveFromMqttDefinition : TokenEndpointDefinition; -SendToMqttDefinition : TokenEndpointDefinition; +ReceiveTokenEndpointDefinition : TokenEndpointDefinition; +SendTokenEndpointDefinition : TokenEndpointDefinition; DependencyDefinition ::= <ID>; rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; diff --git a/ragconnect.base/src/main/jastadd/Util.jadd b/ragconnect.base/src/main/jastadd/Util.jadd new file mode 100644 index 0000000000000000000000000000000000000000..3b59b13c53a8d0e1ca2a05feba43294797a4577e --- /dev/null +++ b/ragconnect.base/src/main/jastadd/Util.jadd @@ -0,0 +1,5 @@ +aspect Util { + static String ASTNode.capitalize(String s) { + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } +} diff --git a/ragconnect.base/src/main/jastadd/YAML.jrag b/ragconnect.base/src/main/jastadd/YAML.jrag new file mode 100644 index 0000000000000000000000000000000000000000..93dd0f34b7b7b0505cf0970b6541fa3ae5bee7fa --- /dev/null +++ b/ragconnect.base/src/main/jastadd/YAML.jrag @@ -0,0 +1,31 @@ +aspect Navigation { + eq Document.getChild().program() = null; + eq Document.getChild().ragconnect() = null; + eq Document.getChild().containedFile() = null; + eq Document.getChild().containedFileName() = getFileName(); + + syn boolean Element.isComplex() = false; + eq ComplexElement.isComplex() = true; + + syn boolean Element.isEmpty() = false; + eq MappingElement.isEmpty() = getNumKeyValuePair() == 0; + eq ListElement.isEmpty() = getNumElement() == 0; +} + +aspect Helper { + public static ValueElement ValueElement.of(int value) { + return new ValueElement(String.valueOf(value)); + } + public static ValueElement ValueElement.of(boolean value) { + return new ValueElement(String.valueOf(value)); + } + public static ValueElement ValueElement.of(String value) { + return new ValueElement(value); + } + public static StringElement StringElement.of(String value) { + return new StringElement(value); + } + public void MappingElement.addKeyValuePair(String key, Element value) { + addKeyValuePair(new KeyValuePair(key, value)); + } +} diff --git a/ragconnect.base/src/main/jastadd/YAML.relast b/ragconnect.base/src/main/jastadd/YAML.relast new file mode 100644 index 0000000000000000000000000000000000000000..94f4ab20fdc3838348bd53b812b3d1477cae3558 --- /dev/null +++ b/ragconnect.base/src/main/jastadd/YAML.relast @@ -0,0 +1,9 @@ +Document ::= <FileName> ComplexElement* ; +abstract Element ; +abstract ComplexElement : Element ; +MappingElement : ComplexElement ::= KeyValuePair* ; +KeyValuePair : ComplexElement ::= <Key> Value:Element ; +ListElement : ComplexElement ::= Element* ; +abstract SimpleElement : Element ; +ValueElement : SimpleElement ::= <Value> ; +StringElement : SimpleElement ::= <Value> ; diff --git a/ragconnect.base/src/main/jastadd/backend/Configuration.jadd b/ragconnect.base/src/main/jastadd/backend/Configuration.jadd index d4f5b43f27cb329f1ff82318c6f383f0f69ebbd6..8d3b16c3f99014408c6aefa5655201d222c3d892 100644 --- a/ragconnect.base/src/main/jastadd/backend/Configuration.jadd +++ b/ragconnect.base/src/main/jastadd/backend/Configuration.jadd @@ -2,4 +2,6 @@ aspect Configuration { public static boolean ASTNode.loggingEnabledForReads = false; public static boolean ASTNode.loggingEnabledForWrites = false; public static TypeDecl ASTNode.rootNode; + public static boolean ASTNode.usesMqtt; + public static boolean ASTNode.usesRest; } diff --git a/ragconnect.base/src/main/jastadd/backend/Generation.jadd b/ragconnect.base/src/main/jastadd/backend/Generation.jadd index 067626ed42f12add896ca4eea7c65917309c0e2e..4b8d357e82e82eb8454451978589434f1ef44505 100644 --- a/ragconnect.base/src/main/jastadd/backend/Generation.jadd +++ b/ragconnect.base/src/main/jastadd/backend/Generation.jadd @@ -1,41 +1,19 @@ -aspect GenerationUtils { - public static final String ASTNode.aspectIndent = " "; - - public String ASTNode.ind(int n) { - StringBuilder s = new StringBuilder(); - for (int i = 0; i < n; i++) { - s.append(aspectIndent); - } - return s.toString(); - } - - // --- prettyPrint --- - syn String MappingDefinitionType.prettyPrint(); - eq JavaMappingDefinitionType.prettyPrint() = getType().getName(); - eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]"; - - syn String JavaTypeUse.prettyPrint() { - StringBuilder sb = new StringBuilder(); - generateAbstractGrammar(sb); - return sb.toString(); - } -} - -/* Open questions -- Should all string constants be defined on the normal AST, or on the special mustache AST? +/* +Design considerations +- InnerMappingDefinition needed for iteration attribute (first, last) - not easily possible with list-relation */ aspect AttributesForMustache { // --- MRagConnect --- - eq MRagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute(); - eq MRagConnect.getChild().mqttHandlerField() = mqttHandlerField(); eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0; - syn String MRagConnect.mqttHandlerAttribute() = getRagConnect().mqttHandlerAttribute(); - syn String MRagConnect.mqttHandlerField() = getRagConnect().mqttHandlerField(); - syn String MRagConnect.mqttSetHostMethod() = getRagConnect().mqttSetHostMethod(); - syn String MRagConnect.mqttWaitUntilReadyMethod() = getRagConnect().mqttWaitUntilReadyMethod(); - syn String MRagConnect.mqttCloseMethod() = getRagConnect().mqttCloseMethod(); + syn String MRagConnect.closeMethod() = "ragconnectCloseConnections"; + syn String MRagConnect.mqttHandlerAttribute() = "_mqttHandler"; + syn String MRagConnect.mqttHandlerField() = "_mqttHandler"; + syn String MRagConnect.mqttSetupWaitUntilReadyMethod() = "ragconnectSetupMqttWaitUntilReady"; + + syn String MRagConnect.restHandlerAttribute() = "_restHandler"; + syn String MRagConnect.restHandlerField() = "_restHandler"; // --- MEndpointDefinition --- syn String MEndpointDefinition.preemptiveExpectedValue(); @@ -44,29 +22,25 @@ aspect AttributesForMustache { syn String MEndpointDefinition.firstInputVarName(); eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1; - eq MEndpointDefinition.getInnerMappingDefinition().resultVarPrefix() = resultVarPrefix(); - eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : resultVarPrefix() + getInnerMappingDefinition(i - 1).getMappingDefinition().methodName(); - - inh String MEndpointDefinition.mqttHandlerAttribute(); + eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName(); - syn String MEndpointDefinition.connectMethod() = endpointDef().connectMethod(); + syn String MEndpointDefinition.connectParameterName() = "uriString"; + syn String MEndpointDefinition.connectMethod() = "connect" + tokenName(); syn TokenComponent MEndpointDefinition.token() = endpointDef().getToken(); syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply(); - syn String MEndpointDefinition.resultVarPrefix() = "result"; // we do not need "_" here, because methodName begins with one syn String MEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName(); syn String MEndpointDefinition.tokenName() = token().getName(); syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1); - syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().ToType(); - syn String MEndpointDefinition.lastDefinitionName() = lastDefinition().methodName(); - syn String MEndpointDefinition.lastResult() = resultVarPrefix() + lastDefinitionName(); + syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType(); + syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName(); syn String MEndpointDefinition.condition() { - if (lastDefinition().getMappingDefinition().getToType().isArray()) { + if (lastDefinition().mappingDef().getToType().isArray()) { return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")"; } - if (token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) { + if (token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) { return preemptiveExpectedValue() + " == " + lastResult(); } - if (lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) { + if (lastDefinition().mappingDef().isDefaultMappingDefinition()) { return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")"; } return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null"; @@ -74,66 +48,67 @@ aspect AttributesForMustache { // --- 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(); + syn String MInnerMappingDefinition.toType() = mappingDef().getToType().prettyPrint(); + syn String MInnerMappingDefinition.methodName() = getMMappingDefinition().methodName(); + syn MappingDefinition MInnerMappingDefinition.mappingDef() = getMMappingDefinition().getMappingDefinition(); + syn String MInnerMappingDefinition.outputVarName() = "result" + methodName(); // we do not need "_" in between here, because methodName begins with one // --- MReceiveDefinition --- eq MReceiveDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()"; eq MReceiveDefinition.preemptiveReturn() = "return;"; - eq MReceiveDefinition.endpointDef() = getReceiveFromMqttDefinition(); + eq MReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition(); eq MReceiveDefinition.firstInputVarName() = "message"; // --- MSendDefinition --- eq MSendDefinition.preemptiveExpectedValue() = lastValue(); eq MSendDefinition.preemptiveReturn() = "return false;"; - eq MSendDefinition.endpointDef() = getSendToMqttDefinition(); + eq MSendDefinition.endpointDef() = getSendTokenEndpointDefinition(); eq MSendDefinition.firstInputVarName() = "get" + tokenName() + "()"; - syn String MSendDefinition.sendTopic() = getSendToMqttDefinition().sendTopic(); - syn String MSendDefinition.lastValue() = getSendToMqttDefinition().lastValue(); - syn String MSendDefinition.updateMethod() = getSendToMqttDefinition().updateMethod(); - syn String MSendDefinition.writeMethod() = getSendToMqttDefinition().writeMethod(); - syn String MSendDefinition.tokenResetMethod() = getSendToMqttDefinition().tokenResetMethod(); + syn String MSendDefinition.sender() = "_sender_" + tokenName(); + syn String MSendDefinition.lastValue() = "_lastValue" + tokenName(); + syn String MSendDefinition.updateMethod() = "_update_" + tokenName(); + syn String MSendDefinition.writeMethod() = "_writeLastValue_" + tokenName(); + syn String MSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset"; // --- MMappingDefinition --- syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint(); - syn String MMappingDefinition.methodName() = getMappingDefinition().methodName(); + syn String MMappingDefinition.methodName() = "_apply_" + getMappingDefinition().getID(); 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.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID()); syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName(); - syn String MDependencyDefinition.internalRelationPrefix() = getDependencyDefinition().internalRelationPrefix(); - syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() { + syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID(); + syn nta MSendDefinition MDependencyDefinition.targetEndpointDefinition() { return getDependencyDefinition().targetEndpointDefinition().toMustache(); } // --- MTypeComponent --- syn String MTypeComponent.name() = getTypeComponent().getName(); - inh String MTypeComponent.mqttHandlerAttribute(); - inh String MTypeComponent.mqttHandlerField(); inh boolean MTypeComponent.isFirst(); // --- 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(); + syn String MTokenComponent.javaType() = getTokenComponent().effectiveJavaTypeUse().prettyPrint(); + syn String MTokenComponent.internalName() = getTokenComponent().getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + name(); + syn String MTokenComponent.externalName() = name(); // --- toMustache --- syn lazy MRagConnect RagConnect.toMustache() { MRagConnect result = new MRagConnect(); result.setRagConnect(this); for (EndpointDefinition def : getEndpointDefinitionList()) { - if (def.isSendToMqttDefinition()) { - result.addSendDefinition(def.asSendToMqttDefinition().toMustache()); + if (def.isSendTokenEndpointDefinition()) { + SendTokenEndpointDefinition sendDef = def.asSendTokenEndpointDefinition(); + result.addSendDefinition(sendDef.toMustache()); } else { - result.addReceiveDefinition(def.asReceiveFromMqttDefinition().toMustache()); + result.addReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache()); } } for (MappingDefinition def : allMappingDefinitions()) { @@ -155,41 +130,46 @@ aspect AttributesForMustache { return result; } -//MInnerMappingDefinition.MappingDefinition -> MappingDefinition; protected void MEndpointDefinition.addInnerMappings() { for (MappingDefinition def : endpointDef().effectiveMappings()) { MInnerMappingDefinition inner = new MInnerMappingDefinition(); - inner.setMappingDefinition(def); + inner.setMMappingDefinition(def.toMustache()); addInnerMappingDefinition(inner); } } - syn lazy MReceiveDefinition ReceiveFromMqttDefinition.toMustache() { + + syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() { MReceiveDefinition result = new MReceiveDefinition(); - result.setReceiveFromMqttDefinition(this); + result.setReceiveTokenEndpointDefinition(this); result.addInnerMappings(); return result; } - syn lazy MSendDefinition SendToMqttDefinition.toMustache() { + + syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache() { MSendDefinition result = new MSendDefinition(); - result.setSendToMqttDefinition(this); + result.setSendTokenEndpointDefinition(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); @@ -201,37 +181,6 @@ aspect AttributesForMustache { } aspect AspectGeneration { - // naming convention attributes - syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName(); - syn String TokenComponent.externalName() = getName(); - - syn String TokenEndpointDefinition.connectMethod() = "connect" + getToken().getName(); - syn String SendToMqttDefinition.sendTopic() = "_topic_" + getToken().getName(); - syn String SendToMqttDefinition.lastValue() = "_lastValue" + getToken().getName(); - syn String SendToMqttDefinition.updateMethod() = "_update_" + getToken().getName(); - syn String SendToMqttDefinition.writeMethod() = "_writeLastValue_" + getToken().getName(); - syn String SendToMqttDefinition.tokenResetMethod() = "get" + getToken().getName() + "_reset"; - syn String MappingDefinition.methodName() = "_apply_" + getID(); - syn String DependencyDefinition.dependencyMethod() = "add" + - Character.toUpperCase(getID().charAt(0)) + - getID().substring(1); - syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID(); - syn String DependencyDefinition.internalTokenName() = getSource().internalName(); - - syn String RagConnect.mqttHandlerAttribute() = "_mqttHandler"; - syn String RagConnect.mqttHandlerField() = "_mqttHandler"; - - syn String RagConnect.mqttSetHostMethod() = "MqttSetHost"; - syn String RagConnect.mqttWaitUntilReadyMethod() = "MqttWaitUntilReady"; - syn String RagConnect.mqttCloseMethod() = "MqttCloseConnections"; - - // naming copy attributes - // --- mqttHandlerAttribute --- - inh String EndpointDefinition.mqttHandlerAttribute(); - inh String MappingDefinition.mqttHandlerAttribute(); - inh String DependencyDefinition.mqttHandlerAttribute(); - eq RagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute(); - // --- rootNodeName --- syn String ASTNode.rootNodeName() = rootNode.getName(); @@ -275,10 +224,11 @@ aspect RelationGeneration { } syn nta Relation DependencyDefinition.getRelationToCreate() { + String internalRelationPrefix = toMustache().internalRelationPrefix(); BidirectionalRelation result = new BidirectionalRelation(); - NavigableRole left = new ListRole(internalRelationPrefix() + "Source"); + NavigableRole left = new ListRole(internalRelationPrefix + "Source"); left.setType(getTarget().containingTypeDecl()); - NavigableRole right = new ListRole(internalRelationPrefix() + "Target"); + NavigableRole right = new ListRole(internalRelationPrefix + "Target"); right.setType(getSource().containingTypeDecl()); result.setLeft(left); result.setRight(right); @@ -294,9 +244,9 @@ aspect GrammarExtension { } b.append("<"); if (!getName().equals("")) { - b.append(internalName()).append(":"); + b.append(toMustache().internalName()).append(":"); } - getJavaTypeUse().generateAbstractGrammar(b); + effectiveJavaTypeUse().generateAbstractGrammar(b); b.append(">"); if (getNTA()) { b.append("/"); diff --git a/ragconnect.base/src/main/jastadd/backend/Mappings.jrag b/ragconnect.base/src/main/jastadd/backend/Mappings.jrag index 07cbf5b81a531638ac092635f915ed856a65df4c..7aa82d304079592d605997484d1348a3d3681875 100644 --- a/ragconnect.base/src/main/jastadd/backend/Mappings.jrag +++ b/ragconnect.base/src/main/jastadd/backend/Mappings.jrag @@ -1,117 +1,91 @@ aspect DefaultMappings { - private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionFromBytes(String typeName) { - DefaultMappingDefinition result = new DefaultMappingDefinition(); - result.setID("_DefaultBytesTo" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "Mapping"); - result.setFromType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte"))); - result.setFromVariableName("bytes"); - result.setToType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName))); - return result; - } - - private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionToBytes(String typeName) { - DefaultMappingDefinition result = new DefaultMappingDefinition(); - result.setID("_Default" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "ToBytesMapping"); - result.setFromType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName))); - result.setFromVariableName("input"); - result.setToType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte"))); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("int"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getInt();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("short"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getShort();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("long"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getLong();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("float"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getFloat();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("double"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getDouble();"); - return result; + private String RagConnect.baseDefaultMappingTypeNamePart(String typeName) { + return capitalize(typeName).replace("[]", "s"); } - syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("char"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getChar();"); - return result; + private MappingDefinitionType RagConnect.baseDefaultMappingTypeFromName(String typeName) { + return typeName.endsWith("[]") ? + new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse(typeName.replace("[]", ""))) : + new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)); } - syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("String"); - result.setContent("return new String(bytes);"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("int"); - result.setContent("return java.nio.ByteBuffer.allocate(4).putInt(input).array();"); + private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) { + DefaultMappingDefinition result = new DefaultMappingDefinition(); + result.setID("_Default" + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping"); + result.setFromType(baseDefaultMappingTypeFromName(fromTypeName)); + result.setFromVariableName("input"); + result.setToType(baseDefaultMappingTypeFromName(toTypeName)); + result.setContent(content); return result; } - syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("short"); - result.setContent("return java.nio.ByteBuffer.allocate(2).putShort(input).array();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() = baseDefaultMappingDefinition( + "byte[]", "int", "return java.nio.ByteBuffer.wrap(input).getInt();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() = baseDefaultMappingDefinition( + "byte[]", "short", "return java.nio.ByteBuffer.wrap(input).getShort();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() = baseDefaultMappingDefinition( + "byte[]", "long", "return java.nio.ByteBuffer.wrap(input).getLong();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() = baseDefaultMappingDefinition( + "byte[]", "float", "return java.nio.ByteBuffer.wrap(input).getFloat();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() = baseDefaultMappingDefinition( + "byte[]", "double", "return java.nio.ByteBuffer.wrap(input).getDouble();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() = baseDefaultMappingDefinition( + "byte[]", "char", "return java.nio.ByteBuffer.wrap(input).getChar();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() = baseDefaultMappingDefinition( + "byte[]", "String", "return new String(input);"); - syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("long"); - result.setContent("return java.nio.ByteBuffer.allocate(8).putLong(input).array();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition( + "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition( + "short", "byte[]", "return java.nio.ByteBuffer.allocate(Short.BYTES).putShort(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() = baseDefaultMappingDefinition( + "long", "byte[]", "return java.nio.ByteBuffer.allocate(Long.BYTES).putLong(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() = baseDefaultMappingDefinition( + "float", "byte[]", "return java.nio.ByteBuffer.allocate(Float.BYTES).putFloat(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() = baseDefaultMappingDefinition( + "double", "byte[]", "return java.nio.ByteBuffer.allocate(Double.BYTES).putDouble(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() = baseDefaultMappingDefinition( + "char", "byte[]", "return java.nio.ByteBuffer.allocate(Character.BYTES).putChar(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() = baseDefaultMappingDefinition( + "String", "byte[]", "return input.getBytes();"); - syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("float"); - result.setContent("return java.nio.ByteBuffer.allocate(4).putFloat(input).array();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultStringToIntMapping() = baseDefaultMappingDefinition( + "String", "int", "return Integer.parseInt(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToShortMapping() = baseDefaultMappingDefinition( + "String", "short", "return Short.parseShort(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToLongMapping() = baseDefaultMappingDefinition( + "String", "long", "return Long.parseLong(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToFloatMapping() = baseDefaultMappingDefinition( + "String", "float", "return Float.parseFloat(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToDoubleMapping() = baseDefaultMappingDefinition( + "String", "double", "return Double.parseDouble(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToCharMapping() = baseDefaultMappingDefinition( + "String", "char", "return input.charAt(0);"); - syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("double"); - result.setContent("return java.nio.ByteBuffer.allocate(8).putDouble(input).array();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("char"); - result.setContent("return java.nio.ByteBuffer.allocate(2).putChar(input).array();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("String"); - result.setContent("return input.getBytes();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultIntToStringMapping() = baseDefaultMappingDefinition( + "int", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultShortToStringMapping() = baseDefaultMappingDefinition( + "short", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultLongToStringMapping() = baseDefaultMappingDefinition( + "long", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultFloatToStringMapping() = baseDefaultMappingDefinition( + "float", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultDoubleToStringMapping() = baseDefaultMappingDefinition( + "double", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultCharToStringMapping() = baseDefaultMappingDefinition( + "char", "String", "return String.valueOf(input);"); } aspect Mappings { // --- effectiveMappings --- syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings(); - eq ReceiveFromMqttDefinition.effectiveMappings() { - // if there is a first mapping, check if its input type is byte[]. + eq ReceiveTokenEndpointDefinition.effectiveMappings() { + // if there is a first mapping, check if it is suitable. // or if no mappings are specified. // then prepend the suitable default mapping java.util.List<MappingDefinition> result; - if (getMappingList().isEmpty() || !getMappingList().get(0).getFromType().isByteArray()) { + if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) { result = new java.util.ArrayList(); result.add(suitableDefaultMapping()); result.addAll(getMappingList()); @@ -120,15 +94,13 @@ aspect Mappings { } return result; } - eq SendToMqttDefinition.effectiveMappings() { - // if there is a mapping, check if the output type of the last mapping is byte[]. + eq SendTokenEndpointDefinition.effectiveMappings() { + // if there is a mapping, check if it is suitable. // or if no mappings are specified. // then append the suitable default mapping java.util.List<MappingDefinition> result; - int numMappings = getMappingList().size(); - if (numMappings == 0 || !getMappingList().get(numMappings - 1).getToType().isByteArray()) { - result = new java.util.ArrayList(); - result.addAll(getMappingList()); + if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) { + result = new java.util.ArrayList(getMappingList()); result.add(suitableDefaultMapping()); } else { result = getMappingList(); @@ -136,8 +108,18 @@ aspect Mappings { return result; } + // --- hasSuitableEdgeMapping --- + syn boolean TokenEndpointDefinition.hasSuitableEdgeMapping(); + eq ReceiveTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(0)); + eq SendTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(getMappingList().size() - 1)); + + // --- isSuitableEdgeMapping(def) --- + syn boolean TokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def); + eq ReceiveTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getFromType().isByteArray(); + eq SendTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getToType().isByteArray(); + // --- isPrimitiveType --- - syn boolean TokenComponent.isPrimitiveType() = getJavaTypeUse().isPrimitiveType(); + syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType(); syn boolean JavaTypeUse.isPrimitiveType() = false; eq SimpleJavaTypeUse.isPrimitiveType() { switch(getName()) { @@ -160,9 +142,9 @@ aspect Mappings { // --- suitableDefaultMapping --- syn DefaultMappingDefinition EndpointDefinition.suitableDefaultMapping(); - eq ReceiveFromMqttDefinition.suitableDefaultMapping() { + eq ReceiveTokenEndpointDefinition.suitableDefaultMapping() { String typeName = getMappingList().isEmpty() ? - getToken().getJavaTypeUse().getName() : + getToken().effectiveJavaTypeUse().getName() : getMappingList().get(0).getFromType().prettyPrint(); switch(typeName) { case "int": @@ -181,9 +163,9 @@ aspect Mappings { default: return null; } } - eq SendToMqttDefinition.suitableDefaultMapping() { + eq SendTokenEndpointDefinition.suitableDefaultMapping() { String typeName = getMappingList().isEmpty() ? - getToken().getJavaTypeUse().getName() : + getToken().effectiveJavaTypeUse().getName() : getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint(); switch(typeName) { case "int": @@ -202,15 +184,60 @@ aspect Mappings { default: return null; } } +// eq ReceiveFromRestDefinition.suitableDefaultMapping() { +// String typeName = getMappingList().isEmpty() ? +// getToken().getJavaTypeUse().getName() : +// getMappingList().get(0).getFromType().prettyPrint(); +// switch(typeName) { +// case "int": +// case "Integer": return ragconnect().defaultStringToIntMapping(); +// case "short": +// case "Short": return ragconnect().defaultStringToShortMapping(); +// case "long": +// case "Long": return ragconnect().defaultStringToLongMapping(); +// case "float": +// case "Float": return ragconnect().defaultStringToFloatMapping(); +// case "double": +// case "Double": return ragconnect().defaultStringToDoubleMapping(); +// case "char": +// case "Character": return ragconnect().defaultStringToCharMapping(); +// default: return null; +// } +// } +// eq SendToRestDefinition.suitableDefaultMapping() { +// String typeName = getMappingList().isEmpty() ? +// getToken().getJavaTypeUse().getName() : +// getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint(); +// switch(typeName) { +// case "int": +// case "Integer": return ragconnect().defaultIntToStringMapping(); +// case "short": +// case "Short": return ragconnect().defaultShortToStringMapping(); +// case "long": +// case "Long": return ragconnect().defaultLongToStringMapping(); +// case "float": +// case "Float": return ragconnect().defaultFloatToStringMapping(); +// case "double": +// case "Double": return ragconnect().defaultDoubleToStringMapping(); +// case "char": +// case "Character": return ragconnect().defaultCharToStringMapping(); +// default: return null; +// } +// } // --- isByteArray --- syn boolean MappingDefinitionType.isByteArray() = false; eq JavaArrayMappingDefinitionType.isByteArray() = getType().getName().equals("byte"); +// // --- isString --- +// syn boolean MappingDefinitionType.isString() = false; +// eq JavaMappingDefinitionType.isString() = getType().getName().equals("String"); + // --- allMappingDefinitions --- syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() { java.util.List<MappingDefinition> result = new java.util.ArrayList<>(); getMappingDefinitionList().iterator().forEachRemaining(result::add); + // byte[] conversion result.add(defaultBytesToIntMapping()); result.add(defaultBytesToShortMapping()); result.add(defaultBytesToLongMapping()); @@ -225,6 +252,19 @@ aspect Mappings { result.add(defaultDoubleToBytesMapping()); result.add(defaultCharToBytesMapping()); result.add(defaultStringToBytesMapping()); +// // string conversion +// result.add(defaultStringToIntMapping()); +// result.add(defaultStringToShortMapping()); +// result.add(defaultStringToLongMapping()); +// result.add(defaultStringToFloatMapping()); +// result.add(defaultStringToDoubleMapping()); +// result.add(defaultStringToCharMapping()); +// result.add(defaultIntToStringMapping()); +// result.add(defaultShortToStringMapping()); +// result.add(defaultLongToStringMapping()); +// result.add(defaultFloatToStringMapping()); +// result.add(defaultDoubleToStringMapping()); +// result.add(defaultCharToStringMapping()); return result; } } diff --git a/ragconnect.base/src/main/jastadd/backend/MustacheNodesToYAML.jrag b/ragconnect.base/src/main/jastadd/backend/MustacheNodesToYAML.jrag new file mode 100644 index 0000000000000000000000000000000000000000..2894d3a8a531a190e8146121dc8e7c97f2cecc91 --- /dev/null +++ b/ragconnect.base/src/main/jastadd/backend/MustacheNodesToYAML.jrag @@ -0,0 +1,155 @@ +aspect MustacheNodesToYAML { + syn Document MRagConnect.toYAML() { + Document doc = new Document(); + MappingElement root = new MappingElement(); + root.addKeyValuePair("rootNodeName", StringElement.of(rootNodeName())); + root.addKeyValuePair("closeMethod", StringElement.of(closeMethod())); + root.addKeyValuePair("usesMqtt", ValueElement.of(usesMqtt)); + root.addKeyValuePair("usesRest", ValueElement.of(usesRest)); + // mqtt + root.addKeyValuePair("mqttHandlerField", StringElement.of(mqttHandlerField())); + root.addKeyValuePair("mqttHandlerAttribute", StringElement.of(mqttHandlerAttribute())); + root.addKeyValuePair("mqttSetupWaitUntilReadyMethod", StringElement.of(mqttSetupWaitUntilReadyMethod())); + + // rootTypeComponents + ListElement rootTypeComponents = new ListElement(); + for (MTypeComponent comp : getRootTypeComponentList()) { + MappingElement inner = new MappingElement(); + inner.addKeyValuePair("first", ValueElement.of(comp.isFirst())); + inner.addKeyValuePair("name", StringElement.of(comp.name())); + rootTypeComponents.addElement(inner); + } + root.addKeyValuePair("RootTypeComponents", rootTypeComponents); + + // rest + root.addKeyValuePair("restHandlerField", StringElement.of(restHandlerField())); + root.addKeyValuePair("restHandlerAttribute", StringElement.of(restHandlerAttribute())); + + // ReceiveDefinitions + ListElement receiveDefinitions = new ListElement(); + for (MReceiveDefinition def : getReceiveDefinitionList()) { + receiveDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("ReceiveDefinitions", receiveDefinitions); + + // SendDefinitions + ListElement sendDefinitions = new ListElement(); + for (MSendDefinition def : getSendDefinitionList()) { + sendDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("SendDefinitions", sendDefinitions); + + // MappingDefinitions + ListElement mappingDefinitions = new ListElement(); + for (MMappingDefinition def : getMappingDefinitionList()) { + mappingDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("MappingDefinitions", mappingDefinitions); + + // DependencyDefinitions + ListElement dependencyDefinitions = new ListElement(); + for (MDependencyDefinition def : getDependencyDefinitionList()) { + dependencyDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("DependencyDefinitions", dependencyDefinitions); + + // TokenComponents + ListElement tokenComponents = new ListElement(); + for (MTokenComponent comp : getTokenComponentList()) { + tokenComponents.addElement(comp.toYAML()); + } + root.addKeyValuePair("TokenComponents", tokenComponents); + + doc.addComplexElement(root); + return doc; + } + + syn MappingElement MEndpointDefinition.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName())); + result.addKeyValuePair("connectMethod", StringElement.of(connectMethod())); + result.addKeyValuePair("connectParameterName", StringElement.of(connectParameterName())); + result.addKeyValuePair("lastDefinitionToType", StringElement.of(lastDefinitionToType())); + result.addKeyValuePair("preemptiveReturn", StringElement.of(preemptiveReturn())); + result.addKeyValuePair("alwaysApply", ValueElement.of(alwaysApply())); + result.addKeyValuePair("condition", StringElement.of( + condition().replace("\"", "\\\"").replace("\n", "\\n"))); + result.addKeyValuePair("lastResult", StringElement.of(lastResult())); + result.addKeyValuePair("tokenName", StringElement.of(tokenName())); + result.addKeyValuePair("InnerMappingDefinitions", innerMappingDefinitionsAsListElement()); + return result; + } + + syn MappingElement MReceiveDefinition.toYAML() { + MappingElement result = super.toYAML(); + result.addKeyValuePair("loggingEnabledForReads", ValueElement.of(loggingEnabledForReads)); + return result; + } + + syn MappingElement MSendDefinition.toYAML() { + MappingElement result = super.toYAML(); + result.addKeyValuePair("sender", StringElement.of(sender())); + result.addKeyValuePair("lastValue", StringElement.of(lastValue())); + result.addKeyValuePair("loggingEnabledForWrites", ValueElement.of(loggingEnabledForWrites)); + result.addKeyValuePair("updateMethod", StringElement.of(updateMethod())); + result.addKeyValuePair("writeMethod", StringElement.of(writeMethod())); + result.addKeyValuePair("tokenResetMethod", StringElement.of(tokenResetMethod())); + return result; + } + + syn Element MMappingDefinition.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("toType", StringElement.of(toType())); + result.addKeyValuePair("methodName", StringElement.of(methodName())); + result.addKeyValuePair("fromType", StringElement.of(fromType())); + result.addKeyValuePair("fromVariableName", StringElement.of(fromVariableName())); + result.addKeyValuePair("content", StringElement.of( + content().replace("\"", "\\\"").replace("\n", "\\n"))); + return result; + } + + syn Element MDependencyDefinition.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("targetParentTypeName", StringElement.of(targetParentTypeName())); + result.addKeyValuePair("dependencyMethod", StringElement.of(dependencyMethod())); + result.addKeyValuePair("sourceParentTypeName", StringElement.of(sourceParentTypeName())); + result.addKeyValuePair("internalRelationPrefix", StringElement.of(internalRelationPrefix())); + return result; + } + + syn Element MTokenComponent.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName())); + result.addKeyValuePair("name", StringElement.of(name())); + result.addKeyValuePair("javaType", StringElement.of(javaType())); + result.addKeyValuePair("internalName", StringElement.of(internalName())); + ListElement dependencyDefinitions = new ListElement(); + for (MDependencyDefinition def : getDependencyDefinitionList()) { + MappingElement inner = new MappingElement(); + inner.addKeyValuePair("targetParentTypeName", StringElement.of(def.targetParentTypeName())); + inner.addKeyValuePair("internalRelationPrefix", StringElement.of(def.internalRelationPrefix())); + MappingElement targetEndpointDefinition = new MappingElement(); + targetEndpointDefinition.addKeyValuePair("updateMethod", StringElement.of(def.targetEndpointDefinition().updateMethod())); + targetEndpointDefinition.addKeyValuePair("writeMethod", StringElement.of(def.targetEndpointDefinition().writeMethod())); + inner.addKeyValuePair("targetEndpointDefinition", targetEndpointDefinition); + dependencyDefinitions.addElement(inner); + } + result.addKeyValuePair("DependencyDefinitions", dependencyDefinitions); + return result; + } + + ListElement MEndpointDefinition.innerMappingDefinitionsAsListElement() { + ListElement innerMappingDefinitions = new ListElement(); + for (MInnerMappingDefinition def : getInnerMappingDefinitionList()) { + MappingElement inner = new MappingElement(); + inner.addKeyValuePair("toType", StringElement.of(def.toType())); + inner.addKeyValuePair("methodName", StringElement.of(def.methodName())); + inner.addKeyValuePair("inputVarName", StringElement.of(def.inputVarName())); + inner.addKeyValuePair("outputVarName", StringElement.of(def.outputVarName())); + inner.addKeyValuePair("last", ValueElement.of(def.isLast())); + innerMappingDefinitions.addElement(inner); + } + return innerMappingDefinitions; + } + +} diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser index e2685c15555056c64d4f5e8bebdc458ecea981a3..0432b0df96c44060f6df1d6918a856f62810230d 100644 --- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser +++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser @@ -10,41 +10,35 @@ RagConnect ragconnect private Iterable<String> makeMappingDefs(ArrayList<?> raw_mapping_defs) { return () -> raw_mapping_defs.stream().map(raw -> ((Symbol) raw).value.toString()).iterator(); } + private TokenEndpointDefinition enableAlwaysApply(TokenEndpointDefinition def) { + def.setAlwaysApply(true); + return def; + } :} ; EndpointDefinition endpoint_definition - = RECEIVE ID.type_name DOT ID.token_name SCOL + = endpoint_definition_type.endpointDef SCOL {: - ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); - return result; + return endpointDef; :} - | RECEIVE ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL + | endpoint_definition_type.endpointDef USING string_list.mapping_defs SCOL {: - ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); for (String mapping_def : makeMappingDefs(mapping_defs)) { - result.addMapping(MappingDefinition.createRef(mapping_def)); + endpointDef.addMapping(MappingDefinition.createRef(mapping_def)); } - return result; - :} - | SEND ID.type_name DOT ID.token_name SCOL - {: - SendToMqttDefinition result = new SendToMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); - return result; - :} - | SEND ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL - {: - SendToMqttDefinition result = new SendToMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); - for (String mapping_def : makeMappingDefs(mapping_defs)) { - result.addMapping(MappingDefinition.createRef(mapping_def)); - } - return result; + return endpointDef; :} ; +EndpointDefinition endpoint_definition_type + = RECEIVE token_ref {: return new ReceiveTokenEndpointDefinition().setToken(token_ref); :} + | SEND token_ref {: return new SendTokenEndpointDefinition().setToken(token_ref); :} +; + +TokenComponent token_ref + = ID.type_name DOT ID.token_name {: return TokenComponent.createRef(type_name + "." + token_name); :} +; + ArrayList string_list = ID | string_list COMMA ID diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java index 8b33412c7994b5443088de6249273b9b83374414..711169ec2ea7d9c21b48dc52c5a7d2cf4cc8b427 100644 --- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java +++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java @@ -14,18 +14,21 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.Collection; -import java.util.MissingResourceException; -import java.util.ResourceBundle; +import java.util.*; public class Compiler extends AbstractCompiler { // private ValueOption optionOutputDir; private ValueOption optionRootNode; + private ValueOption optionProtocols; + private BooleanOption optionPrintYaml; private BooleanOption optionVerbose; private BooleanOption optionLogReads; private BooleanOption optionLogWrites; + private static final String OPTION_PROTOCOL_MQTT = "mqtt"; + private static final String OPTION_PROTOCOL_REST = "rest"; + public Compiler() { super("ragconnect", true); } @@ -51,6 +54,10 @@ public class Compiler extends AbstractCompiler { } } + if (!optionRootNode.isMatched()) { + return error("Root node not specified"); + } + RagConnect ragConnect = parseProgram(getConfiguration().getFiles()); if (!ragConnect.errors().isEmpty()) { @@ -61,25 +68,41 @@ public class Compiler extends AbstractCompiler { System.exit(1); } + if (optionPrintYaml.value()) { + ASTNode.rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value()); + String yamlContent = ragConnect.toMustache().toYAML().prettyPrint(); + System.out.println(yamlContent); + writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.yml"), yamlContent); + return 0; + } + printMessage("Writing output files"); - // copy MqttHandler into outputDir - final String mqttHandlerFileName = "MqttHandler.jadd"; - try { - InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(mqttHandlerFileName); - if (inputStream == null) { - throw new CompilerException("Could not open " + mqttHandlerFileName); + final List<String> handlers = new ArrayList<>(); + if (ASTNode.usesMqtt) { + handlers.add("MqttHandler.jadd"); + } + if (ASTNode.usesRest) { + handlers.add("RestHandler.jadd"); + } + // copy handlers into outputDir + for (String handlerFileName : handlers) { + try { + InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(handlerFileName); + if (inputStream == null) { + throw new CompilerException("Could not open " + handlerFileName); + } + Files.copy(inputStream, + getConfiguration().outputDir().toPath().resolve(handlerFileName), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new CompilerException("Could not copy " + handlerFileName, e); } - Files.copy(inputStream, - getConfiguration().outputDir().toPath().resolve(mqttHandlerFileName), - StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new CompilerException("Could not copy " + mqttHandlerFileName, e); } for (GrammarFile grammarFile : ragConnect.getProgram().getGrammarFileList()) { Path outputFile = getConfiguration().outputDir().toPath().resolve(grammarFile.getFileName()); - sendToFile(outputFile, grammarFile.generateAbstractGrammar()); + writeToFile(outputFile, grammarFile.generateAbstractGrammar()); } - sendToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value())); + writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value())); return 0; } @@ -89,8 +112,8 @@ public class Compiler extends AbstractCompiler { try { new Compiler().run(args); } catch (CompilerException e) { - System.err.println(e.getMessage()); - System.exit(-1); + e.printStackTrace(); + System.exit(1); } } @@ -118,7 +141,7 @@ public class Compiler extends AbstractCompiler { System.out.println(message); } - private void sendToFile(Path path, String str) throws CompilerException { + private void writeToFile(Path path, String str) throws CompilerException { try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.append(str); } catch (Exception e) { @@ -132,6 +155,15 @@ public class Compiler extends AbstractCompiler { new ValueOption("rootNode", "root node in the base grammar.") .acceptAnyValue() .needsValue(true)); + optionProtocols = addOption( + new ValueOption("protocols", "Protocols to enable") + .acceptMultipleValues(true) + .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT") + .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST") + ); + optionPrintYaml = addOption( + new BooleanOption("printYaml", "Print out YAML instead of generating files") + .defaultValue(false)); optionVerbose = addOption( new BooleanOption("verbose", "Print more messages while compiling.") .defaultValue(false)); @@ -145,6 +177,8 @@ public class Compiler extends AbstractCompiler { private RagConnect parseProgram(Collection<String> files) throws CompilerException { Program program = new Program(); + boolean atLeastOneGrammar = false; + boolean atLeastOneRagConnect = false; RagConnect ragConnect = new RagConnect(); ragConnect.setProgram(program); @@ -160,20 +194,34 @@ public class Compiler extends AbstractCompiler { case "relast": // processGrammar parseGrammar(program, filename); + atLeastOneGrammar = true; break; case "connect": + case "ragconnect": // process ragConnect RagConnect parsedRagConnect = parseRagConnect(program, filename); mergeRagConnectDefinitions(ragConnect, parsedRagConnect); + atLeastOneRagConnect = true; break; default: throw new CompilerException("Unknown file extension in " + filename); } } + if (!atLeastOneGrammar) { + System.err.println("No grammar file specified! (*.ast, *.relast)"); + } + if (!atLeastOneRagConnect) { + System.err.println("No ragconnect file specified! (*.connect, *.ragconnect)"); + } + if (!atLeastOneGrammar && !atLeastOneRagConnect) { + System.exit(1); + } ragConnect.treeResolveAll(); ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration); ASTNode.loggingEnabledForReads = optionLogReads.value(); ASTNode.loggingEnabledForWrites = optionLogWrites.value(); + ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT); + ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST); return ragConnect; } diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java index ac1109fef423ed58ebbbdfcaeacbdca42745e341..ee9ed046348efeaa4b12dbc816c55414d93289cf 100644 --- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java +++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java @@ -1,15 +1,11 @@ package org.jastadd.ragconnect.compiler; -import beaver.Parser; import org.jastadd.ragconnect.ast.*; import org.jastadd.ragconnect.parser.RagConnectParser; import org.jastadd.ragconnect.scanner.RagConnectScanner; -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.ByteBuffer; +import java.io.Reader; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; /** @@ -19,141 +15,87 @@ import java.nio.file.Paths; */ public class SimpleMain { - // --- just testing byte[] conversion --- - public static void testing() { - byte[] bytes; - int i = 1; - double d = 2.3d; - float f = 4.2f; - short sh = 13; - long l = 7L; - String s = "Hello"; - char c = 'a'; - - Integer ii = Integer.valueOf(1); - if (!ii.equals(i)) throw new AssertionError("Ints not equal"); - - // int to byte - ByteBuffer i2b = ByteBuffer.allocate(4); - i2b.putInt(i); - bytes = i2b.array(); - - // byte to int - ByteBuffer b2i = ByteBuffer.wrap(bytes); - int actual_i = b2i.getInt(); - if (i != actual_i) throw new AssertionError("Ints not equal"); - - // double to byte - ByteBuffer d2b = ByteBuffer.allocate(8); - d2b.putDouble(d); - bytes = d2b.array(); - - // byte to double - ByteBuffer b2d = ByteBuffer.wrap(bytes); - double actual_d = b2d.getDouble(); - if (d != actual_d) throw new AssertionError("Doubles not equal"); - - // float to byte - ByteBuffer f2b = ByteBuffer.allocate(4); - f2b.putFloat(f); - bytes = f2b.array(); - - // byte to float - ByteBuffer b2f = ByteBuffer.wrap(bytes); - float actual_f = b2f.getFloat(); - if (f != actual_f) throw new AssertionError("Floats not equal"); - - // short to byte - ByteBuffer sh2b = ByteBuffer.allocate(2); - sh2b.putShort(sh); - bytes = sh2b.array(); - - // byte to short - ByteBuffer b2sh = ByteBuffer.wrap(bytes); - short actual_sh = b2sh.getShort(); - if (sh != actual_sh) throw new AssertionError("Shorts not equal"); - - // long to byte - ByteBuffer l2b = ByteBuffer.allocate(8); - l2b.putLong(l); - bytes = l2b.array(); - - // byte to long - ByteBuffer b2l = ByteBuffer.wrap(bytes); - long actual_l = b2l.getLong(); - if (l != actual_l) throw new AssertionError("Longs not equal"); - - // String to byte - bytes = s.getBytes(); - - // byte to String - String actual_s = new String(bytes); - if (!s.equals(actual_s)) throw new AssertionError("Strings not equal"); - - // char to byte - ByteBuffer c2b = ByteBuffer.allocate(2); - c2b.putChar(c); - bytes = c2b.array(); - - // byte to char - ByteBuffer b2c = ByteBuffer.wrap(bytes); - char actual_c = b2c.getChar(); - if (c != actual_c) throw new AssertionError("Floats not equal"); - } - - public static void main(String[] args) { -// testing(); - createManualAST(); - } - - private static void createManualAST() { - RagConnect model = new RagConnect(); - Program program = parseProgram(Paths.get("ros2rag.starter","src", "main", "jastadd", "RobotModel.relast")); - model.setProgram(program); - - MappingDefinition mappingDefinition = new MappingDefinition(); - mappingDefinition.setID("PoseToPosition"); - mappingDefinition.setFromType(makeMappingDefinitionType("int")); - mappingDefinition.setFromVariableName("x"); - mappingDefinition.setToType(makeMappingDefinitionType("Position")); - mappingDefinition.setContent(" pose.position.x += sqrt(.5 * size.x)\n" + - " MAP round(2)\n" + - " x = x / 100\n" + - " IGNORE_IF_SAME\n" + - " ;"); - model.addMappingDefinition(mappingDefinition); - - ReceiveFromMqttDefinition receiveFromMqttDefinition = new ReceiveFromMqttDefinition(); - receiveFromMqttDefinition.setAlwaysApply(false); - receiveFromMqttDefinition.setToken(TokenComponent.createRef("Link.CurrentPosition")); - receiveFromMqttDefinition.addMapping(mappingDefinition); - model.addEndpointDefinition(receiveFromMqttDefinition); - - model.treeResolveAll(); - for (ErrorMessage error : model.errors()) { - System.err.println(error); - } - - System.out.println(model.generateAspect("Model")); + private static void printManualYAML() { + Document doc = new Document(); + KeyValuePair root = new KeyValuePair(); + root.setKey("panda_mqtt_connector"); + MappingElement firstLevel = new MappingElement(); + firstLevel.addKeyValuePair(new KeyValuePair("server", new StringElement("tcp://localhost:1883"))); + firstLevel.addKeyValuePair(new KeyValuePair("robot_speed_factor", new ValueElement(".7"))); + + KeyValuePair topics = new KeyValuePair(); + topics.setKey("topics"); + MappingElement theTopics = new MappingElement(); + theTopics.addKeyValuePair(new KeyValuePair("robotConfig", new StringElement("robotconfig"))); + theTopics.addKeyValuePair(new KeyValuePair("trajectory", new StringElement("trajectory"))); + theTopics.addKeyValuePair(new KeyValuePair("nextStep", new StringElement("ros2rag/nextStep"))); + topics.setValue(theTopics); + firstLevel.addKeyValuePair(topics); + + firstLevel.addKeyValuePair(new KeyValuePair("zone_size", new ValueElement("0.5"))); + + KeyValuePair zones = new KeyValuePair(); + zones.setKey("zones"); + ListElement theZones = new ListElement(); + theZones.addElement(new StringElement("1 1")); + theZones.addElement(new StringElement("0 1")); + theZones.addElement(new StringElement("-1 1")); + zones.setValue(theZones); + firstLevel.addKeyValuePair(zones); + + KeyValuePair parts = new KeyValuePair(); + parts.setKey("end_effectors"); + KeyValuePair pandaParts = new KeyValuePair(); + pandaParts.setKey("panda"); + MappingElement thePanda = new MappingElement(); + thePanda.addKeyValuePair(new KeyValuePair("Link0", new StringElement("panda_link0"))); + thePanda.addKeyValuePair(new KeyValuePair("Link1", new StringElement("panda_link1"))); + thePanda.addKeyValuePair(new KeyValuePair("Link2", new StringElement("panda_link2"))); + thePanda.addKeyValuePair(new KeyValuePair("Link3", new StringElement("panda_link3"))); + thePanda.addKeyValuePair(new KeyValuePair("Link4", new StringElement("panda_link4"))); + thePanda.addKeyValuePair(new KeyValuePair("Link5", new StringElement("panda_link5"))); + thePanda.addKeyValuePair(new KeyValuePair("Link6", new StringElement("panda_link6"))); + thePanda.addKeyValuePair(new KeyValuePair("RightFinger", new StringElement("panda_rightfinger"))); + thePanda.addKeyValuePair(new KeyValuePair("LeftFinger", new StringElement("panda_leftfinger"))); + pandaParts.setValue(thePanda); + parts.setValue(pandaParts); + firstLevel.addKeyValuePair(parts); + + KeyValuePair end_effectors = new KeyValuePair(); + end_effectors.setKey("end_effectors"); + KeyValuePair endEffectorParts = new KeyValuePair(); + endEffectorParts.setKey("panda"); + endEffectorParts.setValue(new KeyValuePair("EndEffector", new StringElement("panda_hand"))); + end_effectors.setValue(endEffectorParts); + firstLevel.addKeyValuePair(end_effectors); + + KeyValuePair goalPoses = new KeyValuePair(); + goalPoses.setKey("goal_poses"); + ListElement theGoalPoses = new ListElement(); + addPose(theGoalPoses, "0.4 0.4 0.3"); + addPose(theGoalPoses, "-0.4 0.4 0.3"); + addPose(theGoalPoses, "-0.4 -0.4 0.3"); + addPose(theGoalPoses, "0.4 0.4 0.3"); + addPose(theGoalPoses, "-0.4 0.4 0.3"); + addPose(theGoalPoses, "0.4 0.4 0.3"); + goalPoses.setValue(theGoalPoses); + firstLevel.addKeyValuePair(goalPoses); + + root.setValue(firstLevel); + doc.addComplexElement(root); + + System.out.println(doc.prettyPrint()); } - private static MappingDefinitionType makeMappingDefinitionType(String type) { - JavaMappingDefinitionType result = new JavaMappingDefinitionType(); - result.setType(new SimpleJavaTypeUse(type)); - return result; + private static void addPose(ListElement theGoalPoses, String position) { + MappingElement goalPose1 = new MappingElement(); + goalPose1.addKeyValuePair(new KeyValuePair("position", new StringElement(position))); + goalPose1.addKeyValuePair(new KeyValuePair("orientation", new StringElement("1 1 0 0"))); + goalPose1.addKeyValuePair(new KeyValuePair("work", new StringElement("20000"))); + theGoalPoses.addElement(goalPose1); } - private static Program parseProgram(Path path) { - try (BufferedReader reader = Files.newBufferedReader(path)) { - RagConnectScanner scanner = new RagConnectScanner(reader); - RagConnectParser parser = new RagConnectParser(); - GrammarFile grammarFile = (GrammarFile) parser.parse(scanner); - Program program = new Program(); - program.addGrammarFile(grammarFile); - return program; - } catch (IOException | Parser.Exception e) { - e.printStackTrace(); - } - return null; + public static void main(String[] args) { + printManualYAML(); } } diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd index 493d8e4452e81c5dbfa45a522a6366345908cdcc..6f40612520cfb97151600dff53c8e0d384ab441e 100644 --- a/ragconnect.base/src/main/resources/MqttHandler.jadd +++ b/ragconnect.base/src/main/resources/MqttHandler.jadd @@ -1,4 +1,73 @@ -aspect MqttHandler { +import java.io.IOException; +import java.util.concurrent.TimeUnit;aspect MqttHandler { +public class MqttServerHandler { + private final java.util.Map<String, MqttHandler> handlers = new java.util.HashMap<>(); + private long time; + private java.util.concurrent.TimeUnit unit; + private String name; + + public MqttServerHandler() { + this("RagConnect"); + } + + public MqttServerHandler(String name) { + this.name = name; + setupWaitUntilReady(1, TimeUnit.SECONDS); + } + + public void setupWaitUntilReady(long time, java.util.concurrent.TimeUnit unit) { + this.time = time; + this.unit = unit; + } + + public MqttHandler resolveHandler(java.net.URI uri) throws IOException { + MqttHandler handler = handlers.get(uri.getHost()); + if (handler == null) { + // first connect to that server + handler = new MqttHandler(); + if (uri.getPort() == -1) { + handler.setHost(uri.getHost()); + } else { + handler.setHost(uri.getHost(), uri.getPort()); + } + handlers.put(uri.getHost(), handler); + } + handler.waitUntilReady(this.time, this.unit); + return handler; + } + + public boolean newConnection(java.net.URI uri, java.util.function.Consumer<byte[]> callback) throws IOException { + return resolveHandler(uri).newConnection(extractTopic(uri), callback); + } + + public void publish(java.net.URI uri, byte[] bytes) throws IOException { + resolveHandler(uri).publish(extractTopic(uri), bytes); + } + + public void publish(java.net.URI uri, byte[] bytes, boolean retain) throws IOException { + resolveHandler(uri).publish(extractTopic(uri), bytes, retain); + } + + public void publish(java.net.URI uri, byte[] bytes, + org.fusesource.mqtt.client.QoS qos, boolean retain) throws IOException { + resolveHandler(uri).publish(extractTopic(uri), bytes, qos, retain); + } + + private String extractTopic(java.net.URI uri) { + String path = uri.getPath(); + if (path.charAt(0) == '/') { + path = path.substring(1); + } + return path; + } + + public void close() { + for (MqttHandler handler : handlers.values()) { + handler.close(); + } + } + +} /** * Helper class to receive updates via MQTT and use callbacks to handle those messages. * @@ -14,10 +83,8 @@ public class MqttHandler { private java.net.URI host; /** The connection to the MQTT broker. */ private org.fusesource.mqtt.client.CallbackConnection connection; - /** Whether we are subscribed to the topics yet */ - private final java.util.concurrent.locks.Condition readyCondition; - private final java.util.concurrent.locks.Lock readyLock; - private boolean ready; + /** Whether we are connected yet */ + private final java.util.concurrent.CountDownLatch readyLatch; private boolean sendWelcomeMessage = true; private org.fusesource.mqtt.client.QoS qos; /** Dispatch knowledge */ @@ -31,9 +98,7 @@ public class MqttHandler { this.name = java.util.Objects.requireNonNull(name, "Name must be set"); this.logger = org.apache.logging.log4j.LogManager.getLogger(MqttHandler.class); this.callbacks = new java.util.HashMap<>(); - this.readyLock = new java.util.concurrent.locks.ReentrantLock(); - this.readyCondition = readyLock.newCondition(); - this.ready = false; + this.readyLatch = new java.util.concurrent.CountDownLatch(1); this.qos = org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE; } @@ -83,7 +148,7 @@ public class MqttHandler { String topicString = topic.toString(); java.util.List<java.util.function.Consumer<byte[]>> callbackList = callbacks.get(topicString); if (callbackList == null || callbackList.isEmpty()) { - logger.debug("Got a message, but no callback to call. Forgot to unsubscribe?"); + logger.debug("Got a message, but no callback to call. Forgot to subscribe?"); } else { byte[] message = body.toByteArray(); // System.out.println("message = " + Arrays.toString(message)); @@ -144,13 +209,7 @@ public class MqttHandler { } private void setReady() { - try { - readyLock.lock(); - ready = true; - readyCondition.signalAll(); - } finally { - readyLock.unlock(); - } + readyLatch.countDown(); } private void throwIf(java.util.concurrent.atomic.AtomicReference<Throwable> error) throws java.io.IOException { @@ -163,12 +222,15 @@ public class MqttHandler { this.qos = qos; } - public void newConnection(String topic, java.util.function.Consumer<byte[]> callback) { - if (!ready) { - // should maybe be something more kind than throwing an exception here - throw new IllegalStateException("Updater not ready"); + public boolean newConnection(String topic, java.util.function.Consumer<byte[]> callback) { + if (readyLatch.getCount() > 0) { + System.err.println("Handler not ready"); + return false; +// // should maybe be something more kind than throwing an exception here +// throw new IllegalStateException("Updater not ready"); } // register callback + logger.debug("new connection for {}", topic); if (callbacks.get(topic) == null) { callbacks.put(topic, new java.util.ArrayList<>()); @@ -189,6 +251,7 @@ public class MqttHandler { }); } callbacks.get(topic).add(callback); + return true; } /** @@ -202,15 +265,9 @@ public class MqttHandler { */ public boolean waitUntilReady(long time, java.util.concurrent.TimeUnit unit) { try { - readyLock.lock(); - if (ready) { - return true; - } - return readyCondition.await(time, unit); + return readyLatch.await(time, unit); } catch (InterruptedException e) { e.printStackTrace(); - } finally { - readyLock.unlock(); } return false; } @@ -253,7 +310,7 @@ public class MqttHandler { @Override public void onFailure(Throwable value) { - logger.warn("Could not publish on topic '{}'", topic); + logger.warn("Could not publish on topic '{}'", topic, value); } }); }); diff --git a/ragconnect.base/src/main/resources/RestHandler.jadd b/ragconnect.base/src/main/resources/RestHandler.jadd new file mode 100644 index 0000000000000000000000000000000000000000..f7b1a83304fe7aaebe97d105514c3f192d052aec --- /dev/null +++ b/ragconnect.base/src/main/resources/RestHandler.jadd @@ -0,0 +1,140 @@ +import java.util.concurrent.TimeUnit;aspect RestHandler { +public class RestServerHandler { + private static final int DEFAULT_PORT = 4567; + private final java.util.Map<Integer, RestHandler> handlers = new java.util.HashMap<>(); + private String name; + + public RestServerHandler() { + this("RagConnect"); + } + + public RestServerHandler(String name) { + this.name = name; + } + + private RestHandler resolveHandler(java.net.URI uri) { + int port = uri.getPort() != -1 ? uri.getPort() : DEFAULT_PORT; + RestHandler handler = handlers.get(port); + if (handler == null) { + // first connect to that server + handler = new RestHandler(); + handler.setPort(port); + handlers.put(port, handler); + } + return handler; + } + + public boolean newPUTConnection(java.net.URI uri, java.util.function.Consumer<String> callback) { + resolveHandler(uri).newPUTConnection(uri.getPath(), callback); + return true; + } + + public boolean newGETConnection(java.net.URI uri, SupplierWithException<String> supplier) { + resolveHandler(uri).newGETConnection(uri.getPath(), supplier); + return true; + } + + public void close() { + for (RestHandler handler : handlers.values()) { + handler.close(); + } + } +} +/** + * Helper class to receive updates and publishes information via REST. + * @author rschoene - Initial contribution + */ +public class RestHandler { + private static final int DEFAULT_PORT = 4567; + + private final org.apache.logging.log4j.Logger logger; + private final String name; + private int port; + private final java.util.concurrent.CountDownLatch exitCondition; + /** Dispatch knowledge */ + private final java.util.Map<String, java.util.List<java.util.function.Consumer<String>>> callbacks; + private final java.util.Map<String, SupplierWithException<String>> suppliers; + + public RestHandler() { + this("RagConnect"); + } + + public RestHandler(String name) { + this.logger = org.apache.logging.log4j.LogManager.getLogger(RestHandler.class); + this.name = name; + this.port = DEFAULT_PORT; + this.exitCondition = new java.util.concurrent.CountDownLatch(1); + this.callbacks = new java.util.HashMap<>(); + this.suppliers = new java.util.HashMap<>(); + } + + public RestHandler setPort(int port) { + this.port = port; + start(); + return this; + } + + public void newPUTConnection(String path, java.util.function.Consumer<String> callback) { + if (callbacks.containsKey(path)) { + callbacks.get(path).add(callback); + } else { + // setup path + java.util.List<java.util.function.Consumer<String>> callbackList = new java.util.ArrayList<>(); + callbackList.add(callback); + callbacks.put(path, callbackList); + spark.Spark.put(path, (request, response) -> { + String content = request.body(); + java.util.Set<String> errors = new java.util.HashSet<>(); + for (java.util.function.Consumer<String> f : callbackList) { + try { + f.accept(content); + } catch (Exception e) { + errors.add(e.getMessage()); + } + } + if (errors.isEmpty()) { + return "OK"; + } else { + return makeError(response, 500, errors.stream().collect(java.util.stream.Collectors.joining("\n", "The following error(s) happened: [", "]"))); + } + }); + } + } + + public void newGETConnection(String path, SupplierWithException<String> supplier) { + if (suppliers.get(path) != null) { + logger.warn("Overriding existing supplier for '{}'", path); + } + suppliers.put(path, supplier); + spark.Spark.get(path, (request, response) -> { + try { + return supplier.get(); + } catch (Exception e) { + return makeError(response, 500, e.getMessage()); + } + }); + } + + private String makeError(spark.Response response, int statusCode, String message) { + response.status(statusCode); + return message; + } + + private void start() { + logger.info("Starting REST server at {}", this.port); + spark.Spark.port(this.port); + spark.Spark.init(); + spark.Spark.awaitInitialization(); + } + + public void close() { + spark.Spark.stop(); + spark.Spark.awaitStop(); + } + +} +@FunctionalInterface +public interface SupplierWithException<T> { + public T get() throws Exception; +} +} diff --git a/ragconnect.base/src/main/resources/dependencyDefinition.mustache b/ragconnect.base/src/main/resources/dependencyDefinition.mustache index b8d74ffe8f8aa5aaa8979682e4442215af1974e8..0ab8d55242816a7d3ada0491c8c7db6dcd70ee42 100644 --- a/ragconnect.base/src/main/resources/dependencyDefinition.mustache +++ b/ragconnect.base/src/main/resources/dependencyDefinition.mustache @@ -1,3 +1,3 @@ - public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) { - add{{internalRelationPrefix}}Source(source); - } +public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) { + add{{internalRelationPrefix}}Source(source); +} diff --git a/ragconnect.base/src/main/resources/handleUri.mustache b/ragconnect.base/src/main/resources/handleUri.mustache new file mode 100644 index 0000000000000000000000000000000000000000..1a20e7c4df2a4c54ccba4c825fff55ca89a51dec --- /dev/null +++ b/ragconnect.base/src/main/resources/handleUri.mustache @@ -0,0 +1,11 @@ +String scheme,host, path; +java.net.URI uri; +try { + uri = new java.net.URI({{connectParameterName}}); + scheme = uri.getScheme(); + host = uri.getHost(); + path = uri.getPath(); +} catch (java.net.URISyntaxException e) { + System.err.println(e.getMessage()); // Maybe re-throw error? + return false; +} diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache new file mode 100644 index 0000000000000000000000000000000000000000..e89c451007ff309a77297b9b66fe0e20dece73b8 --- /dev/null +++ b/ragconnect.base/src/main/resources/handler.mustache @@ -0,0 +1,7 @@ +aspect RagConnectHandler { +interface RagConnectHandler<T> { + boolean connectReceive(String path, java.util.function.Consumer<T> callback); + boolean sendPush(String path, T value); + boolean connectSendPull(String path, SupplierWithException<T> supplier); +} +} diff --git a/ragconnect.base/src/main/resources/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache index 95ce2fe29329d87f880497b37697d50c8c0687be..064004b94886c155be883e7b186557c6fa87d38c 100644 --- a/ragconnect.base/src/main/resources/mappingApplication.mustache +++ b/ragconnect.base/src/main/resources/mappingApplication.mustache @@ -1,7 +1,7 @@ -{{lastDefinitionToType}} {{resultVarPrefix}}{{lastDefinitionName}}; +{{lastDefinitionToType}} {{lastResult}}; try { {{#InnerMappingDefinitions}} - {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});{{!inputVarName has to be computed beforehand}} + {{^last}}{{toType}} {{/last}}{{outputVarName}} = {{methodName}}({{inputVarName}}); {{/InnerMappingDefinitions}} } catch (Exception e) { e.printStackTrace(); diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache index 920c5a610b0ce0cff1687122c97b6e18ce63bf7f..fe26e949b7a8e446bf97947e1632624741a83726 100644 --- a/ragconnect.base/src/main/resources/mappingDefinition.mustache +++ b/ragconnect.base/src/main/resources/mappingDefinition.mustache @@ -1,3 +1,3 @@ - protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception { - {{{content}}}{{!maybe print line by line to get better indentation}} - } +protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception { + {{{content}}}{{!maybe print line by line to get better indentation}} +} diff --git a/ragconnect.base/src/main/resources/mqtt.mustache b/ragconnect.base/src/main/resources/mqtt.mustache index dbdb85e7954dbf50ae7e150ffc43dd4cbda0fd80..9391d1091c41eee257f9e6a1368297ef743e39ce 100644 --- a/ragconnect.base/src/main/resources/mqtt.mustache +++ b/ragconnect.base/src/main/resources/mqtt.mustache @@ -1,23 +1,16 @@ aspect MQTT { - private String {{rootNodeName}}.MqttName() { return "Ros2Rag"; } - private MqttHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttHandler(MqttName()); - public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host) throws java.io.IOException { - {{mqttHandlerField}}.setHost(host); - } - public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host, int port) throws java.io.IOException { - {{mqttHandlerField}}.setHost(host, port); - } - - public boolean {{rootNodeName}}.{{mqttWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) { - return {{mqttHandlerField}}.waitUntilReady(time, unit); - } + private String {{rootNodeName}}.MqttName() { return "RagConnectMQTT"; } + private MqttServerHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttServerHandler(MqttName()); - public void {{rootNodeName}}.{{mqttCloseMethod}}() { - {{mqttHandlerField}}.close(); + public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) { + {{mqttHandlerField}}.setupWaitUntilReady(time, unit); } - {{#getRootTypeComponents}} - {{#first}}inh MqttHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}} + {{#RootTypeComponents}} + {{#first}}inh MqttServerHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}} eq {{rootNodeName}}.get{{name}}().{{mqttHandlerAttribute}}() = {{mqttHandlerField}}; - {{/getRootTypeComponents}} + {{/RootTypeComponents}} + {{^RootTypeComponents}} + syn MqttServerHandler {{rootNodeName}}.{{mqttHandlerAttribute}}() = {{mqttHandlerField}}; + {{/RootTypeComponents}} } diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index 0b455283977cf812a47b5266ee593c6597e97cc9..a39510d73854301efb1f05fe7f9c1fd476916ef7 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -1,22 +1,27 @@ -{{> mqtt}} +{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}} +{{#usesRest}}{{> rest}}{{/usesRest}} aspect ROS2RAG { + public void {{rootNodeName}}.{{closeMethod}}() { + {{#usesMqtt}}{{mqttHandlerField}}.close();{{/usesMqtt}} + {{#usesRest}}{{restHandlerField}}.close();{{/usesRest}} + } {{#ReceiveDefinitions}} - {{> receiveDefinition}} + {{> receiveDefinition}} {{/ReceiveDefinitions}} {{#SendDefinitions}} - {{> sendDefinition}} + {{> sendDefinition}} {{/SendDefinitions}} {{#MappingDefinitions}} - {{> mappingDefinition}} + {{> mappingDefinition}} {{/MappingDefinitions}} {{#DependencyDefinitions}} - {{> dependencyDefinition}} + {{> dependencyDefinition}} {{/DependencyDefinitions}} {{#TokenComponents}} - {{> tokenComponent}} + {{> tokenComponent}} {{/TokenComponents}} } diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache index e2df0433fe82b37df2c016887da52a11cfd2692b..1dd0e0ddbfbfd2acf03536ff41a2d740043eb34b 100644 --- a/ragconnect.base/src/main/resources/receiveDefinition.mustache +++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache @@ -1,9 +1,23 @@ - public void {{parentTypeName}}.{{connectMethod}}(String topic) { - {{mqttHandlerAttribute}}().newConnection(topic, message -> { - {{> mappingApplication}} - {{#loggingEnabledForReads}} - System.out.println("[Receive] " + topic + " -> {{tokenName}} = " + {{lastResult}});{{!lastResult has to be a new attribute}} - {{/loggingEnabledForReads}} - set{{tokenName}}({{lastResult}}); +public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}) throws java.io.IOException { + {{>handleUri}} + java.util.function.Consumer<byte[]> consumer = message -> { + {{> mappingApplication}} + {{#loggingEnabledForReads}} + System.out.println("[Receive] " + {{connectParameterName}} + " -> {{tokenName}} = " + {{lastResult}}); + {{/loggingEnabledForReads}} + set{{tokenName}}({{lastResult}}); + }; + switch (scheme) { + {{#usesMqtt}} + case "mqtt": return {{mqttHandlerAttribute}}().newConnection(uri, consumer); + {{/usesMqtt}} + {{#usesRest}} + case "rest": return {{restHandlerAttribute}}().newPUTConnection(uri, input -> { + consumer.accept(input.getBytes()); }); + {{/usesRest}} + default: + System.err.println("Unknown protocol '" + scheme + "'."); + return false; } +} diff --git a/ragconnect.base/src/main/resources/rest.mustache b/ragconnect.base/src/main/resources/rest.mustache new file mode 100644 index 0000000000000000000000000000000000000000..6bbde2be4d26e519635c85a6f39f9a18514bfb19 --- /dev/null +++ b/ragconnect.base/src/main/resources/rest.mustache @@ -0,0 +1,12 @@ +aspect REST { + private String {{rootNodeName}}.RestName() { return "RagConnectREST"; } + private RestServerHandler {{rootNodeName}}.{{restHandlerField}} = new RestServerHandler(RestName()); + + {{#getRootTypeComponents}} + {{#first}}inh RestServerHandler ASTNode.{{restHandlerAttribute}}();{{/first}} + eq {{rootNodeName}}.get{{name}}().{{restHandlerAttribute}}() = {{restHandlerField}}; + {{/getRootTypeComponents}} + {{^getRootTypeComponents}} + syn RestServerHandler {{rootNodeName}}.{{restHandlerAttribute}}() = {{restHandlerField}}; + {{/getRootTypeComponents}} +} diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache index 477dbc550132af842e53e8fed2cbbee0c8996296..fac4ecd37967b8047c9101b58fab273527979ab2 100644 --- a/ragconnect.base/src/main/resources/sendDefinition.mustache +++ b/ragconnect.base/src/main/resources/sendDefinition.mustache @@ -1,24 +1,51 @@ - private String {{parentTypeName}}.{{sendTopic}} = null; - private byte[] {{parentTypeName}}.{{lastValue}} = null; +private Runnable {{parentTypeName}}.{{sender}} = null; +private byte[] {{parentTypeName}}.{{lastValue}} = null; - public void {{parentTypeName}}.{{connectMethod}}(String topic, boolean writeCurrentValue) { - {{sendTopic}} = topic; - {{updateMethod}}(); - if (writeCurrentValue) { - {{writeMethod}}(); - } +public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) { + {{>handleUri}} + switch (scheme) { + {{#usesMqtt}} + case "mqtt": + // MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri);{{!optimize later}} + {{sender}} = () -> { + {{#loggingEnabledForWrites}} + System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{connectParameterName}}); + {{/loggingEnabledForWrites}} + try { + {{mqttHandlerAttribute}}().publish(uri, {{lastValue}}); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + }; + {{updateMethod}}(); + if (writeCurrentValue) { + {{writeMethod}}(); + } + break; + {{/usesMqtt}} + {{#usesRest}} + case "rest": + {{restHandlerAttribute}}().newGETConnection(uri, () -> { + {{updateMethod}}(); + return new String({{lastValue}}); + }); + break; + {{/usesRest}} + default: + System.err.println("Unknown protocol '" + scheme + "'."); + return false; } + return true; +} - protected boolean {{parentTypeName}}.{{updateMethod}}() { - {{tokenResetMethod}}(); - {{> mappingApplication}} - {{lastValue}} = {{lastResult}}; - return true; - } +protected boolean {{parentTypeName}}.{{updateMethod}}() { + {{tokenResetMethod}}(); + {{> mappingApplication}} + {{lastValue}} = {{lastResult}}; + // normally we would return true here. unless no connect method was called so far to initialize {{sender}} yet + return {{sender}} != null; +} - protected void {{parentTypeName}}.{{writeMethod}}() { - {{#loggingEnabledForWrites}} - System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{sendTopic}}); - {{/loggingEnabledForWrites}} - {{mqttHandlerAttribute}}().publish({{sendTopic}}, {{lastValue}}); - } +protected void {{parentTypeName}}.{{writeMethod}}() { + {{sender}}.run(); +} diff --git a/ragconnect.base/src/main/resources/tokenComponent.mustache b/ragconnect.base/src/main/resources/tokenComponent.mustache index e6a136d1c8de08b6dce9104fd46fafa5d91b67b6..e78efb96b846bd7aa814723472032299a70c7de3 100644 --- a/ragconnect.base/src/main/resources/tokenComponent.mustache +++ b/ragconnect.base/src/main/resources/tokenComponent.mustache @@ -1,17 +1,19 @@ - public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) { - set{{internalName}}(value); - {{#DependencyDefinitions}} - for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) { - {{#targetEndpointDefinition}} - if (target.{{updateMethod}}()) { - target.{{writeMethod}}(); - } - {{/targetEndpointDefinition}} +public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) { + set{{internalName}}(value); + {{#DependencyDefinitions}} + for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) { + {{#targetEndpointDefinition}} + {{!#isPush}} + if (target.{{updateMethod}}()) { + target.{{writeMethod}}(); } - {{/DependencyDefinitions}} - return this; + {{!/isPush}} + {{/targetEndpointDefinition}} } + {{/DependencyDefinitions}} + return this; +} - public {{javaType}} {{parentTypeName}}.get{{name}}() { - return get{{internalName}}(); - } +public {{javaType}} {{parentTypeName}}.get{{name}}() { + return get{{internalName}}(); +} diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle index d0a6997e48aac4caa7b4c5c0c249210c7276271d..d638ec5001a8a58505045e37e480ac7c5d08079d 100644 --- a/ragconnect.tests/build.gradle +++ b/ragconnect.tests/build.gradle @@ -25,7 +25,16 @@ dependencies { 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' + + // mqtt testImplementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' + + // rest and client + testImplementation group: 'com.sparkjava', name: 'spark-core', version: '2.9.2' + testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.11.2' + testImplementation group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '2.31' + testImplementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.31' + testImplementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' api group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0' } @@ -58,10 +67,12 @@ relastTest { compilerLocation = '../libs/relast.jar' } -sourceSets { - test { - java.srcDir "src/test/java-gen" - } +File genSrc = file("src/test/java-gen"); +sourceSets.test.java.srcDir genSrc +idea.module.generatedSourceDirs += genSrc + +clean { + delete 'src/test/02-after-ragconnect/*/', 'src/test/03-after-relast/*/', 'src/test/java-gen/*/' } // --- Test: Example --- @@ -93,7 +104,7 @@ task compileExampleTest(type: RelastTest) { 'src/test/02-after-ragconnect/example/RagConnect.jadd' } -testClasses.dependsOn compileExampleTest +compileTestJava.dependsOn compileExampleTest compileExampleTest.dependsOn preprocessExampleTest // --- Test: default-only-read --- @@ -109,8 +120,7 @@ task preprocessDefaultOnlyReadTest(type: JavaExec, group: 'verification') { args '--o=src/test/02-after-ragconnect/defaultOnlyRead', 'src/test/01-input/defaultOnlyRead/Test.relast', 'src/test/01-input/defaultOnlyRead/Test.connect', - '--rootNode=A', - '--verbose' + '--rootNode=A' } task compileDefaultOnlyReadTest(type: RelastTest) { @@ -124,7 +134,7 @@ task compileDefaultOnlyReadTest(type: RelastTest) { 'src/test/02-after-ragconnect/defaultOnlyRead/RagConnect.jadd' } -testClasses.dependsOn compileDefaultOnlyReadTest +compileTestJava.dependsOn compileDefaultOnlyReadTest compileDefaultOnlyReadTest.dependsOn preprocessDefaultOnlyReadTest // --- Test: default-only-write --- @@ -140,8 +150,7 @@ task preprocessDefaultOnlyWriteTest(type: JavaExec, group: 'verification') { args '--o=src/test/02-after-ragconnect/defaultOnlyWrite', 'src/test/01-input/defaultOnlyWrite/Test.relast', 'src/test/01-input/defaultOnlyWrite/Test.connect', - '--rootNode=A', - '--verbose' + '--rootNode=A' } task compileDefaultOnlyWriteTest(type: RelastTest) { @@ -156,7 +165,7 @@ task compileDefaultOnlyWriteTest(type: RelastTest) { 'src/test/02-after-ragconnect/defaultOnlyWrite/RagConnect.jadd' } -testClasses.dependsOn compileDefaultOnlyWriteTest +compileTestJava.dependsOn compileDefaultOnlyWriteTest compileDefaultOnlyWriteTest.dependsOn preprocessDefaultOnlyWriteTest // --- Test: read1write2 --- @@ -188,7 +197,7 @@ task compileRead1Write2Test(type: RelastTest) { 'src/test/02-after-ragconnect/read1write2/RagConnect.jadd' } -testClasses.dependsOn compileRead1Write2Test +compileTestJava.dependsOn compileRead1Write2Test compileRead1Write2Test.dependsOn preprocessRead1Write2Test // --- Test: read2write1 --- @@ -204,7 +213,7 @@ task preprocessRead2Write1Test(type: JavaExec, group: 'verification') { args '--o=src/test/02-after-ragconnect/read2write1', 'src/test/01-input/read2write1/Test.relast', 'src/test/01-input/read2write1/Test.connect', - '--rootNode=A', '--verbose', + '--rootNode=A', '--logReads', '--logWrites' } @@ -220,9 +229,40 @@ task compileRead2Write1Test(type: RelastTest) { 'src/test/02-after-ragconnect/read2write1/RagConnect.jadd' } -testClasses.dependsOn compileRead2Write1Test +compileTestJava.dependsOn compileRead2Write1Test compileRead2Write1Test.dependsOn preprocessRead2Write1Test -clean { - delete 'src/test/02-after-ragconnect/*/', 'src/test/03-after-relast/*/', 'src/test/java-gen/*/' +// --- Test: via --- +task preprocessViaTest(type: JavaExec, group: 'verification') { + doFirst { + delete 'src/test/02-after-ragconnect/via/Test.relast', + 'src/test/02-after-ragconnect/via/MqttHandler.java', + 'src/test/02-after-ragconnect/via/RestHandler.java', + 'src/test/02-after-ragconnect/via/RagConnect.jadd' + } + + classpath = sourceSets.main.runtimeClasspath + main = 'org.jastadd.ragconnect.compiler.Compiler' + args '--o=src/test/02-after-ragconnect/via', + 'src/test/01-input/via/Test.relast', + 'src/test/01-input/via/Test.connect', + '--rootNode=A', '--verbose', + '--protocols=mqtt,rest', + '--logReads', '--logWrites' +} + +task compileViaTest(type: RelastTest) { + useJastAddNames = true + jastAddList = 'JastAddList' + relastFiles 'src/test/02-after-ragconnect/via/Test.relast', + 'src/test/02-after-ragconnect/via/RagConnect.relast' + grammarName = 'src/test/03-after-relast/via/via' + packageName = 'via.ast' + moreInputFiles 'src/test/01-input/via/Test.jadd', + 'src/test/02-after-ragconnect/via/MqttHandler.jadd', + 'src/test/02-after-ragconnect/via/RestHandler.jadd', + 'src/test/02-after-ragconnect/via/RagConnect.jadd' } + +compileTestJava.dependsOn compileViaTest +compileViaTest.dependsOn preprocessViaTest diff --git a/ragconnect.tests/src/test/01-input/via/Test.connect b/ragconnect.tests/src/test/01-input/via/Test.connect new file mode 100644 index 0000000000000000000000000000000000000000..e4d2862a651dd6e68f67d2a492b6105a15f0a77c --- /dev/null +++ b/ragconnect.tests/src/test/01-input/via/Test.connect @@ -0,0 +1,33 @@ +receive A.Mqtt2MqttInput using MarkMqttInput ; +receive A.Rest2RestInput using MarkRestInput ; +receive A.Mqtt2RestInput using MarkMqttInput ; +receive A.Rest2MqttInput using MarkRestInput ; +receive A.Both2BothInput ; + +send A.Mqtt2MqttOutput using MarkMqttOutput ; +send A.Rest2RestOutput using MarkRestOutput ; +send A.Mqtt2RestOutput using MarkRestOutput ; +send A.Rest2MqttOutput using MarkMqttOutput ; +send A.Both2RestOutput using MarkRestOutput ; +send A.Both2MqttOutput using MarkMqttOutput ; + +A.Mqtt2MqttOutput canDependOn A.Mqtt2MqttInput as dependencyMqtt2Mqtt ; +A.Rest2RestOutput canDependOn A.Rest2RestInput as dependencyRest2Rest ; +A.Mqtt2RestOutput canDependOn A.Mqtt2RestInput as dependencyMqtt2Rest ; +A.Rest2MqttOutput canDependOn A.Rest2MqttInput as dependencyRest2Mqtt ; +A.Both2RestOutput canDependOn A.Both2BothInput as dependencyBoth2Rest ; +A.Both2MqttOutput canDependOn A.Both2BothInput as dependencyBoth2Mqtt ; + +MarkMqttInput maps String s to String {: + return "FromMqtt-" + s; +:} +MarkRestInput maps String s to String {: + return "FromRest-" + s; +:} + +MarkMqttOutput maps String s to String {: + return s + "-ToMqtt"; +:} +MarkRestOutput maps String s to String {: + return s + "-ToRest"; +:} diff --git a/ragconnect.tests/src/test/01-input/via/Test.jadd b/ragconnect.tests/src/test/01-input/via/Test.jadd new file mode 100644 index 0000000000000000000000000000000000000000..9d25387d26e651fe25a937a56a863311b4b7b69a --- /dev/null +++ b/ragconnect.tests/src/test/01-input/via/Test.jadd @@ -0,0 +1,8 @@ +aspect Computation { + syn lazy String A.getMqtt2MqttOutput() = getMqtt2MqttInput() + "-M2M" ; + syn lazy String A.getRest2RestOutput() = getRest2RestInput() + "-R2R" ; + syn lazy String A.getMqtt2RestOutput() = getMqtt2RestInput() + "-M2R" ; + syn lazy String A.getRest2MqttOutput() = getRest2MqttInput() + "-R2M" ; + syn lazy String A.getBoth2MqttOutput() = getBoth2BothInput() + "-B2M" ; + syn lazy String A.getBoth2RestOutput() = getBoth2BothInput() + "-B2R" ; +} diff --git a/ragconnect.tests/src/test/01-input/via/Test.relast b/ragconnect.tests/src/test/01-input/via/Test.relast new file mode 100644 index 0000000000000000000000000000000000000000..1e707fbbc04b1204381351d0c38e826fcbcc8b80 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/via/Test.relast @@ -0,0 +1,5 @@ +A ::= <Mqtt2MqttInput> /<Mqtt2MqttOutput>/ + <Rest2RestInput> /<Rest2RestOutput>/ + <Mqtt2RestInput> /<Mqtt2RestOutput>/ + <Rest2MqttInput> /<Rest2MqttOutput>/ + <Both2BothInput> /<Both2MqttOutput>/ /<Both2RestOutput>/; diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java similarity index 95% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java index 7b7051348445d10305afb7618dedb0264adf9a8d..efaa302508d351f8d1fce557b49634527b54ac6e 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java @@ -1,4 +1,4 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; import defaultOnlyRead.ast.MqttHandler; import org.junit.jupiter.api.BeforeAll; diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java similarity index 83% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java index 36795127d7a10763e4aceac43e90acb0680b4c15..3746ceb3eaf8c83671747000e1fbf70ff37b9e96 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java @@ -1,4 +1,4 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; import defaultOnlyRead.ast.A; import defaultOnlyRead.ast.BoxedTypes; @@ -11,6 +11,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -49,7 +50,7 @@ public class DefaultOnlyReadTest extends AbstractMqttTest { sender.close(); } if (model != null) { - model.MqttCloseConnections(); + model.ragconnectCloseConnections(); } } @@ -62,23 +63,21 @@ public class DefaultOnlyReadTest extends AbstractMqttTest { @Test public void communicate() throws IOException, InterruptedException { createModel(); - - model.MqttSetHost(TestUtils.getMqttHost()); - assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS)); - - integers.connectIntValue(TOPIC_NATIVE_INT); - integers.connectShortValue(TOPIC_NATIVE_SHORT); - integers.connectLongValue(TOPIC_NATIVE_LONG); - floats.connectFloatValue(TOPIC_NATIVE_FLOAT); - floats.connectDoubleValue(TOPIC_NATIVE_DOUBLE); - chars.connectCharValue(TOPIC_NATIVE_CHAR); - chars.connectStringValue(TOPIC_NATIVE_STRING); - allBoxed.connectIntValue(TOPIC_BOXED_INTEGER); - allBoxed.connectShortValue(TOPIC_BOXED_SHORT); - allBoxed.connectLongValue(TOPIC_BOXED_LONG); - allBoxed.connectFloatValue(TOPIC_BOXED_FLOAT); - allBoxed.connectDoubleValue(TOPIC_BOXED_DOUBLE); - allBoxed.connectCharValue(TOPIC_BOXED_CHARACTER); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + integers.connectIntValue(mqttUri(TOPIC_NATIVE_INT)); + integers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT)); + integers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG)); + floats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT)); + floats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE)); + chars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR)); + chars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING)); + allBoxed.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER)); + allBoxed.connectShortValue(mqttUri(TOPIC_BOXED_SHORT)); + allBoxed.connectLongValue(mqttUri(TOPIC_BOXED_LONG)); + allBoxed.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT)); + allBoxed.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE)); + allBoxed.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER)); sender = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); assertTrue(sender.waitUntilReady(2, TimeUnit.SECONDS)); diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java similarity index 89% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java index df75fa7a2594a46556b4a099354ef7d69859bb4c..80bd95c603da385184927862b42a6f54b1d4f57e 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java @@ -1,4 +1,4 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; import defaultOnlyWrite.ast.A; import defaultOnlyWrite.ast.BoxedTypesSyn; @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; import static org.junit.jupiter.api.Assertions.*; /** @@ -50,7 +51,7 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { receiver.close(); } if (model != null) { - model.MqttCloseConnections(); + model.ragconnectCloseConnections(); } } @@ -129,8 +130,7 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { } private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.MqttSetHost(TestUtils.getMqttHost()); - assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS)); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); receiver = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); assertTrue(receiver.waitUntilReady(2, TimeUnit.SECONDS)); @@ -205,19 +205,19 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest { data.lastBoxedCharValue = java.nio.ByteBuffer.wrap(bytes).getChar(); }); - nativeIntegers.connectIntValue(TOPIC_NATIVE_INT, writeCurrentValue); - nativeIntegers.connectShortValue(TOPIC_NATIVE_SHORT, writeCurrentValue); - nativeIntegers.connectLongValue(TOPIC_NATIVE_LONG, writeCurrentValue); - nativeFloats.connectFloatValue(TOPIC_NATIVE_FLOAT, writeCurrentValue); - nativeFloats.connectDoubleValue(TOPIC_NATIVE_DOUBLE, writeCurrentValue); - nativeChars.connectCharValue(TOPIC_NATIVE_CHAR, writeCurrentValue); - nativeChars.connectStringValue(TOPIC_NATIVE_STRING, writeCurrentValue); - boxedIntegers.connectIntValue(TOPIC_BOXED_INTEGER, writeCurrentValue); - boxedIntegers.connectShortValue(TOPIC_BOXED_SHORT, writeCurrentValue); - boxedIntegers.connectLongValue(TOPIC_BOXED_LONG, writeCurrentValue); - boxedFloats.connectFloatValue(TOPIC_BOXED_FLOAT, writeCurrentValue); - boxedFloats.connectDoubleValue(TOPIC_BOXED_DOUBLE, writeCurrentValue); - boxedChars.connectCharValue(TOPIC_BOXED_CHARACTER, writeCurrentValue); + nativeIntegers.connectIntValue(mqttUri(TOPIC_NATIVE_INT), writeCurrentValue); + nativeIntegers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT), writeCurrentValue); + nativeIntegers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG), writeCurrentValue); + nativeFloats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT), writeCurrentValue); + nativeFloats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE), writeCurrentValue); + nativeChars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR), writeCurrentValue); + nativeChars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING), writeCurrentValue); + boxedIntegers.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER), writeCurrentValue); + boxedIntegers.connectShortValue(mqttUri(TOPIC_BOXED_SHORT), writeCurrentValue); + boxedIntegers.connectLongValue(mqttUri(TOPIC_BOXED_LONG), writeCurrentValue); + boxedFloats.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT), writeCurrentValue); + boxedFloats.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE), writeCurrentValue); + boxedChars.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER), writeCurrentValue); } private void setData(String integerDriver, String floatDriver, String stringDriver) { diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Errors.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java similarity index 82% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Errors.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java index be45a8080cc251191c62fcbc3b1793129a45dee3..17d853fba71c2ee46c6c6c7b5424958af0a5f8d2 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Errors.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java @@ -1,4 +1,4 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -15,8 +15,8 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import static org.jastadd.ros2rag.tests.TestUtils.exec; -import static org.jastadd.ros2rag.tests.TestUtils.readFile; +import static org.jastadd.ragconnect.tests.TestUtils.exec; +import static org.jastadd.ragconnect.tests.TestUtils.readFile; import static org.junit.jupiter.api.Assertions.assertTrue; class Errors { @@ -24,7 +24,7 @@ class Errors { private static final Logger logger = LogManager.getLogger(Errors.class); private static final String FILENAME_PATTERN = "$FILENAME"; private static final String INPUT_DIRECTORY = "./src/test/01-input/errors/"; - private static final String OUTPUT_DIRECTORY = "./src/test/02-after-ros2rag/errors/"; + private static final String OUTPUT_DIRECTORY = "./src/test/02-after-ragconnect/errors/"; @BeforeAll public static void createOutputDirectory() { @@ -40,21 +40,21 @@ class Errors { @SuppressWarnings("SameParameterValue") private void test(String name, String rootNode) throws IOException { String grammarFile = INPUT_DIRECTORY + name + ".relast"; - String ros2ragFile = INPUT_DIRECTORY + name + ".ros2rag"; + String ragconnectFile = INPUT_DIRECTORY + name + ".connect"; String outFile = OUTPUT_DIRECTORY + name + ".out"; String expectedFile = INPUT_DIRECTORY + name + ".expected"; try { logger.debug("user.dir: {}", System.getProperty("user.dir")); String[] args = { - "--outputDir=" + OUTPUT_DIRECTORY, - "--inputGrammar=" + grammarFile, - "--inputRos2Rag=" + ros2ragFile, + "--o=" + OUTPUT_DIRECTORY, + grammarFile, + ragconnectFile, "--rootNode=" + rootNode, "--verbose" }; int returnValue = exec(Compiler.class, args, new File(outFile)); - Assertions.assertEquals(1, returnValue, "Ros2Rag did not return with value 1"); + Assertions.assertEquals(1, returnValue, "RagConnect did not return with value 1"); } catch (IOException | InterruptedException e) { e.printStackTrace(); } @@ -78,7 +78,7 @@ class Errors { // FIXME errors not handled correctly at the moment // Assertions.assertLinesMatch(expectedList, outList); - logger.info("ros2rag for " + name + " returned:\n{}", out); + logger.info("ragconnect for " + name + " returned:\n{}", out); } } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java similarity index 96% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java index 9b1ebceb541bddd4cafc3648b10c940af79bc952..3d0706c075d8e4af3b3c5e5fa703f3885b80ddf3 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java @@ -1,4 +1,4 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; import com.google.protobuf.InvalidProtocolBufferException; import config.Config.RobotConfig; @@ -11,6 +11,7 @@ import robot.RobotStateOuterClass.RobotState; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; import static org.junit.jupiter.api.Assertions.*; /** @@ -42,7 +43,7 @@ public class ExampleTest extends AbstractMqttTest { handler.close(); } if (model != null) { - model.MqttCloseConnections(); + model.ragconnectCloseConnections(); } } @@ -210,8 +211,7 @@ public class ExampleTest extends AbstractMqttTest { } private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.MqttSetHost(TestUtils.getMqttHost()); - assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS)); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); @@ -233,9 +233,9 @@ public class ExampleTest extends AbstractMqttTest { } }); - robotArm.connectAppropriateSpeed(TOPIC_CONFIG, writeCurrentValue); - link1.connectCurrentPosition(TOPIC_JOINT1); - link2.connectCurrentPosition(TOPIC_JOINT2); + robotArm.connectAppropriateSpeed(mqttUri(TOPIC_CONFIG), writeCurrentValue); + link1.connectCurrentPosition(mqttUri(TOPIC_JOINT1)); + link2.connectCurrentPosition(mqttUri(TOPIC_JOINT2)); } private void createModel() { diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java similarity index 90% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java index 4e21608a9cee5d8a2c3b18f64ef198760d982874..5ede1e1896afadc70da7b868cf4272eafa250514 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java @@ -1,4 +1,4 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -7,6 +7,7 @@ import read1write2.ast.*; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -44,7 +45,7 @@ public class Read1Write2Test extends AbstractMqttTest { handler.close(); } if (model != null) { - model.MqttCloseConnections(); + model.ragconnectCloseConnections(); } } @@ -137,8 +138,7 @@ public class Read1Write2Test extends AbstractMqttTest { } private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.MqttSetHost(TestUtils.getMqttHost()); - assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS)); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); @@ -181,15 +181,15 @@ public class Read1Write2Test extends AbstractMqttTest { dataOther2.lastStringValue = new String(bytes); }); - onSameNonterminal.connectInput(TOPIC_SAME_READ); - onSameNonterminal.connectOutInteger(TOPIC_SAME_WRITE_INT, writeCurrentValue); - onSameNonterminal.connectOutString(TOPIC_SAME_WRITE_STRING, writeCurrentValue); + onSameNonterminal.connectInput(mqttUri(TOPIC_SAME_READ)); + onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue); + onSameNonterminal.connectOutString(mqttUri(TOPIC_SAME_WRITE_STRING), writeCurrentValue); - onDifferentNonterminal.connectInput(TOPIC_DIFFERENT_READ); - other1.connectOutInteger(TOPIC_DIFFERENT_WRITE1_INT, writeCurrentValue); - other1.connectOutString(TOPIC_DIFFERENT_WRITE1_STRING, writeCurrentValue); - other2.connectOutInteger(TOPIC_DIFFERENT_WRITE2_INT, writeCurrentValue); - other2.connectOutString(TOPIC_DIFFERENT_WRITE2_STRING, writeCurrentValue); + onDifferentNonterminal.connectInput(mqttUri(TOPIC_DIFFERENT_READ)); + other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue); + other1.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE1_STRING), writeCurrentValue); + other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue); + other2.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE2_STRING), writeCurrentValue); } private void sendData(String inputSame, String inputDifferent) { diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java similarity index 90% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java index a7cf6c7822aa9b8f77cad90551bf2bca05e149a3..9881eb7110f98a3bc36301f2f7a163c417e9a54e 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java @@ -1,4 +1,4 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -7,6 +7,7 @@ import read2write1.ast.*; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -43,7 +44,7 @@ public class Read2Write1Test extends AbstractMqttTest { handler.close(); } if (model != null) { - model.MqttCloseConnections(); + model.ragconnectCloseConnections(); } } @@ -146,8 +147,7 @@ public class Read2Write1Test extends AbstractMqttTest { } private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { - model.MqttSetHost(TestUtils.getMqttHost()); - assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS)); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); @@ -178,14 +178,14 @@ public class Read2Write1Test extends AbstractMqttTest { dataOther2.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt(); }); - onSameNonterminal.connectInput1(TOPIC_SAME_READ1); - onSameNonterminal.connectInput2(TOPIC_SAME_READ2); - onSameNonterminal.connectOutInteger(TOPIC_SAME_WRITE_INT, writeCurrentValue); + onSameNonterminal.connectInput1(mqttUri(TOPIC_SAME_READ1)); + onSameNonterminal.connectInput2(mqttUri(TOPIC_SAME_READ2)); + onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue); - onDifferentNonterminal.connectInput1(TOPIC_DIFFERENT_READ1); - onDifferentNonterminal.connectInput2(TOPIC_DIFFERENT_READ2); - other1.connectOutInteger(TOPIC_DIFFERENT_WRITE1_INT, writeCurrentValue); - other2.connectOutInteger(TOPIC_DIFFERENT_WRITE2_INT, writeCurrentValue); + onDifferentNonterminal.connectInput1(mqttUri(TOPIC_DIFFERENT_READ1)); + onDifferentNonterminal.connectInput2(mqttUri(TOPIC_DIFFERENT_READ2)); + other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue); + other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue); } private void sendData(boolean useSameInput1, String inputSame, diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java similarity index 83% rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java index 05db73e8790a25dd9d730ea637e8be500d568d5f..26c32c5a438bf133d984efa31bde4b05b3b15af1 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java @@ -1,7 +1,11 @@ -package org.jastadd.ros2rag.tests; +package org.jastadd.ragconnect.tests; + +import org.junit.jupiter.api.Assertions; import java.io.File; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; @@ -26,6 +30,14 @@ public class TestUtils { } } + public static String mqttUri(String path) { + return "mqtt://" + getMqttHost() + "/" + path; + } + + public static String restUri(String path, int port) { + return "rest://localhost:" + port + "/" + path; + } + public static int getMqttDefaultPort() { return 1883; } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java new file mode 100644 index 0000000000000000000000000000000000000000..896b8711fee14bce4dcf8a12006ddb9e26a5791c --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java @@ -0,0 +1,333 @@ +package org.jastadd.ragconnect.tests; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import via.ast.A; +import via.ast.MqttHandler; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; +import static org.jastadd.ragconnect.tests.TestUtils.restUri; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test case "via". + * + * @author rschoene - Initial contribution + */ +@Tag("rest") +public class ViaTest extends AbstractMqttTest { + + private static final int REST_PORT = 9002; + + private static final String TOPIC_MQTT_2_MQTT_RECEIVE = "mqtt2mqtt/receive"; + private static final String PATH_REST_2_REST_RECEIVE = "rest2rest/receive"; + private static final String TOPIC_MQTT_2_REST_RECEIVE = "mqtt2rest/receive"; + private static final String PATH_REST_2_MQTT_RECEIVE = "rest2mqtt/receive"; + private static final String TOPIC_BOTH_MQTT_RECEIVE = "both/send"; + private static final String PATH_BOTH_REST_RECEIVE = "both/send"; + + private static final String TOPIC_MQTT_2_MQTT_SEND = "mqtt2mqtt/send"; + private static final String PATH_REST_2_REST_SEND = "rest2rest/send"; + private static final String PATH_MQTT_2_REST_SEND = "mqtt2rest/send"; + private static final String TOPIC_REST_2_MQTT_SEND = "rest2mqtt/send"; + private static final String TOPIC_BOTH_2_MQTT_SEND = "both2mqtt/send"; + private static final String PATH_BOTH_2_REST_SEND = "both2rest/send"; + + private static final String REST_SERVER_BASE_URL = "http://localhost:" + REST_PORT + "/"; + + private MqttHandler handler; + private A model; + private ReceiverData dataMqtt2Mqtt; + private ReceiverData dataRest2Mqtt; + private WebTarget dataRest2Rest; + private WebTarget dataMqtt2Rest; + private ReceiverData dataBoth2Mqtt; + private WebTarget dataBoth2Rest; + + private WebTarget senderRest2Rest; + private WebTarget senderRest2Mqtt; + private WebTarget senderBoth2Rest; + + @AfterEach + public void closeConnections() { + if (handler != null) { + handler.close(); + } + if (model != null) { + model.ragconnectCloseConnections(); + } + } + + @Test + public void buildModel() { + createModel(); + } + + @Test + public void communicateSendInitialValue() throws IOException, InterruptedException { + createModel(); + setupReceiverAndConnect(true); + + // check initial value + TestUtils.waitForMqtt(); + checkData(1, "100-M2M-ToMqtt", + "200-R2R-ToRest", + "300-M2R-ToRest", + 1, "400-R2M-ToMqtt", + 1, "500-B2M-ToMqtt", + "500-B2R-ToRest"); + + sendData("101", "201", "301", "401"); + sendDataForBoth("501", true); + + // check new value + TestUtils.waitForMqtt(); + checkData(2, "FromMqtt-101-M2M-ToMqtt", + "FromRest-201-R2R-ToRest", + "FromMqtt-301-M2R-ToRest", + 2, "FromRest-401-R2M-ToMqtt", + 2, "501-B2M-ToMqtt", + "501-B2R-ToRest"); + + // send value only for bothInput via REST + sendDataForBoth("502", false); + + // check this value + TestUtils.waitForMqtt(); + checkData(2, "FromMqtt-101-M2M-ToMqtt", + "FromRest-201-R2R-ToRest", + "FromMqtt-301-M2R-ToRest", + 2, "FromRest-401-R2M-ToMqtt", + 3, "502-B2M-ToMqtt", + "502-B2R-ToRest"); + + // send same value only for bothInput via MQTT + sendDataForBoth("502", true); + + // check this value + TestUtils.waitForMqtt(); + checkData(2, "FromMqtt-101-M2M-ToMqtt", + "FromRest-201-R2R-ToRest", + "FromMqtt-301-M2R-ToRest", + 2, "FromRest-401-R2M-ToMqtt", + 3, "502-B2M-ToMqtt", + "502-B2R-ToRest"); + + // send values for other things + sendData("102", "202", "302", "402"); + + // check this value + TestUtils.waitForMqtt(); + checkData(3, "FromMqtt-102-M2M-ToMqtt", + "FromRest-202-R2R-ToRest", + "FromMqtt-302-M2R-ToRest", + 3, "FromRest-402-R2M-ToMqtt", + 3, "502-B2M-ToMqtt", + "502-B2R-ToRest"); + + // send same values again for other things + sendData("102", "202", "302", "402"); + + // check this value + TestUtils.waitForMqtt(); + checkData(3, "FromMqtt-102-M2M-ToMqtt", + "FromRest-202-R2R-ToRest", + "FromMqtt-302-M2R-ToRest", + 3, "FromRest-402-R2M-ToMqtt", + 3, "502-B2M-ToMqtt", + "502-B2R-ToRest"); + } + + @Test + public void communicateOnlyUpdatedValue() throws IOException, InterruptedException { + createModel(); + setupReceiverAndConnect(false); + + // check initial value + TestUtils.waitForMqtt(); + checkData(0, null, + "200-R2R-ToRest", + "300-M2R-ToRest", + 0, null, + 0, null, + "500-B2R-ToRest"); + + sendData("111", "211", "311", "411"); + sendDataForBoth("511", true); + + // check new value + TestUtils.waitForMqtt(); + checkData(1, "FromMqtt-111-M2M-ToMqtt", + "FromRest-211-R2R-ToRest", + "FromMqtt-311-M2R-ToRest", + 1, "FromRest-411-R2M-ToMqtt", + 1, "511-B2M-ToMqtt", + "511-B2R-ToRest"); + + // send value only for bothInput via REST + sendDataForBoth("512", false); + + // check this value + TestUtils.waitForMqtt(); + checkData(1, "FromMqtt-111-M2M-ToMqtt", + "FromRest-211-R2R-ToRest", + "FromMqtt-311-M2R-ToRest", + 1, "FromRest-411-R2M-ToMqtt", + 2, "512-B2M-ToMqtt", + "512-B2R-ToRest"); + + // send same value only for bothInput via MQTT + sendDataForBoth("512", true); + + // check this value + TestUtils.waitForMqtt(); + checkData(1, "FromMqtt-111-M2M-ToMqtt", + "FromRest-211-R2R-ToRest", + "FromMqtt-311-M2R-ToRest", + 1, "FromRest-411-R2M-ToMqtt", + 2, "512-B2M-ToMqtt", + "512-B2R-ToRest"); + + // send values for other things + sendData("112", "212", "312", "412"); + + // check this value + TestUtils.waitForMqtt(); + checkData(2, "FromMqtt-112-M2M-ToMqtt", + "FromRest-212-R2R-ToRest", + "FromMqtt-312-M2R-ToRest", + 2, "FromRest-412-R2M-ToMqtt", + 2, "512-B2M-ToMqtt", + "512-B2R-ToRest"); + + // send same values again for other things + sendData("112", "212", "312", "412"); + + // check this value + TestUtils.waitForMqtt(); + checkData(2, "FromMqtt-112-M2M-ToMqtt", + "FromRest-212-R2R-ToRest", + "FromMqtt-312-M2R-ToRest", + 2, "FromRest-412-R2M-ToMqtt", + 2, "512-B2M-ToMqtt", + "512-B2R-ToRest"); + } + + private void sendData(String inputMqtt2Mqtt, String inputRest2Rest, String inputMqtt2Rest, String inputRest2Mqtt) { + handler.publish(TOPIC_MQTT_2_MQTT_RECEIVE, inputMqtt2Mqtt.getBytes()); + senderRest2Rest.request().put(Entity.entity(inputRest2Rest, MediaType.TEXT_PLAIN_TYPE)); + handler.publish(TOPIC_MQTT_2_REST_RECEIVE, inputMqtt2Rest.getBytes()); + senderRest2Mqtt.request().put(Entity.entity(inputRest2Mqtt, MediaType.TEXT_PLAIN_TYPE)); + } + + private void sendDataForBoth(String input, boolean useMqtt) { + if (useMqtt) { + handler.publish(TOPIC_BOTH_MQTT_RECEIVE, input.getBytes()); + } else { + senderBoth2Rest.request().put(Entity.entity(input, MediaType.TEXT_PLAIN_TYPE)); + } + } + + private void checkData(int numberOfMqtt2MqttValues, String mqtt2MqttValue, String rest2RestValue, String mqtt2RestValue, int numberOfRest2MqttValues, String rest2MqttValue, int numberOfBoth2MqttValues, String both2MqttValue, String both2RestValue) { + dataMqtt2Mqtt.assertEqualData(numberOfMqtt2MqttValues, mqtt2MqttValue); + dataRest2Mqtt.assertEqualData(numberOfRest2MqttValues, rest2MqttValue); + dataBoth2Mqtt.assertEqualData(numberOfBoth2MqttValues, both2MqttValue); + assertEquals(rest2RestValue, readRest2Rest()); + assertEquals(mqtt2RestValue, readMqtt2Rest()); + assertEquals(both2RestValue, readBoth2Rest()); + } + + private String readRest2Rest() { + return dataRest2Rest.request().get().readEntity(String.class); + } + + private String readMqtt2Rest() { + return dataMqtt2Rest.request().get().readEntity(String.class); + } + + private String readBoth2Rest() { + return dataBoth2Rest.request().get().readEntity(String.class); + } + + private void createModel() { + // Setting value for Input without dependencies does not trigger any updates + model = new A(); + model.setMqtt2MqttInput("100"); + model.setRest2RestInput("200"); + model.setMqtt2RestInput("300"); + model.setRest2MqttInput("400"); + model.setBoth2BothInput("500"); + } + + private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException { + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + + model.addDependencyMqtt2Mqtt(model); + model.addDependencyRest2Rest(model); + model.addDependencyMqtt2Rest(model); + model.addDependencyRest2Mqtt(model); + model.addDependencyBoth2Mqtt(model); + model.addDependencyBoth2Rest(model); + + dataMqtt2Mqtt = new ReceiverData(); + dataRest2Mqtt = new ReceiverData(); + dataBoth2Mqtt = new ReceiverData(); + + handler.newConnection(TOPIC_MQTT_2_MQTT_SEND, bytes -> { + dataMqtt2Mqtt.numberOfStringValues += 1; + dataMqtt2Mqtt.lastStringValue = new String(bytes); + }); + handler.newConnection(TOPIC_REST_2_MQTT_SEND, bytes -> { + dataRest2Mqtt.numberOfStringValues += 1; + dataRest2Mqtt.lastStringValue = new String(bytes); + }); + handler.newConnection(TOPIC_BOTH_2_MQTT_SEND, bytes -> { + dataBoth2Mqtt.numberOfStringValues += 1; + dataBoth2Mqtt.lastStringValue = new String(bytes); + }); + + Client client = ClientBuilder.newClient(); + dataRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_SEND); + dataMqtt2Rest = client.target(REST_SERVER_BASE_URL + PATH_MQTT_2_REST_SEND); + dataBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_2_REST_SEND); + senderRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_RECEIVE); + senderRest2Mqtt = client.target(REST_SERVER_BASE_URL + PATH_REST_2_MQTT_RECEIVE); + senderBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_REST_RECEIVE); + + model.connectMqtt2MqttInput(mqttUri(TOPIC_MQTT_2_MQTT_RECEIVE)); + model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), writeCurrentValue); + model.connectMqtt2RestInput(mqttUri(TOPIC_MQTT_2_REST_RECEIVE)); + model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), writeCurrentValue); + model.connectRest2MqttInput(restUri(PATH_REST_2_MQTT_RECEIVE, REST_PORT)); + model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), writeCurrentValue); + model.connectRest2RestInput(restUri(PATH_REST_2_REST_RECEIVE, REST_PORT)); + model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), writeCurrentValue); + model.connectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE)); + model.connectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT)); + model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), writeCurrentValue); + model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), writeCurrentValue); + } + + private static class ReceiverData { + String lastStringValue; + int numberOfStringValues = 0; + + public void assertEqualData(int expectedNumberOfValues, String expectedLastValue) { + assertEquals(expectedNumberOfValues, this.numberOfStringValues); + assertEquals(expectedLastValue, this.lastStringValue); + } + } +} diff --git a/ragconnect.tests/src/test/resources/log4j2.xml b/ragconnect.tests/src/test/resources/log4j2.xml new file mode 100644 index 0000000000000000000000000000000000000000..4c0d4548c61b23abad6aabc6811e68cd8a928871 --- /dev/null +++ b/ragconnect.tests/src/test/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration status="INFO"> + <Appenders> + <Console name="Console" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> + </Console> + </Appenders> + <Loggers> + <Root level="debug"> + <AppenderRef ref="Console"/> + </Root> + <Logger name="org.eclipse.jetty" level="info" additivity="false"> + <AppenderRef ref="Console"/> + </Logger> + </Loggers> +</Configuration> diff --git a/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java b/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java index 7c6d930aefa358a24b53be7349e1d667fc681380..5ba5e348651a11206b1a01ed919235eaff9d40f8 100644 --- a/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java +++ b/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java @@ -9,6 +9,7 @@ import de.tudresden.inf.st.ros2rag.common.DataConfiguration.ActualConfiguration; import java.io.File; import java.io.IOException; +import java.net.URI; import java.util.Map; import java.util.SortedMap; @@ -24,7 +25,11 @@ public class Util { ObjectMapper mapper = new ObjectMapper( new YAMLFactory().configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true) ); - return mapper.readValue(configFile, DataConfiguration.class).panda_mqtt_connector; + ActualConfiguration config = mapper.readValue(configFile, DataConfiguration.class).panda_mqtt_connector; + URI serverUri = URI.create(config.server); + config.server = serverUri.getHost() + ":" + (serverUri.getPort() == -1 ? 1883 : serverUri.getPort()) + + serverUri.getPath(); + return config; } public static void setMqttHost(SetHost handler, ActualConfiguration config) throws IOException { @@ -32,7 +37,11 @@ public class Util { handler.setHost(hostAndPort.host, hostAndPort.port); } - public static void iterateLinks(HandleLink callback, ActualConfiguration config) { + public static String mqttUri(String topic, ActualConfiguration config) { + return "mqtt://" + config.server + "/" + topic; + } + + public static void iterateLinks(HandleLink callback, ActualConfiguration config) throws IOException { for (Map.Entry<String, SortedMap<String, String>> dataRobot : config.parts.entrySet()) { String topicPrefix = dataRobot.getKey() + "/"; for (Map.Entry<String, String> dataLink : dataRobot.getValue().entrySet()) { @@ -69,6 +78,6 @@ public class Util { @FunctionalInterface public interface HandleLink { - void handle(boolean isEndEffector, String topic, String name); + void handle(boolean isEndEffector, String topic, String name) throws IOException; } } diff --git a/ros2rag.goal/build.gradle b/ros2rag.goal/build.gradle index 0c537c2ba4f8d5a5f92fc572249ddcb0afa94896..3bfe45ab6c9aef96b069ff56faf8c47597f11acf 100644 --- a/ros2rag.goal/build.gradle +++ b/ros2rag.goal/build.gradle @@ -18,7 +18,9 @@ configurations { baseRuntimeClasspath } -sourceSets.main.java.srcDir "src/gen/java" +File genSrc = file("src/gen/java"); +sourceSets.main.java.srcDir genSrc +idea.module.generatedSourceDirs += genSrc dependencies { implementation project (':ragconnect.base') diff --git a/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java b/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java index fc04dc9aeda6cc79222f1042a2d583abeeb86c6d..5dad7c9a5ef62f291fd67967cb067b2597d6239a 100644 --- a/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java +++ b/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java @@ -33,7 +33,6 @@ public class GoalMain { ActualConfiguration config = Util.parseConfig(configFile); model = new GoalModel(); - Util.setMqttHost(model::MqttSetHost, config); for (DataWorkPose dataWorkPose : config.goal_poses) { WorkPose workPose = new WorkPose(); @@ -60,7 +59,7 @@ public class GoalMain { robotState.setLastUpdate(0); model.setRobotState(robotState); - model.MqttWaitUntilReady(2, TimeUnit.SECONDS); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); logger.debug("Setting dependencies"); /* @@ -82,15 +81,15 @@ public class GoalMain { */ Util.iterateLinks((isEndEffector, topic, name) -> { if (isEndEffector) { - robotState.connectCurrentPosition(topic); - robotState.connectLastUpdate(topic); + robotState.connectCurrentPosition(Util.mqttUri(topic, config)); + robotState.connectLastUpdate(Util.mqttUri(topic, config)); } }, config); // next position is not initialized, so don't send it - model.getWorkflow().connectNextTrajectory(config.topics.trajectory, false); - model.getWorkflow().connectCurrentStep(config.topics.nextStep); + model.getWorkflow().connectNextTrajectory(Util.mqttUri(config.topics.trajectory, config), false); + model.getWorkflow().connectCurrentStep(Util.mqttUri(config.topics.nextStep, config)); // initial next step is sent, as soon as this is received, the workflow starts - model.getWorkflow().connectReadyForThisStep(config.topics.nextStep, true); + model.getWorkflow().connectReadyForThisStep(Util.mqttUri(config.topics.nextStep, config), true); logStatus("Start"); CountDownLatch exitCondition = new CountDownLatch(1); @@ -140,7 +139,7 @@ public class GoalMain { private void close() { logger.info("Exiting ..."); mainHandler.close(); - model.MqttCloseConnections(); + model.ragconnectCloseConnections(); } public static void main(String[] args) throws IOException, InterruptedException { diff --git a/ros2rag.safety/build.gradle b/ros2rag.safety/build.gradle index 30d6296bdc98ad7a87f3dedbb0739fe6074120d2..4bfec22f282ed5700cd4bf05edfd4791b0f4515a 100644 --- a/ros2rag.safety/build.gradle +++ b/ros2rag.safety/build.gradle @@ -18,7 +18,10 @@ configurations { baseRuntimeClasspath } -sourceSets.main.java.srcDir "src/gen/java" + +File genSrc = file("src/gen/java"); +sourceSets.main.java.srcDir genSrc +idea.module.generatedSourceDirs += genSrc dependencies { implementation project (':ragconnect.base') diff --git a/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java b/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java index 7627ac005d67c1332323f95866b5f55703cfb532..bf34044f5f194c7a20e600ecd33b02da5116449c 100644 --- a/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java +++ b/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java @@ -29,7 +29,6 @@ public class StarterMain { ActualConfiguration config = Util.parseConfig(configFile); model = new Model(); - Util.setMqttHost(model::MqttSetHost, config); ZoneModel zoneModel = new ZoneModel(); @@ -45,7 +44,7 @@ public class StarterMain { } zoneModel.addSafetyZone(safetyZone); model.setZoneModel(zoneModel); - model.MqttWaitUntilReady(2, TimeUnit.SECONDS); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); RobotArm robotArm = new RobotArm(); model.setRobotArm(robotArm); @@ -63,10 +62,10 @@ public class StarterMain { link.setName(name); link.setCurrentPosition(IntPosition.of(0, 0, 0)); link.containingRobotArm().addDependency1(link); - link.connectCurrentPosition(topic); + link.connectCurrentPosition(Util.mqttUri(topic, config)); }, config); - robotArm.connectAppropriateSpeed(config.topics.robotConfig, true); + robotArm.connectAppropriateSpeed(Util.mqttUri(config.topics.robotConfig, config), true); logStatus("Start", robotArm); CountDownLatch exitCondition = new CountDownLatch(1); @@ -102,7 +101,7 @@ public class StarterMain { private void close() { logger.info("Exiting ..."); mainHandler.close(); - model.MqttCloseConnections(); + model.ragconnectCloseConnections(); } public static void main(String[] args) throws IOException, InterruptedException {