From ecefc4809df87367a40b0d2e1d73ed334b15907e Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Wed, 6 Jul 2022 11:08:09 +0200
Subject: [PATCH] initial version of motion grammar working with real robot

---
 build.gradle                                  |  48 ++++++-
 gradle.properties                             |   2 +
 src/main/jastadd/cleanup/RobotWorld.connect   |  36 +++++
 src/main/jastadd/cleanup/RobotWorld.jadd      |  30 ++--
 src/main/jastadd/cleanup/RobotWorld.relast    |   4 +-
 src/main/jastadd/cleanup/SemanticActions.jadd | 103 +++++++++-----
 src/main/jastadd/cleanup/Tracing.jadd         |  13 +-
 .../inf/st/mg/common/MotionGrammarParser.java |   3 +-
 src/main/proto/connector.proto                | 130 ++++++++++++++++++
 .../de/tudresden/inf/st/mg/ParserTest.java    |  56 +++++++-
 10 files changed, 371 insertions(+), 54 deletions(-)
 create mode 100644 src/main/jastadd/cleanup/RobotWorld.connect
 create mode 100644 src/main/proto/connector.proto

diff --git a/build.gradle b/build.gradle
index 981d28a..00b0076 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,6 +3,7 @@ plugins {
     id 'application'
     id 'idea'
     id 'org.jastadd' version "${jastaddgradle_version}"
+    id "com.google.protobuf" version "${protobuf_plugin_version}"
 }
 
 group 'de.tudresden.inf.st'
@@ -16,6 +17,13 @@ application.mainClass = "${mainClassName}"
 
 java.toolchain.languageVersion = JavaLanguageVersion.of(11)
 
+protobuf {
+    protoc {
+        // The artifact spec for the Protobuf Compiler
+        artifact = "com.google.protobuf:protoc:${protobuf_version}"
+    }
+}
+
 repositories {
     mavenCentral()
     maven {
@@ -28,6 +36,7 @@ configurations {
     // add a configuration to store the grammar printing dependency in
     grammar2uml
     relast
+    ragconnect
 }
 
 File genSrc = file("src/gen/java")
@@ -46,6 +55,11 @@ dependencies {
     implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.3'
     implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.13.3'
     implementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.31'
+    ragconnect group: 'de.tudresden.inf.st', name: 'ragconnect', version: '1.0.0-alpha-203'
+    jastadd2 "org.jastadd:jastadd2:2.3.5-dresden-6"
+    implementation group: 'com.google.protobuf', name: 'protobuf-java', version: "${protobuf_version}"
+    implementation group: 'com.google.protobuf', name: 'protobuf-java-util', version: "${protobuf_version}"
+    implementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15'
 }
 
 def versionFile = 'src/main/resources/Version.properties'
@@ -97,6 +111,33 @@ def loadingGrammarDiagramFile = './src/gen/resources/diagrams/grammar/loading.pn
 def cleaningGrammarDiagramFile = './src/gen/resources/diagrams/grammar/cleaning.png'
 def jastAddListName = 'JastAddList'
 
+// phases: ragConnect -> RelAst -> JastAdd
+// phase: ragConnect
+task ragConnect(type: JavaExec) {
+    group = 'Build'
+    main = 'org.jastadd.ragconnect.compiler.Compiler'
+    classpath = configurations.ragconnect
+
+    args([
+            '--o=src/gen/jastadd',
+            './src/main/jastadd/common/MotionGrammar.relast',
+            './src/main/jastadd/cleanup/RobotWorld.relast',
+            './src/main/jastadd/cleanup/RobotWorld.connect',
+            '--logReads',
+            '--logWrites',
+//            '--logIncremental',
+            '--verbose',
+            '--rootNode=RobotScene',
+            '--protocols=mqtt',
+            "--List=${jastAddListName}",
+            '--experimental-jastadd-329',
+            '--incremental=param',
+            '--tracing=cache,flush',
+            '--evaluationCounter'
+    ])
+
+}
+
 task generateLoadingGrammarDiagrams(type: JavaExec) {
     group = 'Documentation'
     classpath = configurations.grammar2uml
@@ -128,6 +169,7 @@ task relastToJastAdd(type: JavaExec) {
             '--useJastAddNames',
             '--listClass=java.util.ArrayList',
             "--jastAddList=${jastAddListName}",
+            "--resolverHelper",
             '--file'
     args relastFiles
 
@@ -170,11 +212,15 @@ jastadd {
     buildInfoDir = 'src/gen-res'
 
     // default options are: '--rewrite=cnta', '--safeLazy', '--visitCheck=false', '--cacheCycle=false'
-    extraJastAddOptions = ["--List=${jastAddListName}"]
+    extraJastAddOptions = ["--List=${jastAddListName}",
+                           "--flush=api",
+                           "--incremental=param,debug",
+                           "--tracing=cache,flush"]
 }
 
 // Workflow configuration for phases
 clean.dependsOn cleanGen
 generateAst.dependsOn relastToJastAdd
 generateAst.dependsOn generateLoadingGrammarDiagrams, generateCleaningGrammarDiagrams
+relastToJastAdd.dependsOn ragConnect
 
diff --git a/gradle.properties b/gradle.properties
index ccc134c..3888fab 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,3 +5,5 @@ assertj_version = 3.22.0
 grammar2uml_version = 0.2.1
 jastaddgradle_version = 1.14.5
 javalin_version = 4.6.3
+protobuf_version = 4.0.0-rc-2
+protobuf_plugin_version = 0.8.18
diff --git a/src/main/jastadd/cleanup/RobotWorld.connect b/src/main/jastadd/cleanup/RobotWorld.connect
new file mode 100644
index 0000000..b871382
--- /dev/null
+++ b/src/main/jastadd/cleanup/RobotWorld.connect
@@ -0,0 +1,36 @@
+// --- receiving ---
+receive RobotScene.Table using ParseScene, ConvertScene ;
+
+ParseScene maps byte[] bytes to de.tudresden.inf.st.ceti.Scene {:
+  return de.tudresden.inf.st.ceti.Scene.parseFrom(bytes);
+:}
+
+ConvertScene maps de.tudresden.inf.st.ceti.Scene pbScene to Table {:
+  var result = new Table();
+  result.setRobot(new Robot());
+  for (var pbObject : pbScene.getObjectsList()) {
+    PhysicalObject obj;
+    if (pbObject.getType() == de.tudresden.inf.st.ceti.Object.Type.BOX || pbObject.getType() == de.tudresden.inf.st.ceti.Object.Type.BIN) {
+      PhysicalObject physicalObject = pbObject.getType() == de.tudresden.inf.st.ceti.Object.Type.BOX ? new MovableObject() : new Bin();
+      physicalObject.setName(pbObject.getId());
+      java.awt.Color color = new java.awt.Color(pbObject.getColor().getR(), pbObject.getColor().getG(), pbObject.getColor().getB());
+      physicalObject.setColor("#" + Integer.toHexString(color.getRGB()).substring(2));
+      Pose pose = new Pose();
+      pose.setX(pbObject.getPos().getX());
+      pose.setY(pbObject.getPos().getY());
+      pose.setZ(pbObject.getPos().getZ());
+      pose.setQX(pbObject.getOrientation().getX());
+      pose.setQY(pbObject.getOrientation().getY());
+      pose.setQZ(pbObject.getOrientation().getZ());
+      pose.setQW(pbObject.getOrientation().getW());
+      physicalObject.setPose(pose);
+      if (pbObject.getOwner() != "" && pbObject.getType() == de.tudresden.inf.st.ceti.Object.Type.BOX) {
+        ((MovableObject) physicalObject).setAttachedRobot(result.getRobot());
+      }
+      result.addPhysicalObject(physicalObject);
+    } else if (pbObject.getType() == de.tudresden.inf.st.ceti.Object.Type.ARM) {
+        result.getRobot().setIsIdle(pbObject.getState() == de.tudresden.inf.st.ceti.Object.State.STATE_IDLE);
+    }
+  }
+  return result;
+:}
diff --git a/src/main/jastadd/cleanup/RobotWorld.jadd b/src/main/jastadd/cleanup/RobotWorld.jadd
index 335eba4..af9aded 100644
--- a/src/main/jastadd/cleanup/RobotWorld.jadd
+++ b/src/main/jastadd/cleanup/RobotWorld.jadd
@@ -1,14 +1,28 @@
 aspect RobotWorld {
 
-  public static RobotScene RobotScene.initialWorld(java.util.Random r) {
+  public boolean RobotScene.simulated = true;
+  public org.fusesource.mqtt.client.BlockingConnection RobotScene.connection;
+
+  public static RobotScene RobotScene.initialWorld(java.util.Random random) {
     Table t = new Table();
     t.addPhysicalObject(new Bin("binRed", Pose.of(-10, -10, 0), "red"));
     t.addPhysicalObject(new Bin("binGreen", Pose.of(-10, 0, 0), "green"));
     t.addPhysicalObject(new MovableObject("boxRed", Pose.of(-5, -10, 0), "red"));
     t.addPhysicalObject(new MovableObject("boxGreen", Pose.of(-5, 0, 0), "green"));
-    Robot b = new Robot();
-    b.setIsIdle(true);
-    return new RobotScene(t, b);
+    Robot r = new Robot();
+    r.setIsIdle(true);
+    t.setRobot(r);
+    return new RobotScene(t);
+  }
+
+  public static RobotScene RobotScene.initialWorld() {
+    Robot r = new Robot();
+    r.setIsIdle(true);
+    Table t = new Table();
+    t.setRobot(r);
+    RobotScene result = new RobotScene(t);
+    result.simulated = false;
+    return result;
   }
 
   public static TokenType EmptyTable.type()                 { return TokenType.of("EMPTY_TABLE"); }
@@ -151,7 +165,7 @@ aspect RobotWorld {
   }
   public RobotIsIdle RobotScene.parseRobotIsIdle() {
     System.out.print("Trying to parse token <RobotIsIdle>... ");
-    if (getRobot().getIsIdle()) {
+    if (getTable().getRobot().getIsIdle()) {
       System.out.println("success");
       return new RobotIsIdle();
     } else {
@@ -165,7 +179,7 @@ aspect RobotWorld {
   }
   public RobotIsNotIdle RobotScene.parseRobotIsNotIdle() {
     System.out.print("Trying to parse token <RobotIsNotIdle>... ");
-    if (!getRobot().getIsIdle()) {
+    if (!getTable().getRobot().getIsIdle()) {
       System.out.println("success");
       return new RobotIsNotIdle();
     } else {
@@ -179,7 +193,7 @@ aspect RobotWorld {
   }
   public RobotHasItemAttached RobotScene.parseRobotHasItemAttached() {
     System.out.print("Trying to parse token <RobotHasItemAttached>... ");
-    if (getRobot().hasAttachedItem()) {
+    if (getTable().getRobot().hasAttachedItem()) {
       System.out.println("success");
       return new RobotHasItemAttached();
     } else {
@@ -193,7 +207,7 @@ aspect RobotWorld {
   }
   public RobotHasNoItemAttached RobotScene.parseRobotHasNoItemAttached() {
     System.out.print("Trying to parse token <RobotHasNoItemAttached>... ");
-    if (!getRobot().hasAttachedItem()) {
+    if (!getTable().getRobot().hasAttachedItem()) {
       System.out.println("success");
       return new RobotHasNoItemAttached();
     } else {
diff --git a/src/main/jastadd/cleanup/RobotWorld.relast b/src/main/jastadd/cleanup/RobotWorld.relast
index b4bf3d5..c0d7c32 100644
--- a/src/main/jastadd/cleanup/RobotWorld.relast
+++ b/src/main/jastadd/cleanup/RobotWorld.relast
@@ -1,8 +1,8 @@
 // World Model of the Motion Grammar
 
-RobotScene : World ::=  Table Robot;
+RobotScene : World ::= Table;
 
-Table ::= PhysicalObject*;
+Table ::= PhysicalObject* Robot;
 abstract PhysicalObject ::= <Name> Pose <Color>;
 MovableObject : PhysicalObject;
 Bin : PhysicalObject;
diff --git a/src/main/jastadd/cleanup/SemanticActions.jadd b/src/main/jastadd/cleanup/SemanticActions.jadd
index 54aee93..5a64ba9 100644
--- a/src/main/jastadd/cleanup/SemanticActions.jadd
+++ b/src/main/jastadd/cleanup/SemanticActions.jadd
@@ -1,45 +1,72 @@
 aspect SemanticActions {
 
-  public void PickUpObject.action(World world) {
-    RobotScene scene = world.asRobotScene();
-    System.out.println("performing semantic action for element Pick");
-    String objectName = object();
-    java.util.concurrent.Executors.newSingleThreadExecutor().submit(() -> {
-      scene.getRobot().setIsIdle(false);
-      try { Thread.sleep(1200); } catch (InterruptedException e) { /* ignore */ }
-      MovableObject object = scene.getTable().getMovableObjectByName(objectName);
-      scene.getRobot().setAttachedItem(object);
-      object.setPose(Pose.of(-1,-1,-1));
-      try { Thread.sleep(1200); } catch (InterruptedException e) { /* ignore */ }
-      scene.getRobot().setIsIdle(true);
-    });
-  }
+    public void PickUpObject.action(World world) {
+        RobotScene scene = world.asRobotScene();
+        System.out.println("performing semantic action for element Pick");
+        String objectName = object();
 
-  public void DropObjectAtRightPlace.action(World world) {
-    RobotScene scene = world.asRobotScene();
-    System.out.println("performing semantic action for element Pick");
-    String objectName = object();
-    String placeName = place();
-    java.util.concurrent.Executors.newSingleThreadExecutor().submit(() -> {
-      scene.getRobot().setIsIdle(false);
-      try { Thread.sleep(1200); } catch (InterruptedException e) { /* ignore */ }
-      MovableObject object = scene.getTable().getMovableObjectByName(objectName);
-      Bin bin = scene.getTable().getBinByName(placeName);
-      object.setPose(Pose.of(bin.getPose()));
-      scene.getRobot().setAttachedItem(null);
-      try { Thread.sleep(1200); } catch (InterruptedException e) { /* ignore */ }
-      scene.getRobot().setIsIdle(true);
-    });
-  }
+        if (scene.simulated) {
+            java.util.concurrent.Executors.newSingleThreadExecutor().submit(() -> {
+                scene.getTable().getRobot().setIsIdle(false);
+                try {
+                    Thread.sleep(1200);
+                } catch (InterruptedException e) { /* ignore */ }
+                MovableObject object = scene.getTable().getMovableObjectByName(objectName);
+                scene.getTable().getRobot().setAttachedItem(object);
+                object.setPose(Pose.of(-1, -1, -1));
+                try {
+                    Thread.sleep(1200);
+                } catch (InterruptedException e) { /* ignore */ }
+                scene.getTable().getRobot().setIsIdle(true);
+            });
+        } else {
+            de.tudresden.inf.st.ceti.Command c = de.tudresden.inf.st.ceti.Command.newBuilder().setPick(de.tudresden.inf.st.ceti.Pick.newBuilder().setIdRobot("arm").setIdPick(object()).build()).build();
+            try {
+                scene.connection.publish("/ceti_cell_placeworld/command", c.toByteArray(), org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE, false);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void DropObjectAtRightPlace.action(World world) {
+        RobotScene scene = world.asRobotScene();
+        System.out.println("performing semantic action for element Pick");
+        String objectName = object();
+        String placeName = place();
+        if (scene.simulated) {
+            java.util.concurrent.Executors.newSingleThreadExecutor().submit(() -> {
+                scene.getTable().getRobot().setIsIdle(false);
+                try {
+                    Thread.sleep(1200);
+                } catch (InterruptedException e) { /* ignore */ }
+                MovableObject object = scene.getTable().getMovableObjectByName(objectName);
+                Bin bin = scene.getTable().getBinByName(placeName);
+                object.setPose(Pose.of(bin.getPose()));
+                scene.getTable().getRobot().setAttachedItem(null);
+                try {
+                    Thread.sleep(1200);
+                } catch (InterruptedException e) { /* ignore */ }
+                scene.getTable().getRobot().setIsIdle(true);
+            });
+        } else {
+            de.tudresden.inf.st.ceti.Command c = de.tudresden.inf.st.ceti.Command.newBuilder().setDrop(de.tudresden.inf.st.ceti.Drop.newBuilder().setIdRobot("arm").setIdBin(place()).build()).build();
+            try {
+                scene.connection.publish("/ceti_cell_placeworld/command", c.toByteArray(), org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE, false);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
 
-  public void Wait.action(World world) {
-    RobotScene container = world.asRobotScene();
-    System.out.println("performing semantic action for element Wait");
-    try {
-      Thread.sleep((long) (time() * 1000));
-    } catch (InterruptedException e) {
-      // ignore
+    public void Wait.action(World world) {
+        RobotScene container = world.asRobotScene();
+        System.out.println("performing semantic action for element Wait");
+        try {
+            Thread.sleep((long) (time() * 1000));
+        } catch (InterruptedException e) {
+            // ignore
+        }
     }
-  }
 
 }
diff --git a/src/main/jastadd/cleanup/Tracing.jadd b/src/main/jastadd/cleanup/Tracing.jadd
index a5174db..a1aaa38 100644
--- a/src/main/jastadd/cleanup/Tracing.jadd
+++ b/src/main/jastadd/cleanup/Tracing.jadd
@@ -24,21 +24,30 @@ aspect Tracing {
     World.printContextOf("Pose.setZ()-DONE", this, de.tudresden.inf.st.mg.common.MotionGrammarConfig.DEFAULT_COLOR);
   }
 
+  refine
+  public RobotScene RobotScene.setTable(Table t) {
+    refined(t);
+    World.printContextOf("RobotScene.setTable()-DONE", getTable(), de.tudresden.inf.st.mg.common.MotionGrammarConfig.DEFAULT_COLOR);
+    return this;
+  }
+
   // this is probably never called because each child overrides the setter method
   refine
-  public void PhysicalObject.setPose(Pose p) {
+  public PhysicalObject PhysicalObject.setPose(Pose p) {
     World.printContextOf("PhysicalObject.setPose()-BEFORE", getPose(), de.tudresden.inf.st.mg.common.MotionGrammarConfig.REMOVE_COLOR);
     refined(p);
     World.printContextOf("PhysicalObject.setPose()-AFTER", getPose(), de.tudresden.inf.st.mg.common.MotionGrammarConfig.ADD_COLOR);
     World.printContextOf("PhysicalObject.setPose()-DONE", getPose(), de.tudresden.inf.st.mg.common.MotionGrammarConfig.DEFAULT_COLOR);
+    return this;
   }
 
   refine
-  public void MovableObject.setPose(Pose p) {
+  public MovableObject MovableObject.setPose(Pose p) {
     World.printContextOf("MovableObject.setPose()-BEFORE", getPose(), de.tudresden.inf.st.mg.common.MotionGrammarConfig.REMOVE_COLOR);
     refined(p);
     World.printContextOf("MovableObject.setPose()-AFTER", getPose(), de.tudresden.inf.st.mg.common.MotionGrammarConfig.ADD_COLOR);
     World.printContextOf("MovableObject.setPose()-DONE", getPose(), de.tudresden.inf.st.mg.common.MotionGrammarConfig.DEFAULT_COLOR);
+    return this;
   }
 
   refine
diff --git a/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java b/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java
index 9eb57c6..8d02498 100644
--- a/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java
+++ b/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java
@@ -65,8 +65,7 @@ public abstract class MotionGrammarParser<T extends MotionGrammarElement> {
           .customPreamble("title " + step)
           .skinParam(SkinParamBooleanSetting.Shadowing, false)
           .skinParam(SkinParamStringSetting.backgroundColor, "white")
-          .dumpAsSVG(svgPath)
-          .dumpAsPNG(MotionGrammarConfig.astDiagramDir.resolve("AST-" + String.format("%03d", timeStep_) + "-" + new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss.SSS").format(new java.util.Date()) + "-" + step + ".png"));
+          .dumpAsSVG(svgPath);
       DiagramProvider.getInstance().publishAst(new Date(), timeStep_, step, svgPath);
       timeStep_++;
     } catch (IOException e) {
diff --git a/src/main/proto/connector.proto b/src/main/proto/connector.proto
new file mode 100644
index 0000000..1dc7b47
--- /dev/null
+++ b/src/main/proto/connector.proto
@@ -0,0 +1,130 @@
+// connector.proto
+// this file contains the messages that are exchanged between the ROS connector and other systems
+
+syntax = "proto3";
+
+option java_package = "de.tudresden.inf.st.ceti";
+option java_multiple_files = true;
+
+message Object {
+
+    // Position is object-center related
+    message Position {
+        double x = 1; // in m
+        double y = 2; // in m
+        double z = 3; // height in m
+    }
+
+    // 3D description of the object
+    message Size {
+        double length = 1; // in m
+        double width = 2;  // in m
+        double height = 3; // in m
+    }
+    message Orientation {
+        double x = 1; // normalized quaternion
+        double y = 2;
+        double z = 3;
+        double w = 4;
+    }
+    message Color {
+        float r = 1; // 0..1
+        float g = 2; // 0..1
+        float b = 3; // 0..1
+    }
+    enum Type {
+        UNKNOWN = 0;
+        BOX = 1;
+        BIN = 2;
+        ARM = 3;
+        DROP_OFF_LOCATION = 4;
+        HUMAN = 5;
+        ROBOT = 6;
+        COLLABORATION_ZONE = 7;
+    }
+    enum State {
+        STATE_UNKNOWN = 0;
+
+        // robot states
+        STATE_IDLE = 101;
+        STATE_PICKING = 102;
+        STATE_PLACING = 103;
+        STATE_MOVING = 104;
+
+        // object
+        STATE_STATIONARY = 200;
+        STATE_PICKED = 201;
+    }
+
+    // determines the meaning of a scene object when the scene has is_delta flag
+    enum Mode {
+        MODE_UNKNOWN = 0; // if and only if is_delta = false, this should be the value of the field
+        MODE_KEEP = 1;    // the object's properties do not change, thus don't need to be processed by the receiver
+        MODE_MODIFY = 2;  // the object's properties have changed
+        MODE_ADD = 3;     // the object did not exist in the scene before
+        MODE_REMOVE = 4;  // the object is removed from the scene, its properties can be ignored
+    }
+
+    string id = 1;
+    Type type = 2;
+    Position pos = 3;
+    Size size = 4;
+    Orientation orientation = 5;
+    Color color = 6;
+    State state = 7;
+    string owner = 8;
+    Mode mode = 9;
+}
+
+// the scene is stored within the ROS side and sent to clients
+message Scene {
+    repeated Object objects = 1;
+    bool is_delta = 2;
+}
+
+// the selection is done by a client and sent to ROS
+message Selection {
+    string id = 1; // the id corresponds to an id of an Object in a Scene
+}
+
+message Command {
+    oneof msg {
+        PickAndPlace pickAndPlace = 1;
+        ConfigChange configChange = 2;
+        Evacuate evacuate = 3;
+        Pick pick = 4;
+        Place place = 5;
+        Drop drop = 6;
+    }
+}
+
+message PickAndPlace {
+    string idRobot = 1;  // id of the robot that should execute this operation
+    string idPick = 2;  // id of the object in the scene to be picked
+    string idPlace = 3;  // id of the location the picked object shall be placed.
+}
+
+message ConfigChange {
+    string idCollaborationZone = 1;  // id of collaboration zone to change
+    string idRobotNewOwner = 2;  // id of robot that will become new owner
+}
+
+message Evacuate {
+    string idRobot = 1;  // id of robot that need to move out of its currently defined collision objects
+    string idCollaborationZone = 2;  // id of collaboration zone to evacuate
+}
+
+message Pick {
+    string idRobot = 1;  // id of the robot that should execute this operation
+    string idPick = 2;  // id of the object in the scene to be picked
+}
+
+message Place {
+    string idRobot = 1;  // id of the robot that should execute this operation
+    string idPlace = 2;  // id of the location currently grasped object shall be placed.
+}
+
+message Drop {
+    string idRobot = 1;  // id of the robot that should execute this operation
+    string idBin = 2;  // id of the bin the picked object shall be dropped in.
+}
\ No newline at end of file
diff --git a/src/test/java/de/tudresden/inf/st/mg/ParserTest.java b/src/test/java/de/tudresden/inf/st/mg/ParserTest.java
index 378e0d3..3733c6c 100644
--- a/src/test/java/de/tudresden/inf/st/mg/ParserTest.java
+++ b/src/test/java/de/tudresden/inf/st/mg/ParserTest.java
@@ -1,5 +1,6 @@
 package de.tudresden.inf.st.mg;
 
+import de.tudresden.inf.st.mg.common.MotionGrammarConfig;
 import de.tudresden.inf.st.mg.common.MotionGrammarParser;
 import de.tudresden.inf.st.mg.jastadd.model.*;
 import org.junit.jupiter.api.BeforeAll;
@@ -8,10 +9,12 @@ import org.junit.jupiter.api.Test;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Comparator;
 import java.util.Random;
+import java.util.concurrent.TimeUnit;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -55,7 +58,7 @@ public class ParserTest {
   void runTidyParser() throws MotionGrammarParser.ParseException {
 
     // for some reason, the best random seed value here is 1 and not 0???
-    World world = RobotScene.initialWorld(new Random(1));
+    RobotScene world = RobotScene.initialWorld(new Random(1));
 
     // create a parser using the world
     RobotParser parser = new RobotParser(world);
@@ -72,4 +75,55 @@ public class ParserTest {
     }
   }
 
+  @Test
+  @Disabled
+  void runRealTidyParser() throws MotionGrammarParser.ParseException {
+
+    MotionGrammarConfig.astDiagramDir = TIDY_AST_DIAGRAM_DIR;
+
+    RobotScene world = RobotScene.initialWorld();
+
+    org.fusesource.mqtt.client.MQTT handler = new org.fusesource.mqtt.client.MQTT();
+    try {
+      handler.setHost("tcp://localhost:1883");
+    } catch (URISyntaxException e) {
+      e.printStackTrace();
+    }
+    world.connection = handler.blockingConnection();
+    try {
+      world.connection.connect();
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    System.err.println(world.connection.isConnected());
+
+    try {
+      world.connectTable("mqtt://localhost//ceti_cell_empty/scene/update");
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    while (world.getTable().getNumPhysicalObject() == 0) {
+      try {
+        Thread.sleep(1000);
+      } catch (InterruptedException e) {
+        e.printStackTrace();
+      }
+    }
+
+    // create a parser using the world
+    RobotParser parser = new RobotParser(world);
+
+    // parse (synchonously, long-running)
+    var result = parser.parse();
+
+    assertThat(result).isNotNull().isInstanceOf(Tidy.class);
+    try {
+      Thread.sleep(100000*1000);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+  }
+
 }
-- 
GitLab