Browse Source

Pull Generator out of the rest of the model, make spawn command a realtime interrupt (workpiece) and create an 'action replay' on the generator

anfeny 7 months ago
parent
commit
90f008e7a8

+ 13 - 10
simulator/devs_models/fischertechnik_factory.py

@@ -1,25 +1,29 @@
+from pypdevs.DEVS import CoupledDEVS
+
+from devs_models.collector import Collector
 from devs_models.crate_transporter import CrateTransporter
 from devs_models.crate_transporter import CrateTransporter
-from devs_models.generator import Generator
-from devs_models.high_bay_warehouse import HighBayWarehouse, InventoryPublisher
 from devs_models.dsi import DSI
 from devs_models.dsi import DSI
+from devs_models.dso import DSO
+from devs_models.high_bay_warehouse import HighBayWarehouse, InventoryPublisher
 from devs_models.mpo import MPO
 from devs_models.mpo import MPO
 from devs_models.mqtt_control_unit import MQTTControlUnit
 from devs_models.mqtt_control_unit import MQTTControlUnit
 from devs_models.reader_station import ReaderStation
 from devs_models.reader_station import ReaderStation
 from devs_models.simple_conveyor import SimpleConveyor
 from devs_models.simple_conveyor import SimpleConveyor
 from devs_models.sld import SLD
 from devs_models.sld import SLD
-from devs_models.dso import DSO
 from devs_models.vacuum_gripper import VacuumGripper
 from devs_models.vacuum_gripper import VacuumGripper
-from devs_models.collector import Collector
-from pypdevs.DEVS import CoupledDEVS
 
 
 
 
 class FischertechnikFactory(CoupledDEVS):
 class FischertechnikFactory(CoupledDEVS):
-    def __init__(self, name: str, automatic: bool = True):
+    def __init__(self, name: str):
         super(FischertechnikFactory, self).__init__(name)
         super(FischertechnikFactory, self).__init__(name)
+        # Input port for workpieces
+        self.inp = self.addInPort('inp')
+        # Input for MQTT messages
+        self.mqtt_in = self.addInPort('mqtt_in')
         # Add output port for MQTT
         # Add output port for MQTT
         self.REALTIME_OBSERVED = self.addOutPort('REALTIME_OBSERVED')
         self.REALTIME_OBSERVED = self.addOutPort('REALTIME_OBSERVED')
+
         # Create models
         # Create models
-        self.gen: Generator = self.addSubModel(Generator("Generator", automatic=automatic))
         self.dsi: DSI = self.addSubModel(DSI("DSI"))
         self.dsi: DSI = self.addSubModel(DSI("DSI"))
         self.reader: ReaderStation = self.addSubModel(ReaderStation("ReaderStation"))  # NFC and Color reading station
         self.reader: ReaderStation = self.addSubModel(ReaderStation("ReaderStation"))  # NFC and Color reading station
         self.vgr: VacuumGripper = self.addSubModel(VacuumGripper("VacuumGripper"))
         self.vgr: VacuumGripper = self.addSubModel(VacuumGripper("VacuumGripper"))
@@ -34,7 +38,7 @@ class FischertechnikFactory(CoupledDEVS):
         self.collector: Collector = self.addSubModel(Collector("Collector"))
         self.collector: Collector = self.addSubModel(Collector("Collector"))
 
 
         # Connect ports
         # Connect ports
-        self.connectPorts(self.gen.out, self.dsi.inp)
+        self.connectPorts(self.inp, self.dsi.inp)
         self.connectPorts(self.dsi.out, self.vgr.dsi_in)
         self.connectPorts(self.dsi.out, self.vgr.dsi_in)
         self.connectPorts(self.vgr.dsi_out, self.dsi.vgr_in)
         self.connectPorts(self.vgr.dsi_out, self.dsi.vgr_in)
         self.connectPorts(self.vgr.mpo_out, self.mpo.vgr_in)
         self.connectPorts(self.vgr.mpo_out, self.mpo.vgr_in)
@@ -58,9 +62,8 @@ class FischertechnikFactory(CoupledDEVS):
         self.connectPorts(self.vgr.dso_out, self.dso.inp)
         self.connectPorts(self.vgr.dso_out, self.dso.inp)
         self.connectPorts(self.dso.out, self.collector.inp)
         self.connectPorts(self.dso.out, self.collector.inp)
 
 
-
         # Connect MQTT ports
         # Connect MQTT ports
-        self.connectPorts(self.mqtt_control.mqtt_out, self.gen.mqtt_in)
+        self.connectPorts(self.mqtt_in, self.mqtt_control.REALTIME_INTERRUPT)
         self.connectPorts(self.mqtt_control.mqtt_out, self.dsi.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.reader.mqtt_in)
         self.connectPorts(self.mqtt_control.mqtt_out, self.vgr.mqtt_in)
         self.connectPorts(self.mqtt_control.mqtt_out, self.vgr.mqtt_in)

+ 27 - 48
simulator/devs_models/generator.py

@@ -1,74 +1,53 @@
-from dataclasses import dataclass
+from dataclasses import dataclass, field
 from loguru import logger
 from loguru import logger
 
 
 from data_models.mqtt_message import MqttMessage
 from data_models.mqtt_message import MqttMessage
-from data_models.workpiece import Workpiece, WorkpieceColor
+from data_models.workpiece import Workpiece
 from pypdevs.DEVS import AtomicDEVS
 from pypdevs.DEVS import AtomicDEVS
 from pypdevs.infinity import INFINITY
 from pypdevs.infinity import INFINITY
 
 
+@dataclass 
+class GeneratorInput:
+    """ Class representing an item that the generator needs to generate."""
+    time: float # generation time in seconds
+    item : Workpiece | MqttMessage
+
 
 
 @dataclass
 @dataclass
 class GeneratorState:
 class GeneratorState:
-    generation_delay: float  # the time after which the next workpiece will be generated
-    automatic: bool = True  # whether to automatically generate or wait for mqtt instructions
     curr_time: int = 0  # the current time (in simulation, in minutes)
     curr_time: int = 0  # the current time (in simulation, in minutes)
-    id_counter: int = 0  # id counter for workpieces
-    next_color: WorkpieceColor | None = None
-    next_id: str | None = None  # next workpiece id
     delta_t: float = INFINITY
     delta_t: float = INFINITY
+    generation_queue: list[GeneratorInput] = field(default_factory=list)  # queue of items to be generated
 
 
 
 
 class Generator(AtomicDEVS):
 class Generator(AtomicDEVS):
-    def __init__(self, name: str, automatic: bool, generation_delay: float = 60.0):
+    def __init__(self, name: str, items_to_generate: list[GeneratorInput]):
         # name needs to be unique to refer to it
         # name needs to be unique to refer to it
         super(Generator, self).__init__(name)
         super(Generator, self).__init__(name)
-        self.out = self.addOutPort("out")
-
-        self.mqtt_in = self.addInPort("mqtt_in")
-
-        self.state = GeneratorState(generation_delay=generation_delay, automatic=automatic)
-        if automatic:
-            self.state.next_color = [WorkpieceColor.BLUE, WorkpieceColor.RED, WorkpieceColor.WHITE][self.state.id_counter % 3]
-            self.state.delta_t = 0.0  # start immediately
-
-    def get_id(self) -> str:
-        identifier = self.state.id_counter
-        self.state.id_counter += 1
-        return str(identifier)
+        self.out = self.addOutPort("out") # out port for workpieces
+        self.mqtt_out = self.addOutPort("mqtt_out") # out port for MQTT messages
 
 
-    def extTransition(self, inputs):
-        self.state.delta_t -= self.elapsed
-
-        if self.mqtt_in in inputs:
-            message: MqttMessage = inputs[self.mqtt_in][0]
-            if message.topic == "simulation/spawn":
-                workpiece = message.payload['workpiece']
-                self.state.next_color = WorkpieceColor(workpiece['type'])
-                self.state.next_id = self.get_id()
-
-        return self.state  # important, return state
+        self.state = GeneratorState(generation_queue=items_to_generate)
 
 
     def timeAdvance(self):
     def timeAdvance(self):
-        if self.state.automatic:
-            return self.state.delta_t  # Next workpiece generation time
-        elif self.state.next_color:
-            return 0.0  # immediately generate the next workpiece
+        if self.state.generation_queue:
+            next_generation_time = self.state.generation_queue[0].time - self.state.curr_time
+            return next_generation_time  # Next item generation time
         else:
         else:
-            return INFINITY  # idle until mqtt input
+            return INFINITY  # idle
 
 
     def outputFnc(self):
     def outputFnc(self):
-        next_workpiece = Workpiece(id=self.state.next_id, color=self.state.next_color)
-        logger.trace(f"Generator '{self.name}' outputs: {next_workpiece}")
-        return {self.out: [next_workpiece]}
+        item = self.state.generation_queue[0]  # Get the next item to generate
+        # Output on correct port based on item type
+        if isinstance(item.item, Workpiece):
+            logger.trace(f"{type(self).__name__} '{self.name}' outputs workpiece: {item.item}")
+            return {self.out: [item.item]}
+        else: # MQTT
+            logger.trace(f"{type(self).__name__} '{self.name}' outputs MQTT message: {item.item}")
+            return {self.mqtt_out: [item.item]}
 
 
     def intTransition(self):
     def intTransition(self):
         self.state.curr_time += self.timeAdvance()
         self.state.curr_time += self.timeAdvance()
-        self.state.next_color = None  # we just output a workpiece
-
-        if self.state.automatic:
-            self.state.next_color = [WorkpieceColor.BLUE, WorkpieceColor.RED, WorkpieceColor.WHITE][self.state.id_counter % 3]
-            self.state.next_id = self.get_id()
-
-        # Set the delay between generating the next workpiece
-        self.state.delta_t = self.state.generation_delay
+        self.state.generation_queue.pop(0)  # Remove the generated item from the queue
+        
         return self.state  # important
         return self.state  # important

+ 1 - 0
simulator/devs_models/vacuum_gripper.py

@@ -278,6 +278,7 @@ class VacuumGripper(AtomicDEVS):
             self.state.dso_clear = (msg.payload["active"] == 0) # check if dso is clear or not
             self.state.dso_clear = (msg.payload["active"] == 0) # check if dso is clear or not
             if self.state.phase == OutputRoutine.AWAIT_DSO_CLEAR and self.state.dso_clear:
             if self.state.phase == OutputRoutine.AWAIT_DSO_CLEAR and self.state.dso_clear:
                 self.change_phase(self.state.phase.next())
                 self.change_phase(self.state.phase.next())
+        return None
 
 
 
 
     def extTransition(self, inputs):
     def extTransition(self, inputs):

+ 49 - 8
simulator/fast_simulation.py

@@ -1,24 +1,65 @@
-from devs_models.fischertechnik_factory import FischertechnikFactory
+import json
+from sys import stderr
+
+from loguru import logger
+from pypdevs.DEVS import CoupledDEVS
 from pypdevs.simulator import Simulator
 from pypdevs.simulator import Simulator
+
+from data_models.workpiece import Workpiece, WorkpieceColor
+from devs_models.fischertechnik_factory import FischertechnikFactory
+from devs_models.generator import Generator, GeneratorInput
 from utils.flowchart_generator import FlowchartGenerator
 from utils.flowchart_generator import FlowchartGenerator
-from loguru import logger
-from sys import stderr
+
+
+class FastSimModel(CoupledDEVS):
+    """" Generator + Fischertechnik Factory model for fast simulation. """
+
+    def __init__(self, name: str, items_to_generate: list[GeneratorInput]):
+        super(FastSimModel, self).__init__(name)
+        self.generator: Generator = self.addSubModel(Generator("Generator", items_to_generate))
+        self.factory: FischertechnikFactory = self.addSubModel(FischertechnikFactory("FischertechnikFactory"))
+
+        self.connectPorts(self.generator.out, self.factory.inp)
+        self.connectPorts(self.generator.mqtt_out, self.factory.mqtt_in)
+
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     # Enable logging traces:
     # Enable logging traces:
     logger.remove(0)
     logger.remove(0)
     logger.add(stderr, level="TRACE")
     logger.add(stderr, level="TRACE")
 
 
-    model = FischertechnikFactory("FischertechnikFactory")
+    order_string = {
+        "topic": "f/o/order",
+        "payload": json.dumps({"type": "RED"}),
+        "origin": "sim"
+    }
+    clear_dso_string = {
+        "topic": "simulation/ctrl/dso",
+        "payload": json.dumps({"action": "clear"}),
+        "origin": "sim"
+    }
+
+    items_to_gen = [
+        GeneratorInput(time=0, item=Workpiece(color=WorkpieceColor.RED, id='1')),
+        GeneratorInput(time=100, item=Workpiece(color=WorkpieceColor.BLUE, id='2')),
+        GeneratorInput(time=150, item=order_string),  # TODO: change MQTT passthrough
+        GeneratorInput(time=330, item=clear_dso_string),
+        GeneratorInput(time=335, item=Workpiece(color=WorkpieceColor.WHITE, id='3')),
+        GeneratorInput(time=420, item=Workpiece(color=WorkpieceColor.BLUE, id='4')),
+        GeneratorInput(time=450, item=Workpiece(color=WorkpieceColor.WHITE, id='5')),
+    ]
 
 
+    model = FastSimModel("FastSimModel", items_to_generate=items_to_gen)
     FlowchartGenerator(model).generate_file()
     FlowchartGenerator(model).generate_file()
 
 
     sim = Simulator(model)
     sim = Simulator(model)
     sim.setClassicDEVS()
     sim.setClassicDEVS()
-    sim.setTerminationTime(240)  # otherwise it will run forever; TerminationCondition also possible (uses a function)
-    #sim.setVerbose(filename=None)
+    sim.setTerminationTime(330)  # otherwise it will run forever; TerminationCondition also possible
+    # sim.setVerbose(filename=None)
     sim.simulate()
     sim.simulate()
 
 
     logger.info("Simulation finished")
     logger.info("Simulation finished")
-    logger.info("Warehouse collected:")
-    logger.info(model.get_inventory())
+    logger.info("HBW inventory:")
+    logger.info(sim.model.factory.get_inventory())
+    logger.info("Collector contents:")
+    logger.info(sim.model.factory.collector.get_contents())

+ 52 - 59
simulator/flowchart.md

@@ -3,30 +3,24 @@
 title: FischertechnikFactory
 title: FischertechnikFactory
 ---
 ---
 flowchart LR
 flowchart LR
-	subgraph Generator
-		Generator_mqtt_in("mqtt_in")
-		Generator_out("out")
-		Generator_mqtt_in ~~~ Generator_out
-	end
-	Generator_out --> DSI_inp
 	subgraph DSI
 	subgraph DSI
-		DSI_inp("inp")
-		DSI_mqtt_out("mqtt_out")
-		DSI_vgr_in("vgr_in")
 		DSI_out("out")
 		DSI_out("out")
 		DSI_mqtt_in("mqtt_in")
 		DSI_mqtt_in("mqtt_in")
+		DSI_mqtt_out("mqtt_out")
+		DSI_vgr_in("vgr_in")
+		DSI_inp("inp")
 		DSI_inp ~~~ DSI_out
 		DSI_inp ~~~ DSI_out
 		DSI_mqtt_in ~~~ DSI_mqtt_out
 		DSI_mqtt_in ~~~ DSI_mqtt_out
 	end
 	end
 	DSI_out --> VacuumGripper_dsi_in
 	DSI_out --> VacuumGripper_dsi_in
 	DSI_mqtt_out --> MQTTControlUnit_mqtt_in
 	DSI_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph ReaderStation
 	subgraph ReaderStation
-		ReaderStation_nfc_in("nfc_in")
-		ReaderStation_mqtt_in("mqtt_in")
 		ReaderStation_color_out("color_out")
 		ReaderStation_color_out("color_out")
-		ReaderStation_color_in("color_in")
-		ReaderStation_mqtt_out("mqtt_out")
 		ReaderStation_nfc_out("nfc_out")
 		ReaderStation_nfc_out("nfc_out")
+		ReaderStation_mqtt_out("mqtt_out")
+		ReaderStation_color_in("color_in")
+		ReaderStation_nfc_in("nfc_in")
+		ReaderStation_mqtt_in("mqtt_in")
 		ReaderStation_nfc_in ~~~ ReaderStation_nfc_out
 		ReaderStation_nfc_in ~~~ ReaderStation_nfc_out
 		ReaderStation_color_in ~~~ ReaderStation_color_out
 		ReaderStation_color_in ~~~ ReaderStation_color_out
 		ReaderStation_mqtt_in ~~~ ReaderStation_mqtt_out
 		ReaderStation_mqtt_in ~~~ ReaderStation_mqtt_out
@@ -35,24 +29,24 @@ flowchart LR
 	ReaderStation_color_out --> VacuumGripper_color_sensor_in
 	ReaderStation_color_out --> VacuumGripper_color_sensor_in
 	ReaderStation_mqtt_out --> MQTTControlUnit_mqtt_in
 	ReaderStation_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph VacuumGripper
 	subgraph VacuumGripper
-		VacuumGripper_sld_blue_in("sld_blue_in")
+		VacuumGripper_ct_out("ct_out")
+		VacuumGripper_ct_in("ct_in")
+		VacuumGripper_nfc_out("nfc_out")
+		VacuumGripper_color_sensor_out("color_sensor_out")
+		VacuumGripper_dsi_in("dsi_in")
+		VacuumGripper_mpo_out("mpo_out")
+		VacuumGripper_nfc_in("nfc_in")
 		VacuumGripper_sld_red_out("sld_red_out")
 		VacuumGripper_sld_red_out("sld_red_out")
+		VacuumGripper_color_sensor_in("color_sensor_in")
+		VacuumGripper_sld_blue_out("sld_blue_out")
 		VacuumGripper_sld_red_in("sld_red_in")
 		VacuumGripper_sld_red_in("sld_red_in")
 		VacuumGripper_sld_white_out("sld_white_out")
 		VacuumGripper_sld_white_out("sld_white_out")
-		VacuumGripper_mpo_out("mpo_out")
-		VacuumGripper_color_sensor_in("color_sensor_in")
-		VacuumGripper_sld_white_in("sld_white_in")
-		VacuumGripper_color_sensor_out("color_sensor_out")
-		VacuumGripper_nfc_in("nfc_in")
-		VacuumGripper_nfc_out("nfc_out")
-		VacuumGripper_ct_in("ct_in")
 		VacuumGripper_mqtt_out("mqtt_out")
 		VacuumGripper_mqtt_out("mqtt_out")
-		VacuumGripper_ct_out("ct_out")
-		VacuumGripper_dsi_out("dsi_out")
-		VacuumGripper_mqtt_in("mqtt_in")
+		VacuumGripper_sld_blue_in("sld_blue_in")
 		VacuumGripper_dso_out("dso_out")
 		VacuumGripper_dso_out("dso_out")
-		VacuumGripper_sld_blue_out("sld_blue_out")
-		VacuumGripper_dsi_in("dsi_in")
+		VacuumGripper_sld_white_in("sld_white_in")
+		VacuumGripper_mqtt_in("mqtt_in")
+		VacuumGripper_dsi_out("dsi_out")
 		VacuumGripper_dsi_in ~~~ VacuumGripper_dsi_out
 		VacuumGripper_dsi_in ~~~ VacuumGripper_dsi_out
 		VacuumGripper_ct_in ~~~ VacuumGripper_ct_out
 		VacuumGripper_ct_in ~~~ VacuumGripper_ct_out
 		VacuumGripper_nfc_in ~~~ VacuumGripper_nfc_out
 		VacuumGripper_nfc_in ~~~ VacuumGripper_nfc_out
@@ -74,11 +68,11 @@ flowchart LR
 	VacuumGripper_mqtt_out --> MQTTControlUnit_mqtt_in
 	VacuumGripper_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph Transporter
 	subgraph Transporter
 		Transporter_right_out("right_out")
 		Transporter_right_out("right_out")
-		Transporter_right_in("right_in")
-		Transporter_left_out("left_out")
 		Transporter_left_in("left_in")
 		Transporter_left_in("left_in")
-		Transporter_mqtt_out("mqtt_out")
 		Transporter_mqtt_in("mqtt_in")
 		Transporter_mqtt_in("mqtt_in")
+		Transporter_left_out("left_out")
+		Transporter_mqtt_out("mqtt_out")
+		Transporter_right_in("right_in")
 		Transporter_left_in ~~~ Transporter_right_out
 		Transporter_left_in ~~~ Transporter_right_out
 		Transporter_right_in ~~~ Transporter_left_out
 		Transporter_right_in ~~~ Transporter_left_out
 		Transporter_mqtt_in ~~~ Transporter_mqtt_out
 		Transporter_mqtt_in ~~~ Transporter_mqtt_out
@@ -87,11 +81,11 @@ flowchart LR
 	Transporter_left_out --> HighBayWarehouse_inp
 	Transporter_left_out --> HighBayWarehouse_inp
 	Transporter_mqtt_out --> MQTTControlUnit_mqtt_in
 	Transporter_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph HighBayWarehouse
 	subgraph HighBayWarehouse
-		HighBayWarehouse_mqtt_in("mqtt_in")
-		HighBayWarehouse_mqtt_out("mqtt_out")
 		HighBayWarehouse_inventory_out("inventory_out")
 		HighBayWarehouse_inventory_out("inventory_out")
+		HighBayWarehouse_mqtt_out("mqtt_out")
 		HighBayWarehouse_inp("inp")
 		HighBayWarehouse_inp("inp")
 		HighBayWarehouse_out("out")
 		HighBayWarehouse_out("out")
+		HighBayWarehouse_mqtt_in("mqtt_in")
 		HighBayWarehouse_inp ~~~ HighBayWarehouse_out
 		HighBayWarehouse_inp ~~~ HighBayWarehouse_out
 		HighBayWarehouse_mqtt_in ~~~ HighBayWarehouse_mqtt_out
 		HighBayWarehouse_mqtt_in ~~~ HighBayWarehouse_mqtt_out
 	end
 	end
@@ -99,22 +93,22 @@ flowchart LR
 	HighBayWarehouse_mqtt_out --> MQTTControlUnit_mqtt_in
 	HighBayWarehouse_mqtt_out --> MQTTControlUnit_mqtt_in
 	HighBayWarehouse_inventory_out --> InventoryPublisher_inp
 	HighBayWarehouse_inventory_out --> InventoryPublisher_inp
 	subgraph InventoryPublisher
 	subgraph InventoryPublisher
-		InventoryPublisher_inp("inp")
 		InventoryPublisher_mqtt_out("mqtt_out")
 		InventoryPublisher_mqtt_out("mqtt_out")
+		InventoryPublisher_inp("inp")
 		InventoryPublisher_inp ~~~ InventoryPublisher_mqtt_out
 		InventoryPublisher_inp ~~~ InventoryPublisher_mqtt_out
 	end
 	end
 	InventoryPublisher_mqtt_out --> MQTTControlUnit_mqtt_in
 	InventoryPublisher_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MPO
 	subgraph MPO
-		MPO_mqtt_out("mqtt_out")
-		MPO_vgr_in("vgr_in")
-		MPO_conveyor_out("conveyor_out")
 		MPO_mqtt_in("mqtt_in")
 		MPO_mqtt_in("mqtt_in")
+		MPO_conveyor_out("conveyor_out")
+		MPO_vgr_in("vgr_in")
+		MPO_mqtt_out("mqtt_out")
 		MPO_vgr_in ~~~ MPO_mqtt_out
 		MPO_vgr_in ~~~ MPO_mqtt_out
 		MPO_mqtt_in ~~~ MPO_conveyor_out
 		MPO_mqtt_in ~~~ MPO_conveyor_out
 		subgraph MPO_oven
 		subgraph MPO_oven
+			MPO_oven_mqtt_in("mqtt_in")
 			MPO_oven_gripper_out("gripper_out")
 			MPO_oven_gripper_out("gripper_out")
 			MPO_oven_gripper_in("gripper_in")
 			MPO_oven_gripper_in("gripper_in")
-			MPO_oven_mqtt_in("mqtt_in")
 			MPO_oven_vgr_in("vgr_in")
 			MPO_oven_vgr_in("vgr_in")
 			MPO_oven_mqtt_out("mqtt_out")
 			MPO_oven_mqtt_out("mqtt_out")
 			MPO_oven_vgr_in ~~~ MPO_oven_gripper_out
 			MPO_oven_vgr_in ~~~ MPO_oven_gripper_out
@@ -123,12 +117,12 @@ flowchart LR
 		MPO_oven_gripper_out --> MPO_gripper_oven_in
 		MPO_oven_gripper_out --> MPO_gripper_oven_in
 		MPO_oven_mqtt_out --> MPO_mqtt_out
 		MPO_oven_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_gripper
 		subgraph MPO_gripper
+			MPO_gripper_mqtt_in("mqtt_in")
 			MPO_gripper_table_out("table_out")
 			MPO_gripper_table_out("table_out")
+			MPO_gripper_oven_in("oven_in")
 			MPO_gripper_table_in("table_in")
 			MPO_gripper_table_in("table_in")
 			MPO_gripper_oven_out("oven_out")
 			MPO_gripper_oven_out("oven_out")
 			MPO_gripper_mqtt_out("mqtt_out")
 			MPO_gripper_mqtt_out("mqtt_out")
-			MPO_gripper_oven_in("oven_in")
-			MPO_gripper_mqtt_in("mqtt_in")
 			MPO_gripper_oven_in ~~~ MPO_gripper_oven_out
 			MPO_gripper_oven_in ~~~ MPO_gripper_oven_out
 			MPO_gripper_table_in ~~~ MPO_gripper_table_out
 			MPO_gripper_table_in ~~~ MPO_gripper_table_out
 			MPO_gripper_mqtt_in ~~~ MPO_gripper_mqtt_out
 			MPO_gripper_mqtt_in ~~~ MPO_gripper_mqtt_out
@@ -138,10 +132,10 @@ flowchart LR
 		MPO_gripper_mqtt_out --> MPO_mqtt_out
 		MPO_gripper_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_saw
 		subgraph MPO_saw
 			MPO_saw_gripper_in("gripper_in")
 			MPO_saw_gripper_in("gripper_in")
-			MPO_saw_gripper_out("gripper_out")
 			MPO_saw_mqtt_out("mqtt_out")
 			MPO_saw_mqtt_out("mqtt_out")
-			MPO_saw_conveyor_out("conveyor_out")
 			MPO_saw_mqtt_in("mqtt_in")
 			MPO_saw_mqtt_in("mqtt_in")
+			MPO_saw_conveyor_out("conveyor_out")
+			MPO_saw_gripper_out("gripper_out")
 			MPO_saw_gripper_in ~~~ MPO_saw_gripper_out
 			MPO_saw_gripper_in ~~~ MPO_saw_gripper_out
 			MPO_saw_mqtt_in ~~~ MPO_saw_conveyor_out
 			MPO_saw_mqtt_in ~~~ MPO_saw_conveyor_out
 		end
 		end
@@ -156,36 +150,36 @@ flowchart LR
 	MPO_mqtt_in --> MPO_gripper_mqtt_in
 	MPO_mqtt_in --> MPO_gripper_mqtt_in
 	MPO_mqtt_in --> MPO_saw_mqtt_in
 	MPO_mqtt_in --> MPO_saw_mqtt_in
 	subgraph Conveyor
 	subgraph Conveyor
-		Conveyor_inp("inp")
-		Conveyor_mqtt_out("mqtt_out")
 		Conveyor_mqtt_in("mqtt_in")
 		Conveyor_mqtt_in("mqtt_in")
 		Conveyor_out("out")
 		Conveyor_out("out")
+		Conveyor_mqtt_out("mqtt_out")
+		Conveyor_inp("inp")
 		Conveyor_inp ~~~ Conveyor_out
 		Conveyor_inp ~~~ Conveyor_out
 		Conveyor_mqtt_in ~~~ Conveyor_mqtt_out
 		Conveyor_mqtt_in ~~~ Conveyor_mqtt_out
 	end
 	end
 	Conveyor_out --> SLD_inp
 	Conveyor_out --> SLD_inp
 	Conveyor_mqtt_out --> MQTTControlUnit_mqtt_in
 	Conveyor_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph SLD
 	subgraph SLD
-		SLD_mqtt_out("mqtt_out")
-		SLD_inp("inp")
 		SLD_blue_out("blue_out")
 		SLD_blue_out("blue_out")
+		SLD_red_out("red_out")
 		SLD_mqtt_in("mqtt_in")
 		SLD_mqtt_in("mqtt_in")
+		SLD_white_out("white_out")
+		SLD_inp("inp")
 		SLD_blue_in("blue_in")
 		SLD_blue_in("blue_in")
-		SLD_white_in("white_in")
 		SLD_red_in("red_in")
 		SLD_red_in("red_in")
-		SLD_white_out("white_out")
-		SLD_red_out("red_out")
+		SLD_white_in("white_in")
+		SLD_mqtt_out("mqtt_out")
 		SLD_inp ~~~ SLD_white_out
 		SLD_inp ~~~ SLD_white_out
 		SLD_mqtt_in ~~~ SLD_red_out
 		SLD_mqtt_in ~~~ SLD_red_out
 		SLD_white_in ~~~ SLD_blue_out
 		SLD_white_in ~~~ SLD_blue_out
 		SLD_red_in ~~~ SLD_mqtt_out
 		SLD_red_in ~~~ SLD_mqtt_out
 		subgraph SLD_conveyor
 		subgraph SLD_conveyor
+			SLD_conveyor_out_blue("out_blue")
+			SLD_conveyor_out_red("out_red")
 			SLD_conveyor_inp("inp")
 			SLD_conveyor_inp("inp")
-			SLD_conveyor_mqtt_out("mqtt_out")
 			SLD_conveyor_out_white("out_white")
 			SLD_conveyor_out_white("out_white")
-			SLD_conveyor_out_red("out_red")
 			SLD_conveyor_mqtt_in("mqtt_in")
 			SLD_conveyor_mqtt_in("mqtt_in")
-			SLD_conveyor_out_blue("out_blue")
+			SLD_conveyor_mqtt_out("mqtt_out")
 			SLD_conveyor_inp ~~~ SLD_conveyor_out_white
 			SLD_conveyor_inp ~~~ SLD_conveyor_out_white
 			SLD_conveyor_mqtt_in ~~~ SLD_conveyor_out_red
 			SLD_conveyor_mqtt_in ~~~ SLD_conveyor_out_red
 		end
 		end
@@ -205,10 +199,10 @@ flowchart LR
 		SLD_white_bay_vgr_out --> SLD_white_out
 		SLD_white_bay_vgr_out --> SLD_white_out
 		SLD_white_bay_mqtt_out --> SLD_mqtt_out
 		SLD_white_bay_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_red_bay
 		subgraph SLD_red_bay
-			SLD_red_bay_vgr_in("vgr_in")
 			SLD_red_bay_mqtt_in("mqtt_in")
 			SLD_red_bay_mqtt_in("mqtt_in")
 			SLD_red_bay_vgr_out("vgr_out")
 			SLD_red_bay_vgr_out("vgr_out")
 			SLD_red_bay_mqtt_out("mqtt_out")
 			SLD_red_bay_mqtt_out("mqtt_out")
+			SLD_red_bay_vgr_in("vgr_in")
 			SLD_red_bay_sld_in("sld_in")
 			SLD_red_bay_sld_in("sld_in")
 			SLD_red_bay_sld_in ~~~ SLD_red_bay_vgr_out
 			SLD_red_bay_sld_in ~~~ SLD_red_bay_vgr_out
 			SLD_red_bay_vgr_in ~~~ SLD_red_bay_mqtt_out
 			SLD_red_bay_vgr_in ~~~ SLD_red_bay_mqtt_out
@@ -217,10 +211,10 @@ flowchart LR
 		SLD_red_bay_mqtt_out --> SLD_mqtt_out
 		SLD_red_bay_mqtt_out --> SLD_mqtt_out
 		subgraph SLD_blue_bay
 		subgraph SLD_blue_bay
 			SLD_blue_bay_vgr_in("vgr_in")
 			SLD_blue_bay_vgr_in("vgr_in")
-			SLD_blue_bay_mqtt_in("mqtt_in")
-			SLD_blue_bay_vgr_out("vgr_out")
 			SLD_blue_bay_sld_in("sld_in")
 			SLD_blue_bay_sld_in("sld_in")
 			SLD_blue_bay_mqtt_out("mqtt_out")
 			SLD_blue_bay_mqtt_out("mqtt_out")
+			SLD_blue_bay_vgr_out("vgr_out")
+			SLD_blue_bay_mqtt_in("mqtt_in")
 			SLD_blue_bay_sld_in ~~~ SLD_blue_bay_vgr_out
 			SLD_blue_bay_sld_in ~~~ SLD_blue_bay_vgr_out
 			SLD_blue_bay_vgr_in ~~~ SLD_blue_bay_mqtt_out
 			SLD_blue_bay_vgr_in ~~~ SLD_blue_bay_mqtt_out
 		end
 		end
@@ -240,9 +234,9 @@ flowchart LR
 	SLD_red_in --> SLD_red_bay_vgr_in
 	SLD_red_in --> SLD_red_bay_vgr_in
 	SLD_blue_in --> SLD_blue_bay_vgr_in
 	SLD_blue_in --> SLD_blue_bay_vgr_in
 	subgraph DSO
 	subgraph DSO
-		DSO_out("out")
-		DSO_mqtt_in("mqtt_in")
 		DSO_inp("inp")
 		DSO_inp("inp")
+		DSO_mqtt_in("mqtt_in")
+		DSO_out("out")
 		DSO_mqtt_out("mqtt_out")
 		DSO_mqtt_out("mqtt_out")
 		DSO_inp ~~~ DSO_out
 		DSO_inp ~~~ DSO_out
 		DSO_mqtt_in ~~~ DSO_mqtt_out
 		DSO_mqtt_in ~~~ DSO_mqtt_out
@@ -250,14 +244,13 @@ flowchart LR
 	DSO_out --> Collector_inp
 	DSO_out --> Collector_inp
 	DSO_mqtt_out --> MQTTControlUnit_mqtt_in
 	DSO_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MQTTControlUnit
 	subgraph MQTTControlUnit
-		MQTTControlUnit_REALTIME_INTERRUPT("REALTIME_INTERRUPT")
-		MQTTControlUnit_mqtt_in("mqtt_in")
-		MQTTControlUnit_REALTIME_OBSERVED("REALTIME_OBSERVED")
 		MQTTControlUnit_mqtt_out("mqtt_out")
 		MQTTControlUnit_mqtt_out("mqtt_out")
+		MQTTControlUnit_REALTIME_OBSERVED("REALTIME_OBSERVED")
+		MQTTControlUnit_mqtt_in("mqtt_in")
+		MQTTControlUnit_REALTIME_INTERRUPT("REALTIME_INTERRUPT")
 		MQTTControlUnit_mqtt_in ~~~ MQTTControlUnit_mqtt_out
 		MQTTControlUnit_mqtt_in ~~~ MQTTControlUnit_mqtt_out
 		MQTTControlUnit_REALTIME_INTERRUPT ~~~ MQTTControlUnit_REALTIME_OBSERVED
 		MQTTControlUnit_REALTIME_INTERRUPT ~~~ MQTTControlUnit_REALTIME_OBSERVED
 	end
 	end
-	MQTTControlUnit_mqtt_out --> Generator_mqtt_in
 	MQTTControlUnit_mqtt_out --> DSI_mqtt_in
 	MQTTControlUnit_mqtt_out --> DSI_mqtt_in
 	MQTTControlUnit_mqtt_out --> ReaderStation_mqtt_in
 	MQTTControlUnit_mqtt_out --> ReaderStation_mqtt_in
 	MQTTControlUnit_mqtt_out --> VacuumGripper_mqtt_in
 	MQTTControlUnit_mqtt_out --> VacuumGripper_mqtt_in

+ 51 - 35
simulator/realtime_simulation.py

@@ -1,15 +1,19 @@
 import json
 import json
-import time
+import sys
 import threading
 import threading
-import paho.mqtt.client as mqtt
+import time
+from datetime import datetime
 from sys import stdout
 from sys import stdout
-from loguru import logger
+
+import paho.mqtt.client as mqtt
 from dotenv import load_dotenv
 from dotenv import load_dotenv
+from loguru import logger
 from pypdevs.simulator import Simulator
 from pypdevs.simulator import Simulator
+
+from config import load_config  # Local config.py using dotenv
+from data_models.workpiece import Workpiece, WorkpieceColor
 from devs_models.fischertechnik_factory import FischertechnikFactory
 from devs_models.fischertechnik_factory import FischertechnikFactory
 from utils.flowchart_generator import FlowchartGenerator
 from utils.flowchart_generator import FlowchartGenerator
-from config import load_config  # Local config.py using dotenv
-from datetime import datetime
 
 
 load_dotenv()
 load_dotenv()
 CFG = load_config()
 CFG = load_config()
@@ -24,12 +28,12 @@ class MQTTSimulationBridge:
         self.sim_client.on_message = self._on_sim
         self.sim_client.on_message = self._on_sim
         self.real_client.on_message = self._on_real
         self.real_client.on_message = self._on_real
 
 
-        self.trace_real = [] # (time_wall, topic, payload)
+        self.trace_real = []  # (time_wall, topic, payload)
         self.trace_sim = []
         self.trace_sim = []
 
 
         # vars to keep track of the real factory state, and notifications to the simulation
         # vars to keep track of the 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)
+        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 _log(self, origin, msg):
     def _log(self, origin, msg):
         """ Logs the received mqtt messages """
         """ Logs the received mqtt messages """
@@ -38,7 +42,7 @@ class MQTTSimulationBridge:
             self.trace_real.append((t, msg.topic, msg.payload.decode()))
             self.trace_real.append((t, msg.topic, msg.payload.decode()))
         else:
         else:
             self.trace_sim.append((t, msg.topic, msg.payload.decode()))
             self.trace_sim.append((t, msg.topic, msg.payload.decode()))
-    
+
     def log_trace_to_jsonl(self, filename_prefix="logs/trace_log"):
     def log_trace_to_jsonl(self, filename_prefix="logs/trace_log"):
         """
         """
         Outputs the trace_real and trace_sim data to a JSON Lines (.jsonl) file.
         Outputs the trace_real and trace_sim data to a JSON Lines (.jsonl) file.
@@ -55,7 +59,7 @@ class MQTTSimulationBridge:
                     if any(bad_topic in topic for bad_topic in topic_blacklist):
                     if any(bad_topic in topic for bad_topic in topic_blacklist):
                         continue
                         continue
                     timestamp_str = datetime.fromtimestamp(float(time_wall)).isoformat()
                     timestamp_str = datetime.fromtimestamp(float(time_wall)).isoformat()
-                    #timestamp_str = time_wall
+                    # timestamp_str = time_wall
                     try:
                     try:
                         payload = json.loads(payload)
                         payload = json.loads(payload)
                     except json.JSONDecodeError:
                     except json.JSONDecodeError:
@@ -79,18 +83,23 @@ class MQTTSimulationBridge:
         if all(bad_topic not in msg.topic for bad_topic in topic_blacklist):
         if all(bad_topic not in msg.topic for bad_topic in topic_blacklist):
             self._log("sim", msg)
             self._log("sim", msg)
 
 
-        if self.sim and msg.topic.startswith("simulation/"):
+        if not self.sim:
+            return
+        if msg.topic.startswith("simulation/spawn"):  # TODO: change
+            wp = Workpiece(id="?", color=WorkpieceColor.RED)  # TODO: get from payload
+            self._inject_wp_in_sim(wp)  # Injects the workpiece into the simulation
+        elif msg.topic.startswith("simulation/"):
             print(self.sim.server.getTime())
             print(self.sim.server.getTime())
             logger.info(f"Received SIM MQTT message, topic: {msg.topic}")
             logger.info(f"Received SIM MQTT message, topic: {msg.topic}")
-            self.sim.realtime_interrupt(f"REALTIME_INTERRUPT {json_string}") # Interrupts can only send strings
+            self.sim.realtime_interrupt(f"REALTIME_INTERRUPT {json_string}")  # Interrupts can only send strings
 
 
     def _on_real(self, _cli, _u, msg):
     def _on_real(self, _cli, _u, msg):
         data = json.loads(msg.payload.decode())
         data = json.loads(msg.payload.decode())
         self._log("real", msg)
         self._log("real", msg)
-        
+
         # --- Messages directly passed through to the simulation ---
         # --- 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
-            self._inject("f/o/order", data)
+        if msg.topic == "f/o/order":  # orders placed in the real world should be passed to the simulation
+            self._inject_mqtt_in_sim("f/o/order", data)
             return
             return
 
 
         # --- Messages that are used to control the simulation ---
         # --- Messages that are used to control the simulation ---
@@ -99,26 +108,27 @@ class MQTTSimulationBridge:
 
 
         # Input: new workpiece
         # Input: new workpiece
         if msg.topic == "f/i/state/dsi":
         if msg.topic == "f/i/state/dsi":
-            if data.get("active") == 1 and data.get("code") == 0: # new workpiece
+            if data.get("active") == 1 and data.get("code") == 0:  # new workpiece
                 if not self.is_new_wp_notified:
                 if not self.is_new_wp_notified:
                     command["action"] = "workpiece_arrived"
                     command["action"] = "workpiece_arrived"
-                    self._inject("simulation/ctrl/dsi", command)
+                    self._inject_mqtt_in_sim("simulation/ctrl/dsi", command)
                     self.is_new_wp_notified = True
                     self.is_new_wp_notified = True
 
 
-            elif data.get("active") == 1 and data.get("code") == 1: # workpiece is being picked up -> unset flag
+            elif data.get("active") == 1 and data.get("code") == 1:  # workpiece is being picked up -> unset flag
                 self.is_new_wp_notified = False
                 self.is_new_wp_notified = False
 
 
         # Output Clear 
         # Output Clear 
-        elif msg.topic == "f/i/state/dso": # TODO: check on state change
+        elif msg.topic == "f/i/state/dso":  # TODO: check on state change
             if data.get("active") == 0 and data.get("code") == 1:
             if data.get("active") == 0 and data.get("code") == 1:
                 if self.is_dso_busy:
                 if self.is_dso_busy:
                     command["action"] = "clear"
                     command["action"] = "clear"
-                    self._inject("simulation/ctrl/dso", command) # simulation dso should be cleared (sync with reality)
+                    self._inject_mqtt_in_sim("simulation/ctrl/dso",
+                                             command)  # simulation dso should be cleared (sync with reality)
                 self.is_dso_busy = False
                 self.is_dso_busy = False
             elif data.get("active") == 0 and data.get("code") == 0:
             elif data.get("active") == 0 and data.get("code") == 0:
                 self.is_dso_busy = True
                 self.is_dso_busy = True
 
 
-        elif msg.topic == "f/i/nfc/ds": # TODO: check content if color or id update
+        elif msg.topic == "f/i/nfc/ds":  # TODO: check content if color or id update
             print(data)
             print(data)
             if data.get("workpiece").get("type") == "NONE":
             if data.get("workpiece").get("type") == "NONE":
                 command["action"] = "id_update"
                 command["action"] = "id_update"
@@ -126,18 +136,22 @@ class MQTTSimulationBridge:
             elif data.get("workpiece"):
             elif data.get("workpiece"):
                 command["action"] = "color_update"
                 command["action"] = "color_update"
                 command["workpiece"] = data.get("workpiece")
                 command["workpiece"] = data.get("workpiece")
-            
-            self._inject("simulation/ctrl/nfc", command)
-      
-    def _inject(self, topic: str, payload, origin: str='real'):
+
+            self._inject_mqtt_in_sim("simulation/ctrl/nfc", command)
+
+    def _inject_mqtt_in_sim(self, topic: str, payload, origin: str = 'real'):
         """Injects a MQTT message into the simulation."""
         """Injects a MQTT message into the simulation."""
         json_string = json.dumps({
         json_string = json.dumps({
-                "topic": topic,
-                "payload": json.dumps(payload),
-                "origin": origin
-            })
+            "topic": topic,
+            "payload": json.dumps(payload),
+            "origin": origin
+        })
         self.sim.realtime_interrupt(f"REALTIME_INTERRUPT {json_string}")
         self.sim.realtime_interrupt(f"REALTIME_INTERRUPT {json_string}")
 
 
+    def _inject_wp_in_sim(self, wp: Workpiece):
+        sys.modules["pypdevs.basesimulator"].wp = wp  # Sadly this is needed...
+        self.sim.realtime_interrupt("WP_INTERRUPT (wp)")  # This will call the WP_INTERRUPT function in the simulator
+
     def start_clients(self):
     def start_clients(self):
         self.sim_client.connect(CFG.MQTT_SIM.HOST, CFG.MQTT_SIM.PORT)
         self.sim_client.connect(CFG.MQTT_SIM.HOST, CFG.MQTT_SIM.PORT)
         self.real_client.connect(CFG.MQTT_REAL.HOST, CFG.MQTT_REAL.PORT)
         self.real_client.connect(CFG.MQTT_REAL.HOST, CFG.MQTT_REAL.PORT)
@@ -145,8 +159,8 @@ class MQTTSimulationBridge:
         # self.sim_client.subscribe("simulation/#")
         # self.sim_client.subscribe("simulation/#")
         # self.real_client.subscribe("f/i/#")
         # self.real_client.subscribe("f/i/#")
         # self.real_client.subscribe("f/o/#") # orders
         # self.real_client.subscribe("f/o/#") # orders
-        self.sim_client.subscribe("#") # subscribe to all
-        self.real_client.subscribe("#") # subscribe to all
+        self.sim_client.subscribe("#")  # subscribe to all
+        self.real_client.subscribe("#")  # subscribe to all
         self.sim_client.loop_start()
         self.sim_client.loop_start()
         self.real_client.loop_start()
         self.real_client.loop_start()
 
 
@@ -159,15 +173,16 @@ class MQTTSimulationBridge:
 
 
     def start_simulation(self):
     def start_simulation(self):
         logger.info("Starting simulation...")
         logger.info("Starting simulation...")
-        model = FischertechnikFactory("FischertechnikFactory", automatic=False)
+        model = FischertechnikFactory("FischertechnikFactory")
         FlowchartGenerator(model).generate_file()
         FlowchartGenerator(model).generate_file()
 
 
         self.sim = Simulator(model)
         self.sim = Simulator(model)
         self.sim.setRealTime(True)
         self.sim.setRealTime(True)
-        self.sim.setRealTimePorts({"REALTIME_INTERRUPT": model.mqtt_control.REALTIME_INTERRUPT})
+        self.sim.setRealTimePorts(
+            {"REALTIME_INTERRUPT": model.mqtt_control.REALTIME_INTERRUPT, "WP_INTERRUPT": model.dsi.inp})
         self.sim.setListenPorts(model.REALTIME_OBSERVED, self.publish_mqtt)
         self.sim.setListenPorts(model.REALTIME_OBSERVED, self.publish_mqtt)
         self.sim.setRealTimePlatformThreads()
         self.sim.setRealTimePlatformThreads()
-        #self.sim.setVerbose(filename=None)
+        # self.sim.setVerbose(filename=None)
         self.sim.simulate()
         self.sim.simulate()
         logger.info("Simulation started")
         logger.info("Simulation started")
 
 
@@ -180,7 +195,7 @@ class MQTTSimulationBridge:
             colorize=True,
             colorize=True,
             level="TRACE"
             level="TRACE"
         )
         )
-        #level="DEBUG" if CFG.LOGGING else "INFO"
+        # level="DEBUG" if CFG.LOGGING else "INFO"
 
 
         # Start MQTT clients in a separate thread
         # Start MQTT clients in a separate thread
         threading.Thread(target=self.start_clients, daemon=True).start()
         threading.Thread(target=self.start_clients, daemon=True).start()
@@ -201,6 +216,7 @@ class MQTTSimulationBridge:
             self.log_trace_to_jsonl()
             self.log_trace_to_jsonl()
             logger.info("Traces logged successfully.")
             logger.info("Traces logged successfully.")
 
 
+
 if __name__ == "__main__":
 if __name__ == "__main__":
     bridge = MQTTSimulationBridge()
     bridge = MQTTSimulationBridge()
     bridge.run()
     bridge.run()