From d121217f6cda244dd6e9bc31ede5a741ca761b7b Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Mon, 11 May 2020 17:54:17 +0200 Subject: [PATCH] First real test using MQTT to send and receive messages. - also try to setup CI (might fail) --- .gitlab-ci.yml | 12 +-- .../src/main/resources/MqttUpdater.jadd | 60 ++++++----- ros2rag.tests/build.gradle | 1 + .../src/test/01-input/example/Example.jadd | 5 +- .../jastadd/ros2rag/tests/ExampleTest.java | 99 ++++++++++++++++++- .../org/jastadd/ros2rag/tests/TestUtils.java | 24 +++++ 6 files changed, 160 insertions(+), 41 deletions(-) create mode 100644 ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2119e45..5b20de7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,10 @@ variables: GIT_SUBMODULE_STRATEGY: recursive +services: + - name: "eclipse-mosquitto:1.6.9" + alias: "mqtt" + stages: - build @@ -8,14 +12,6 @@ build: image: openjdk:8 stage: build before_script: - # - git submodule init - # - git submodule sync - # - cat .gitmodules - # - cat .git/config - # - ls -lah * - # - rm -rf relast.preprocessor/* - # - ls -lah * - # - git submodule update --remote - ls -lah * script: - ./gradlew --no-daemon build diff --git a/ros2rag.base/src/main/resources/MqttUpdater.jadd b/ros2rag.base/src/main/resources/MqttUpdater.jadd index ef3e483..6e986f2 100644 --- a/ros2rag.base/src/main/resources/MqttUpdater.jadd +++ b/ros2rag.base/src/main/resources/MqttUpdater.jadd @@ -145,16 +145,18 @@ public class MqttUpdater { // subscribe at broker org.fusesource.mqtt.client.Topic[] topicArray = { new org.fusesource.mqtt.client.Topic(topic, this.qos) }; - connection.subscribe(topicArray, new org.fusesource.mqtt.client.Callback<byte[]>() { - @Override - public void onSuccess(byte[] qoses) { - logger.debug("Subscribed to {}, qoses: {}", topic, qoses); - } + connection.getDispatchQueue().execute(() -> { + connection.subscribe(topicArray, new org.fusesource.mqtt.client.Callback<byte[]>() { + @Override + public void onSuccess(byte[] qoses) { + logger.debug("Subscribed to {}, qoses: {}", topic, qoses); + } - @Override - public void onFailure(Throwable cause) { - logger.error("Could not subscribe to {}", topic, cause); - } + @Override + public void onFailure(Throwable cause) { + logger.error("Could not subscribe to {}", topic, cause); + } + }); }); } @@ -187,30 +189,34 @@ public class MqttUpdater { logger.warn("Stopping without connection. Was setHost() called?"); return; } - connection.disconnect(new org.fusesource.mqtt.client.Callback<Void>() { - @Override - public void onSuccess(Void value) { - logger.info("Disconnected {} from {}", name, host); - } + connection.getDispatchQueue().execute(() -> { + connection.disconnect(new org.fusesource.mqtt.client.Callback<Void>() { + @Override + public void onSuccess(Void value) { + logger.info("Disconnected {} from {}", name, host); + } - @Override - public void onFailure(Throwable ignored) { - // Disconnects never fail. And we do not care either. - } + @Override + public void onFailure(Throwable ignored) { + // Disconnects never fail. And we do not care either. + } + }); }); } public void publish(String topic, byte[] bytes) { - connection.publish(topic, bytes, qos, false, new org.fusesource.mqtt.client.Callback<Void>() { - @Override - public void onSuccess(Void value) { - logger.debug("Published some bytes to {}", topic); - } + connection.getDispatchQueue().execute(() -> { + connection.publish(topic, bytes, qos, false, new org.fusesource.mqtt.client.Callback<Void>() { + @Override + public void onSuccess(Void value) { + logger.debug("Published some bytes to {}", topic); + } - @Override - public void onFailure(Throwable value) { - logger.warn("Could not publish on topic '{}'", topic); - } + @Override + public void onFailure(Throwable value) { + logger.warn("Could not publish on topic '{}'", topic); + } + }); }); } } diff --git a/ros2rag.tests/build.gradle b/ros2rag.tests/build.gradle index f8225fc..c37cfdd 100644 --- a/ros2rag.tests/build.gradle +++ b/ros2rag.tests/build.gradle @@ -89,6 +89,7 @@ task preprocessExampleTest(type: JavaExec, group: 'verification') { task compileExampleTest(type: RelastTest) { verbose = true useJastAddNames = true + jastAddList = 'JastAddList' relastFiles 'src/test/02-after-ros2rag/example/Grammar.relast' grammarName = 'src/test/03-after-relast/example/example' packageName = 'example.ast' diff --git a/ros2rag.tests/src/test/01-input/example/Example.jadd b/ros2rag.tests/src/test/01-input/example/Example.jadd index 40ae586..53f41b6 100644 --- a/ros2rag.tests/src/test/01-input/example/Example.jadd +++ b/ros2rag.tests/src/test/01-input/example/Example.jadd @@ -79,7 +79,10 @@ aspect GrammarTypes { return false; } + syn double RobotArm.speedLow() = 0.4d; + syn double RobotArm.speedHigh() = 1.0d; + syn double RobotArm.getAppropriateSpeed() { - return isInSafetyZone() ? 0.4d : 1.0d; + return isInSafetyZone() ? speedLow() : speedHigh(); } } diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java index 9ff94b7..d70f893 100644 --- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java +++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java @@ -1,9 +1,18 @@ package org.jastadd.ros2rag.tests; +import com.google.protobuf.InvalidProtocolBufferException; +import config.Robotconfig.RobotConfig; import example.ast.*; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.*; /** * Test case "example". @@ -12,10 +21,90 @@ import java.io.IOException; */ public class ExampleTest { + private static final double DELTA = 0.001d; + private static final String TOPIC_CONFIG = "robot/config"; + private static final String TOPIC_JOINT1 = "robot/arm/joint1"; + + private Model model; + private RobotArm robotArm; + private Joint joint1; + + private CountDownLatch stop; + + @AfterEach + public void signalCondition() { + if (stop != null) { + stop.countDown(); + } + } + @Test - public void buildModel() throws IOException { - Model model = new Model(); - model.MqttSetHost("localhost"); + public void buildModel() { + createModel(); + } + + @Test + public void communicate() throws IOException, InterruptedException { + createModel(); + List<RobotConfig> receivedConfigs = new ArrayList<>(); + + CountDownLatch start = new CountDownLatch(1); + stop = new CountDownLatch(1); + createListenerThread(receivedConfigs, start, stop); + start.await(); + + model.MqttSetHost(TestUtils.getMqttHost()); + assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS)); + // joint is currently within the safety zone, so speed should be low + robotArm.connectAppropriateSpeed(TOPIC_CONFIG, true); + joint1.connectCurrentPosition(TOPIC_JOINT1); + + // now change the position of the joint out of the safety zone + joint1.setCurrentPosition(makePosition(2, 2, 2)); + + // and wait for MQTT to send/receive messages + TimeUnit.SECONDS.sleep(2); + + // there should be two configs received by now (the initial one, and the updated) + assertEquals(2, receivedConfigs.size()); + RobotConfig actualInitialConfig = receivedConfigs.get(0); + assertEquals(robotArm.speedLow(), actualInitialConfig.getSpeed(), DELTA); + + RobotConfig actualUpdatedConfig = receivedConfigs.get(1); + assertEquals(robotArm.speedHigh(), actualUpdatedConfig.getSpeed(), DELTA); + model.MqttCloseConnections(); + } + + private void createListenerThread(List<RobotConfig> receivedConfigs, + CountDownLatch startCondition, CountDownLatch stopCondition) { + new Thread(() -> { + MqttUpdater receiver = new MqttUpdater("receiver"); + try { + receiver.setHost(TestUtils.getMqttHost(), TestUtils.getMqttDefaultPort()); + } catch (IOException e) { + fail("Could not set host: " + e.getMessage()); + } + assertTrue(receiver.waitUntilReady(2, TimeUnit.SECONDS)); + receiver.newConnection(TOPIC_CONFIG, bytes -> { + try { + RobotConfig config = RobotConfig.parseFrom(bytes); + receivedConfigs.add(config); + } catch (InvalidProtocolBufferException e) { + fail("Received bad config: " + e.getMessage()); + } + }); + startCondition.countDown(); + try { + stopCondition.await(); + receiver.close(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + } + + private void createModel() { + model = new Model(); ZoneModel zoneModel = new ZoneModel(); zoneModel.setSize(makePosition(1, 1, 1)); @@ -32,10 +121,10 @@ public class ExampleTest { zoneModel.addSafetyZone(safetyZone); model.setZoneModel(zoneModel); - RobotArm robotArm = new RobotArm(); + robotArm = new RobotArm(); robotArm.setAttributeTestSource(1); // set initial value, no trigger - Joint joint1 = new Joint(); + joint1 = new Joint(); joint1.setName("joint1"); joint1.setCurrentPosition(myPosition); diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java new file mode 100644 index 0000000..083b998 --- /dev/null +++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java @@ -0,0 +1,24 @@ +package org.jastadd.ros2rag.tests; + +/** + * Utility methods for tests. + * + * @author rschoene - Initial contribution + */ +public class TestUtils { + + public static String getMqttHost() { + if (System.getenv("GITLAB_CI") != null) { + // we are in the CI, so use "mqtt" as host + return "mqtt"; + } { + // else assume a locally running mqtt broker + return "localhost"; + } + } + + public static int getMqttDefaultPort() { + return 1883; + } + +} -- GitLab