Sfoglia il codice sorgente

Add automatic injection of workpieces when added irl, same without output removal. Reworked several commands. Also fixed bug with readerstation

anfeny 8 mesi fa
parent
commit
346425c3ce

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

@@ -281,6 +281,17 @@ function placeOrder(color, topic = 'f/o/order') {
     publishMqtt(topic, message, target = 'real');
 }
 
+/**
+ * Sends a 'clear' command to the DSO, signifying the workpiece should be removed
+ */
+function clearDso(topic = "simulation/ctrl/dso"){
+    let message = {
+        action: "clear",
+    };
+    message = JSON.stringify(message);
+    publishMqtt(topic, message, target = 'sim');
+}
+
 /**
  * Function to set camera fps
  * @param fps

+ 1 - 1
dashboard/src/templates/cards/order.html

@@ -57,7 +57,7 @@
         <div class="row mt-3">
             <div class="col col-12 justify-content-center text-center">
                 <button class="btn btn-outline-danger" title="Remove a workpiece from the output (DSO) of the simulated factory"
-                onclick="publishMqtt('simulation/clear_dso', '{}');">
+                onclick="clearDso()">
                     <i class="bi bi-x-square"></i> Clear output station (simulation)
                 </button>
             </div>

+ 242 - 0
flowchart.md

@@ -0,0 +1,242 @@
+```mermaid
+---
+title: FischertechnikFactory
+---
+flowchart LR
+	subgraph Generator
+		Generator_out("out")
+		Generator_mqtt_in("mqtt_in")
+		Generator_mqtt_in ~~~ Generator_out
+	end
+	Generator_out --> DSI_inp
+	subgraph DSI
+		DSI_vgr_in("vgr_in")
+		DSI_out("out")
+		DSI_inp("inp")
+		DSI_mqtt_out("mqtt_out")
+		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_color_out("color_out")
+		ReaderStation_color_in("color_in")
+		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
+		ReaderStation_color_in ~~~ ReaderStation_color_out
+		ReaderStation_mqtt_in ~~~ ReaderStation_mqtt_out
+	end
+	ReaderStation_nfc_out --> VacuumGripper_nfc_in
+	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_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_dso_out("dso_out")
+		VacuumGripper_dsi_in ~~~ VacuumGripper_dsi_out
+		VacuumGripper_ct_in ~~~ VacuumGripper_ct_out
+		VacuumGripper_nfc_in ~~~ VacuumGripper_nfc_out
+		VacuumGripper_color_sensor_in ~~~ VacuumGripper_color_sensor_out
+		VacuumGripper_sld_red_in ~~~ VacuumGripper_mpo_out
+		VacuumGripper_sld_blue_in ~~~ VacuumGripper_sld_red_out
+		VacuumGripper_sld_white_in ~~~ VacuumGripper_sld_blue_out
+		VacuumGripper_mqtt_in ~~~ VacuumGripper_sld_white_out
+	end
+	VacuumGripper_dsi_out --> DSI_vgr_in
+	VacuumGripper_ct_out --> Transporter_right_in
+	VacuumGripper_nfc_out --> ReaderStation_nfc_in
+	VacuumGripper_color_sensor_out --> ReaderStation_color_in
+	VacuumGripper_mpo_out --> MPO_vgr_in
+	VacuumGripper_sld_red_out --> SLD_red_in
+	VacuumGripper_sld_blue_out --> SLD_blue_in
+	VacuumGripper_sld_white_out --> SLD_white_in
+	VacuumGripper_dso_out --> DSO_inp
+	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_right_in("right_in")
+		Transporter_left_in ~~~ Transporter_right_out
+		Transporter_right_in ~~~ Transporter_left_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_inp ~~~ HighBayWarehouse_out
+		HighBayWarehouse_mqtt_in ~~~ HighBayWarehouse_mqtt_out
+	end
+	HighBayWarehouse_out --> Transporter_left_in
+	HighBayWarehouse_mqtt_out --> MQTTControlUnit_mqtt_in
+	HighBayWarehouse_inventory_out --> InventoryPublisher_inp
+	subgraph InventoryPublisher
+		InventoryPublisher_inp("inp")
+		InventoryPublisher_mqtt_out("mqtt_out")
+		InventoryPublisher_inp ~~~ InventoryPublisher_mqtt_out
+	end
+	InventoryPublisher_mqtt_out --> MQTTControlUnit_mqtt_in
+	subgraph MPO
+		MPO_mqtt_out("mqtt_out")
+		MPO_conveyor_out("conveyor_out")
+		MPO_vgr_in("vgr_in")
+		MPO_vgr_in ~~~ MPO_mqtt_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_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_table_in("table_in")
+			MPO_gripper_oven_in ~~~ MPO_gripper_oven_out
+			MPO_gripper_table_in ~~~ MPO_gripper_table_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_conveyor_out("conveyor_out")
+			MPO_saw_gripper_in("gripper_in")
+			MPO_saw_mqtt_out("mqtt_out")
+			MPO_saw_gripper_in ~~~ MPO_saw_gripper_out
+		end
+		MPO_saw_gripper_out --> MPO_gripper_table_in
+		MPO_saw_conveyor_out --> MPO_conveyor_out
+		MPO_saw_mqtt_out --> MPO_mqtt_out
+	end
+	MPO_mqtt_out --> MQTTControlUnit_mqtt_in
+	MPO_conveyor_out --> Conveyor_inp
+	MPO_vgr_in --> MPO_oven_vgr_in
+	subgraph Conveyor
+		Conveyor_out("out")
+		Conveyor_inp("inp")
+		Conveyor_mqtt_out("mqtt_out")
+		Conveyor_inp ~~~ Conveyor_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_white_in("white_in")
+		SLD_inp ~~~ SLD_white_out
+		SLD_white_in ~~~ SLD_red_out
+		SLD_red_in ~~~ SLD_blue_out
+		SLD_blue_in ~~~ SLD_mqtt_out
+		subgraph SLD_conveyor
+			SLD_conveyor_out_blue("out_blue")
+			SLD_conveyor_inp("inp")
+			SLD_conveyor_out_white("out_white")
+			SLD_conveyor_out_red("out_red")
+			SLD_conveyor_mqtt_out("mqtt_out")
+			SLD_conveyor_inp ~~~ SLD_conveyor_out_white
+		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_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_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_sld_in("sld_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_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
+		SLD_blue_bay_vgr_out --> SLD_blue_out
+		SLD_blue_bay_mqtt_out --> SLD_mqtt_out
+	end
+	SLD_white_out --> VacuumGripper_sld_white_in
+	SLD_red_out --> VacuumGripper_sld_red_in
+	SLD_blue_out --> VacuumGripper_sld_blue_in
+	SLD_mqtt_out --> MQTTControlUnit_mqtt_in
+	SLD_inp --> SLD_conveyor_inp
+	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_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_mqtt_in ~~~ MQTTControlUnit_mqtt_out
+		MQTTControlUnit_REALTIME_INTERRUPT ~~~ MQTTControlUnit_REALTIME_OBSERVED
+	end
+	MQTTControlUnit_mqtt_out --> Generator_mqtt_in
+	MQTTControlUnit_mqtt_out --> DSI_mqtt_in
+	MQTTControlUnit_mqtt_out --> ReaderStation_mqtt_in
+	MQTTControlUnit_mqtt_out --> VacuumGripper_mqtt_in
+	MQTTControlUnit_mqtt_out --> HighBayWarehouse_mqtt_in
+	MQTTControlUnit_mqtt_out --> DSO_mqtt_in
+	MQTTControlUnit_REALTIME_OBSERVED --> FischertechnikFactory_REALTIME_OBSERVED
+```

+ 2 - 1
simulator/devs_models/dso.py

@@ -88,7 +88,8 @@ class DSO(AtomicDEVS):
 
         if self.mqtt_in in inputs:
             msg: MqttMessage = inputs[self.mqtt_in][0]
-            if msg.topic == "simulation/clear_dso":
+            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

+ 1 - 0
simulator/devs_models/fischertechnik_factory.py

@@ -59,6 +59,7 @@ class FischertechnikFactory(CoupledDEVS):
         # Connect MQTT ports
         self.connectPorts(self.mqtt_control.mqtt_out, self.gen.mqtt_in)
         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.hbw.mqtt_in)
         self.connectPorts(self.mqtt_control.mqtt_out, self.dso.mqtt_in)

+ 6 - 1
simulator/devs_models/input_bay.py

@@ -4,7 +4,7 @@ from pypdevs.infinity import INFINITY
 from dataclasses import dataclass
 from loguru import logger
 
-from data_models.workpiece import Workpiece
+from data_models.workpiece import Workpiece, WorkpieceColor
 from utils.get_timestamp import get_timestamp
 from utils.timed_phase_enum import TimedPhaseEnum
 
@@ -94,6 +94,11 @@ class InputBay(AtomicDEVS):
             msg = inputs[self.mqtt_in][0]
             if msg.topic == "simulation/get_status":
                 self.state.status_requested = True
+            if 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:
                 if self.state.phase == DsiPhase.AWAIT_VGR_CONFIRMATION:
                     self.change_phase(DsiPhase.IDLE)

+ 17 - 5
simulator/devs_models/reader_station.py

@@ -16,7 +16,9 @@ class InputRoutine(TimedPhaseEnum):
     """
     IDLE = ('IDLE', INFINITY)
     NFC_READING = ('NFC_READING', 1.367)
+    IDLE_WAIT = ('IDLE_WAIT', INFINITY) # wait for VGR to move to color sensor
     COLOR_READING = ('COLOR_READING', 0.100)
+    IDLE_WAIT_2 = ('IDLE_WAIT_2', INFINITY) # wait for VGR to move to NFC reader
     NFC_WRITING = ('NFC_WRITING', 7.603) # yes, this takes a while
 
 class OutputRoutine(TimedPhaseEnum):
@@ -48,6 +50,7 @@ class ReaderStation(AtomicDEVS):
         self.color_in = self.addInPort("color_in")
         self.color_out = self.addOutPort("color_out")
 
+        self.mqtt_in = self.addInPort("mqtt_in")
         self.mqtt_out = self.addOutPort("mqtt_out")
 
         self.state = ReaderStationState()
@@ -90,7 +93,16 @@ class ReaderStation(AtomicDEVS):
     def extTransition(self, inputs):
         self.state.delta_t -= self.elapsed
 
-        if not self.state.phase == InputRoutine.IDLE:
+        if self.mqtt_in in inputs:
+            msg = inputs[self.mqtt_in][0]
+            if 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":
+                    self.state.workpiece.color = WorkpieceColor[msg.payload["workpiece"]["type"]]
+
+        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!")
 
         if self.nfc_in in inputs:
@@ -100,9 +112,9 @@ class ReaderStation(AtomicDEVS):
                 self.change_phase(OutputRoutine.NFC_WRITING)
                 self.state.color_read = wp.color
                 wp.state = "PROCESSED" # TODO: check if this logic is correct
-            if self.state.color_read == WorkpieceColor.NONE:
+            if self.state.phase == InputRoutine.IDLE:
                 self.change_phase(InputRoutine.NFC_READING) # new, read
-            else:
+            elif self.state.phase == InputRoutine.IDLE_WAIT_2:
                 self.change_phase(InputRoutine.NFC_WRITING) # seen, write nfc
             self.state.workpiece = wp
 
@@ -142,7 +154,7 @@ class ReaderStation(AtomicDEVS):
         if self.state.phase in [InputRoutine.NFC_WRITING, OutputRoutine.NFC_WRITING]:
             self.state.color_read = WorkpieceColor.NONE # reset for next input
 
-        # Transition to IDLE
+        # Transition to next phase (idle phase)
         self.state.workpiece = None  # We have just output the workpiece, so mark it as empty again
-        self.change_phase(InputRoutine.IDLE)
+        self.change_phase(self.state.phase.next())
         return self.state

+ 8 - 0
simulator/devs_models/vacuum_gripper.py

@@ -285,6 +285,14 @@ class VacuumGripper(AtomicDEVS):
             msg = inputs[self.mqtt_in][0]
             if msg.topic == "simulation/get_status":
                 self.state.immediate_message = self.get_status_message(use_next_phase=False)
+            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":
+                    self.state.workpiece.color = WorkpieceColor[msg.payload["workpiece"]["type"]]
+
+            
             elif msg.topic == "f/i/state/dsi":
                 if msg.payload['active'] == 1:
                     self.change_phase(InputRoutine.MOVE_TO_DSI)

+ 42 - 39
simulator/flowchart.md

@@ -10,47 +10,49 @@ flowchart LR
 	end
 	Generator_out --> DSI_inp
 	subgraph DSI
-		DSI_mqtt_out("mqtt_out")
-		DSI_out("out")
-		DSI_vgr_in("vgr_in")
 		DSI_mqtt_in("mqtt_in")
+		DSI_out("out")
+		DSI_mqtt_out("mqtt_out")
 		DSI_inp("inp")
+		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_in("color_in")
-		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_color_in("color_in")
+		ReaderStation_nfc_out("nfc_out")
 		ReaderStation_nfc_in ~~~ ReaderStation_nfc_out
 		ReaderStation_color_in ~~~ ReaderStation_color_out
+		ReaderStation_mqtt_in ~~~ ReaderStation_mqtt_out
 	end
 	ReaderStation_nfc_out --> VacuumGripper_nfc_in
 	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_ct_in("ct_in")
+		VacuumGripper_dsi_in("dsi_in")
 		VacuumGripper_mpo_out("mpo_out")
-		VacuumGripper_nfc_in("nfc_in")
+		VacuumGripper_dso_out("dso_out")
+		VacuumGripper_ct_in("ct_in")
 		VacuumGripper_sld_red_out("sld_red_out")
-		VacuumGripper_dsi_in("dsi_in")
-		VacuumGripper_color_sensor_in("color_sensor_in")
+		VacuumGripper_mqtt_out("mqtt_out")
+		VacuumGripper_nfc_in("nfc_in")
 		VacuumGripper_sld_blue_out("sld_blue_out")
-		VacuumGripper_sld_red_in("sld_red_in")
-		VacuumGripper_sld_white_out("sld_white_out")
-		VacuumGripper_sld_blue_in("sld_blue_in")
-		VacuumGripper_dso_out("dso_out")
+		VacuumGripper_color_sensor_in("color_sensor_in")
 		VacuumGripper_sld_white_in("sld_white_in")
-		VacuumGripper_mqtt_out("mqtt_out")
+		VacuumGripper_sld_red_in("sld_red_in")
 		VacuumGripper_mqtt_in("mqtt_in")
-		VacuumGripper_dsi_out("dsi_out")
-		VacuumGripper_ct_out("ct_out")
-		VacuumGripper_nfc_out("nfc_out")
 		VacuumGripper_dsi_in ~~~ VacuumGripper_dsi_out
 		VacuumGripper_ct_in ~~~ VacuumGripper_ct_out
 		VacuumGripper_nfc_in ~~~ VacuumGripper_nfc_out
@@ -71,11 +73,11 @@ flowchart LR
 	VacuumGripper_dso_out --> DSO_inp
 	VacuumGripper_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph Transporter
-		Transporter_left_in("left_in")
-		Transporter_right_in("right_in")
 		Transporter_right_out("right_out")
 		Transporter_left_out("left_out")
+		Transporter_left_in("left_in")
 		Transporter_mqtt_out("mqtt_out")
+		Transporter_right_in("right_in")
 		Transporter_left_in ~~~ Transporter_right_out
 		Transporter_right_in ~~~ Transporter_left_out
 	end
@@ -83,11 +85,11 @@ flowchart LR
 	Transporter_left_out --> HighBayWarehouse_inp
 	Transporter_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph HighBayWarehouse
-		HighBayWarehouse_mqtt_in("mqtt_in")
 		HighBayWarehouse_mqtt_out("mqtt_out")
-		HighBayWarehouse_inventory_out("inventory_out")
 		HighBayWarehouse_inp("inp")
 		HighBayWarehouse_out("out")
+		HighBayWarehouse_inventory_out("inventory_out")
+		HighBayWarehouse_mqtt_in("mqtt_in")
 		HighBayWarehouse_inp ~~~ HighBayWarehouse_out
 		HighBayWarehouse_mqtt_in ~~~ HighBayWarehouse_mqtt_out
 	end
@@ -95,8 +97,8 @@ flowchart LR
 	HighBayWarehouse_mqtt_out --> MQTTControlUnit_mqtt_in
 	HighBayWarehouse_inventory_out --> InventoryPublisher_inp
 	subgraph InventoryPublisher
-		InventoryPublisher_inp("inp")
 		InventoryPublisher_mqtt_out("mqtt_out")
+		InventoryPublisher_inp("inp")
 		InventoryPublisher_inp ~~~ InventoryPublisher_mqtt_out
 	end
 	InventoryPublisher_mqtt_out --> MQTTControlUnit_mqtt_in
@@ -107,20 +109,20 @@ flowchart LR
 		MPO_vgr_in ~~~ MPO_mqtt_out
 		subgraph MPO_oven
 			MPO_oven_gripper_out("gripper_out")
-			MPO_oven_vgr_in("vgr_in")
-			MPO_oven_mqtt_out("mqtt_out")
 			MPO_oven_gripper_in("gripper_in")
+			MPO_oven_mqtt_out("mqtt_out")
+			MPO_oven_vgr_in("vgr_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_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_oven_out("oven_out")
-			MPO_gripper_table_in("table_in")
 			MPO_gripper_oven_in ~~~ MPO_gripper_oven_out
 			MPO_gripper_table_in ~~~ MPO_gripper_table_out
 		end
@@ -150,14 +152,14 @@ flowchart LR
 	Conveyor_out --> SLD_inp
 	Conveyor_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph SLD
-		SLD_red_out("red_out")
-		SLD_blue_out("blue_out")
+		SLD_red_in("red_in")
 		SLD_mqtt_out("mqtt_out")
 		SLD_inp("inp")
-		SLD_white_in("white_in")
-		SLD_red_in("red_in")
+		SLD_blue_out("blue_out")
 		SLD_blue_in("blue_in")
 		SLD_white_out("white_out")
+		SLD_red_out("red_out")
+		SLD_white_in("white_in")
 		SLD_inp ~~~ SLD_white_out
 		SLD_white_in ~~~ SLD_red_out
 		SLD_red_in ~~~ SLD_blue_out
@@ -165,9 +167,9 @@ flowchart LR
 		subgraph SLD_conveyor
 			SLD_conveyor_inp("inp")
 			SLD_conveyor_out_white("out_white")
-			SLD_conveyor_out_red("out_red")
 			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
 		end
 		SLD_conveyor_out_white --> SLD_white_bay_sld_in
@@ -175,10 +177,10 @@ flowchart LR
 		SLD_conveyor_out_blue --> SLD_blue_bay_sld_in
 		SLD_conveyor_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_white_bay
-			SLD_white_bay_mqtt_out("mqtt_out")
-			SLD_white_bay_vgr_in("vgr_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_vgr_in("vgr_in")
 			SLD_white_bay_sld_in ~~~ SLD_white_bay_vgr_out
 			SLD_white_bay_vgr_in ~~~ SLD_white_bay_mqtt_out
 		end
@@ -186,19 +188,19 @@ flowchart LR
 		SLD_white_bay_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_red_bay
 			SLD_red_bay_vgr_out("vgr_out")
-			SLD_red_bay_sld_in("sld_in")
-			SLD_red_bay_mqtt_out("mqtt_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_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_sld_in("sld_in")
 			SLD_blue_bay_mqtt_out("mqtt_out")
 			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
 		end
@@ -214,24 +216,25 @@ flowchart LR
 	SLD_red_in --> SLD_red_bay_vgr_in
 	SLD_blue_in --> SLD_blue_bay_vgr_in
 	subgraph DSO
-		DSO_out("out")
 		DSO_mqtt_in("mqtt_in")
-		DSO_inp("inp")
 		DSO_mqtt_out("mqtt_out")
+		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_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
 	MQTTControlUnit_mqtt_out --> Generator_mqtt_in
 	MQTTControlUnit_mqtt_out --> DSI_mqtt_in
+	MQTTControlUnit_mqtt_out --> ReaderStation_mqtt_in
 	MQTTControlUnit_mqtt_out --> VacuumGripper_mqtt_in
 	MQTTControlUnit_mqtt_out --> HighBayWarehouse_mqtt_in
 	MQTTControlUnit_mqtt_out --> DSO_mqtt_in

+ 42 - 23
simulator/realtime_simulation.py

@@ -23,6 +23,10 @@ class MQTTSimulationBridge:
         self.sim_client.on_message = self._on_sim
         self.real_client.on_message = self._on_real
 
+        # vars to keep track of real factory state, and notifications to the simulation
+        self.is_new_wp_notified: bool = False # if there is new input, will be set to true to prevent double notification
+        self.is_dso_busy: bool = False # whether there is a workpiece in the DSO (output tray)
+
     def _on_sim(self, _cli, _u, msg):
         json_string = json.dumps({
             "topic": msg.topic,
@@ -30,49 +34,64 @@ class MQTTSimulationBridge:
             "origin": "sim"
         })
 
-        allowed_topics = ['simulation/spawn', 'simulation/get_status', 'simulation/clear_dso', 'f/o/order']
-        if msg.topic in allowed_topics and self.sim:
-            logger.info(f"SIM topic: {msg.topic}")
+        if self.sim:
+            logger.info(f"Received SIM MQTT message, topic: {msg.topic}")
             self.sim.realtime_interrupt(f"REALTIME_INTERRUPT {json_string}") # Interrupts can only send strings
 
     def _on_real(self, _cli, _u, msg):
         data = json.loads(msg.payload.decode())
         
-        # Directly pass through some messages
+        # --- Messages directly passed through to the simulation ---
         if msg.topic == "f/o/order": # orders placed in the real world should be passed to the simulation
-            json_string = json.dumps({
-                "topic": msg.topic,
-                "payload": msg.payload.decode(),
-                "origin": "real"
-            })
-            self.sim.realtime_interrupt(f"REALTIME_INTERRUPT {json_string}")
+            self._inject("f/o/order", data)
             return
 
-        # Inject selected real events as control messages directly into the simulation
-        # -> Topic: simulation/ctrl/<station>
-
-        if msg.topic == "f/i/state/dsi" and data.get("active") == 1:
-            action = "workpiece_arrived"
-            self._inject("simulation/ctrl/dsi", action)
-
-        elif msg.topic == "f/i/state/dso" and data.get("active") == 0: # TODO: check on state change
-            action = "cleared"
-            self._inject("simulation/ctrl/dso", action)
+        # --- Messages that are used to control the simulation ---
+        # [Topic]: simulation/ctrl/<station>
+        command: dict = {"action": ""}
+
+        # Input: new workpiece
+        if msg.topic == "f/i/state/dsi":
+            if data.get("active") == 1 and data.get("code") == 0: # new workpiece
+                if not self.is_new_wp_notified:
+                    command["action"] = "workpiece_arrived"
+                    self._inject("simulation/ctrl/dsi", command)
+                    self.is_new_wp_notified = True
+
+            elif data.get("active") == 1 and data.get("code") == 1: # workpiece is being picked up -> unset flag
+                self.is_new_wp_notified = False
+
+        # Output Clear 
+        elif msg.topic == "f/i/state/dso": # TODO: check on state change
+            if data.get("active") == 0 and data.get("code") == 1:
+                if self.is_dso_busy:
+                    command["action"] = "clear"
+                    self._inject("simulation/ctrl/dso", command) # simulation dso should be cleared (sync with reality)
+                self.is_dso_busy = False
+            elif data.get("active") == 0 and data.get("code") == 0:
+                self.is_dso_busy = True
 
         elif msg.topic == "f/i/nfc/ds": # TODO: check content if color or id update
-            self._inject("simualtion/ctrl/nfc", "color_update")
+            print(data)
+            if data.get("workpiece").get("type") == "NONE":
+                command["action"] = "id_update"
+                command["workpiece"] = data.get("workpiece")
+            elif data.get("workpiece"):
+                command["action"] = "color_update"
+                command["workpiece"] = data.get("workpiece")
+            
+            self._inject("simulation/ctrl/nfc", command)
 
         
     def _inject(self, topic: str, payload, origin: str='real'):
         """Injects a MQTT message into the simulation."""
         json_string = json.dumps({
                 "topic": topic,
-                "payload": {},
+                "payload": json.dumps(payload),
                 "origin": origin
             })
         self.sim.realtime_interrupt(f"REALTIME_INTERRUPT {json_string}")
 
-
     def start_clients(self):
         self.sim_client.connect(CFG.MQTT_SIM.HOST, CFG.MQTT_SIM.PORT)
         self.real_client.connect(CFG.MQTT_REAL.HOST, CFG.MQTT_REAL.PORT)