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"] } + ] +}