From b5bb1c020168ac914bc9e665799f9a9f016f51a9 Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Wed, 27 Apr 2022 20:21:23 +0200
Subject: [PATCH] more work to get relations converted to terminals

---
 .../src/main/resources/jastadd/types.jadd     | 22 ++++++++-
 .../src/main/resources/jastadd/types.relast   |  4 +-
 .../src/main/jastadd/WorldModelB.connect      | 20 +++++----
 .../src/main/jastadd/WorldModelB.jadd         | 45 ++++++++++++++++---
 .../src/main/jastadd/WorldModelB.relast       |  8 ++--
 .../tudresden/inf/st/placeB/SimpleMainB.java  |  2 +-
 .../de/tudresden/inf/st/placeB/UtilB.java     |  5 +--
 7 files changed, 81 insertions(+), 25 deletions(-)

diff --git a/ros3rag.common/src/main/resources/jastadd/types.jadd b/ros3rag.common/src/main/resources/jastadd/types.jadd
index f31b6ab..a0290bf 100644
--- a/ros3rag.common/src/main/resources/jastadd/types.jadd
+++ b/ros3rag.common/src/main/resources/jastadd/types.jadd
@@ -103,7 +103,7 @@ aspect Computation {
       logicalRegion.setName(region.getName());
       result.addLogicalRegion(logicalRegion);
       for (MovableObject movableObject : getMovableObjectList()) {
-        for (DropOffLocation location : region.getLocationList()) {
+        for (DropOffLocation location : region.locationList()) {
           if (movableObject.isLocatedAt(location)) {
             LogicalMovableObject logicalObject = objects.get(movableObject);
             logicalRegion.addContainedObject(logicalObject);
@@ -168,6 +168,26 @@ aspect Navigation {
 
   // must be "implemented" in concrete world models, i.e., an equation must be defined
   inh JastAddList<Region> Scene.regionList();
+
+  syn List<Region> DropOffLocation.containedInRegion() {
+    List<Region> result = new ArrayList<>();
+    for (Region region : containingScene().regionList()) {
+      List<String> locationNames = Arrays.asList(region.getLocationNames().split(","));
+      if (locationNames.contains(getName())) {
+        result.add(region);
+      }
+    }
+    return result;
+  }
+
+  protected static List<String> ASTNode.arrayAsList(String array) {
+    if (array == null || array.length() == 0) {
+      return new ArrayList<>();
+    }
+    return new ArrayList<>(Arrays.asList(array.split(",")));
+  }
+  syn List<String> Region.locationNamesAsList() = arrayAsList(getLocationNames()) ;
+  syn List<DropOffLocation> Region.locationList();
 }
 
 aspect Printing {
diff --git a/ros3rag.common/src/main/resources/jastadd/types.relast b/ros3rag.common/src/main/resources/jastadd/types.relast
index 0be3487..c31c1c3 100644
--- a/ros3rag.common/src/main/resources/jastadd/types.relast
+++ b/ros3rag.common/src/main/resources/jastadd/types.relast
@@ -23,8 +23,8 @@ 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* ;
+Region ::= <Name:String> <LocationNames> ;
+//rel Region.Location* <-> DropOffLocation.ContainedInRegion* ;
 
 // TODO should this be only in site-B??
 CollaborationZone : DropOffLocation ;
diff --git a/ros3rag.placeB/src/main/jastadd/WorldModelB.connect b/ros3rag.placeB/src/main/jastadd/WorldModelB.connect
index 6529e04..379efaa 100644
--- a/ros3rag.placeB/src/main/jastadd/WorldModelB.connect
+++ b/ros3rag.placeB/src/main/jastadd/WorldModelB.connect
@@ -3,7 +3,7 @@ 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.OwnedCollaborationZoneNames using ConfigChangeCommandCheckForOwnedCollaborationZone ;
 receive Robot.Busy using ConfigChangeCommandCheckForBusy ;
 
 // --- sending ---
@@ -35,18 +35,20 @@ ConfigChangeCommandCheckForOwnedCollaborationZone maps byte[] bytes to String {:
   DropOffLocation loc = obj.asDropOffLocation();
   CollaborationZone cz = loc.asCollaborationZone();
   Robot thisRobot = (Robot) this;
-  if (thisRobot.getName().equals(cc.getIdRobotNewOwner()) && !thisRobot.getOwnedCollaborationZoneList().contains(cz)) {
+  List<String> collaborationZoneNames = thisRobot.ownedCollaborationZonesAsList();
+  boolean robotIsNewOwner = thisRobot.getName().equals(cc.getIdRobotNewOwner());
+  boolean collaborationZoneAlreadyOwned = collaborationZoneNames.contains(cc.getIdCollaborationZone());
+  if (robotIsNewOwner && !collaborationZoneAlreadyOwned) {
     // config change is for this robot, add collaboration zone
-    thisRobot.addOwnedCollaborationZone(cz);
-    reject();
-  }
-  if (!thisRobot.getName().equals(cc.getIdRobotNewOwner()) && thisRobot.getOwnedCollaborationZoneList().contains(cz)) {
+    collaborationZoneNames.add(cc.getIdCollaborationZone());
+  } else if (!robotIsNewOwner && collaborationZoneAlreadyOwned) {
     // config change is for another robot, remove collaboration zone
-    thisRobot.removeOwnedCollaborationZone(cz);
+    collaborationZoneNames.remove(cc.getIdCollaborationZone());
+  } else {
+    // otherwise don't change
     reject();
   }
-  // need return to make compiler happy
-  return "";
+  return String.join(",", collaborationZoneNames);
 :}
 
 ConfigChangeCommandCheckForBusy maps byte[] bytes to boolean {:
diff --git a/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd b/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd
index 6a66bfc..c96e250 100644
--- a/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd
+++ b/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd
@@ -138,7 +138,7 @@ aspect Computation {
     try {
       previousLocation.correspondingVertex();
     } catch (NullPointerException e) {
-      return error("Could not resolve WorldModelB");
+      return error("Could not resolve WorldModelB linked to previousLocation " + previousLocation.nameAndHash());
     }
     return previousLocation.correspondingVertex().map(previousVertex -> {
       Region region = getNewRegion().realRegion();
@@ -146,10 +146,16 @@ aspect Computation {
         return error("No region found for " + getNewRegion().nameAndHash());
       }
       // pick location from newRegion, that is free
-      return region.getLocationList().stream()
+      return region.locationList().stream()
           .filter(location -> location.getObjectLocatedHere() == null)
           .findFirst()
           .map(newLocation -> {
+            // (workaround) check if WorldModelB is not null ... could happen if scene has no parent
+            try {
+              newLocation.correspondingVertex();
+            } catch (NullPointerException e) {
+              return error("Could not resolve WorldModelB linked to newLocation " + newLocation.nameAndHash());
+            }
             return newLocation.correspondingVertex().map(newVertex -> {
               List<Edge> shortestPath = previousVertex.BFS(newVertex);
               if (shortestPath == null || shortestPath.isEmpty()) {
@@ -170,10 +176,10 @@ aspect Computation {
                     */
                     CollaborationZone cz = targetLocation.asCollaborationZone();
                     // order is important here, first add Evacuate, then ConfigChange
-                    if (cz.hasOccupient() && !cz.getOccupient().equals(executingRobot)) {
-                      result.add(Evacuate.of(cz.getOccupient(), cz));
+                    if (cz.hasOccupient() && !cz.occupient().equals(executingRobot)) {
+                      result.add(Evacuate.of(cz.occupient(), cz));
                     }
-                    if (!cz.hasOwner() || (cz.hasOwner() && !cz.getOwner().equals(executingRobot))) {
+                    if (!cz.hasOwner() || (cz.hasOwner() && !cz.owner().equals(executingRobot))) {
                       result.add(ConfigChange.of(executingRobot, cz));
                     }
                   }
@@ -259,7 +265,7 @@ aspect Computation {
   }
 
   syn DropOffLocation Region.firstFreeDropOffLocation() {
-    for (DropOffLocation location : getLocationList()) {
+    for (DropOffLocation location : locationList()) {
       if (location.getObjectLocatedHere() == null) {
         return location;
       }
@@ -346,6 +352,33 @@ aspect Navigation {
   //--- allMappings ---
 //  inh LocationMapping LogicalDropOffLocation.allMappings();
 //  eq WorldModelB.getChild().allMappings() = getLocationMapping();
+  syn boolean CollaborationZone.hasOccupient() = occupient() != null;
+  syn Robot CollaborationZone.occupient() {
+    for (Robot robot : worldModelB().getRobotList()) {
+      if (getName().equals(robot.getOccupiedCollaborationZoneName())) {
+        return robot;
+      }
+    }
+    return null;
+  }
+  syn boolean CollaborationZone.hasOwner() = owner() != null;
+  syn Robot CollaborationZone.owner() {
+    for (Robot robot : worldModelB().getRobotList()) {
+      List<String> collaborationZoneNames = Arrays.asList(robot.getOwnedCollaborationZoneNames().split(","));
+      if (collaborationZoneNames.contains(getName())) {
+        return robot;
+      }
+    }
+    return null;
+  }
+  syn List<String> Robot.ownedCollaborationZonesAsList() = arrayAsList(getOwnedCollaborationZoneNames());
+  eq Region.locationList() {
+    List<DropOffLocation> result = new ArrayList<>();
+    for (String locationName : locationNamesAsList()) {
+      result.add(worldModelB().getMyScene().resolveObjectOfInterest(locationName).asDropOffLocation());
+    }
+    return result;
+  }
 }
 
 aspect GlueForShared {
diff --git a/ros3rag.placeB/src/main/jastadd/WorldModelB.relast b/ros3rag.placeB/src/main/jastadd/WorldModelB.relast
index 919d372..856d1b0 100644
--- a/ros3rag.placeB/src/main/jastadd/WorldModelB.relast
+++ b/ros3rag.placeB/src/main/jastadd/WorldModelB.relast
@@ -1,8 +1,10 @@
 WorldModelB ::= Region* Robot* [MyScene:Scene] OtherScene:LogicalScene* /NextOperation:Operation/ TestingOtherScene:LogicalScene* ;
 
-Robot ::= <Name:String> CanReachObjectOfInterestWrapper <Busy:boolean> <DummyOwnedCollaborationZone> ;
-rel Robot.OwnedCollaborationZone* <-> CollaborationZone.Owner? ;
-rel Robot.OccupiedCollaborationZone? <-> CollaborationZone.Occupient? ;
+// FIXME inline CanReachObjectOfInterestWrapper
+Robot ::= <Name:String> CanReachObjectOfInterestWrapper <Busy:boolean> <OwnedCollaborationZoneNames> <OccupiedCollaborationZoneName> ;
+// relations into nodes received by RagConnect are not allowed
+//rel Robot.OwnedCollaborationZone* <-> CollaborationZone.Owner? ;
+//rel Robot.OccupiedCollaborationZone? <-> CollaborationZone.Occupient? ;
 
 abstract Difference ;
 rel Difference.Object -> LogicalMovableObject ;
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 c65771d..5895d96 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
@@ -113,7 +113,7 @@ public class SimpleMainB {
     model.connectMyScene(Util.mqttUri(topicSceneUpdateB, config));
     for (Robot robot : model.getRobotList()) {
       robot.connectBusy(Util.mqttUri(topicCommand, config));
-      robot.connectDummyOwnedCollaborationZone(Util.mqttUri(topicCommand, config));
+      robot.connectOwnedCollaborationZoneNames(Util.mqttUri(topicCommand, config));
     }
     model.connectNextOperation(Util.mqttUri(topicCommand, config), true);
 
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 8de49d5..622b717 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
@@ -12,6 +12,7 @@ import org.apache.logging.log4j.Logger;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.stream.Collectors;
 
 /**
  * Static utility methods used only for place B.
@@ -143,9 +144,7 @@ public class UtilB {
     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());
-      }
+      region.setLocationNames(String.join(",", def.positions));
       result.add(region);
     }
     model.setRegionList(result);
-- 
GitLab