diff --git a/buildSrc/src/main/groovy/ros3rag.java-ragconnect-conventions.gradle b/buildSrc/src/main/groovy/ros3rag.java-ragconnect-conventions.gradle
index ad1b6a77d40560af9619a15e0791845b4a506306..7a5e541fedb5b50e5a85dad17bafe418163a4f2b 100644
--- a/buildSrc/src/main/groovy/ros3rag.java-ragconnect-conventions.gradle
+++ b/buildSrc/src/main/groovy/ros3rag.java-ragconnect-conventions.gradle
@@ -15,7 +15,6 @@ dependencies {
   relast group: 'org.jastadd', name: 'relast', version: "0.4.0-143"
   ragconnect group: 'de.tudresden.inf.st', name: 'ragconnect', version: '1.0.0-alpha-198'
 
-//  implementation group: 'de.tudresden.inf.st', name: 'dumpAstWithPlantuml', version: '0.3.5'
-//  implementation fileTree(include: ['dumpAstWithPlantuml-0.3.5.jar'], dir: '../libs')
+  implementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: '1.0.1-67'
 }
 
diff --git a/open-latest-svg-b.sh b/open-latest-svg-b.sh
new file mode 100755
index 0000000000000000000000000000000000000000..34fd6ee4b295c3f8fb24c8518e85162373263f0d
--- /dev/null
+++ b/open-latest-svg-b.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+ls -c ros3rag.placeB/*.svg | head -n1 | xargs xdg-open
diff --git a/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/RegionConfiguration.java b/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/RegionConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..278ad91a130bfb1b9ae63e32f17fa3811e68f16b
--- /dev/null
+++ b/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/RegionConfiguration.java
@@ -0,0 +1,17 @@
+package de.tudresden.inf.st.ros3rag.common;
+
+import java.util.List;
+
+/**
+ * Data class for region definitions.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class RegionConfiguration {
+  public List<RegionDefinition> regions;
+
+  public static class RegionDefinition {
+    public String name;
+    public List<String> positions;
+  }
+}
diff --git a/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/Util.java b/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/Util.java
index 1b8b116cf7dd075207d3b3177f619b543841a814..cb868eccfbc3ce6180ce82b8c25fe6f39a9da2a7 100644
--- a/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/Util.java
+++ b/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/Util.java
@@ -27,13 +27,19 @@ public class Util {
   private static final Logger logger = LogManager.getLogger(Util.class);
 
   public static Configuration parseConfig(File configFile) throws IOException {
-    System.out.println("Using config file: " + configFile.getAbsolutePath());
+    logger.info("Using config file: {}", configFile.getAbsolutePath());
     ObjectMapper mapper = new ObjectMapper(
         new YAMLFactory().configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true)
     );
     return mapper.readValue(configFile, Configuration.class);
   }
 
+  public static RegionConfiguration parseRegionConfig(File regionConfigFile) throws IOException {
+    logger.info("Using region config file: {}", regionConfigFile.getAbsolutePath());
+    ObjectMapper mapper = new ObjectMapper();
+    return mapper.readValue(regionConfigFile, RegionConfiguration.class);
+  }
+
   public static String mqttUri(String topic, Configuration config) {
     return "mqtt://" + config.mqttHost + "/" + topic;
   }
diff --git a/ros3rag.common/src/main/proto/cgv_connector.proto b/ros3rag.common/src/main/proto/cgv_connector.proto
index 1488917edfdec7ba51d75e077f50ba82bbc32585..b80690640592cccf383f78abf439d1a86b2ab901 100644
--- a/ros3rag.common/src/main/proto/cgv_connector.proto
+++ b/ros3rag.common/src/main/proto/cgv_connector.proto
@@ -64,7 +64,7 @@ message Selection {
 message MergedSelection {
     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. TODO should be a position
+    string idPlace = 3;  // id of the location the picked object shall be placed.
 }
 
 message ConfigChange {
diff --git a/ros3rag.common/src/main/resources/jastadd/types.connect b/ros3rag.common/src/main/resources/jastadd/types.connect
index ed12a0066808358bff82d26c5a8630e3b35436e1..84730eb21d622f26726f658c247e9d76a860cc11 100644
--- a/ros3rag.common/src/main/resources/jastadd/types.connect
+++ b/ros3rag.common/src/main/resources/jastadd/types.connect
@@ -9,13 +9,13 @@ ConvertScene maps de.tudresden.inf.st.ceti.Scene pbScene to Scene {:
     switch (pbObject.getType()) {
       case BOX:
         // BOX == MovableObject
-        var movableObject = new MovableObject();
+        MovableObject movableObject = new MovableObject();
         result.addMovableObject(movableObject);
         obj = movableObject;
         break;
       case DROP_OFF_LOCATION:
         // DROP_OFF_LOCATION == DropOffLocation
-        var dropOffLocation = new DropOffLocation();
+        DropOffLocation dropOffLocation = new DropOffLocation();
         result.addDropOffLocation(dropOffLocation);
         obj = dropOffLocation;
         break;
diff --git a/ros3rag.common/src/main/resources/jastadd/types.jadd b/ros3rag.common/src/main/resources/jastadd/types.jadd
index b2f7f85f1e0826e1fde6c9865571204108f3be69..55c36d43513dee2b85dc319034fc213b2e1bf7e7 100644
--- a/ros3rag.common/src/main/resources/jastadd/types.jadd
+++ b/ros3rag.common/src/main/resources/jastadd/types.jadd
@@ -97,19 +97,21 @@ aspect Computation {
     var result = new LogicalScene();
     Map<MovableObject, LogicalMovableObject> objects = new HashMap<>();
     for (MovableObject movableObject : getMovableObjectList()) {
-      var newLogicalMovableObject = new LogicalMovableObject();
+      LogicalMovableObject newLogicalMovableObject = new LogicalMovableObject();
       newLogicalMovableObject.setName(movableObject.getName());
       result.addLogicalMovableObject(newLogicalMovableObject);
       objects.put(movableObject, newLogicalMovableObject);
     }
-    for (Region region : getRegionList()) {
-      var logicalRegion = new LogicalRegion();
+    for (Region region : regionList()) {
+      LogicalRegion logicalRegion = new LogicalRegion();
       logicalRegion.setName(region.getName());
       result.addLogicalRegion(logicalRegion);
       for (MovableObject movableObject : getMovableObjectList()) {
         for (DropOffLocation location : region.getLocationList()) {
           if (movableObject.isLocatedAt(location)) {
-            logicalRegion.addContainedObject(objects.get(movableObject));
+            LogicalMovableObject logicalObject = objects.get(movableObject);
+            logicalRegion.addContainedObject(logicalObject);
+            logicalObject.setMyLocation(location);
           }
         }
       }
@@ -167,6 +169,9 @@ aspect Navigation {
   eq Scene.getMovableObject().containingScene() = this;
 
   syn boolean LogicalMovableObject.hasLocatedAt() = !getLocatedAtList().isEmpty();
+
+  // must be "implemented" in concrete world models, i.e., an equation must be defined
+  inh JastAddList<Region> Scene.regionList();
 }
 
 aspect Printing {
@@ -202,9 +207,12 @@ aspect Printing {
   }
 
   syn String DropOffLocation.prettyPrint() {
-    return "{loc " + getName() + getPosition().prettyPrint() + getOrientation().prettyPrint() + getSize().prettyPrint() + "}";
+    return "{" + printPrefix() + " " + getName() + getPosition().prettyPrint() + getOrientation().prettyPrint() + getSize().prettyPrint() + "}";
   }
 
+  syn String DropOffLocation.printPrefix() = "loc";
+  eq CollaborationZone.printPrefix() = "CZ";
+
   syn String Region.prettyPrint() {
     return "{reg " + nameAndHash() + "}";
   }
diff --git a/ros3rag.common/src/main/resources/jastadd/types.relast b/ros3rag.common/src/main/resources/jastadd/types.relast
index 4948abe610da2f9e743d1ad4f7339678a96bf89e..783bf00d21f8ec7a6ef01e25437dee6efc830f93 100644
--- a/ros3rag.common/src/main/resources/jastadd/types.relast
+++ b/ros3rag.common/src/main/resources/jastadd/types.relast
@@ -2,7 +2,8 @@ Position ::= <X:float> <Y:float> <Z:float> ;
 Size ::= <Length:float> <Width:float> <Height:float> ;
 Orientation ::= <X:float> <Y:float> <Z:float> <W:float> ;
 
-Scene ::= Region* DropOffLocation* MovableObject* /LogicalScene/ ;
+// Regions cannot be contained in scene, but must be retrieved via attribute
+Scene ::= DropOffLocation* MovableObject* /LogicalScene/ ;
 
 CanReachObjectOfInterestWrapper ::= CanReachObjectOfInterest* ;
 ObjectOfInterest ::= <Name:String> Position Size Orientation ;
@@ -19,6 +20,9 @@ LogicalRegion : LogicalObjectOfInterest ;
 LogicalMovableObject : LogicalObjectOfInterest ;
 rel LogicalRegion.ContainedObject* <-> LogicalMovableObject.LocatedAt* ;
 
+// TODO could lead to problems when including this information and sending it
+rel LogicalMovableObject.MyLocation? -> DropOffLocation ;
+
 Region ::= <Name:String> ;
 rel Region.Location* <-> DropOffLocation.ContainedInRegion* ;
 
diff --git a/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd b/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd
index 2f7fb2f5cfbc01f852a62d34d1d79e34c9896132..993b37486fb20dc4a15fd3d7104a7829c156c5d5 100644
--- a/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd
+++ b/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd
@@ -3,4 +3,6 @@ aspect Glue {
 
   //--- getLogicalScene ---
   syn LogicalScene WorldModelA.getLogicalScene() = hasScene() ? getScene().getLogicalScene() : new LogicalScene();
+
+  eq WorldModelA.getScene().regionList() = getRegionList();
 }
diff --git a/ros3rag.placeA/src/main/jastadd/WorldModelA.relast b/ros3rag.placeA/src/main/jastadd/WorldModelA.relast
index 1e84df6672b3287fbf244a7c0aede4de741aafdd..2e9b0a603dd4d811a3e3f9e56548f2670d0f790a 100644
--- a/ros3rag.placeA/src/main/jastadd/WorldModelA.relast
+++ b/ros3rag.placeA/src/main/jastadd/WorldModelA.relast
@@ -1 +1 @@
-WorldModelA ::= [Scene] /LogicalScene/ ;
+WorldModelA ::= Region* [Scene] /LogicalScene/ ;
diff --git a/ros3rag.placeA/src/main/resources/mappping-a.json b/ros3rag.placeA/src/main/resources/mappping-a.json
deleted file mode 100644
index 062593023d070db663d263b0ae71e9b2df6f8f20..0000000000000000000000000000000000000000
--- a/ros3rag.placeA/src/main/resources/mappping-a.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "mappings": [
-    { "location": "L1", "positions": ["P1"] },
-    { "location": "L2", "positions": ["P2"] },
-    { "location": "L3", "positions": ["P3"] },
-    { "location": "L4", "positions": ["P4"] },
-    { "location": "L5", "positions": ["P5"] },
-    { "location": "L6", "positions": ["P6"] },
-    { "location": "L-E", "positions": ["P-E"] },
-    { "location": "L-F", "positions": ["P-F"] }
-  ]
-}
diff --git a/ros3rag.placeA/src/main/resources/regions-a.json b/ros3rag.placeA/src/main/resources/regions-a.json
new file mode 100644
index 0000000000000000000000000000000000000000..9c7aaf80748c7cd926f8bc9596f2542b6f185d2e
--- /dev/null
+++ b/ros3rag.placeA/src/main/resources/regions-a.json
@@ -0,0 +1,12 @@
+{
+  "regions": [
+    { "name": "Reg1", "positions": ["P1"] },
+    { "name": "Reg2", "positions": ["P2"] },
+    { "name": "Reg3", "positions": ["P3"] },
+    { "name": "Reg4", "positions": ["P4"] },
+    { "name": "Reg5", "positions": ["P5"] },
+    { "name": "Reg6", "positions": ["P6"] },
+    { "name": "Reg-E", "positions": ["P-E"] },
+    { "name": "Reg-F", "positions": ["P-F"] }
+  ]
+}
diff --git a/ros3rag.placeB/.gitignore b/ros3rag.placeB/.gitignore
index 87b4cdd3d7c6a41502ca98703abeeb69a1d536fb..e6a248d26d9db140d397c0b81d57239b09f09777 100644
--- a/ros3rag.placeB/.gitignore
+++ b/ros3rag.placeB/.gitignore
@@ -3,3 +3,5 @@ src/gen-res/
 src/gen/
 out/
 *.class
+*.png
+*.svg
diff --git a/ros3rag.placeB/build.gradle b/ros3rag.placeB/build.gradle
index 5a2da9d4f49dc04cf2096201b826400e2e32dd21..08db7115996afb8f990a7055d07916b310ac81a8 100644
--- a/ros3rag.placeB/build.gradle
+++ b/ros3rag.placeB/build.gradle
@@ -33,3 +33,15 @@ ext.relastFiles = ["src/gen/jastadd/WorldModelB.relast", "src/main/jastadd/BFS/B
 ext.jastaddAstPackage = 'de.tudresden.inf.st.placeB.ast'
 
 apply from: '../ros3rag.common/src/main/resources/tasks.gradle'
+
+ragConnect {
+    doLast {
+        exec {
+            executable "pwd"
+        }
+        exec {
+            executable "./monkey-patch-ragconnect.sh"
+            args = [ "src/gen/jastadd/RagConnect.jadd" ]
+        }
+    }
+}
diff --git a/ros3rag.placeB/monkey-patch-ragconnect.sh b/ros3rag.placeB/monkey-patch-ragconnect.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ddf330bee5aab31c2b7445027b6c4ee327a88796
--- /dev/null
+++ b/ros3rag.placeB/monkey-patch-ragconnect.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+sed -i 's/protected static/protected/' $1
diff --git a/ros3rag.placeB/src/main/jastadd/RobotReachabilityToBFS.jrag b/ros3rag.placeB/src/main/jastadd/RobotReachabilityToBFS.jrag
index a65379dedd4a92702d9608b573f4b7bbfc86c213..d20b32b2ff383f978ecb9ba68de19f66c181639f 100644
--- a/ros3rag.placeB/src/main/jastadd/RobotReachabilityToBFS.jrag
+++ b/ros3rag.placeB/src/main/jastadd/RobotReachabilityToBFS.jrag
@@ -44,12 +44,14 @@ aspect RobotReachabilityToBFS {
   }
 
   //--- correspondingVertex ---
-  syn Optional<Vertex> LogicalObjectOfInterest.correspondingVertex() {
-    if (!worldModelB().hasMyScene()) {
+  syn Optional<Vertex> LogicalObjectOfInterest.correspondingVertex() = worldModelB().correspondingVertex(this.getName());
+  syn Optional<Vertex> ObjectOfInterest.correspondingVertex() = worldModelB().correspondingVertex(this.getName());
+  syn Optional<Vertex> WorldModelB.correspondingVertex(String name) {
+    if (!hasMyScene()) {
       return Optional.empty();
     }
-    for (Vertex v : worldModelB().toReachabilityGraph().getVertexList()) {
-      if (v.getObjectOfInterest().getName().equals(this.getName())) {
+    for (Vertex v : toReachabilityGraph().getVertexList()) {
+      if (v.getObjectOfInterest().getName().equals(name)) {
         return Optional.of(v);
       }
     }
diff --git a/ros3rag.placeB/src/main/jastadd/WorldModelB.connect b/ros3rag.placeB/src/main/jastadd/WorldModelB.connect
index 7949de6d9688e9d1e046c8794ab0eb038154d39f..09270e201371e33e79de133fbdca1c1afbfad27e 100644
--- a/ros3rag.placeB/src/main/jastadd/WorldModelB.connect
+++ b/ros3rag.placeB/src/main/jastadd/WorldModelB.connect
@@ -3,6 +3,8 @@ receive WorldModelB.MyScene using ParseScene, ConvertScene ;
 receive indexed WorldModelB.OtherScene ;
 receive WorldModelB.TestingOtherScene ;
 receive Robot.CanReachObjectOfInterestWrapper using ParseReachability, ConvertReachability ;
+receive Robot.DummyOwnedCollaborationZone using ConfigChangeCommandCheckForOwnedCollaborationZone ;
+receive Robot.Busy using ConfigChangeCommandCheckForBusy ;
 
 // --- sending ---
 send WorldModelB.NextOperation using PrintOperation ;
@@ -14,3 +16,48 @@ PrintOperation maps Operation op to byte[] {:
   }
   return result;
 :}
+
+ConfigChangeCommandCheckForOwnedCollaborationZone maps byte[] bytes to String {:
+  de.tudresden.inf.st.ceti.ConfigChange cc = null;
+  try {
+    cc = de.tudresden.inf.st.ceti.ConfigChange.parseFrom(bytes);
+  } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+    reject();
+  }
+  System.out.println(cc);
+  Scene scene = worldModelB().getMyScene();
+  ObjectOfInterest obj = scene.resolveObjectOfInterest(cc.getIdCollaborationZone());
+  DropOffLocation loc = obj.asDropOffLocation();
+  CollaborationZone cz = loc.asCollaborationZone();
+  Robot thisRobot = (Robot) this;
+  if (thisRobot.getName().equals(cc.getIdRobotNewOwner()) && !thisRobot.getOwnedCollaborationZoneList().contains(cz)) {
+    // config change is for this robot, add collaboration zone
+    thisRobot.addOwnedCollaborationZone(cz);
+    reject();
+  }
+  if (!thisRobot.getName().equals(cc.getIdRobotNewOwner()) && thisRobot.getOwnedCollaborationZoneList().contains(cz)) {
+    // config change is for another robot, remove collaboration zone
+    thisRobot.removeOwnedCollaborationZone(cz);
+    reject();
+  }
+  // need return to make compiler happy
+  return "";
+:}
+
+ConfigChangeCommandCheckForBusy maps byte[] bytes to boolean {:
+  de.tudresden.inf.st.ceti.MergedSelection ms = null;
+  try {
+    ms = de.tudresden.inf.st.ceti.MergedSelection.parseFrom(bytes);
+  } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+    reject();
+  }
+  System.out.println(ms);
+  Robot thisRobot = (Robot) this;
+  if (thisRobot.getName().equals(ms.getIdRobot())) {
+    return true;
+  }
+  // if the command is not about this robot, do not change anything
+  reject();
+  // need return to make compiler happy
+  return false;
+:}
diff --git a/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd b/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd
index c302e0c922adf29bb0fe5ca2bef5c292fd53d438..a64332153b7acb7fc81a3429b3b2d687a11b7cda 100644
--- a/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd
+++ b/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd
@@ -128,41 +128,53 @@ aspect Computation {
   //--- computeOperations ---
   syn List<Operation> Difference.computeOperations();
   eq DifferenceObjectAtWrongPlace.computeOperations() {
-    return (hasPreviousRegion() ? getPreviousRegion() : getObject()).correspondingVertex().map(previousVertex -> {
-      return getNewRegion().correspondingVertex().map(newVertex -> {
-        List<Edge> shortestPath = previousVertex.BFS(newVertex);
-        if (shortestPath == null || shortestPath.isEmpty()) {
-          return error("No sequence of operations to move " + getObject().getName() + (hasPreviousRegion() ? " from " + getPreviousRegion().getName() : "") + " to " + getNewRegion().getName());
-        }
-        // TODO insert config-change and evacuate commands
-        List<Operation> result = new ArrayList<>();
-        Vertex transit = previousVertex;
-        for (Edge edge : shortestPath) {
-          Vertex target = edge.getFrom().equals(transit) ? edge.getTo() : edge.getFrom();
-          DropOffLocation targetLocation = target.getObjectOfInterest().asDropOffLocation();
-          Robot executingRobot = edge.getRobot();
-          if (targetLocation.isCollaborationZone()) {
-            /* here we need to check, if the collaboration zone is
-               (a) claimed by the executing robot, and
-               (b) occupied by another robot, and
-               (c) free (as no movable object is contained) -- this should be reflected in the graph!
-            */
+    // need to use position of object and pick one from new region
+    DropOffLocation previousLocation = getObject().getMyLocation();
+    return previousLocation.correspondingVertex().map(previousVertex -> {
+      Region region = getNewRegion().realRegion();
+      if (region == null) {
+        return error("No region found for " + getNewRegion().nameAndHash());
+      }
+      // pick location from newRegion, that is free
+      return region.getLocationList().stream()
+          .filter(location -> location.getObjectLocatedHere() == null)
+          .findFirst()
+          .map(newLocation -> {
+            return newLocation.correspondingVertex().map(newVertex -> {
+              List<Edge> shortestPath = previousVertex.BFS(newVertex);
+              if (shortestPath == null || shortestPath.isEmpty()) {
+                return error("No sequence of operations to move " + getObject().getName() + (hasPreviousRegion() ? " from " + getPreviousRegion().getName() : "") + " to " + getNewRegion().getName());
+              }
+              // TODO insert config-change and evacuate commands
+              List<Operation> result = new ArrayList<>();
+              Vertex transit = previousVertex;
+              for (Edge edge : shortestPath) {
+                Vertex target = edge.getFrom().equals(transit) ? edge.getTo() : edge.getFrom();
+                DropOffLocation targetLocation = target.getObjectOfInterest().asDropOffLocation();
+                Robot executingRobot = edge.getRobot();
+                if (targetLocation.isCollaborationZone()) {
+              /* here we need to check, if the collaboration zone is
+                 (a) claimed by the executing robot, and
+                 (b) occupied by another robot, and
+                 (c) free (as no movable object is contained) -- this should be reflected in the graph!
+              */
 
-            // order is important here, first add ConfigChange, then Evacuate
-            CollaborationZone cz = targetLocation.asCollaborationZone();
-            if (cz.hasOwner() && !cz.getOwner().equals(executingRobot)) {
-              result.add(ConfigChange.of(executingRobot, cz));
-            }
-            if (cz.hasOccupient() && !cz.getOccupient().equals(executingRobot)) {
-              result.add(Evacuate.of(cz.getOccupient()));
-            }
-          }
-          result.add(PickAndPlace.of(executingRobot, getObject(), targetLocation));
-          transit = target;
-        }
-        return result;
-      }).orElseGet(() -> error("Could not resolve graph vertex of new region " + getNewRegion().getName()));
-    }).orElseGet(() -> error("Could not resolve graph vertex of previous region " + getPreviousRegion().getName()));
+                  // order is important here, first add ConfigChange, then Evacuate
+                  CollaborationZone cz = targetLocation.asCollaborationZone();
+                  if (!cz.hasOwner() || (cz.hasOwner() && !cz.getOwner().equals(executingRobot))) {
+                    result.add(ConfigChange.of(executingRobot, cz));
+                  }
+                  if (cz.hasOccupient() && !cz.getOccupient().equals(executingRobot)) {
+                    result.add(Evacuate.of(cz.getOccupient()));
+                  }
+                }
+                result.add(PickAndPlace.of(executingRobot, getObject(), targetLocation));
+                transit = target;
+              }
+              return result;
+            }).orElseGet(() -> error("Could not resolve graph vertex of new region " + getNewRegion().nameAndHash()));
+          }).orElseGet(() -> error("Could not find a free location in " + getNewRegion().nameAndHash()));
+    }).orElseGet(() -> error("Could not resolve graph vertex of previous location " + previousLocation.nameAndHash()));
   }
   eq DifferenceNewObject.computeOperations() {
     // FIXME. stub, may be implemented later
@@ -215,7 +227,7 @@ aspect Computation {
 
   //--- canReach ---
   syn boolean Robot.canReach(String objectName) {
-    for (var canReachObj : getCanReachObjectOfInterestWrapper().getCanReachObjectOfInterestList()) {
+    for (CanReachObjectOfInterest canReachObj : getCanReachObjectOfInterestWrapper().getCanReachObjectOfInterestList()) {
       if (canReachObj.getObjectName().equals(objectName)) {
         return true;
       }
@@ -229,7 +241,7 @@ aspect Computation {
       return Collections.emptyList();
     }
     List<ObjectOfInterest> result = new ArrayList<>();
-    for (var canReachObj : getCanReachObjectOfInterestWrapper().getCanReachObjectOfInterestList()) {
+    for (CanReachObjectOfInterest canReachObj : getCanReachObjectOfInterestWrapper().getCanReachObjectOfInterestList()) {
       result.add(worldModelB().getMyScene().resolveObjectOfInterest(canReachObj.getObjectName()));
     }
     return result;
@@ -311,6 +323,8 @@ aspect Navigation {
   syn boolean Operation.isErrorOperation() = false;
   eq ErrorOperation.isErrorOperation() = true;
 
+  syn Region LogicalRegion.realRegion() = worldModelB().findRegion(getName());
+
   //--- allMappings ---
 //  inh LocationMapping LogicalDropOffLocation.allMappings();
 //  eq WorldModelB.getChild().allMappings() = getLocationMapping();
@@ -323,6 +337,8 @@ aspect GlueForShared {
   eq WorldModelB.diffScenes().resolveLogicalObjectOfInterest(String name) = getMyScene().getLogicalScene().resolveLogicalObjectOfInterest(name);
 
   class WorldModelB implements de.tudresden.inf.st.ros3rag.common.SharedMainParts.WorldModelWrapper {}
+
+  eq WorldModelB.getMyScene().regionList() = getRegionList();
 }
 
 aspect Printing {
@@ -358,6 +374,20 @@ aspect Printing {
   }
 }
 
+aspect DumpAst {
+  public void WorldModelB.dumpAst(java.util.function.Consumer<de.tudresden.inf.st.jastadd.dumpAst.ast.DumpBuilder> options) {
+    String now = java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME).replaceAll("[:\\.]", "-");
+    try {
+      de.tudresden.inf.st.jastadd.dumpAst.ast.DumpBuilder builder = de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper.read(this);
+      options.accept(builder);
+      //builder.dumpAsPNG(java.nio.file.Paths.get(now + ".png"));
+      builder.dumpAsSVG(java.nio.file.Paths.get(now + ".svg"));
+    } catch (java.io.IOException e) {
+      e.printStackTrace();
+    }
+  }
+}
+
 aspect Resolving {
   //--- findRobot ---
   public Optional<Robot> WorldModelB.findRobot(String name) {
@@ -368,4 +398,17 @@ aspect Resolving {
     }
     return Optional.empty();
   }
+
+  // TODO use Optional here, or not?
+  public Region WorldModelB.findRegion(String name) {
+    for (Region region : getRegionList()) {
+      if (region.getName().equals(name)) {
+        return region;
+      }
+    }
+    return null;
+  }
+
+  // do not resolve real regions and real locations (send by site-A)
+  refine RefResolverStubs eq LogicalMovableObject.resolveMyLocationByToken(String name) = null;
 }
diff --git a/ros3rag.placeB/src/main/jastadd/WorldModelB.relast b/ros3rag.placeB/src/main/jastadd/WorldModelB.relast
index 7e84df331c5f8f9faf56ea1cded039ed1664fa15..7e45d74c5cb4ff0850e04c26d9caf46caf22bbac 100644
--- a/ros3rag.placeB/src/main/jastadd/WorldModelB.relast
+++ b/ros3rag.placeB/src/main/jastadd/WorldModelB.relast
@@ -1,6 +1,6 @@
-WorldModelB ::= Robot* [MyScene:Scene] OtherScene:LogicalScene* /NextOperation:Operation/ TestingOtherScene:LogicalScene* ;
+WorldModelB ::= Region* Robot* [MyScene:Scene] OtherScene:LogicalScene* /NextOperation:Operation/ TestingOtherScene:LogicalScene* ;
 
-Robot ::= <Name:String> CanReachObjectOfInterestWrapper <Busy:boolean> ;
+Robot ::= <Name:String> CanReachObjectOfInterestWrapper <Busy:boolean> <DummyOwnedCollaborationZone> ;
 rel Robot.OwnedCollaborationZone* <-> CollaborationZone.Owner? ;
 rel Robot.OccupiedCollaborationZone? <-> CollaborationZone.Occupient? ;
 
@@ -28,7 +28,6 @@ rel PickAndPlace.ObjectToPick -> LogicalMovableObject ;
 rel PickAndPlace.TargetLocation -> DropOffLocation ;
 
 ConfigChange : Operation ;
-rel ConfigChange.CollaborationZone -> DropOffLocation ;
-// TODO maybe should be: rel ConfigChange.CollaborationZone -> CollaborationZone ;
+rel ConfigChange.CollaborationZone -> CollaborationZone ;
 
 Evacuate : Operation ;
diff --git a/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/SimpleMainB.java b/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/SimpleMainB.java
index 64ab76d7ed3e3d0366378659d5c6788948d140b3..c9e5e34d3e3b5b677a258e94cb232bef6b43a616 100644
--- a/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/SimpleMainB.java
+++ b/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/SimpleMainB.java
@@ -8,20 +8,17 @@ import de.tudresden.inf.st.ros3rag.common.Util;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Scanner;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
-import static de.tudresden.inf.st.ros3rag.common.Util.mqttUri;
-
 /**
  * Testing features for placeB.
  *
@@ -29,6 +26,7 @@ import static de.tudresden.inf.st.ros3rag.common.Util.mqttUri;
  */
 public class SimpleMainB {
   private static final Logger logger = LogManager.getLogger(SimpleMainB.class);
+  boolean exitAutomatically = true;
 
   public static void main(String[] args) throws Exception {
     System.out.println("Running SimpleMainB");
@@ -48,6 +46,7 @@ public class SimpleMainB {
   }
 
   private void readModelAndReceiveFromA() throws Exception {
+
     Configuration config = new Configuration();
     config.mqttHost = "localhost";
     ReachabilityConfig reachabilityR1 = new ReachabilityConfig();
@@ -76,8 +75,26 @@ public class SimpleMainB {
     Path initialSceneFile = UtilB.pathToDirectoryOfPlaceB().resolve("src/main/resources/config-scene-b-2022.json");
     de.tudresden.inf.st.ceti.Scene scene = Util.readScene(initialSceneFile);
     Scene myScene = UtilB.convert(scene);
+    // change locations P-E and P-F to collaboration zones
+    for (String collaborationZoneName : new String[]{ "P-E", "P-F" }) {
+      DropOffLocation loc = myScene.resolveObjectOfInterest(collaborationZoneName).asDropOffLocation();
+      CollaborationZone cz = new CollaborationZone();
+      cz.setName(collaborationZoneName);
+      cz.setPosition(loc.getPosition());
+      cz.setOrientation(loc.getOrientation());
+      cz.setSize(loc.getSize());
+      int index = myScene.getDropOffLocationList().getIndexOfChild(loc);
+      if (index == -1) {
+        throw new RuntimeException("Could not find location " + collaborationZoneName + " in list");
+      }
+      myScene.setDropOffLocation(cz, index);
+    }
     model.setMyScene(myScene);
 
+    // read and set regions
+    File regionBFile = UtilB.pathToDirectoryOfPlaceB().resolve("src/main/resources/regions-b.json").toFile();
+    UtilB.setRegions(model, Util.parseRegionConfig(regionBFile));
+
     // init robots (copied from MainB)
     for (String name : Util.extractRobotNames(scene)) {
       Robot robot = UtilB.createRobot(name);
@@ -104,6 +121,10 @@ public class SimpleMainB {
     Runtime.getRuntime().addShutdownHook(new Thread(close));
 
     model.connectOtherScene(Util.mqttUri(topicUpdateFromPlaceA, config), 0);
+    for (Robot robot : model.getRobotList()) {
+      robot.connectBusy(Util.mqttUri(topicCommand, config));
+      robot.connectDummyOwnedCollaborationZone(Util.mqttUri(topicCommand, config));
+    }
     model.connectNextOperation(Util.mqttUri(topicCommand, config), true);
 
     // prepare exit condition
@@ -114,6 +135,13 @@ public class SimpleMainB {
     // prepare model status
     mqttHandler.newConnection(topicModel, bytes -> {
       String message = new String(bytes);
+      model.dumpAst(builder -> {
+        builder.excludeChildren("Orientation", "Size");
+        builder.excludeRelations("ContainedInRegion");
+        builder.includeNonterminalAttributes("LogicalScene", "diffScenes", "diffToOperations");
+        builder.includeAttributes("realRegion", "computeOperations");
+        builder.includeNullNodes();
+      });
       String content = UtilB.getModelInfos(model, message.equals("Start") || message.startsWith("detail"));
       logger.info("WorldModel\n{}", content);
       mqttHandler.publish(topicModelStatus, content.getBytes(StandardCharsets.UTF_8));
@@ -121,8 +149,13 @@ public class SimpleMainB {
 
     // read scene from A, serialize it and send it via mqtt
     Path sceneAFile = Util.pathToModuleDirectory("ros3rag.placeA").resolve("src/main/resources/config-scene-a-2022.json");
+    File regionAFile = Util.pathToModuleDirectory("ros3rag.placeA").resolve("src/main/resources/regions-a.json").toFile();
     de.tudresden.inf.st.ceti.Scene scenePlaceA = Util.readScene(sceneAFile);
     Scene sceneFromPlaceA = UtilB.convert(scenePlaceA);
+    // need to "wrap" the scene in a world model to access regions. will use one from site-B here for convenience
+    WorldModelB tempWorldModel = new WorldModelB();
+    tempWorldModel.setMyScene(sceneFromPlaceA);
+    UtilB.setRegions(tempWorldModel, Util.parseRegionConfig(regionAFile));
     LogicalScene logicalSceneFromPlaceA = sceneFromPlaceA.getLogicalScene();
     byte[] bytesToSend = _ragconnect__apply__TreeDefaultLogicalSceneToBytesMapping(logicalSceneFromPlaceA);
 
@@ -134,6 +167,11 @@ public class SimpleMainB {
     TimeUnit.SECONDS.sleep(2);
     mqttHandler.publish(topicModel, "detailed".getBytes(StandardCharsets.UTF_8));
 
+    if (exitAutomatically) {
+      TimeUnit.SECONDS.sleep(3);
+      exitCondition.countDown();
+    }
+
     exitCondition.await();
   }
 
diff --git a/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/UtilB.java b/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/UtilB.java
index 773c0018762e9b96761062ccb0570601883d237e..8de49d58742ead0b9a46d4668f45093959b3067a 100644
--- a/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/UtilB.java
+++ b/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/UtilB.java
@@ -3,6 +3,8 @@ package de.tudresden.inf.st.placeB;
 import com.google.protobuf.util.JsonFormat;
 import de.tudresden.inf.st.ceti.Reachability;
 import de.tudresden.inf.st.placeB.ast.*;
+import de.tudresden.inf.st.ros3rag.common.RegionConfiguration;
+import de.tudresden.inf.st.ros3rag.common.RegionConfiguration.RegionDefinition;
 import de.tudresden.inf.st.ros3rag.common.Util;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -136,13 +138,26 @@ public class UtilB {
     return new ExposingASTNode().exposed_apply_ConvertReachability(r);
   }
 
+  static void setRegions(WorldModelB model, RegionConfiguration config) {
+    JastAddList<Region> result = new JastAddList<>();
+    for (RegionDefinition def : config.regions) {
+      Region region = new Region();
+      region.setName(def.name);
+      for (String position : def.positions) {
+        region.addLocation(model.getMyScene().resolveObjectOfInterest(position).asDropOffLocation());
+      }
+      result.add(region);
+    }
+    model.setRegionList(result);
+  }
+
   @SuppressWarnings("rawtypes")
   static class ExposingASTNode extends ASTNode {
     public Scene exposed_apply_ConvertScene(de.tudresden.inf.st.ceti.Scene pbScene) throws Exception {
-      return ASTNode._ragconnect__apply_ConvertScene(pbScene);
+      return _ragconnect__apply_ConvertScene(pbScene);
     }
     public CanReachObjectOfInterestWrapper exposed_apply_ConvertReachability(de.tudresden.inf.st.ceti.Reachability r) throws Exception {
-      return ASTNode._ragconnect__apply_ConvertReachability(r);
+      return _ragconnect__apply_ConvertReachability(r);
     }
   }
 
diff --git a/ros3rag.placeB/src/main/resources/mappping-b.json b/ros3rag.placeB/src/main/resources/mappping-b.json
deleted file mode 100644
index 062593023d070db663d263b0ae71e9b2df6f8f20..0000000000000000000000000000000000000000
--- a/ros3rag.placeB/src/main/resources/mappping-b.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "mappings": [
-    { "location": "L1", "positions": ["P1"] },
-    { "location": "L2", "positions": ["P2"] },
-    { "location": "L3", "positions": ["P3"] },
-    { "location": "L4", "positions": ["P4"] },
-    { "location": "L5", "positions": ["P5"] },
-    { "location": "L6", "positions": ["P6"] },
-    { "location": "L-E", "positions": ["P-E"] },
-    { "location": "L-F", "positions": ["P-F"] }
-  ]
-}
diff --git a/ros3rag.placeB/src/main/resources/regions-b.json b/ros3rag.placeB/src/main/resources/regions-b.json
new file mode 100644
index 0000000000000000000000000000000000000000..9c7aaf80748c7cd926f8bc9596f2542b6f185d2e
--- /dev/null
+++ b/ros3rag.placeB/src/main/resources/regions-b.json
@@ -0,0 +1,12 @@
+{
+  "regions": [
+    { "name": "Reg1", "positions": ["P1"] },
+    { "name": "Reg2", "positions": ["P2"] },
+    { "name": "Reg3", "positions": ["P3"] },
+    { "name": "Reg4", "positions": ["P4"] },
+    { "name": "Reg5", "positions": ["P5"] },
+    { "name": "Reg6", "positions": ["P6"] },
+    { "name": "Reg-E", "positions": ["P-E"] },
+    { "name": "Reg-F", "positions": ["P-F"] }
+  ]
+}