diff --git a/ros2rag.example/src/main/jastadd/Computation.jrag b/ros2rag.example/src/main/jastadd/Computation.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..11af4952a8f2390a5bcec26c8a3098c7d19f42d2
--- /dev/null
+++ b/ros2rag.example/src/main/jastadd/Computation.jrag
@@ -0,0 +1,35 @@
+aspect Computation {
+  syn boolean RobotArm.isInSafetyZone() {
+    for (Joint joint : getJointList()) {
+      if (model().getZoneModel().isInSafetyZone(joint.getCurrentPosition())) {
+        return true;
+      }
+    }
+    return model().getZoneModel().isInSafetyZone(getEndEffector().getCurrentPosition());
+  }
+
+  cache ZoneModel.isInSafetyZone(Position pos);
+  syn boolean ZoneModel.isInSafetyZone(Position pos) {
+    for (Zone sz : getSafetyZoneList()) {
+      for (PositionWrapper szp : sz.getPositionWrapperList()) {
+        Position inside = szp.getPosition();
+        if (inside.getPositionX() <= pos.getPositionX() &&
+              inside.getPositionY() <= pos.getPositionY() &&
+              inside.getPositionZ() <= pos.getPositionZ() &&
+              pos.getPositionX() <= inside.getPositionX() + getSize().getPositionX() &&
+              pos.getPositionY() <= inside.getPositionY() + getSize().getPositionY() &&
+              pos.getPositionZ() <= inside.getPositionZ() + getSize().getPositionZ()) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  syn config.Robotconfig.RobotConfig RobotArm.createSlowSpeedMessage() {
+    return config.Robotconfig.RobotConfig.newBuilder()
+        .setSpeed(isInSafetyZone() ? 0.4d : 1.0d)
+        .build();
+  }
+
+}
diff --git a/ros2rag.example/src/main/jastadd/Example.relast b/ros2rag.example/src/main/jastadd/Example.relast
index b72ddd50ee92242e425145b373ad32d304f8d87b..09517aa1e47ded59a979b7fbc7d50d7449a3a0d0 100644
--- a/ros2rag.example/src/main/jastadd/Example.relast
+++ b/ros2rag.example/src/main/jastadd/Example.relast
@@ -1,11 +1,11 @@
 Model ::= RobotArm ZoneModel ;
 
-ZoneModel ::= <Size:Position> SafetyZone:Zone*;
+ZoneModel ::= <Size:Position> SafetyZone:Zone* ;
 
-Zone ::= PositionWrapper*;
+Zone ::= PositionWrapper* ;
 
 // Do not use terminal-NTA's for now, as relast has problems with it "/<ShouldUseLowSpeed:Boolean>/" ;
-RobotArm ::= Joint* EndEffector ;
+RobotArm ::= Joint* EndEffector <_AttributeTestSource:int> ; // normally this would be: <AttributeTestSource:int> ;
 
 Joint ::= <Name> <CurrentPosition:Position> ;
 //rel Joint.CurrentPosition -> Position_Old ;
diff --git a/ros2rag.example/src/main/jastadd/Generated.jrag b/ros2rag.example/src/main/jastadd/Generated.jrag
index 0c0fce1b28b8d85447d4603101f1526a6a62e778..3b1ca622c18876985a8e945328df529a211d308b 100644
--- a/ros2rag.example/src/main/jastadd/Generated.jrag
+++ b/ros2rag.example/src/main/jastadd/Generated.jrag
@@ -22,6 +22,7 @@ aspect GrammarExtension {
   }
 
   inh MqttUpdater Joint._mqttUpdater();
+  inh MqttUpdater RobotArm._mqttUpdater();
   eq Model.getRobotArm()._mqttUpdater() = get_MqttRoot().getUpdater();
   eq Model.getZoneModel()._mqttUpdater() = get_MqttRoot().getUpdater();
 }
diff --git a/ros2rag.example/src/main/jastadd/Navigation.jrag b/ros2rag.example/src/main/jastadd/Navigation.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..918d61f24265e4475c99d93e37afb7359f3095be
--- /dev/null
+++ b/ros2rag.example/src/main/jastadd/Navigation.jrag
@@ -0,0 +1,4 @@
+aspect Navigation {
+  inh Model RobotArm.model();
+  eq Model.getRobotArm().model() = this;
+}
diff --git a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedJoint.java b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedJoint.java
index 6641c840b1166876f1d89e6c6a3823ecc3340d72..3ac9639e22337a0163495a8a60f05b195f14f348 100644
--- a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedJoint.java
+++ b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedJoint.java
@@ -19,14 +19,16 @@ public class GeneratedJoint extends Joint {
   [always] read Joint.CurrentPosition using LinkStateToPosition;
 
   // panda.LinkState is a datatype defined in protobuf
-  LinkStateToPosition: map panda.Linkstate x to Position y using {
+  LinkStateToPosition: map panda.Linkstate.PandaLinkState x to Position y using {
     y = x.getPos();
   }
    */
   public void connectCurrentPosition(String topic) {
     _mqttUpdater().newConnection(topic, message -> {
-      // Parse message into a LinkState
+      // Parse message into a PandaLinkState
       try {
+        // TODO the line with parseFrom should be generated, but needs a hint, that the type is protobuf
+        //  maybe: "map protobuf panda.Linkstate.PandaLinkState [...]"?
         PandaLinkState x = PandaLinkState.parseFrom(message);
         Position y = x.getPos();
         setCurrentPosition(y);
diff --git a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedRobotArm.java b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedRobotArm.java
index 56d326a696fea1019783120b2437ce371a4367dc..f439cc850cec29f3be705912a89beb7e696383f2 100644
--- a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedRobotArm.java
+++ b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/GeneratedRobotArm.java
@@ -1,5 +1,6 @@
 package de.tudresden.inf.st.ros2rag.example;
 
+import config.Robotconfig;
 import de.tudresden.inf.st.ros2rag.ast.RobotArm;
 
 /**
@@ -8,4 +9,52 @@ import de.tudresden.inf.st.ros2rag.ast.RobotArm;
  * @author rschoene - Initial contribution
  */
 public class GeneratedRobotArm extends RobotArm {
+    /*
+    // as soon as the cache of isInSafetyZone is invalidated, update the value of Robot.ShouldUseLowSpeed with its value
+    [always] update Robot.ShouldUseLowSpeed with isInSafetyZone() using transformation();
+
+    // when a (new?) value for ShouldUseLowSpeed is set, send it over via mqtt
+    [always] write Robot.ShouldUseLowSpeed;
+
+    ---
+
+    actually, it should look like:
+    (A) [always] write RobotArm.createSpeedMessage() when isInSafetyZone() ; // write attribute value, when the second attribute changes. maybe we should not allow the "when" clause, if we allow attribute dependencies
+    (B) RobotArm.isInSafetyZone() dependsOn RobotArm.AttributeTestSource ; // explicit dependency for implicit instance of the same RobotArm
+    (C) RobotArm.createSpeedMessage() dependsOn RobotArm.isInSafetyZone() ; // explicit attribute dependency
+
+    interesting detail: an attribute has no object, and can not be connected, but a generated method like
+    "connect_isInSafetyZone(String topic)" can :)
+
+    - the line "TYPE.ATTRIBUTE() dependsOn TYPE.TOKEN" implies: 1) change TOKEN to _TOKEN, 2) generate "virtual" setter/getter for TOKEN
+    - the implications of line "TYPE.ATTRIBUTE_1() dependsOn TYPE.ATTRIBUTE_2()" are currently unclear
+    - the line "write TYPE.ATTRIBUTE_1() when ATTRIBUTE_2()" implies: 1) add a private field for the topic to publish, 2) add a "connect_ATTRIBUTE_1(String topic)" method to set the topic, 3) inject a trigger in every setter the ATTRIBUTE_2 depends on, to check for a change (unless "always" was specified), store the result of ATTRIBUTE_1 in a variable, and publish this variable for the stored topic
+     */
+
+  /*
+  this is one way to store the topic, i.e., close to the producing nonterminal.
+  another way would be a map (or multiple) in the MqttUpdater mapping nonterminal and operation to a topic
+   */
+  private String /*GeneratedRobotArm.*/write_isInSafetyZone_topic = null;
+
+  public RobotArm setAttributeTestSource(int value) {
+    int old_value = super.get_AttributeTestSource();
+    RobotArm result = super.set_AttributeTestSource(value);
+    // this is a primitive type, so compare with "!=" instead of !equals()
+    if (old_value != value && write_isInSafetyZone_topic != null) {
+      // TODO the method-call "toByteArray" can be generated, if known, that RobotConfig is a protobuf type
+      //  maybe "write [...] using protobuf createSlowSpeedMessage() ;"
+      Robotconfig.RobotConfig config = createSlowSpeedMessage();
+      _mqttUpdater().publish(write_isInSafetyZone_topic, config.toByteArray());
+    }
+    return result;
+  }
+
+  public int getAttributeTestSource() {
+    return super.get_AttributeTestSource();
+  }
+
+  public void connect_isInSafetyZone(String topic) {
+    write_isInSafetyZone_topic = topic;
+  }
 }
diff --git a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/Main.java b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/Main.java
index 8606a19ed07ed77b57b71be4464b490d73441f19..0bfc1776b883dd3aaca90df14a71bc2759f3783b 100644
--- a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/Main.java
+++ b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/Main.java
@@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit;
  * @author rschoene - Initial contribution
  */
 public class Main {
-  public static void main(String[] args) throws InvalidProtocolBufferException, IOException, InterruptedException {
+  public static void main(String[] args) throws IOException, InterruptedException {
     Model model = new Model();
     model.updateMqttHost("localhost");
 
@@ -30,8 +30,10 @@ public class Main {
     safetyZone.addPositionWrapper(leftPosition);
     safetyZone.addPositionWrapper(rightPosition);
     zoneModel.addSafetyZone(safetyZone);
+    model.setZoneModel(zoneModel);
 
-    RobotArm robotArm = new GeneratedRobotArm();
+    GeneratedRobotArm robotArm = new GeneratedRobotArm();
+    robotArm.set_AttributeTestSource(1);  // set initial value, no trigger
 
     GeneratedJoint joint1 = new GeneratedJoint();
     joint1.setName("joint1");
@@ -48,9 +50,15 @@ public class Main {
     model.waitUntilReady(2, TimeUnit.SECONDS);
 
     joint1.connectCurrentPosition("robot/joint1");
+    robotArm.connect_isInSafetyZone("robot/config");
+
     System.out.println("BEFORE joint1.getCurrentPosition() = " + stringify(joint1.getCurrentPosition()));
 
-    Thread.sleep(10000);
+    Thread.sleep(3000);
+    robotArm.setAttributeTestSource(1);
+    Thread.sleep(3000);
+    robotArm.setAttributeTestSource(5);
+    Thread.sleep(3000);
 
     System.out.println("AFTER joint1.getCurrentPosition() = " + stringify(joint1.getCurrentPosition()));
 
diff --git a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/MqttUpdater.java b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/MqttUpdater.java
index cc9be87ca4aeb63085144b2b9cbd16b8049c7e14..f08ce43f6c71d089f9c932e92c1d3e07148319c9 100644
--- a/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/MqttUpdater.java
+++ b/ros2rag.example/src/main/java/de/tudresden/inf/st/ros2rag/example/MqttUpdater.java
@@ -32,8 +32,8 @@ public class MqttUpdater {
   /** The connection to the MQTT broker. */
   private CallbackConnection connection;
   /** Whether we are subscribed to the topics yet */
-  private Condition readyCondition;
-  private Lock readyLock;
+  private final Condition readyCondition;
+  private final Lock readyLock;
   private boolean ready;
   private QoS qos;
   /** Dispatch knowledge */
@@ -211,4 +211,17 @@ public class MqttUpdater {
     });
   }
 
+  public void publish(String topic, byte[] bytes) {
+    connection.publish(topic, bytes, qos, false, new Callback<Void>() {
+      @Override
+      public void onSuccess(Void value) {
+        logger.debug("Published some bytes");
+      }
+
+      @Override
+      public void onFailure(Throwable value) {
+        logger.warn("Could not publish on topic '{}'", topic);
+      }
+    });
+  }
 }
diff --git a/ros2rag.example/src/main/proto/robotconfig.proto b/ros2rag.example/src/main/proto/robotconfig.proto
new file mode 100644
index 0000000000000000000000000000000000000000..c3e0862e0216e5ea00f79966dff8d5ce434f3dac
--- /dev/null
+++ b/ros2rag.example/src/main/proto/robotconfig.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package config;
+
+message RobotConfig {
+
+  double speed = 1;
+  bool loopTrajectory = 2;
+
+  enum PlanningMode {
+    FLUID = 0;
+    CARTESIAN = 1;
+  }
+
+  PlanningMode planningMode = 3;
+}
diff --git a/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java b/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java
index a3d0ae1ed41d1401b4a4cacab9848e5fa1df4654..5eda5a0db1d914f7a9c5f02ca9594353a8776a59 100644
--- a/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java
+++ b/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java
@@ -2,8 +2,6 @@ package de.tudresden.inf.st.ros2rag.senderstub;
 
 import panda.Linkstate;
 
-import java.util.Arrays;
-
 public class Main {
   public static void main(String[] args) throws Exception {
     String topic;