Skip to content
Snippets Groups Projects
Select Git revision
  • 928649d18a998f0684adc17f21a6112785e4b491
  • dev default protected
  • main protected
  • feature/ros-java-integration
4 results

using.md

Blame
  • Using RagConnect -- an example

    The full example is available at https://git-st.inf.tu-dresden.de/jastadd/ragconnect-minimal.

    Preparation

    The following examples are inspired by the real test case read1write2 The idea is to have two nonterminals, where input information is received on one of them, and - after transformation - is sent out by both.

    Let the following grammar be used:

    A ::= <Input:String> /<OutputOnA:String>/ B* ;
    B ::= /<OutputOnB:String>/ ;

    To declare receiving and sending tokens, a dedicated DSL is used:

    // endpoint definitions
    receive A.Input ;
    send A.OutputOnA ;
    send B.OutputOnB using Transformation ;
    
    // mapping definitions
    Transformation maps String s to String {:
      return s + "postfix";
    :}

    This defines A.Input to receive updates, and the other two tokens to send their value, whenever it changes. Additionally, a transformation will be applied on B.OutputOnB before sending out its value.

    Such mapping definitions can be defined for receiving tokens as well. In this case, they are applied before the value is set. If no mapping definition is given, or if the required type (depending on the communication protocol, see later) does not match, a "default mapping definition" is used to avoid boilerplate code converting from or to primitive types.

    Furthermore, let the following attribute definitions be given:

    syn String A.getOutputOnA() = "a" + getInput();
    
    syn String B.getOutputOnB() = "b" + input();
    inh String B.input();
    eq A.getB().input() = getInput();

    In other words, OutputOnA depends on Input of the same node, and OutputOnB depends on Input of its parent node. Currently, those dependencies have to be explicitely written down. It is expected, that in future version, this won't be necessary anymore. This happens also in the DSL (dependencies have to be named to uniquely identify them):

    // dependency definitions
    A.OutputOnA canDependOn A.Input as dependencyA ;
    B.OutputOnB canDependOn A.Input as dependencyB ;

    Using generated code

    After specifying everything, code will be generated if setup properly. Let's create an AST in some driver code:

    A a = new A();
    // set some default value for input
    a.setInput("");
    B b1 = new B();
    B b2 = new B();
    a.addB(b1);
    a.addB(b2);

    Now, we have to set the dependencies as described earlier.

    // a.OutputOnA -> a.Input
    a.addDependencyA(a);
    // b1.OutputOnB -> a.Input
    b1.addDependencyB(a);
    // b2.OutputOnB -> a.Input
    b2.addDependencyB(a);

    Finally, we can actually connect the tokens. Depending on the enabled protocols, different URI schemes are allowed. In this example, we use the default protocol: MQTT.

    a.connectInput("mqtt://localhost/topic/for/input");
    a.connectOutputOnA("mqtt://localhost/a/out", true);
    b1.connectOutputOnB("mqtt://localhost/b1/out", true);
    b2.connectOutputOnB("mqtt://localhost/b2/out", false);

    The first parameter of those connect-methods is always an URI-like String, to identify the protocol to use, the server operating the protocol, and a path to identify the concrete token. In case of MQTT, the server is the host running an MQTT broker, and the path is equal to the topic to publish or subscribe to. Please note, that the first leading slash (/) is removed for MQTT topics, e.g., for A.Input the topic is actually topic/for/input.

    For sending endpoints, there is a second boolean parameter to specify whether the current value shall be send immediately after connecting.

    Communication protocol characteristics

    Protocol URI scheme Default port Type for mapping definitions Remarks
    mqtt mqtt://<broker-host>[:port]/<topic> 1883 byte[] First leading slash not included in topic.
    rest rest://localhost[:port]/<path> 4567 String Host is always localhost.

    Compiler options

    The compiler is JastAdd-compliant, i.e., it accepts all flags available for JastAdd, though there is no process how to chain pre-processors yet. Additional options are as follows.

    Name Required (Default) Description
    --rootNode Yes Root node in the base grammar.
    --protocols No (mqtt) Protocols to enable, currently available: mqtt, rest.
    --printYaml No (false) Print out YAML instead of generating files.
    --verbose No (false) Print more messages while compiling.
    --logReads No (false) Enable logging for every received message.
    --logWrites No (false) Enable logging for every sent message.
    --version No (false) Print version info and exit (reused JastAdd option)
    --o No (.) Output directory (reused JastAdd option)

    All files to be process have to be passed as arguments. Their type is decided by the file extension (ast and relast for input grammars, connect and ragconnect for RagConnect definitions file).

    Remarks

    When constructing the AST and connecting it, one should always set dependencies before connecting, especially if updates already arriving for receiving endpoints. Otherwise, updates might not be propagated after setting dependencies, if values are equal after applying transformations of mapping definitions.

    As an example, when using the following grammar and definitions for RagConnect ...

    A ::= <Input:int> /<Output:String>/ ;
    receive A.Input using Round ;
    send A.Output ;
    
    A.Output canDependOn A.Input as dependency1 ;
    
    Round maps float f to int {:
      return Math.round(f);
    :}

    ... connecting first could mean to store the first rounded value and not propagating this update, since no dependencies are set, and not propagating further updates leading to the same rounded value even after setting the dependencies.