From a33d69953d82e9cc1fdc12d7b6f03a6470ff633e Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Fri, 7 May 2021 22:05:33 +0200 Subject: [PATCH] Harmonize Main classes --- .../st/ros3rag/common/SharedMainParts.java | 147 ++++++++++++++++++ .../src/main/resources/jastadd/types.jadd | 4 + .../src/main/jastadd/WorldModelA.jadd | 27 +--- .../de/tudresden/inf/st/placeA/MainA.java | 133 +++++----------- .../src/main/jastadd/WorldModelB.jadd | 2 + .../de/tudresden/inf/st/placeB/MainB.java | 125 +++++---------- 6 files changed, 234 insertions(+), 204 deletions(-) create mode 100644 ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/SharedMainParts.java diff --git a/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/SharedMainParts.java b/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/SharedMainParts.java new file mode 100644 index 0000000..da23711 --- /dev/null +++ b/ros3rag.common/src/main/java/de/tudresden/inf/st/ros3rag/common/SharedMainParts.java @@ -0,0 +1,147 @@ +package de.tudresden.inf.st.ros3rag.common; + +import de.tudresden.inf.st.ceti.Scene; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Shared parts of both main classes. + * + * @author rschoene - Initial contribution + */ +public abstract class SharedMainParts<MqttHandler extends SharedMainParts.MqttHandlerWrapper<MqttHandler>, + WorldModel extends SharedMainParts.WorldModelWrapper> { + protected final Logger logger = LogManager.getLogger(this.getClass()); + + private final String TOPIC_MODEL; + private final String TOPIC_STATUS; + private final String TOPIC_REWIND; + private final String TOPIC_EXIT; + private final String TOPIC_SCENE_INIT; + + protected MqttHandler mainHandler; + protected WorldModel model; + protected Configuration config; + protected final String cellName; + protected final Path pathToConfig; + + public SharedMainParts(String cellName, Path pathToConfig) { + this.cellName = cellName; + this.pathToConfig = pathToConfig; + + this.TOPIC_MODEL = cellName + "/model"; + this.TOPIC_STATUS = cellName + "/status"; + this.TOPIC_REWIND = cellName + "/rewind"; + this.TOPIC_EXIT = cellName + "/exit"; + + this.TOPIC_SCENE_INIT = cellName + "/scene/init"; + } + + protected abstract MqttHandler createMqttHandler(); + + private Map<String, String> channelDescriptions() { + return new LinkedHashMap<>() {{ + put(TOPIC_MODEL, "Print current model (detailed if message starts with 'detail"); + put(TOPIC_REWIND, "Rewind app to start"); + put(TOPIC_EXIT, "Exit app"); + }}; + } + + public void run() throws Exception { + config = Util.parseConfig(pathToConfig.toFile()); + + /// Prepare main handler + mainHandler = createMqttHandler().dontSendWelcomeMessage(); + mainHandler.setHost(config.mqttHost); + mainHandler.waitUntilReady(2, TimeUnit.SECONDS); + CountDownLatch exitCondition = new CountDownLatch(1); + mainHandler.newConnection(TOPIC_EXIT, bytes -> exitCondition.countDown()); + mainHandler.newConnection(TOPIC_MODEL, bytes -> logStatus(new String(bytes))); + mainHandler.newConnection(TOPIC_REWIND, bytes -> + { + try { + rewind("rewind"); + } catch (Exception e) { + logger.catching(e); + } + } + ); + + createSpecificMainHandlerConnections(); + + logger.info("Supported commands: {}", channelDescriptions()); + + rewind("Start"); + + Runtime.getRuntime().addShutdownHook(new Thread(this::close)); + + exitCondition.await(); + } + + protected abstract void createSpecificMainHandlerConnections(); + + private void rewind(String statusMessage) throws Exception { + model = createWorldModel(); + + Scene scene = readSceneAndRobots(); + + /// Setup model connection + model.ragconnectCheckIncremental(); + model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + + connectEndpoints(); + + logStatus(statusMessage); + + mainHandler.publish(TOPIC_SCENE_INIT, scene.toByteArray()); + } + + protected abstract Scene readSceneAndRobots() throws Exception; + + protected abstract void connectEndpoints() throws IOException; + + protected abstract WorldModel createWorldModel(); + + protected abstract String getModelInfos(WorldModel model, boolean detailed); + + private void logStatus(String message) { + logger.info(message); + String content = getModelInfos(model, message.equals("Start") || message.startsWith("detail")); + logger.info("WorldModelB\n{}", content); + if (mainHandler != null) { + mainHandler.publish(TOPIC_STATUS, content.getBytes(StandardCharsets.UTF_8)); + } + } + + private void close() { + logger.info("Exiting ..."); + mainHandler.close(); + model.ragconnectCloseConnections(); + } + + public interface MqttHandlerWrapper<SELF> { + SELF dontSendWelcomeMessage(); + boolean newConnection(String topic, Consumer<byte[]> callback); + void publish(String topic, byte[] payload); + void close(); + + SELF setHost(String host) throws java.io.IOException; + + boolean waitUntilReady(long value, TimeUnit unit); + } + + public interface WorldModelWrapper { + void ragconnectCheckIncremental(); + void ragconnectSetupMqttWaitUntilReady(long value, TimeUnit unit); + void ragconnectCloseConnections(); + } +} diff --git a/ros3rag.common/src/main/resources/jastadd/types.jadd b/ros3rag.common/src/main/resources/jastadd/types.jadd index cc6d247..18ffe2a 100644 --- a/ros3rag.common/src/main/resources/jastadd/types.jadd +++ b/ros3rag.common/src/main/resources/jastadd/types.jadd @@ -237,3 +237,7 @@ aspect ConvenienceMethods { return new Size(length, width, height); } } + +aspect Glue { + class MqttHandler implements de.tudresden.inf.st.ros3rag.common.SharedMainParts.MqttHandlerWrapper<MqttHandler> {} +} diff --git a/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd b/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd index 3ad5cae..4d99a57 100644 --- a/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd +++ b/ros3rag.placeA/src/main/jastadd/WorldModelA.jadd @@ -1,8 +1,3 @@ -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; - aspect Resolving { inh ObjectOfInterest Robot.resolveObjectOfInterest(String name); eq WorldModelA.getRobot().resolveObjectOfInterest(String name) { @@ -10,24 +5,6 @@ aspect Resolving { } } -aspect Computation { -// syn LogicalScene WorldModelA.getLogicalScene() { -// LogicalScene result = new LogicalScene(); -// Map<MovableObject, LogicalMovableObject> objects = new HashMap<>(); -// for (DropOffLocation location : getScene().getDropOffLocationList()) { -// var logicalLocation = new LogicalDropOffLocation(); -// logicalLocation.setName(location.getName()); -// for (MovableObject movableObject : getScene().getMovableObjectList()) { -// if (movableObject.isLocatedAt(location)) { -// LogicalMovableObject logicalMovableObject = objects.computeIfAbsent(movableObject, k -> { -// var newLogicalMovableObject = new LogicalMovableObject(); -// newLogicalMovableObject.setName(k.getName()); -// return newLogicalMovableObject; -// }); -// logicalLocation.addContainedObjects(logicalMovableObject); -// } -// } -// } -// return result; -// } +aspect Glue { + class WorldModelA implements de.tudresden.inf.st.ros3rag.common.SharedMainParts.WorldModelWrapper {} } diff --git a/ros3rag.placeA/src/main/java/de/tudresden/inf/st/placeA/MainA.java b/ros3rag.placeA/src/main/java/de/tudresden/inf/st/placeA/MainA.java index 6ecddba..a0249be 100644 --- a/ros3rag.placeA/src/main/java/de/tudresden/inf/st/placeA/MainA.java +++ b/ros3rag.placeA/src/main/java/de/tudresden/inf/st/placeA/MainA.java @@ -4,16 +4,10 @@ import de.tudresden.inf.st.placeA.ast.MqttHandler; import de.tudresden.inf.st.placeA.ast.Robot; import de.tudresden.inf.st.placeA.ast.Scene; import de.tudresden.inf.st.placeA.ast.WorldModelA; -import de.tudresden.inf.st.ros3rag.common.Configuration; +import de.tudresden.inf.st.ros3rag.common.SharedMainParts; import de.tudresden.inf.st.ros3rag.common.Util; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import java.nio.charset.StandardCharsets; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import java.io.IOException; import static de.tudresden.inf.st.ros3rag.common.Util.mqttUri; import static de.tudresden.inf.st.ros3rag.common.Util.readScene; @@ -23,78 +17,39 @@ import static de.tudresden.inf.st.ros3rag.common.Util.readScene; * * @author rschoene - Initial contribution */ -public class MainA { - private static final Logger logger = LogManager.getLogger(MainA.class); +public class MainA extends SharedMainParts<MqttHandler, WorldModelA> { + private final String TOPIC_SCENE_UPDATE_FROM_ROS; + private final String TOPIC_SCENE_UPDATE_TO_PLACE_B; - private final static String TOPIC_MODEL = "place-a/model"; - private final static String TOPIC_STATUS = "place-a/status"; - private final static String TOPIC_REWIND = "place-a/rewind"; - private final static String TOPIC_EXIT = "place-a/exit"; + private final String TOPIC_DEMO_MOVE_objectRed1_BLUE; + private final String TOPIC_DEMO_MOVE_objectRed1_RED; - private final static String TOPIC_SCENE_INIT = "place-a/scene/init"; - private final static String TOPIC_SCENE_UPDATE_FROM_ROS = "place-a/scene/update"; - private final static String TOPIC_SCENE_UPDATE_TO_PLACE_B = "place-a/logical/update"; + MainA(String configFile) { + super("place-a", UtilA.pathToDirectoryOfPlaceA().resolve(configFile)); + this.TOPIC_SCENE_UPDATE_FROM_ROS = cellName + "/scene/update"; + this.TOPIC_SCENE_UPDATE_TO_PLACE_B = cellName + "/logical/update"; - private final static String TOPIC_DEMO_MOVE_objectRed1_BLUE = "place-a/demo/move/objectRed1/blue"; - private final static String TOPIC_DEMO_MOVE_objectRed1_RED = "place-a/demo/move/objectRed1/red"; - - private MqttHandler mainHandler; - private WorldModelA model; - private Configuration config; - - private static final Map<String, String> channelDescriptions = new LinkedHashMap<>() {{ - put(TOPIC_MODEL, "Print current model (detailed if message starts with 'detail"); - put(TOPIC_REWIND, "Rewind app to start"); - put(TOPIC_EXIT, "Exit app"); - }}; - - public static void main(String[] args) throws Exception { - new MainA().run(args); + this.TOPIC_DEMO_MOVE_objectRed1_BLUE = cellName + "/demo/move/objectRed1/blue"; + this.TOPIC_DEMO_MOVE_objectRed1_RED = cellName + "/demo/move/objectRed1/red"; } - private void run(String[] args) throws Exception { + public static void main(String[] args) throws Exception { String configFile = args.length == 0 ? "src/main/resources/config-a.yaml" : args[0]; + new MainA(configFile).run(); + } - // --- No configuration below this line --- - - config = Util.parseConfig(UtilA.pathToDirectoryOfPlaceA().resolve(configFile).toFile()); - - /// Prepare main handler - mainHandler = new MqttHandler("mainHandler").dontSendWelcomeMessage(); - mainHandler.setHost(config.mqttHost); - CountDownLatch exitCondition = new CountDownLatch(1); - mainHandler.waitUntilReady(2, TimeUnit.SECONDS); - mainHandler.newConnection(TOPIC_EXIT, bytes -> exitCondition.countDown()); - mainHandler.newConnection(TOPIC_MODEL, bytes -> logStatus(new String(bytes))); - mainHandler.newConnection(TOPIC_REWIND, bytes -> - { - try { - rewind("rewind"); - } catch (Exception e) { - logger.catching(e); - } - } - ); - + @Override + protected void createSpecificMainHandlerConnections() { mainHandler.newConnection(TOPIC_DEMO_MOVE_objectRed1_BLUE, bytes -> UtilA.updatePositionOfObjectToLocation(model.getScene(), "objectRed1", "binBlue") ); mainHandler.newConnection(TOPIC_DEMO_MOVE_objectRed1_RED, bytes -> - UtilA.updatePositionOfObjectToLocation(model.getScene(), "objectRed1", "binRed") + UtilA.updatePositionOfObjectToLocation(model.getScene(), "objectRed1", "binRed") ); - - logger.info("Supported commands: {}", channelDescriptions); - - rewind("Start"); - - Runtime.getRuntime().addShutdownHook(new Thread(this::close)); - - exitCondition.await(); } - private void rewind(String statusMessage) throws Exception { - model = new WorldModelA(); - + @Override + protected de.tudresden.inf.st.ceti.Scene readSceneAndRobots() throws Exception { /// Reading scene and robot de.tudresden.inf.st.ceti.Scene scene = readScene( UtilA.pathToDirectoryOfPlaceA().resolve(config.filenameInitialScene) @@ -103,40 +58,34 @@ public class MainA { model.setScene(myScene); // if multiple robots are specified, then error message will be displayed - Util.extractRobotNames(scene).forEach(this::setRobot); - - /// Setup model connection - model.ragconnectCheckIncremental(); - model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + Util.extractRobotNames(scene).forEach(name -> { + if (model.getRobot() != null) { + logger.error("Robot already set. Overriding with newly found name."); + } + model.setRobot(new Robot().setName(name)); + }); + + return scene; + } - /// Connect endpoints + @Override + protected void connectEndpoints() throws IOException { model.connectScene(mqttUri(TOPIC_SCENE_UPDATE_FROM_ROS, config)); model.getScene().connectLogicalScene(mqttUri(TOPIC_SCENE_UPDATE_TO_PLACE_B, config), true); - - logStatus(statusMessage); - - mainHandler.publish(TOPIC_SCENE_INIT, scene.toByteArray()); } - private void setRobot(String name) { - if (model.getRobot() != null) { - logger.error("Robot already set. Overriding with newly found name."); - } - model.setRobot(new Robot().setName(name)); + @Override + protected MqttHandler createMqttHandler() { + return new MqttHandler("mainHandlerA"); } - private void logStatus(String message) { - logger.info(message); - String content = UtilA.getModelInfos(model, message.equals("Start") || message.startsWith("detail")); - logger.info("WorldModelA\n{}", content); - if (mainHandler != null) { - mainHandler.publish(TOPIC_STATUS, content.getBytes(StandardCharsets.UTF_8)); - } + @Override + protected WorldModelA createWorldModel() { + return new WorldModelA(); } - private void close() { - logger.info("Exiting ..."); - mainHandler.close(); - model.ragconnectCloseConnections(); + @Override + protected String getModelInfos(WorldModelA worldModelA, boolean detailed) { + return UtilA.getModelInfos(model, detailed); } } diff --git a/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd b/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd index 8443d34..bc1313f 100644 --- a/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd +++ b/ros3rag.placeB/src/main/jastadd/WorldModelB.jadd @@ -182,6 +182,8 @@ aspect GlueForShared { // uncache Difference.resolveLogicalObjectOfInterest inh LogicalObjectOfInterest Difference.resolveLogicalObjectOfInterest(String name); eq WorldModelB.diffScenes().resolveLogicalObjectOfInterest(String name) = getMyScene().getLogicalScene().resolveLogicalObjectOfInterest(name); + + class WorldModelB implements de.tudresden.inf.st.ros3rag.common.SharedMainParts.WorldModelWrapper {} } aspect Printing { diff --git a/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/MainB.java b/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/MainB.java index 201fa0e..930b048 100644 --- a/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/MainB.java +++ b/ros3rag.placeB/src/main/java/de/tudresden/inf/st/placeB/MainB.java @@ -3,18 +3,12 @@ package de.tudresden.inf.st.placeB; import de.tudresden.inf.st.ceti.Reachability; import de.tudresden.inf.st.placeB.ast.*; import de.tudresden.inf.st.ros3rag.common.Configuration; +import de.tudresden.inf.st.ros3rag.common.SharedMainParts; import de.tudresden.inf.st.ros3rag.common.Util; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import static de.tudresden.inf.st.ros3rag.common.Util.mqttUri; import static de.tudresden.inf.st.ros3rag.common.Util.readScene; @@ -24,77 +18,38 @@ import static de.tudresden.inf.st.ros3rag.common.Util.readScene; * * @author rschoene - Initial contribution */ -public class MainB { - private static final Logger logger = LogManager.getLogger(MainB.class); - - private final static String TOPIC_MODEL = "place-b/model"; - private final static String TOPIC_STATUS = "place-b/status"; - private final static String TOPIC_REWIND = "place-b/rewind"; - private final static String TOPIC_EXIT = "place-b/exit"; - - private final static String TOPIC_MY_SCENE_INIT = "place-b/scene/init"; - private final static String TOPIC_ROBOT_REACHABILITY = "place-b/reachability/%s"; - private final static String TOPIC_MY_SCENE_UPDATE_FROM_ROS = "place-b/scene/update"; - private final static String TOPIC_COMMAND = "place-b/command"; - - private final static String TOPIC_OTHER_SCENE_UPDATE_FROM_PLACE_A = "place-a/logical/update"; - - private final static String TOPIC_DEMO_MOVE_objectRed1_RED = "place-b/demo/move/objectRed1/red"; - - private MqttHandler mainHandler; - private WorldModelB model; - private Configuration config; - - private static final Map<String, String> channelDescriptions = new LinkedHashMap<>() {{ - put(TOPIC_MODEL, "Print current model (detailed if message starts with 'detail"); - put(TOPIC_REWIND, "Rewind app to start"); - put(TOPIC_EXIT, "Exit app"); - }}; - - public static void main(String[] args) throws Exception { - new MainB().run(args); +public class MainB extends SharedMainParts<MqttHandler, WorldModelB> { + private final String TOPIC_ROBOT_REACHABILITY; + private final String TOPIC_MY_SCENE_UPDATE_FROM_ROS; + private final String TOPIC_COMMAND; + private final String TOPIC_OTHER_SCENE_UPDATE_FROM_PLACE_A; + private final String TOPIC_DEMO_MOVE_objectRed1_RED; + + MainB(String configFile) { + super("place-b", UtilB.pathToDirectoryOfPlaceB().resolve(configFile)); + this.TOPIC_ROBOT_REACHABILITY = cellName + "/reachability/%s"; + this.TOPIC_MY_SCENE_UPDATE_FROM_ROS = cellName + "/scene/update"; + this.TOPIC_COMMAND = cellName + "/command"; + + this.TOPIC_OTHER_SCENE_UPDATE_FROM_PLACE_A = "place-a/logical/update"; + + this.TOPIC_DEMO_MOVE_objectRed1_RED = cellName + "/demo/move/objectRed1/red"; } - private void run(String[] args) throws Exception { + public static void main(String[] args) throws Exception { String configFile = args.length == 0 ? "src/main/resources/config-b.yaml" : args[0]; + new MainB(configFile).run(); + } - // --- No configuration below this line --- - - config = Util.parseConfig(UtilB.pathToDirectoryOfPlaceB().resolve(configFile).toFile()); - - /// Prepare main handler - mainHandler = new MqttHandler("mainHandler").dontSendWelcomeMessage(); - mainHandler.setHost(config.mqttHost); - mainHandler.waitUntilReady(2, TimeUnit.SECONDS); - CountDownLatch exitCondition = new CountDownLatch(1); - mainHandler.newConnection(TOPIC_EXIT, bytes -> exitCondition.countDown()); - mainHandler.newConnection(TOPIC_MODEL, bytes -> logStatus(new String(bytes))); - mainHandler.newConnection(TOPIC_REWIND, bytes -> - { - try { - rewind("rewind"); - } catch (Exception e) { - logger.catching(e); - } - } - ); - + @Override + protected void createSpecificMainHandlerConnections() { mainHandler.newConnection(TOPIC_DEMO_MOVE_objectRed1_RED, bytes -> UtilB.updatePositionOfObjectToLocation(model.getMyScene(), "objectRed1", "binRed") ); - - logger.info("Supported commands: {}", channelDescriptions); - - rewind("Start"); - - Runtime.getRuntime().addShutdownHook(new Thread(this::close)); - - exitCondition.await(); } - private void rewind(String statusMessage) throws Exception { - model = new WorldModelB(); - + @Override + protected de.tudresden.inf.st.ceti.Scene readSceneAndRobots() throws Exception { /// Reading scene and robots de.tudresden.inf.st.ceti.Scene scene = readScene( UtilB.pathToDirectoryOfPlaceB().resolve(config.filenameInitialScene) @@ -121,33 +76,29 @@ public class MainB { } } - /// Setup model connection - model.ragconnectCheckIncremental(); - model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + return scene; + } - /// Connect endpoints + @Override + protected void connectEndpoints() throws IOException { model.connectMyScene(mqttUri(TOPIC_MY_SCENE_UPDATE_FROM_ROS, config)); model.connectOtherScene(mqttUri(TOPIC_OTHER_SCENE_UPDATE_FROM_PLACE_A, config)); model.connectNextOperation(mqttUri(TOPIC_COMMAND, config), false); + } - logStatus(statusMessage); - - mainHandler.publish(TOPIC_MY_SCENE_INIT, scene.toByteArray()); + @Override + protected MqttHandler createMqttHandler() { + return new MqttHandler("mainHandlerB"); } - private void logStatus(String message) { - logger.info(message); - String content = UtilB.getModelInfos(model, message.equals("Start") || message.startsWith("detail")); - logger.info("WorldModelB\n{}", content); - if (mainHandler != null) { - mainHandler.publish(TOPIC_STATUS, content.getBytes(StandardCharsets.UTF_8)); - } + @Override + protected WorldModelB createWorldModel() { + return new WorldModelB(); } - private void close() { - logger.info("Exiting ..."); - mainHandler.close(); - model.ragconnectCloseConnections(); + @Override + protected String getModelInfos(WorldModelB worldModelB, boolean detailed) { + return UtilB.getModelInfos(model, detailed); } } -- GitLab