浏览代码

Add 'refresh' ability, to reload the visual and mqtt status updates of the simulation

anfeny 8 月之前
父节点
当前提交
ef83206161

+ 15 - 0
dashboard/src/static/js/dashboard.js

@@ -262,6 +262,8 @@ if (mqttForm) {
     }
 }
 
+// -------------------- FUNCTIONS FOR BUTTONS ----------------------
+
 // Get current timestamp
 function getTimestamp() {
     return new Date().toISOString();
@@ -292,6 +294,19 @@ function clearDso(topic = "simulation/ctrl/dso"){
     publishMqtt(topic, message, target = 'sim');
 }
 
+/**
+ * Function to refresh the simulation status
+ * This will send a request to the simulation controller to send its current status
+ * @param topic the topic to publish the request to
+ */
+function refreshSimulationStatus(topic = 'simulation/ctrl/all') {
+    let message = {
+        action: "refresh",
+    };
+    message = JSON.stringify(message);
+    publishMqtt(topic, message, target = 'sim');
+}
+
 /**
  * Function to set camera fps
  * @param fps

+ 11 - 1
dashboard/src/templates/cards/sim-visualization.html

@@ -1,5 +1,15 @@
 <div class="card h-100">
-    <div class="card-header"><i class="bi bi-boxes me-2"></i>Simulation Visualization</div>
+    <div class="card-header d-flex justify-content-between align-items-center">
+        <div>
+            <i class="bi bi-boxes me-2"></i>Simulation Visualization
+        </div>
+        <div>
+            <button class="btn btn-sm btn-outline-secondary" type="button" title="Refresh Simulation Status"
+                onclick="refreshSimulationStatus()">
+                <i class="bi bi-arrow-clockwise"></i> Refresh
+        </div>
+        </button>
+    </div>
     <div class="card-body d-flex justify-content-center align-items-center" style="height: 100%;">
         <div id="factory-sim-visualization"></div>
     </div>

+ 77 - 50
flowchart.md

@@ -10,21 +10,21 @@ flowchart LR
 	end
 	Generator_out --> DSI_inp
 	subgraph DSI
-		DSI_vgr_in("vgr_in")
-		DSI_out("out")
 		DSI_inp("inp")
+		DSI_out("out")
 		DSI_mqtt_out("mqtt_out")
 		DSI_mqtt_in("mqtt_in")
+		DSI_vgr_in("vgr_in")
 		DSI_inp ~~~ DSI_out
 		DSI_mqtt_in ~~~ DSI_mqtt_out
 	end
 	DSI_out --> VacuumGripper_dsi_in
 	DSI_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph ReaderStation
-		ReaderStation_color_out("color_out")
+		ReaderStation_nfc_out("nfc_out")
 		ReaderStation_color_in("color_in")
+		ReaderStation_color_out("color_out")
 		ReaderStation_mqtt_out("mqtt_out")
-		ReaderStation_nfc_out("nfc_out")
 		ReaderStation_nfc_in("nfc_in")
 		ReaderStation_mqtt_in("mqtt_in")
 		ReaderStation_nfc_in ~~~ ReaderStation_nfc_out
@@ -35,24 +35,24 @@ flowchart LR
 	ReaderStation_color_out --> VacuumGripper_color_sensor_in
 	ReaderStation_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph VacuumGripper
-		VacuumGripper_mqtt_in("mqtt_in")
 		VacuumGripper_sld_white_out("sld_white_out")
-		VacuumGripper_sld_blue_out("sld_blue_out")
-		VacuumGripper_dsi_in("dsi_in")
-		VacuumGripper_ct_in("ct_in")
-		VacuumGripper_nfc_in("nfc_in")
-		VacuumGripper_sld_red_out("sld_red_out")
-		VacuumGripper_color_sensor_in("color_sensor_in")
-		VacuumGripper_mpo_out("mpo_out")
 		VacuumGripper_sld_white_in("sld_white_in")
+		VacuumGripper_mqtt_in("mqtt_in")
+		VacuumGripper_dsi_out("dsi_out")
+		VacuumGripper_ct_out("ct_out")
 		VacuumGripper_sld_red_in("sld_red_in")
 		VacuumGripper_mqtt_out("mqtt_out")
-		VacuumGripper_color_sensor_out("color_sensor_out")
-		VacuumGripper_sld_blue_in("sld_blue_in")
 		VacuumGripper_nfc_out("nfc_out")
-		VacuumGripper_ct_out("ct_out")
-		VacuumGripper_dsi_out("dsi_out")
+		VacuumGripper_color_sensor_in("color_sensor_in")
+		VacuumGripper_sld_blue_in("sld_blue_in")
 		VacuumGripper_dso_out("dso_out")
+		VacuumGripper_nfc_in("nfc_in")
+		VacuumGripper_color_sensor_out("color_sensor_out")
+		VacuumGripper_mpo_out("mpo_out")
+		VacuumGripper_ct_in("ct_in")
+		VacuumGripper_dsi_in("dsi_in")
+		VacuumGripper_sld_red_out("sld_red_out")
+		VacuumGripper_sld_blue_out("sld_blue_out")
 		VacuumGripper_dsi_in ~~~ VacuumGripper_dsi_out
 		VacuumGripper_ct_in ~~~ VacuumGripper_ct_out
 		VacuumGripper_nfc_in ~~~ VacuumGripper_nfc_out
@@ -74,22 +74,24 @@ flowchart LR
 	VacuumGripper_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph Transporter
 		Transporter_left_in("left_in")
-		Transporter_mqtt_out("mqtt_out")
 		Transporter_right_out("right_out")
-		Transporter_left_out("left_out")
+		Transporter_mqtt_in("mqtt_in")
+		Transporter_mqtt_out("mqtt_out")
 		Transporter_right_in("right_in")
+		Transporter_left_out("left_out")
 		Transporter_left_in ~~~ Transporter_right_out
 		Transporter_right_in ~~~ Transporter_left_out
+		Transporter_mqtt_in ~~~ Transporter_mqtt_out
 	end
 	Transporter_right_out --> VacuumGripper_ct_in
 	Transporter_left_out --> HighBayWarehouse_inp
 	Transporter_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph HighBayWarehouse
-		HighBayWarehouse_inventory_out("inventory_out")
-		HighBayWarehouse_inp("inp")
-		HighBayWarehouse_out("out")
-		HighBayWarehouse_mqtt_in("mqtt_in")
 		HighBayWarehouse_mqtt_out("mqtt_out")
+		HighBayWarehouse_mqtt_in("mqtt_in")
+		HighBayWarehouse_out("out")
+		HighBayWarehouse_inp("inp")
+		HighBayWarehouse_inventory_out("inventory_out")
 		HighBayWarehouse_inp ~~~ HighBayWarehouse_out
 		HighBayWarehouse_mqtt_in ~~~ HighBayWarehouse_mqtt_out
 	end
@@ -103,38 +105,45 @@ flowchart LR
 	end
 	InventoryPublisher_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MPO
+		MPO_vgr_in("vgr_in")
 		MPO_mqtt_out("mqtt_out")
 		MPO_conveyor_out("conveyor_out")
-		MPO_vgr_in("vgr_in")
+		MPO_mqtt_in("mqtt_in")
 		MPO_vgr_in ~~~ MPO_mqtt_out
+		MPO_mqtt_in ~~~ MPO_conveyor_out
 		subgraph MPO_oven
-			MPO_oven_gripper_in("gripper_in")
 			MPO_oven_mqtt_out("mqtt_out")
-			MPO_oven_gripper_out("gripper_out")
 			MPO_oven_vgr_in("vgr_in")
+			MPO_oven_gripper_out("gripper_out")
+			MPO_oven_gripper_in("gripper_in")
+			MPO_oven_mqtt_in("mqtt_in")
 			MPO_oven_vgr_in ~~~ MPO_oven_gripper_out
 			MPO_oven_gripper_in ~~~ MPO_oven_mqtt_out
 		end
 		MPO_oven_gripper_out --> MPO_gripper_oven_in
 		MPO_oven_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_gripper
-			MPO_gripper_table_out("table_out")
-			MPO_gripper_oven_out("oven_out")
 			MPO_gripper_mqtt_out("mqtt_out")
-			MPO_gripper_oven_in("oven_in")
+			MPO_gripper_mqtt_in("mqtt_in")
+			MPO_gripper_oven_out("oven_out")
+			MPO_gripper_table_out("table_out")
 			MPO_gripper_table_in("table_in")
+			MPO_gripper_oven_in("oven_in")
 			MPO_gripper_oven_in ~~~ MPO_gripper_oven_out
 			MPO_gripper_table_in ~~~ MPO_gripper_table_out
+			MPO_gripper_mqtt_in ~~~ MPO_gripper_mqtt_out
 		end
 		MPO_gripper_oven_out --> MPO_oven_gripper_in
 		MPO_gripper_table_out --> MPO_saw_gripper_in
 		MPO_gripper_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_saw
-			MPO_saw_gripper_out("gripper_out")
+			MPO_saw_mqtt_in("mqtt_in")
 			MPO_saw_conveyor_out("conveyor_out")
-			MPO_saw_gripper_in("gripper_in")
+			MPO_saw_gripper_out("gripper_out")
 			MPO_saw_mqtt_out("mqtt_out")
+			MPO_saw_gripper_in("gripper_in")
 			MPO_saw_gripper_in ~~~ MPO_saw_gripper_out
+			MPO_saw_mqtt_in ~~~ MPO_saw_conveyor_out
 		end
 		MPO_saw_gripper_out --> MPO_gripper_table_in
 		MPO_saw_conveyor_out --> MPO_conveyor_out
@@ -143,34 +152,42 @@ flowchart LR
 	MPO_mqtt_out --> MQTTControlUnit_mqtt_in
 	MPO_conveyor_out --> Conveyor_inp
 	MPO_vgr_in --> MPO_oven_vgr_in
+	MPO_mqtt_in --> MPO_oven_mqtt_in
+	MPO_mqtt_in --> MPO_gripper_mqtt_in
+	MPO_mqtt_in --> MPO_saw_mqtt_in
 	subgraph Conveyor
 		Conveyor_out("out")
-		Conveyor_inp("inp")
+		Conveyor_mqtt_in("mqtt_in")
 		Conveyor_mqtt_out("mqtt_out")
+		Conveyor_inp("inp")
 		Conveyor_inp ~~~ Conveyor_out
+		Conveyor_mqtt_in ~~~ Conveyor_mqtt_out
 	end
 	Conveyor_out --> SLD_inp
 	Conveyor_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph SLD
-		SLD_mqtt_out("mqtt_out")
-		SLD_blue_out("blue_out")
-		SLD_red_out("red_out")
-		SLD_white_out("white_out")
 		SLD_blue_in("blue_in")
-		SLD_red_in("red_in")
 		SLD_inp("inp")
+		SLD_red_in("red_in")
+		SLD_mqtt_in("mqtt_in")
+		SLD_white_out("white_out")
+		SLD_blue_out("blue_out")
 		SLD_white_in("white_in")
+		SLD_mqtt_out("mqtt_out")
+		SLD_red_out("red_out")
 		SLD_inp ~~~ SLD_white_out
-		SLD_white_in ~~~ SLD_red_out
-		SLD_red_in ~~~ SLD_blue_out
-		SLD_blue_in ~~~ SLD_mqtt_out
+		SLD_mqtt_in ~~~ SLD_red_out
+		SLD_white_in ~~~ SLD_blue_out
+		SLD_red_in ~~~ SLD_mqtt_out
 		subgraph SLD_conveyor
-			SLD_conveyor_out_blue("out_blue")
-			SLD_conveyor_inp("inp")
+			SLD_conveyor_mqtt_in("mqtt_in")
 			SLD_conveyor_out_white("out_white")
-			SLD_conveyor_out_red("out_red")
+			SLD_conveyor_inp("inp")
+			SLD_conveyor_out_blue("out_blue")
 			SLD_conveyor_mqtt_out("mqtt_out")
+			SLD_conveyor_out_red("out_red")
 			SLD_conveyor_inp ~~~ SLD_conveyor_out_white
+			SLD_conveyor_mqtt_in ~~~ SLD_conveyor_out_red
 		end
 		SLD_conveyor_out_white --> SLD_white_bay_sld_in
 		SLD_conveyor_out_red --> SLD_red_bay_sld_in
@@ -178,29 +195,32 @@ flowchart LR
 		SLD_conveyor_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_white_bay
 			SLD_white_bay_vgr_in("vgr_in")
-			SLD_white_bay_vgr_out("vgr_out")
 			SLD_white_bay_mqtt_out("mqtt_out")
 			SLD_white_bay_sld_in("sld_in")
+			SLD_white_bay_vgr_out("vgr_out")
+			SLD_white_bay_mqtt_in("mqtt_in")
 			SLD_white_bay_sld_in ~~~ SLD_white_bay_vgr_out
 			SLD_white_bay_vgr_in ~~~ SLD_white_bay_mqtt_out
 		end
 		SLD_white_bay_vgr_out --> SLD_white_out
 		SLD_white_bay_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_red_bay
-			SLD_red_bay_vgr_in("vgr_in")
-			SLD_red_bay_mqtt_out("mqtt_out")
 			SLD_red_bay_vgr_out("vgr_out")
+			SLD_red_bay_mqtt_in("mqtt_in")
 			SLD_red_bay_sld_in("sld_in")
+			SLD_red_bay_vgr_in("vgr_in")
+			SLD_red_bay_mqtt_out("mqtt_out")
 			SLD_red_bay_sld_in ~~~ SLD_red_bay_vgr_out
 			SLD_red_bay_vgr_in ~~~ SLD_red_bay_mqtt_out
 		end
 		SLD_red_bay_vgr_out --> SLD_red_out
 		SLD_red_bay_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_blue_bay
-			SLD_blue_bay_vgr_out("vgr_out")
+			SLD_blue_bay_vgr_in("vgr_in")
+			SLD_blue_bay_mqtt_in("mqtt_in")
 			SLD_blue_bay_mqtt_out("mqtt_out")
+			SLD_blue_bay_vgr_out("vgr_out")
 			SLD_blue_bay_sld_in("sld_in")
-			SLD_blue_bay_vgr_in("vgr_in")
 			SLD_blue_bay_sld_in ~~~ SLD_blue_bay_vgr_out
 			SLD_blue_bay_vgr_in ~~~ SLD_blue_bay_mqtt_out
 		end
@@ -212,23 +232,27 @@ flowchart LR
 	SLD_blue_out --> VacuumGripper_sld_blue_in
 	SLD_mqtt_out --> MQTTControlUnit_mqtt_in
 	SLD_inp --> SLD_conveyor_inp
+	SLD_mqtt_in --> SLD_conveyor_mqtt_in
+	SLD_mqtt_in --> SLD_white_bay_mqtt_in
+	SLD_mqtt_in --> SLD_red_bay_mqtt_in
+	SLD_mqtt_in --> SLD_blue_bay_mqtt_in
 	SLD_white_in --> SLD_white_bay_vgr_in
 	SLD_red_in --> SLD_red_bay_vgr_in
 	SLD_blue_in --> SLD_blue_bay_vgr_in
 	subgraph DSO
 		DSO_mqtt_out("mqtt_out")
-		DSO_inp("inp")
 		DSO_mqtt_in("mqtt_in")
+		DSO_inp("inp")
 		DSO_out("out")
 		DSO_inp ~~~ DSO_out
 		DSO_mqtt_in ~~~ DSO_mqtt_out
 	end
 	DSO_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MQTTControlUnit
-		MQTTControlUnit_REALTIME_OBSERVED("REALTIME_OBSERVED")
 		MQTTControlUnit_mqtt_in("mqtt_in")
-		MQTTControlUnit_REALTIME_INTERRUPT("REALTIME_INTERRUPT")
 		MQTTControlUnit_mqtt_out("mqtt_out")
+		MQTTControlUnit_REALTIME_OBSERVED("REALTIME_OBSERVED")
+		MQTTControlUnit_REALTIME_INTERRUPT("REALTIME_INTERRUPT")
 		MQTTControlUnit_mqtt_in ~~~ MQTTControlUnit_mqtt_out
 		MQTTControlUnit_REALTIME_INTERRUPT ~~~ MQTTControlUnit_REALTIME_OBSERVED
 	end
@@ -236,7 +260,10 @@ flowchart LR
 	MQTTControlUnit_mqtt_out --> DSI_mqtt_in
 	MQTTControlUnit_mqtt_out --> ReaderStation_mqtt_in
 	MQTTControlUnit_mqtt_out --> VacuumGripper_mqtt_in
+	MQTTControlUnit_mqtt_out --> Transporter_mqtt_in
 	MQTTControlUnit_mqtt_out --> HighBayWarehouse_mqtt_in
+	MQTTControlUnit_mqtt_out --> MPO_mqtt_in
+	MQTTControlUnit_mqtt_out --> SLD_mqtt_in
 	MQTTControlUnit_mqtt_out --> DSO_mqtt_in
 	MQTTControlUnit_REALTIME_OBSERVED --> FischertechnikFactory_REALTIME_OBSERVED
 ```

+ 10 - 2
simulator/devs_models/crate_transporter.py

@@ -37,6 +37,7 @@ class CrateTransporter(AtomicDEVS):
         # IN PORTS
         self.left_in = self.addInPort("left_in")
         self.right_in = self.addInPort("right_in")
+        self.mqtt_in = self.addInPort("mqtt_in")
         # OUT PORTS
         self.right_out = self.addOutPort("right_out")
         self.left_out = self.addOutPort("left_out")
@@ -77,12 +78,19 @@ class CrateTransporter(AtomicDEVS):
     def extTransition(self, inputs):
         self.state.delta_t -= self.elapsed
 
+        if self.mqtt_in in inputs:
+            # Handle MQTT input, which is used for visual update requests
+            mqtt_message = inputs[self.mqtt_in][0]
+            if mqtt_message.topic == "simulation/ctrl/all" and mqtt_message.payload.get("action") == "refresh":
+                self.state.visual_update_pending = True
+                return self.state
+
         # Check if input while moving
-        if not self.state.phase == CrateTransporterPhase.IDLE:
+        elif not self.state.phase == CrateTransporterPhase.IDLE:
             logger.error(f"{type(self).__name__} '{self.name}' received input while moving {self.state.phase}")
             return self.state
 
-        elif self.left_in in inputs or self.right_in in inputs:
+        if self.left_in in inputs or self.right_in in inputs:
             if self.left_in in inputs:
                 item = inputs[self.left_in][0]
                 new_phase = CrateTransporterPhase.MOVE_L_TO_R

+ 4 - 1
simulator/devs_models/dso.py

@@ -89,11 +89,14 @@ class DSO(AtomicDEVS):
         if self.mqtt_in in inputs:
             msg: MqttMessage = inputs[self.mqtt_in][0]
             if msg.topic == "simulation/ctrl/dso" and msg.payload["action"] == "clear":
-
                 self.state.workpiece = None
                 self.change_phase(DsoPhase.IDLE)
                 self.state.status_requested = True
                 # TODO: output to collector ?
+            elif msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.status_requested = True
+                self.state.visual_update_pending = True
+                return self.state
 
         return self.state # important, return state
 

+ 3 - 0
simulator/devs_models/fischertechnik_factory.py

@@ -61,7 +61,10 @@ class FischertechnikFactory(CoupledDEVS):
         self.connectPorts(self.mqtt_control.mqtt_out, self.dsi.mqtt_in)
         self.connectPorts(self.mqtt_control.mqtt_out, self.reader.mqtt_in)
         self.connectPorts(self.mqtt_control.mqtt_out, self.vgr.mqtt_in)
+        self.connectPorts(self.mqtt_control.mqtt_out, self.ct.mqtt_in)
         self.connectPorts(self.mqtt_control.mqtt_out, self.hbw.mqtt_in)
+        self.connectPorts(self.mqtt_control.mqtt_out, self.mpo.mqtt_in)
+        self.connectPorts(self.mqtt_control.mqtt_out, self.sld.mqtt_in)
         self.connectPorts(self.mqtt_control.mqtt_out, self.dso.mqtt_in)
 
         self.connectPorts(self.dsi.mqtt_out, self.mqtt_control.mqtt_in)

+ 7 - 3
simulator/devs_models/high_bay_warehouse.py

@@ -135,7 +135,7 @@ class InventoryPublisher(AtomicDEVS):
 
         if self.inp in inputs:
             new_inventory = inputs[self.inp][0]
-            logger.trace(f"{type(self).__name__} '{self.name}' received: {new_inventory}")
+            #logger.trace(f"{type(self).__name__} '{self.name}' received: {new_inventory}")
             self.state.inventory = new_inventory
             self.state.delta_t = 0.0 # publish immediately
         return self.state  # important, return state
@@ -239,8 +239,12 @@ class HighBayWarehouse(AtomicDEVS):
                     self.change_phase(HbwPhase.CMD_FETCH_CRATE)
                     self.state.target = WorkpieceColor(mqtt_msg.payload['workpiece']['type'])
                     self.state.target_location = self.state.mqtt_inventory.get_loc_workpiece_crate(color=self.state.target)
-            elif mqtt_msg.topic == "simulation/get_status":
+
+            elif mqtt_msg.topic == "simulation/ctrl/all" and mqtt_msg.payload.get("action") == "refresh":
                 self.state.status_requested = True
+                self.state.visual_update_pending = True
+                return self.state
+            
         return self.state  # important, return state
 
     def timeAdvance(self):
@@ -252,7 +256,7 @@ class HighBayWarehouse(AtomicDEVS):
         if self.state.visual_update_pending:
             return {self.mqtt_out: [self.get_visual_update_data(), self.state.visual_inventory.get_visual_update_data()]}
         if self.state.status_requested:
-            return {self.mqtt_out: [self.get_status_message(use_next_phase=False)]}
+            return {self.mqtt_out: [self.get_status_message(use_next_phase=False)], self.inventory_out: [self.state.mqtt_inventory]}
 
         if self.state.phase == HbwPhase.PUSH_ONTO_CT:
             return {self.out: [self.state.current_item], self.mqtt_out: [self.get_status_message()]}

+ 10 - 1
simulator/devs_models/holding_bay.py

@@ -24,6 +24,8 @@ class HoldingBay(AtomicDEVS):
         super(HoldingBay, self).__init__(name)
         self.sld_in = self.addInPort("sld_in")  # input of workpiece via SLD
         self.vgr_in = self.addInPort("vgr_in")  # input for VGR requesting workpiece
+        self.mqtt_in = self.addInPort("mqtt_in")  # input for MQTT messages (e.g. visual refresh request)
+
         self.vgr_out = self.addOutPort("vgr_out")  # output for workpiece to the VGR
         self.mqtt_out = self.addOutPort("mqtt_out") # output for visuals
 
@@ -40,7 +42,14 @@ class HoldingBay(AtomicDEVS):
 
     def extTransition(self, inputs):
 
+        if self.mqtt_in in inputs:
+            msg = inputs[self.mqtt_in][0]
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.visual_update_pending = True
+                return self.state
+
         if self.sld_in in inputs:
+            self.state.visual_update_pending = True  # every transition requires a new visual update
             if self.state.workpiece is not None:
                 logger.error(f"{type(self).__name__} '{self.name}' received workpiece while already holding one!")
                 return self.state
@@ -49,13 +58,13 @@ class HoldingBay(AtomicDEVS):
             logger.trace(f"{type(self).__name__} '{self.name}' received: {wp}")
 
         if self.vgr_in in inputs:
+            self.state.visual_update_pending = True  # every transition requires a new visual update
             if not self.state.workpiece:
                 logger.trace(
                     f"{type(self).__name__} '{self.name}' received VGR pickup request whilst not having workpiece!")
             else:
                 self.state.output_requested = True
 
-        self.state.visual_update_pending = True # every transition requires a new visual update
         return self.state  # important, return state
 
     def timeAdvance(self):

+ 6 - 4
simulator/devs_models/input_bay.py

@@ -91,15 +91,17 @@ class InputBay(AtomicDEVS):
             self.state.workpiece = wp
 
         if self.mqtt_in in inputs:
-            msg = inputs[self.mqtt_in][0]
-            if msg.topic == "simulation/get_status":
+            msg = inputs[self.mqtt_in][0] # TODO: check if multiple messages are possible
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
                 self.state.status_requested = True
-            if msg.topic == "simulation/ctrl/dsi" and msg.payload['action'] == "workpiece_arrived":
+                self.state.visual_update_pending = True
+                return self.state
+            elif msg.topic == "simulation/ctrl/dsi" and msg.payload['action'] == "workpiece_arrived":
                 self.change_phase(DsiPhase.WP_RECEIVED)
                 self.state.workpiece = Workpiece("?", WorkpieceColor.NONE)
                 self.state.status_requested = True
 
-            if msg.topic == "fl/vgr/do" and msg.payload['code'] == 1:
+            elif msg.topic == "fl/vgr/do" and msg.payload['code'] == 1:
                 if self.state.phase == DsiPhase.AWAIT_VGR_CONFIRMATION:
                     self.change_phase(DsiPhase.IDLE)
                     self.state.status_requested = True # publish idle update

+ 0 - 46
simulator/devs_models/io_bay.py

@@ -1,46 +0,0 @@
-from pypdevs.DEVS import AtomicDEVS
-from pypdevs.infinity import INFINITY
-from dataclasses import dataclass
-from loguru import logger
-
-from data_models.workpiece import Workpiece
-
-
-@dataclass
-class IOBayState:
-    detection_delay: float  # the delay between the workpiece landing in the bay and the photoelectric barrier detecting it
-    workpiece: Workpiece | None = None
-
-
-class IOBay(AtomicDEVS):
-    """ Bay which holds a single workpiece, can be used for input or output """
-
-    def __init__(self, name: str, detection_delay: float = 0.2):
-        # name needs to be unique to refer to it
-        super(IOBay, self).__init__(name)
-        self.inp = self.addInPort("inp")
-        self.out = self.addOutPort("out")
-
-        self.state = IOBayState(detection_delay)
-
-    def extTransition(self, inputs):
-        new_workpiece = inputs[self.inp][0]
-        logger.trace(f"IOBay '{self.name}' received: {new_workpiece}")
-        self.state.workpiece = new_workpiece
-        return self.state # important, return state
-
-    def timeAdvance(self):
-        # Next workpiece generation time
-        if self.state.workpiece:
-            return self.state.detection_delay
-        else:
-            return INFINITY  # idle
-
-    def outputFnc(self):
-        logger.trace(f"IOBay '{self.name}' outputs: {self.state.workpiece}")
-        return {self.out: [self.state.workpiece]}
-
-    def intTransition(self):
-        # We have just output the workpiece, so mark it as empty again
-        self.state.workpiece = None
-        return self.state

+ 6 - 3
simulator/devs_models/mpo.py

@@ -22,8 +22,8 @@ class MPO(CoupledDEVS):
 
         # Own ports
         self.vgr_in = self.addInPort("vgr_in")
-        # self.vgr_out = self.addOutPort("vgr_out")
-        # self.mqtt_in = self.addInPort("mqtt_in")
+
+        self.mqtt_in = self.addInPort("mqtt_in")
         self.mqtt_out = self.addOutPort("mqtt_out")
         self.conveyor_out = self.addOutPort("conveyor_out")
 
@@ -36,7 +36,10 @@ class MPO(CoupledDEVS):
         self.connectPorts(self.table_saw.conveyor_out, self.conveyor_out)
 
         # Connect MQTT ports
-        #self.connectPorts(self.mqtt_in, self.)
+        self.connectPorts(self.mqtt_in, self.oven.mqtt_in)
+        self.connectPorts(self.mqtt_in, self.gripper.mqtt_in)
+        self.connectPorts(self.mqtt_in, self.table_saw.mqtt_in)
+        
         self.connectPorts(self.oven.mqtt_out, self.mqtt_out)
         self.connectPorts(self.gripper.mqtt_out, self.mqtt_out)
         self.connectPorts(self.table_saw.mqtt_out, self.mqtt_out)

+ 16 - 2
simulator/devs_models/mpo_gripper.py

@@ -24,6 +24,7 @@ class MPOGrState:
     delta_t: float = INFINITY
     phase: MPOGrPhase = MPOGrPhase.IDLE
     workpiece: Workpiece | None = None
+    status_requested: bool = True  # if true, publish status
     visual_update_pending: bool = False
 
 
@@ -38,6 +39,7 @@ class MPOGripper(AtomicDEVS):
         self.table_in = self.addInPort("table_in") # input for getting table saw turning confirmation
         self.table_out = self.addOutPort("table_out") # output to push wp to the table saw
 
+        self.mqtt_in = self.addInPort("mqtt_in")  # input for MQTT messages (e.g. visual refresh request)
         self.mqtt_out = self.addOutPort("mqtt_out")
 
         self.state = MPOGrState()
@@ -81,7 +83,14 @@ class MPOGripper(AtomicDEVS):
     def extTransition(self, inputs):
         self.state.delta_t -= self.elapsed
 
-        if not (self.state.phase == MPOGrPhase.IDLE or self.state.phase == MPOGrPhase.AWAIT_TABLE_TURN or self.state.phase == MPOGrPhase.AWAIT_WP):
+        if self.mqtt_in in inputs:
+            msg = inputs[self.mqtt_in][0]
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.status_requested = True
+                self.state.visual_update_pending = True
+                return self.state
+
+        elif not (self.state.phase == MPOGrPhase.IDLE or self.state.phase == MPOGrPhase.AWAIT_TABLE_TURN or self.state.phase == MPOGrPhase.AWAIT_WP):
             logger.error(f"{type(self).__name__} '{self.name}' received input while not expecting: {inputs}")
             return self.state
 
@@ -106,13 +115,15 @@ class MPOGripper(AtomicDEVS):
         return self.state # important, return state
 
     def timeAdvance(self):
-        if self.state.visual_update_pending:
+        if self.state.visual_update_pending or self.state.status_requested:
             return 0.0
         return self.state.delta_t
 
     def outputFnc(self):
         if self.state.visual_update_pending:
             return {self.mqtt_out: [self.get_visual_update_data()]}
+        if self.state.status_requested:
+            return {self.mqtt_out: [self.get_status_message(use_next_phase=False)]}
 
         if self.state.phase == MPOGrPhase.MOVE_TO_OVEN:
             return {self.table_out: {}, self.mqtt_out: [self.get_status_message()]} # signal Table Saw to turn its table to face us
@@ -127,6 +138,9 @@ class MPOGripper(AtomicDEVS):
         if self.state.visual_update_pending:
             self.state.visual_update_pending = False
             return self.state
+        if self.state.status_requested:
+            self.state.status_requested = False
+            return self.state
 
         if self.state.phase == MPOGrPhase.MOVE_TO_SAW_TABLE:
             self.state.workpiece = None # remove the workpiece we just output to the table saw

+ 9 - 0
simulator/devs_models/mpo_oven.py

@@ -53,6 +53,7 @@ class MPOOven(AtomicDEVS):
         self.gripper_in = self.addInPort("gripper_in")
         self.gripper_out = self.addOutPort("gripper_out")
 
+        self.mqtt_in = self.addInPort("mqtt_in")  # input for MQTT messages (e.g. visual refresh request)
         self.mqtt_out = self.addOutPort("mqtt_out")
 
         self.state = BakeOvenState()
@@ -95,6 +96,14 @@ class MPOOven(AtomicDEVS):
 
     def extTransition(self, inputs):
         self.state.delta_t -= self.elapsed
+
+        if self.mqtt_in in inputs:
+            msg = inputs[self.mqtt_in][0]
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.status_requested = True
+                self.state.visual_update_pending = True
+            return self.state
+
         logger.trace(f"BakeOven '{self.name}' received: {inputs}")
 
         if not (self.state.phase == BakeOvenSteps.IDLE or self.state.phase == BakeOvenSteps.AWAIT_PICKUP):

+ 15 - 1
simulator/devs_models/mpo_table_saw.py

@@ -25,6 +25,7 @@ class MPOTableSawState:
     delta_t: float = INFINITY
     phase: MPOTableSawPhase = MPOTableSawPhase.IDLE
     workpiece: Workpiece | None = None
+    status_requested: bool = True  # if true, publish status
     visual_update_pending: bool = False
 
 
@@ -47,6 +48,7 @@ class MPOTableSaw(AtomicDEVS):
         self.gripper_in = self.addInPort("gripper_in") # vacuum gripper which brings us workpiece
         self.gripper_out = self.addOutPort("gripper_out") # to acknowledge table turning to the gripper
         self.conveyor_out = self.addOutPort("conveyor_out") # conveyor belt output after we're done
+        self.mqtt_in = self.addInPort("mqtt_in")
         self.mqtt_out = self.addOutPort("mqtt_out")
 
         self.state = MPOTableSawState()
@@ -90,7 +92,14 @@ class MPOTableSaw(AtomicDEVS):
     def extTransition(self, inputs):
         self.state.delta_t -= self.elapsed
 
-        if not (self.state.phase == MPOTableSawPhase.IDLE or self.state.phase == MPOTableSawPhase.AWAIT_WP):
+        if self.mqtt_in in inputs:
+            msg = inputs[self.mqtt_in][0]
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.status_requested = True
+                self.state.visual_update_pending = True
+                return self.state
+
+        elif not (self.state.phase == MPOTableSawPhase.IDLE or self.state.phase == MPOTableSawPhase.AWAIT_WP):
             logger.error(f"{type(self).__name__} '{self.name}' received input while not expecting: {inputs}")
             return self.state
 
@@ -116,6 +125,8 @@ class MPOTableSaw(AtomicDEVS):
     def outputFnc(self):
         if self.state.visual_update_pending:
             return {self.mqtt_out: [self.get_visual_update_data()]}
+        if self.state.status_requested:
+            return {self.mqtt_out: [self.get_status_message(use_next_phase=False)]}
 
         if self.state.phase == MPOTableSawPhase.ROTATE_TO_FRONT:
             return {self.gripper_out: {}, self.mqtt_out: [self.get_status_message()]} # acknowledge rotation
@@ -130,6 +141,9 @@ class MPOTableSaw(AtomicDEVS):
         if self.state.visual_update_pending:
             self.state.visual_update_pending = False
             return self.state
+        if self.state.status_requested:
+            self.state.status_requested = False
+            return self.state
 
         if self.state.phase == MPOTableSawPhase.EJECT_WP:
             self.state.workpiece = None # remove the workpiece we just output to the conveyor

+ 4 - 0
simulator/devs_models/reader_station.py

@@ -102,6 +102,10 @@ class ReaderStation(AtomicDEVS):
                 elif self.state.workpiece and msg.payload["action"] == "color_update":
                     self.state.workpiece.color = WorkpieceColor[msg.payload["workpiece"]["type"]]
 
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.visual_update_pending = True
+                return self.state
+
         elif not self.state.phase in [InputRoutine.IDLE, InputRoutine.IDLE_WAIT, InputRoutine.IDLE_WAIT_2, OutputRoutine.IDLE]:
             logger.error(f"{type(self).__name__} '{self.name}' received workpiece while not Idle!")
 

+ 8 - 0
simulator/devs_models/simple_conveyor.py

@@ -28,6 +28,8 @@ class SimpleConveyor(AtomicDEVS):
         super(SimpleConveyor, self).__init__(name)
         self.inp = self.addInPort("inp")
         self.out = self.addOutPort("out")
+
+        self.mqtt_in = self.addInPort("mqtt_in") # to receive visual update requests
         self.mqtt_out = self.addOutPort("mqtt_out")
 
         self.state = ConveyorState()
@@ -53,6 +55,12 @@ class SimpleConveyor(AtomicDEVS):
         return message
 
     def extTransition(self, inputs):
+        if self.mqtt_in in inputs:
+            msg = inputs[self.mqtt_in][0]
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.visual_update_pending = True
+                return self.state
+
         if self.inp in inputs:
             new_workpiece = inputs[self.inp][0]
             logger.trace(f"{type(self).__name__} '{self.name}' received: {new_workpiece}")

+ 17 - 1
simulator/devs_models/sld.py

@@ -39,6 +39,8 @@ class SLDConveyor(AtomicDEVS):
     def __init__(self, name: str):
         super(SLDConveyor, self).__init__(name)
         self.inp = self.addInPort("inp")
+        self.mqtt_in = self.addInPort("mqtt_in")  # to receive refresh status request
+
         # Out ports to holding bays for all colors
         self.out_white = self.addOutPort("out_white")
         self.out_red = self.addOutPort("out_red")
@@ -106,7 +108,15 @@ class SLDConveyor(AtomicDEVS):
     def extTransition(self, inputs):
         self.state.delta_t -= self.elapsed
 
-        if not self.state.phase == SLDPhase.IDLE:
+        if self.mqtt_in in inputs:
+            # Handle MQTT input, which is used for visual update requests
+            mqtt_message = inputs[self.mqtt_in][0]
+            if mqtt_message.topic == "simulation/ctrl/all" and mqtt_message.payload.get("action") == "refresh":
+                self.state.status_requested = True
+                self.state.visual_update_pending = True
+                return self.state
+
+        elif not self.state.phase == SLDPhase.IDLE:
             logger.error(f"{type(self).__name__} '{self.name}' received input while not expecting: {inputs}")
             return self.state
 
@@ -191,6 +201,7 @@ class SLD(CoupledDEVS):
 
         # Own ports
         self.inp = self.addInPort("inp")  # SLD conveyor input
+        self.mqtt_in = self.addInPort("mqtt_in") # for refresh requests
         self.white_in = self.addInPort("white_in")  # vgr requesting pickup
         self.red_in = self.addInPort("red_in")  # vgr requesting pickup
         self.blue_in = self.addInPort("blue_in")  # vgr requesting pickup
@@ -214,6 +225,11 @@ class SLD(CoupledDEVS):
         self.connectPorts(self.blue_bay.vgr_out, self.blue_out)
 
         # Connect MQTT ports
+        self.connectPorts(self.mqtt_in, self.sld_conveyor.mqtt_in)
+        self.connectPorts(self.mqtt_in, self.white_bay.mqtt_in)
+        self.connectPorts(self.mqtt_in, self.red_bay.mqtt_in)
+        self.connectPorts(self.mqtt_in, self.blue_bay.mqtt_in)
+
         self.connectPorts(self.sld_conveyor.mqtt_out, self.mqtt_out)
         self.connectPorts(self.white_bay.mqtt_out, self.mqtt_out)
         self.connectPorts(self.red_bay.mqtt_out, self.mqtt_out)

+ 4 - 4
simulator/devs_models/vacuum_gripper.py

@@ -218,7 +218,6 @@ class VacuumGripper(AtomicDEVS):
             self.state.target = 'dso'
 
 
-
     def get_visual_update_data(self) -> MqttMessage:
         """ Get visual update data for the animation, contains the action taken and the duration of that action left """
         message = MqttMessage()
@@ -283,10 +282,11 @@ class VacuumGripper(AtomicDEVS):
 
         elif self.mqtt_in in inputs:
             msg = inputs[self.mqtt_in][0]
-            if msg.topic == "simulation/get_status":
-                self.state.immediate_message = self.get_status_message(use_next_phase=False)
+            if msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
+                self.state.immediate_message = self.get_status_message(use_next_phase=False) # send a status message
+                self.state.visual_update_pending = True # send visual update
+                return self.state
             elif msg.topic == "simulation/ctrl/nfc":
-                print(msg)
                 if self.state.workpiece and msg.payload["action"] == "id_update":
                     self.state.workpiece.id = msg.payload["workpiece"]["id"]
                 elif self.state.workpiece and msg.payload["action"] == "color_update":

+ 75 - 48
simulator/flowchart.md

@@ -10,23 +10,23 @@ flowchart LR
 	end
 	Generator_out --> DSI_inp
 	subgraph DSI
-		DSI_mqtt_in("mqtt_in")
+		DSI_inp("inp")
 		DSI_out("out")
 		DSI_mqtt_out("mqtt_out")
-		DSI_inp("inp")
 		DSI_vgr_in("vgr_in")
+		DSI_mqtt_in("mqtt_in")
 		DSI_inp ~~~ DSI_out
 		DSI_mqtt_in ~~~ DSI_mqtt_out
 	end
 	DSI_out --> VacuumGripper_dsi_in
 	DSI_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph ReaderStation
+		ReaderStation_nfc_out("nfc_out")
 		ReaderStation_nfc_in("nfc_in")
-		ReaderStation_mqtt_in("mqtt_in")
-		ReaderStation_mqtt_out("mqtt_out")
 		ReaderStation_color_out("color_out")
+		ReaderStation_mqtt_out("mqtt_out")
 		ReaderStation_color_in("color_in")
-		ReaderStation_nfc_out("nfc_out")
+		ReaderStation_mqtt_in("mqtt_in")
 		ReaderStation_nfc_in ~~~ ReaderStation_nfc_out
 		ReaderStation_color_in ~~~ ReaderStation_color_out
 		ReaderStation_mqtt_in ~~~ ReaderStation_mqtt_out
@@ -35,24 +35,24 @@ flowchart LR
 	ReaderStation_color_out --> VacuumGripper_color_sensor_in
 	ReaderStation_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph VacuumGripper
-		VacuumGripper_sld_blue_in("sld_blue_in")
-		VacuumGripper_dsi_out("dsi_out")
-		VacuumGripper_sld_white_out("sld_white_out")
-		VacuumGripper_ct_out("ct_out")
-		VacuumGripper_nfc_out("nfc_out")
-		VacuumGripper_color_sensor_out("color_sensor_out")
-		VacuumGripper_dsi_in("dsi_in")
-		VacuumGripper_mpo_out("mpo_out")
-		VacuumGripper_dso_out("dso_out")
-		VacuumGripper_ct_in("ct_in")
 		VacuumGripper_sld_red_out("sld_red_out")
-		VacuumGripper_mqtt_out("mqtt_out")
 		VacuumGripper_nfc_in("nfc_in")
 		VacuumGripper_sld_blue_out("sld_blue_out")
-		VacuumGripper_color_sensor_in("color_sensor_in")
+		VacuumGripper_mpo_out("mpo_out")
+		VacuumGripper_ct_in("ct_in")
+		VacuumGripper_color_sensor_out("color_sensor_out")
+		VacuumGripper_dsi_in("dsi_in")
+		VacuumGripper_nfc_out("nfc_out")
+		VacuumGripper_ct_out("ct_out")
+		VacuumGripper_dsi_out("dsi_out")
+		VacuumGripper_mqtt_in("mqtt_in")
 		VacuumGripper_sld_white_in("sld_white_in")
+		VacuumGripper_sld_blue_in("sld_blue_in")
+		VacuumGripper_dso_out("dso_out")
 		VacuumGripper_sld_red_in("sld_red_in")
-		VacuumGripper_mqtt_in("mqtt_in")
+		VacuumGripper_sld_white_out("sld_white_out")
+		VacuumGripper_mqtt_out("mqtt_out")
+		VacuumGripper_color_sensor_in("color_sensor_in")
 		VacuumGripper_dsi_in ~~~ VacuumGripper_dsi_out
 		VacuumGripper_ct_in ~~~ VacuumGripper_ct_out
 		VacuumGripper_nfc_in ~~~ VacuumGripper_nfc_out
@@ -73,23 +73,25 @@ flowchart LR
 	VacuumGripper_dso_out --> DSO_inp
 	VacuumGripper_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph Transporter
-		Transporter_right_out("right_out")
-		Transporter_left_out("left_out")
 		Transporter_left_in("left_in")
-		Transporter_mqtt_out("mqtt_out")
+		Transporter_left_out("left_out")
+		Transporter_right_out("right_out")
+		Transporter_mqtt_in("mqtt_in")
 		Transporter_right_in("right_in")
+		Transporter_mqtt_out("mqtt_out")
 		Transporter_left_in ~~~ Transporter_right_out
 		Transporter_right_in ~~~ Transporter_left_out
+		Transporter_mqtt_in ~~~ Transporter_mqtt_out
 	end
 	Transporter_right_out --> VacuumGripper_ct_in
 	Transporter_left_out --> HighBayWarehouse_inp
 	Transporter_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph HighBayWarehouse
-		HighBayWarehouse_mqtt_out("mqtt_out")
-		HighBayWarehouse_inp("inp")
-		HighBayWarehouse_out("out")
 		HighBayWarehouse_inventory_out("inventory_out")
+		HighBayWarehouse_mqtt_out("mqtt_out")
 		HighBayWarehouse_mqtt_in("mqtt_in")
+		HighBayWarehouse_out("out")
+		HighBayWarehouse_inp("inp")
 		HighBayWarehouse_inp ~~~ HighBayWarehouse_out
 		HighBayWarehouse_mqtt_in ~~~ HighBayWarehouse_mqtt_out
 	end
@@ -103,14 +105,17 @@ flowchart LR
 	end
 	InventoryPublisher_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MPO
+		MPO_mqtt_in("mqtt_in")
+		MPO_conveyor_out("conveyor_out")
 		MPO_vgr_in("vgr_in")
 		MPO_mqtt_out("mqtt_out")
-		MPO_conveyor_out("conveyor_out")
 		MPO_vgr_in ~~~ MPO_mqtt_out
+		MPO_mqtt_in ~~~ MPO_conveyor_out
 		subgraph MPO_oven
-			MPO_oven_gripper_out("gripper_out")
-			MPO_oven_gripper_in("gripper_in")
 			MPO_oven_mqtt_out("mqtt_out")
+			MPO_oven_gripper_in("gripper_in")
+			MPO_oven_gripper_out("gripper_out")
+			MPO_oven_mqtt_in("mqtt_in")
 			MPO_oven_vgr_in("vgr_in")
 			MPO_oven_vgr_in ~~~ MPO_oven_gripper_out
 			MPO_oven_gripper_in ~~~ MPO_oven_mqtt_out
@@ -119,22 +124,26 @@ flowchart LR
 		MPO_oven_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_gripper
 			MPO_gripper_oven_out("oven_out")
-			MPO_gripper_table_in("table_in")
-			MPO_gripper_table_out("table_out")
-			MPO_gripper_mqtt_out("mqtt_out")
 			MPO_gripper_oven_in("oven_in")
+			MPO_gripper_mqtt_out("mqtt_out")
+			MPO_gripper_mqtt_in("mqtt_in")
+			MPO_gripper_table_out("table_out")
+			MPO_gripper_table_in("table_in")
 			MPO_gripper_oven_in ~~~ MPO_gripper_oven_out
 			MPO_gripper_table_in ~~~ MPO_gripper_table_out
+			MPO_gripper_mqtt_in ~~~ MPO_gripper_mqtt_out
 		end
 		MPO_gripper_oven_out --> MPO_oven_gripper_in
 		MPO_gripper_table_out --> MPO_saw_gripper_in
 		MPO_gripper_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_saw
-			MPO_saw_mqtt_out("mqtt_out")
-			MPO_saw_gripper_out("gripper_out")
+			MPO_saw_mqtt_in("mqtt_in")
 			MPO_saw_conveyor_out("conveyor_out")
+			MPO_saw_gripper_out("gripper_out")
 			MPO_saw_gripper_in("gripper_in")
+			MPO_saw_mqtt_out("mqtt_out")
 			MPO_saw_gripper_in ~~~ MPO_saw_gripper_out
+			MPO_saw_mqtt_in ~~~ MPO_saw_conveyor_out
 		end
 		MPO_saw_gripper_out --> MPO_gripper_table_in
 		MPO_saw_conveyor_out --> MPO_conveyor_out
@@ -143,43 +152,52 @@ flowchart LR
 	MPO_mqtt_out --> MQTTControlUnit_mqtt_in
 	MPO_conveyor_out --> Conveyor_inp
 	MPO_vgr_in --> MPO_oven_vgr_in
+	MPO_mqtt_in --> MPO_oven_mqtt_in
+	MPO_mqtt_in --> MPO_gripper_mqtt_in
+	MPO_mqtt_in --> MPO_saw_mqtt_in
 	subgraph Conveyor
-		Conveyor_out("out")
+		Conveyor_mqtt_in("mqtt_in")
 		Conveyor_mqtt_out("mqtt_out")
+		Conveyor_out("out")
 		Conveyor_inp("inp")
 		Conveyor_inp ~~~ Conveyor_out
+		Conveyor_mqtt_in ~~~ Conveyor_mqtt_out
 	end
 	Conveyor_out --> SLD_inp
 	Conveyor_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph SLD
-		SLD_red_in("red_in")
 		SLD_mqtt_out("mqtt_out")
 		SLD_inp("inp")
-		SLD_blue_out("blue_out")
+		SLD_mqtt_in("mqtt_in")
+		SLD_white_in("white_in")
+		SLD_red_in("red_in")
 		SLD_blue_in("blue_in")
 		SLD_white_out("white_out")
 		SLD_red_out("red_out")
-		SLD_white_in("white_in")
+		SLD_blue_out("blue_out")
 		SLD_inp ~~~ SLD_white_out
-		SLD_white_in ~~~ SLD_red_out
-		SLD_red_in ~~~ SLD_blue_out
-		SLD_blue_in ~~~ SLD_mqtt_out
+		SLD_mqtt_in ~~~ SLD_red_out
+		SLD_white_in ~~~ SLD_blue_out
+		SLD_red_in ~~~ SLD_mqtt_out
 		subgraph SLD_conveyor
 			SLD_conveyor_inp("inp")
-			SLD_conveyor_out_white("out_white")
-			SLD_conveyor_out_blue("out_blue")
 			SLD_conveyor_mqtt_out("mqtt_out")
+			SLD_conveyor_out_blue("out_blue")
+			SLD_conveyor_mqtt_in("mqtt_in")
+			SLD_conveyor_out_white("out_white")
 			SLD_conveyor_out_red("out_red")
 			SLD_conveyor_inp ~~~ SLD_conveyor_out_white
+			SLD_conveyor_mqtt_in ~~~ SLD_conveyor_out_red
 		end
 		SLD_conveyor_out_white --> SLD_white_bay_sld_in
 		SLD_conveyor_out_red --> SLD_red_bay_sld_in
 		SLD_conveyor_out_blue --> SLD_blue_bay_sld_in
 		SLD_conveyor_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_white_bay
+			SLD_white_bay_mqtt_in("mqtt_in")
 			SLD_white_bay_vgr_out("vgr_out")
-			SLD_white_bay_sld_in("sld_in")
 			SLD_white_bay_mqtt_out("mqtt_out")
+			SLD_white_bay_sld_in("sld_in")
 			SLD_white_bay_vgr_in("vgr_in")
 			SLD_white_bay_sld_in ~~~ SLD_white_bay_vgr_out
 			SLD_white_bay_vgr_in ~~~ SLD_white_bay_mqtt_out
@@ -188,18 +206,20 @@ flowchart LR
 		SLD_white_bay_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_red_bay
 			SLD_red_bay_vgr_out("vgr_out")
-			SLD_red_bay_vgr_in("vgr_in")
-			SLD_red_bay_mqtt_out("mqtt_out")
 			SLD_red_bay_sld_in("sld_in")
+			SLD_red_bay_mqtt_out("mqtt_out")
+			SLD_red_bay_mqtt_in("mqtt_in")
+			SLD_red_bay_vgr_in("vgr_in")
 			SLD_red_bay_sld_in ~~~ SLD_red_bay_vgr_out
 			SLD_red_bay_vgr_in ~~~ SLD_red_bay_mqtt_out
 		end
 		SLD_red_bay_vgr_out --> SLD_red_out
 		SLD_red_bay_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_blue_bay
+			SLD_blue_bay_vgr_out("vgr_out")
 			SLD_blue_bay_mqtt_out("mqtt_out")
+			SLD_blue_bay_mqtt_in("mqtt_in")
 			SLD_blue_bay_vgr_in("vgr_in")
-			SLD_blue_bay_vgr_out("vgr_out")
 			SLD_blue_bay_sld_in("sld_in")
 			SLD_blue_bay_sld_in ~~~ SLD_blue_bay_vgr_out
 			SLD_blue_bay_vgr_in ~~~ SLD_blue_bay_mqtt_out
@@ -212,23 +232,27 @@ flowchart LR
 	SLD_blue_out --> VacuumGripper_sld_blue_in
 	SLD_mqtt_out --> MQTTControlUnit_mqtt_in
 	SLD_inp --> SLD_conveyor_inp
+	SLD_mqtt_in --> SLD_conveyor_mqtt_in
+	SLD_mqtt_in --> SLD_white_bay_mqtt_in
+	SLD_mqtt_in --> SLD_red_bay_mqtt_in
+	SLD_mqtt_in --> SLD_blue_bay_mqtt_in
 	SLD_white_in --> SLD_white_bay_vgr_in
 	SLD_red_in --> SLD_red_bay_vgr_in
 	SLD_blue_in --> SLD_blue_bay_vgr_in
 	subgraph DSO
-		DSO_mqtt_in("mqtt_in")
 		DSO_mqtt_out("mqtt_out")
-		DSO_inp("inp")
 		DSO_out("out")
+		DSO_mqtt_in("mqtt_in")
+		DSO_inp("inp")
 		DSO_inp ~~~ DSO_out
 		DSO_mqtt_in ~~~ DSO_mqtt_out
 	end
 	DSO_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MQTTControlUnit
+		MQTTControlUnit_REALTIME_INTERRUPT("REALTIME_INTERRUPT")
 		MQTTControlUnit_mqtt_in("mqtt_in")
 		MQTTControlUnit_REALTIME_OBSERVED("REALTIME_OBSERVED")
 		MQTTControlUnit_mqtt_out("mqtt_out")
-		MQTTControlUnit_REALTIME_INTERRUPT("REALTIME_INTERRUPT")
 		MQTTControlUnit_mqtt_in ~~~ MQTTControlUnit_mqtt_out
 		MQTTControlUnit_REALTIME_INTERRUPT ~~~ MQTTControlUnit_REALTIME_OBSERVED
 	end
@@ -236,7 +260,10 @@ flowchart LR
 	MQTTControlUnit_mqtt_out --> DSI_mqtt_in
 	MQTTControlUnit_mqtt_out --> ReaderStation_mqtt_in
 	MQTTControlUnit_mqtt_out --> VacuumGripper_mqtt_in
+	MQTTControlUnit_mqtt_out --> Transporter_mqtt_in
 	MQTTControlUnit_mqtt_out --> HighBayWarehouse_mqtt_in
+	MQTTControlUnit_mqtt_out --> MPO_mqtt_in
+	MQTTControlUnit_mqtt_out --> SLD_mqtt_in
 	MQTTControlUnit_mqtt_out --> DSO_mqtt_in
 	MQTTControlUnit_REALTIME_OBSERVED --> FischertechnikFactory_REALTIME_OBSERVED
 ```