diff --git a/build.gradle b/build.gradle index 1d0c641eb4ab30b6544c6edbeb1890438d8cbf08..6bcf15e536e07fb19f008c40a35f2cb14b9895b8 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,7 @@ dependencies { implementation group: 'org.json', name: 'json', version: '20220320' 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: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', 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" diff --git a/src/main/jastadd/cleanup/Serialization.jadd b/src/main/jastadd/cleanup/Serialization.jadd new file mode 100644 index 0000000000000000000000000000000000000000..cfb8dc4629404e38d8e4638255053737d823938d --- /dev/null +++ b/src/main/jastadd/cleanup/Serialization.jadd @@ -0,0 +1,19 @@ +aspect Serialization { + + // @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) + @com.fasterxml.jackson.annotation.JsonIgnoreProperties({"type", "pos", "size", "orientation"}) + interface PhysicalObjectInterface {} + PhysicalObject implements PhysicalObjectInterface; + + refine + @com.fasterxml.jackson.annotation.JsonGetter("objects") + public JastAddList<PhysicalObject> Table.getPhysicalObjectList() { return refined(); } + + refine + @com.fasterxml.jackson.annotation.JsonGetter("id") + public String PhysicalObject.getName() { return refined(); } + + refine + @com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = de.tudresden.inf.st.mg.common.ColorDeserializer.class) + public String PhysicalObject.getColor() { return refined(); } +} diff --git a/src/main/jastadd/common/Tracing.jadd b/src/main/jastadd/common/Tracing.jadd index cce799e68e276a4a8fe2a0f922355f6cca0bd95c..bc807152f65e6c8e3077ce840a81a6827bbd60fc 100644 --- a/src/main/jastadd/common/Tracing.jadd +++ b/src/main/jastadd/common/Tracing.jadd @@ -1,15 +1,33 @@ aspect Tracing { + private static boolean World.printContext = false; + + public static void World.enableContextPrinting(boolean printContext) { + World.printContext = printContext; + } + + public void World.printContext(String step) { + World.printContextOf(step, this, de.tudresden.inf.st.mg.common.MotionGrammarConfig.DEFAULT_COLOR); + } + synchronized public static void World.printContextOf(String step, ASTNode<?> highlightNode, String highlightColor) { + if (!printContext) { + return; + } + final String DEFAULT_COLOR = de.tudresden.inf.st.mg.common.MotionGrammarConfig.DEFAULT_COLOR; World world; - try { - world = highlightNode.containingWorld(); - } catch (NullPointerException e) { - // the element is not in a full AST (yet?), so we don't print it - return; + if (highlightNode instanceof World) { + world = (World) highlightNode; + } else { + try { + world = highlightNode.containingWorld(); + } catch (NullPointerException e) { + // the element is not in a full AST (yet?), so we don't print it + return; + } } String worldName = world.getClass().getSimpleName(); @@ -21,7 +39,6 @@ aspect Tracing { .includeAttributeWhen((node, attributeName, isNTA, value) -> isNTA) .<ASTNode>includeChildWhen((parentNode, childNode, contextName) -> !((contextName.equals("Pose") || contextName.equals("Robot")) && childNode.inDemonstrationTable())) .<ASTNode>includeRelationsWhen((sourceNode, targetNode, roleName) -> !roleName.equals("AttachedRobot") && !(sourceNode != null && sourceNode.inDemonstrationTable()) && !(targetNode != null && targetNode.inDemonstrationTable())) // !sourceNode.inDemonstrationTable() -// .excludeRelations("AttachedRobot") .dumpAsSVG(de.tudresden.inf.st.mg.common.MotionGrammarConfig.astDiagramDir.resolve("Context-" + worldName + "-" + new java.text.SimpleDateFormat("yyyy.MM.dd.HH.mm.ss.SSS").format(new java.util.Date()) + "-" + step + ".svg")); } catch (java.io.IOException e) { e.printStackTrace(); diff --git a/src/main/java/de/tudresden/inf/st/mg/Main.java b/src/main/java/de/tudresden/inf/st/mg/Main.java index 872da41b3fdb95c189ec5f5b4010f7cc97681f0b..9dcea6127799f2aab18d3a6c853777d222cca781 100644 --- a/src/main/java/de/tudresden/inf/st/mg/Main.java +++ b/src/main/java/de/tudresden/inf/st/mg/Main.java @@ -1,12 +1,23 @@ package de.tudresden.inf.st.mg; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.protobuf.util.JsonFormat; +import de.tudresden.inf.st.ceti.Scene; import de.tudresden.inf.st.mg.common.MotionGrammarConfig; import de.tudresden.inf.st.mg.common.MotionGrammarParser; +import de.tudresden.inf.st.mg.common.TableDeserializer; import de.tudresden.inf.st.mg.jastadd.model.RobotWorld; +import de.tudresden.inf.st.mg.jastadd.model.Table; +import de.tudresden.inf.st.mg.jastadd.model.World; +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; public class Main { @@ -19,9 +30,28 @@ public class Main { public static void main(String[] args) { MotionGrammarConfig.astDiagramDir = TIDY_AST_DIAGRAM_DIR; + try { + Files.walk(TIDY_AST_DIAGRAM_DIR).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + Files.createDirectories(TIDY_AST_DIAGRAM_DIR); + } catch (IOException e) { + // do nothing + } RobotWorld world = RobotWorld.initialWorld(); + try { + SimpleModule module = new SimpleModule(); + module.addDeserializer(Table.class, new TableDeserializer()); + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + mapper.registerModule(module); + world.setDemonstrationTable(mapper.readValue(Main.class.getResourceAsStream("config_scene_virtual-table.yaml"), Table.class)); + } catch (IOException ignored) { + ignored.printStackTrace(); + } + + World.enableContextPrinting(true); + world.printContext("initial"); + org.fusesource.mqtt.client.MQTT handler = new org.fusesource.mqtt.client.MQTT(); try { handler.setHost("tcp://localhost:1883"); @@ -43,14 +73,6 @@ public class Main { 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); diff --git a/src/main/java/de/tudresden/inf/st/mg/common/ColorDeserializer.java b/src/main/java/de/tudresden/inf/st/mg/common/ColorDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..437aac189abb0b73fcaa27f737eae2040422055d --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/mg/common/ColorDeserializer.java @@ -0,0 +1,32 @@ +package de.tudresden.inf.st.mg.common; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ValueNode; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.tudresden.inf.st.mg.jastadd.model.Bin; +import de.tudresden.inf.st.mg.jastadd.model.MovableObject; +import de.tudresden.inf.st.mg.jastadd.model.Robot; +import de.tudresden.inf.st.mg.jastadd.model.Table; + +import java.io.IOException; + +public class ColorDeserializer extends JsonDeserializer<String> { + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.getCodec().readTree(p); + JsonNode nodeR = node.get("r"); + Long r = nodeR == null ? 0 : Math.round(nodeR.asDouble() * 255); + JsonNode nodeG = node.get("g"); + Long g = nodeG == null ? 0 : Math.round(nodeG.asDouble() * 255); + JsonNode nodeB = node.get("b"); + Long b = nodeB == null ? 0 : Math.round(nodeB.asDouble() * 255); + System.out.println("#" + String.format("%02X%02X%02X", r, g, b)); + return "\"#" + String.format("%02X%02X%02X", r, g, b) + "\""; // FIXME remove quotes + } +} diff --git a/src/main/java/de/tudresden/inf/st/mg/common/TableDeserializer.java b/src/main/java/de/tudresden/inf/st/mg/common/TableDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..72a54608072b57c25387c3b0f5193ea57bed5132 --- /dev/null +++ b/src/main/java/de/tudresden/inf/st/mg/common/TableDeserializer.java @@ -0,0 +1,61 @@ +package de.tudresden.inf.st.mg.common; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.node.ValueNode; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.tudresden.inf.st.mg.jastadd.model.*; + +import java.io.IOException; + +public class TableDeserializer extends JsonDeserializer<Table> { + + // idea taken from https://gist.github.com/sverhagen/c199d924245a078c287b46546e1ac26c + // TODO this looks strange, since we should already have an ObjectMapper + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(new YAMLFactory()); + + @Override + public Table deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + JsonNode node = p.getCodec().readTree(p); + + Table result = new Table(); + result.setRobot(new Robot()); + + if (node.isObject() && node.get("objects").isArray()) { + for (TreeNode child : node.get("objects")) { + TreeNode typeNode = child.get("type"); + String type = "UNKNOWN"; + if (typeNode != null && typeNode.isValueNode()) { + type = ((ValueNode) typeNode).asText(); + } + switch (type) { + case "BIN": + Bin bin = OBJECT_MAPPER.convertValue(child, Bin.class); + bin.setPose(new Pose(0,0,0,0,0,0,1)); //TODO use real pose + result.addPhysicalObject(bin); + break; + case "BOX": + MovableObject movableObject = OBJECT_MAPPER.convertValue(child, MovableObject.class); + movableObject.setPose(new Pose(0,0,0,0,0,0,1)); //TODO use real pose + result.addPhysicalObject(movableObject); + break; + case "ARM": + case "UNKNOWN": + // ignore the arm + break; + default: + System.out.println("ignoring scene content of type " + type); + } + + } + } else { + throw new IOException("YAML node is not an object with an 'objects' array."); + } + + return result; + } +} diff --git a/src/main/resources/de/tudresden/inf/st/mg/config_scene_virtual-table.yaml b/src/main/resources/de/tudresden/inf/st/mg/config_scene_virtual-table.yaml new file mode 100644 index 0000000000000000000000000000000000000000..642d9e21d345c32356eb33f022020e9ff908ad21 --- /dev/null +++ b/src/main/resources/de/tudresden/inf/st/mg/config_scene_virtual-table.yaml @@ -0,0 +1,25 @@ +# create json file using +# yq eval -o=json config_scene_virtual-table.yaml > config_scene_virtual-table.json +{ 'objects': [ + # table + # height: 1m + # width: 2m + { 'id': 'table', 'pos': { 'z': 0.25 },'size': { 'length': 1,'width': 2,'height': .5 },'orientation': { 'w': 1 },'color': { 'r': 0.3,'g': 0.3,'b': 0.3 } }, + + # idea: all elements are sorted by name + { 'id': 'binBlue', 'type': 'BIN','pos': { 'x': -0.35,'y': -0.60,'z': 0.65 },'size': { 'length': 0.20,'width': 0.4,'height': 0.3 },'orientation': { 'w': 1 },'color': { 'b': 0.5 } }, + { 'id': 'binGreen', 'type': 'BIN','pos': { 'x': -0.35,'y': 0,'z': 0.65 },'size': { 'length': 0.20,'width': 0.4,'height': 0.3 },'orientation': { 'w': 1 },'color': { 'g': 0.5 } }, + { 'id': 'binRed','type': 'BIN','pos': { 'x': -0.35,'y': 0.60,'z': 0.65 },'size': { 'length': 0.20,'width': 0.4,'height': 0.3 },'orientation': { 'w': 1 },'color': { 'r': 0.5 } }, + + { 'id': 'bigBlue', 'type': 'BOX','pos': { 'x': 0.2,'y': -0.300000,'z': 0.58 },'size': { 'length': .1, 'width': .16,'height': 0.16 },'orientation': { 'w': 1 },'color': { 'b': 0.5 } }, + { 'id': 'bigGreen', 'type': 'BOX','pos': { 'x': 0.2,'y': -0.100000,'z': 0.58 },'size': { 'length': .1, 'width': .16,'height': 0.16 },'orientation': { 'w': 1 },'color': { 'g': 0.5 } }, + { 'id': 'bigRed', 'type': 'BOX','pos': { 'x': 0.2,'y': 0.100000, 'z': 0.58 },'size': { 'length': .1, 'width': .16,'height': 0.16 },'orientation': { 'w': 1 },'color': { 'r': 0.5 } }, + { 'id': 'bigYellow','type': 'BOX','pos': { 'x': 0.2,'y': 0.300000, 'z': 0.58 },'size': { 'length': .1, 'width': .16,'height': 0.16 },'orientation': { 'w': 1 },'color': { 'r': 0.5, 'g': 0.5 } }, + + { 'id': 'smallBlue', 'type': 'BOX','pos': { 'x': 0.4,'y': -0.200000,'z': 0.54 },'size': { 'length': .1, 'width': .08,'height': 0.08 },'orientation': { 'w': 1 },'color': { 'b': 0.5 } }, + { 'id': 'smallGreen', 'type': 'BOX','pos': { 'x': 0.4,'y': 0.000000, 'z': 0.54 },'size': { 'length': .1, 'width': .08,'height': 0.08 },'orientation': { 'w': 1 },'color': { 'g': 0.5 } }, + { 'id': 'smallRed', 'type': 'BOX','pos': { 'x': 0.4,'y': 0.200000, 'z': 0.54 },'size': { 'length': .1, 'width': .08,'height': 0.08 },'orientation': { 'w': 1 },'color': { 'r': 0.5 } }, + { 'id': 'smallYellow','type': 'BOX','pos': { 'x': 0.4,'y': 0.400000, 'z': 0.54 },'size': { 'length': .1, 'width': .08,'height': 0.08 },'orientation': { 'w': 1 },'color': { 'r': 0.5, 'g': 0.5 } }, + + { 'id': 'arm','type': 'ARM','pos': { },'size': { },'orientation': { 'w': 1 },'color': { } } +] } 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 35414ad9224b3d71b8397bbf3f02d1e8d313d4d7..9ad07f03f28c133a1b32401d173b0f6f7edcc2e2 100644 --- a/src/test/java/de/tudresden/inf/st/mg/ParserTest.java +++ b/src/test/java/de/tudresden/inf/st/mg/ParserTest.java @@ -33,6 +33,8 @@ public class ParserTest { } Files.createDirectories(LOAD_AST_DIAGRAM_DIR); Files.createDirectories(TIDY_AST_DIAGRAM_DIR); + + World.enableContextPrinting(true); } @Test