瀏覽代碼

Refactor + MPO tuning (messages now match reality)

anfeny 3 月之前
父節點
當前提交
e1a64b8b2a

+ 7 - 3
simulator/devs_models/simple_collector.py

@@ -1,17 +1,21 @@
 from pypdevs.DEVS import AtomicDEVS
 from loguru import logger
 
-class SimpleCollector(AtomicDEVS):
+class Collector(AtomicDEVS):
     """A simple collector which holds every received item in a list in its state"""
     def __init__(self, name):
-        super(SimpleCollector, self).__init__(name)
+        super(Collector, self).__init__(name)
         self.inp = self.addInPort("inp")
 
-        # keep track of nr of customers obtained
+        # keep track of items received
         self.state = {
             "items": []
         }
 
+    def get_contents(self):
+        """ Get the current contents of the collector """
+        return self.state["items"]
+
     def extTransition(self, inputs):
         if self.inp in inputs:
             new_item = inputs[self.inp]

+ 3 - 3
simulator/devs_models/input_bay.py

@@ -27,12 +27,12 @@ class InputBayState:
     visual_update_pending: bool = False
 
 
-class InputBay(AtomicDEVS):
-    """ Input bay which holds a single workpiece """
+class DSI(AtomicDEVS):
+    """ Input bay which holds a single workpiece (Delivery Station Input) """
 
     def __init__(self, name: str):
         # name needs to be unique to refer to it
-        super(InputBay, self).__init__(name)
+        super(DSI, self).__init__(name)
         self.inp = self.addInPort("inp")
         self.out = self.addOutPort("out")
         self.mqtt_in = self.addInPort("mqtt_in")

+ 8 - 4
simulator/devs_models/dso.py

@@ -14,6 +14,7 @@ class DsoPhase(TimedPhaseEnum):
     IDLE = ('IDLE', INFINITY)
     WP_RECEIVED = ('WP_RECEIVED', 0.2) # delay before sending a message
     AWAIT_PICKUP = ('AWAIT_PICKUP', INFINITY) # await pickup by user
+    PICKUP = ('PICKUP', 0.0) # immediately handover wp to collector
 
 @dataclass
 class DsoState:
@@ -89,10 +90,7 @@ 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 ?
+                self.change_phase(DsoPhase.PICKUP)
             elif msg.topic == "simulation/ctrl/all" and msg.payload.get("action") == "refresh":
                 self.state.status_requested = True
                 self.state.visual_update_pending = True
@@ -111,6 +109,9 @@ class DSO(AtomicDEVS):
 
         if self.state.status_requested or self.state.phase == DsoPhase.WP_RECEIVED:
             return {self.mqtt_out: [self.get_status_message(use_next_phase=False)]}
+        
+        if self.state.phase == DsoPhase.PICKUP:
+            return {self.out: [self.state.workpiece]} # output the workpiece to the collector
 
         return {}
 
@@ -122,6 +123,9 @@ class DSO(AtomicDEVS):
         if self.state.status_requested:
             self.state.status_requested = False # if requested, we've handled, so unset
             return self.state
+        
+        if self.state.phase == DsoPhase.PICKUP:
+            self.state.workpiece = None # clear the workpiece after handing it over
 
         # Transition to next state
         self.change_phase(self.state.phase.next())

+ 5 - 2
simulator/devs_models/fischertechnik_factory.py

@@ -1,7 +1,7 @@
 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.input_bay import InputBay
+from devs_models.dsi import DSI
 from devs_models.mpo import MPO
 from devs_models.mqtt_control_unit import MQTTControlUnit
 from devs_models.reader_station import ReaderStation
@@ -9,6 +9,7 @@ from devs_models.simple_conveyor import SimpleConveyor
 from devs_models.sld import SLD
 from devs_models.dso import DSO
 from devs_models.vacuum_gripper import VacuumGripper
+from devs_models.collector import Collector
 from pypdevs.DEVS import CoupledDEVS
 
 
@@ -19,7 +20,7 @@ class FischertechnikFactory(CoupledDEVS):
         self.REALTIME_OBSERVED = self.addOutPort('REALTIME_OBSERVED')
         # Create models
         self.gen: Generator = self.addSubModel(Generator("Generator", automatic=automatic))
-        self.dsi: InputBay = self.addSubModel(InputBay("DSI"))
+        self.dsi: DSI = self.addSubModel(DSI("DSI"))
         self.reader: ReaderStation = self.addSubModel(ReaderStation("ReaderStation"))  # NFC and Color reading station
         self.vgr: VacuumGripper = self.addSubModel(VacuumGripper("VacuumGripper"))
         self.ct: CrateTransporter = self.addSubModel(CrateTransporter("Transporter"))
@@ -30,6 +31,7 @@ class FischertechnikFactory(CoupledDEVS):
         self.sld: SLD = self.addSubModel(SLD("SLD"))
         self.dso: DSO = self.addSubModel(DSO("DSO"))
         self.mqtt_control: MQTTControlUnit = self.addSubModel(MQTTControlUnit("MQTTControlUnit"))
+        self.collector: Collector = self.addSubModel(Collector("Collector"))
 
         # Connect ports
         self.connectPorts(self.gen.out, self.dsi.inp)
@@ -54,6 +56,7 @@ class FischertechnikFactory(CoupledDEVS):
         self.connectPorts(self.vgr.sld_out["BLUE"], self.sld.blue_in)
         self.connectPorts(self.vgr.sld_out["WHITE"], self.sld.white_in)
         self.connectPorts(self.vgr.dso_out, self.dso.inp)
+        self.connectPorts(self.dso.out, self.collector.inp)
 
 
         # Connect MQTT ports

+ 3 - 1
simulator/devs_models/mpo_gripper.py

@@ -123,6 +123,8 @@ class MPOGripper(AtomicDEVS):
         if self.state.visual_update_pending:
             return {self.mqtt_out: [self.get_visual_update_data()]}
         if self.state.status_requested:
+            if self.state.phase == MPOGrPhase.IDLE:
+                return {} # do not output status when idle (other MPO parts likely not idle)
             return {self.mqtt_out: [self.get_status_message(use_next_phase=False)]}
 
         if self.state.phase == MPOGrPhase.MOVE_TO_OVEN:
@@ -130,7 +132,7 @@ class MPOGripper(AtomicDEVS):
         elif self.state.phase == MPOGrPhase.PICKUP_WP:
             return {self.oven_out: {}, self.mqtt_out: [self.get_status_message()]} # signal oven that we are ready to pick up the workpiece
         elif self.state.phase == MPOGrPhase.MOVE_TO_SAW_TABLE:
-            return {self.table_out: [self.state.workpiece], self.mqtt_out: [self.get_status_message()]} # output workpiece to table
+            return {self.table_out: [self.state.workpiece]} # output workpiece to table, do not signal own status
 
         return {self.mqtt_out: [self.get_status_message()]}
 

+ 4 - 0
simulator/devs_models/mpo_oven.py

@@ -133,6 +133,8 @@ class MPOOven(AtomicDEVS):
             return {self.mqtt_out: [self.get_visual_update_data()]}
 
         if self.state.status_requested:
+            if self.state.phase == BakeOvenSteps.IDLE:
+                return {} # no status update needed when idle (other MPO parts likely not idle)
             return {self.mqtt_out: [self.get_status_message(use_next_phase=False)]}
 
         if self.state.phase == BakeOvenSteps.DETECTION:
@@ -144,6 +146,8 @@ class MPOOven(AtomicDEVS):
         elif self.state.phase == BakeOvenSteps.OUTPUT_WP:
             return {self.gripper_out: [self.state.workpiece], self.mqtt_out: [self.get_status_message()]}
 
+        if self.state.phase.next() == BakeOvenSteps.IDLE:
+            return {} # no status update needed when idle (other MPO parts likely not idle)
         return {self.mqtt_out: [self.get_status_message()]}
 
     def intTransition(self):

+ 1 - 1
simulator/devs_models/mpo_table_saw.py

@@ -118,7 +118,7 @@ class MPOTableSaw(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
 

+ 1 - 1
simulator/devs_models/sld.py

@@ -4,7 +4,7 @@ from loguru import logger
 
 from data_models.mqtt_message import MqttMessage
 from data_models.workpiece import Workpiece, WorkpieceColor
-from devs_models.holding_bay import HoldingBay
+from devs_models.sld_holding_bay import HoldingBay
 from pypdevs.DEVS import AtomicDEVS, CoupledDEVS
 from pypdevs.infinity import INFINITY
 from utils.get_timestamp import get_timestamp

simulator/devs_models/holding_bay.py → simulator/devs_models/sld_holding_bay.py


+ 55 - 51
simulator/flowchart.md

@@ -4,29 +4,29 @@ title: FischertechnikFactory
 ---
 flowchart LR
 	subgraph Generator
-		Generator_out("out")
 		Generator_mqtt_in("mqtt_in")
+		Generator_out("out")
 		Generator_mqtt_in ~~~ Generator_out
 	end
 	Generator_out --> DSI_inp
 	subgraph DSI
 		DSI_mqtt_in("mqtt_in")
 		DSI_vgr_in("vgr_in")
-		DSI_out("out")
-		DSI_inp("inp")
 		DSI_mqtt_out("mqtt_out")
+		DSI_inp("inp")
+		DSI_out("out")
 		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_nfc_out("nfc_out")
-		ReaderStation_nfc_in("nfc_in")
-		ReaderStation_mqtt_out("mqtt_out")
 		ReaderStation_mqtt_in("mqtt_in")
+		ReaderStation_mqtt_out("mqtt_out")
+		ReaderStation_nfc_in("nfc_in")
+		ReaderStation_nfc_out("nfc_out")
+		ReaderStation_color_in("color_in")
+		ReaderStation_color_out("color_out")
 		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_nfc_in("nfc_in")
 		VacuumGripper_mqtt_out("mqtt_out")
-		VacuumGripper_ct_out("ct_out")
-		VacuumGripper_ct_in("ct_in")
+		VacuumGripper_nfc_in("nfc_in")
 		VacuumGripper_dso_out("dso_out")
-		VacuumGripper_nfc_out("nfc_out")
+		VacuumGripper_color_sensor_in("color_sensor_in")
+		VacuumGripper_sld_red_in("sld_red_in")
+		VacuumGripper_ct_in("ct_in")
 		VacuumGripper_dsi_in("dsi_in")
-		VacuumGripper_sld_white_in("sld_white_in")
 		VacuumGripper_sld_blue_in("sld_blue_in")
+		VacuumGripper_ct_out("ct_out")
+		VacuumGripper_sld_white_in("sld_white_in")
 		VacuumGripper_sld_white_out("sld_white_out")
-		VacuumGripper_color_sensor_out("color_sensor_out")
-		VacuumGripper_color_sensor_in("color_sensor_in")
-		VacuumGripper_sld_blue_out("sld_blue_out")
+		VacuumGripper_nfc_out("nfc_out")
 		VacuumGripper_mqtt_in("mqtt_in")
-		VacuumGripper_sld_red_in("sld_red_in")
+		VacuumGripper_color_sensor_out("color_sensor_out")
 		VacuumGripper_dsi_out("dsi_out")
-		VacuumGripper_sld_red_out("sld_red_out")
 		VacuumGripper_mpo_out("mpo_out")
+		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
@@ -73,11 +73,11 @@ flowchart LR
 	VacuumGripper_dso_out --> DSO_inp
 	VacuumGripper_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph Transporter
-		Transporter_left_out("left_out")
-		Transporter_left_in("left_in")
 		Transporter_mqtt_out("mqtt_out")
-		Transporter_right_in("right_in")
+		Transporter_left_in("left_in")
+		Transporter_left_out("left_out")
 		Transporter_mqtt_in("mqtt_in")
+		Transporter_right_in("right_in")
 		Transporter_right_out("right_out")
 		Transporter_left_in ~~~ Transporter_right_out
 		Transporter_right_in ~~~ Transporter_left_out
@@ -87,11 +87,11 @@ flowchart LR
 	Transporter_left_out --> HighBayWarehouse_inp
 	Transporter_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph HighBayWarehouse
-		HighBayWarehouse_out("out")
-		HighBayWarehouse_mqtt_in("mqtt_in")
-		HighBayWarehouse_mqtt_out("mqtt_out")
-		HighBayWarehouse_inventory_out("inventory_out")
 		HighBayWarehouse_inp("inp")
+		HighBayWarehouse_inventory_out("inventory_out")
+		HighBayWarehouse_mqtt_out("mqtt_out")
+		HighBayWarehouse_mqtt_in("mqtt_in")
+		HighBayWarehouse_out("out")
 		HighBayWarehouse_inp ~~~ HighBayWarehouse_out
 		HighBayWarehouse_mqtt_in ~~~ HighBayWarehouse_mqtt_out
 	end
@@ -105,30 +105,30 @@ flowchart LR
 	end
 	InventoryPublisher_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MPO
-		MPO_mqtt_out("mqtt_out")
-		MPO_conveyor_out("conveyor_out")
 		MPO_mqtt_in("mqtt_in")
 		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_mqtt_out("mqtt_out")
-			MPO_oven_mqtt_in("mqtt_in")
 			MPO_oven_gripper_out("gripper_out")
 			MPO_oven_gripper_in("gripper_in")
 			MPO_oven_vgr_in("vgr_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_oven_out("oven_out")
-			MPO_gripper_oven_in("oven_in")
+			MPO_gripper_table_in("table_in")
 			MPO_gripper_mqtt_out("mqtt_out")
+			MPO_gripper_oven_out("oven_out")
 			MPO_gripper_mqtt_in("mqtt_in")
+			MPO_gripper_oven_in("oven_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
@@ -137,11 +137,11 @@ flowchart LR
 		MPO_gripper_table_out --> MPO_saw_gripper_in
 		MPO_gripper_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_saw
-			MPO_saw_gripper_in("gripper_in")
-			MPO_saw_gripper_out("gripper_out")
-			MPO_saw_conveyor_out("conveyor_out")
 			MPO_saw_mqtt_in("mqtt_in")
+			MPO_saw_gripper_out("gripper_out")
 			MPO_saw_mqtt_out("mqtt_out")
+			MPO_saw_conveyor_out("conveyor_out")
+			MPO_saw_gripper_in("gripper_in")
 			MPO_saw_gripper_in ~~~ MPO_saw_gripper_out
 			MPO_saw_mqtt_in ~~~ MPO_saw_conveyor_out
 		end
@@ -156,9 +156,9 @@ flowchart LR
 	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_out("out")
 		Conveyor_mqtt_out("mqtt_out")
 		Conveyor_inp ~~~ Conveyor_out
 		Conveyor_mqtt_in ~~~ Conveyor_mqtt_out
@@ -166,26 +166,26 @@ flowchart LR
 	Conveyor_out --> SLD_inp
 	Conveyor_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph SLD
-		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_blue_out("blue_out")
 		SLD_mqtt_out("mqtt_out")
 		SLD_inp("inp")
 		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_inp ~~~ SLD_white_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_white("out_white")
+			SLD_conveyor_mqtt_out("mqtt_out")
 			SLD_conveyor_mqtt_in("mqtt_in")
 			SLD_conveyor_inp("inp")
 			SLD_conveyor_out_blue("out_blue")
 			SLD_conveyor_out_red("out_red")
-			SLD_conveyor_mqtt_out("mqtt_out")
-			SLD_conveyor_out_white("out_white")
 			SLD_conveyor_inp ~~~ SLD_conveyor_out_white
 			SLD_conveyor_mqtt_in ~~~ SLD_conveyor_out_red
 		end
@@ -194,33 +194,33 @@ 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_sld_in("sld_in")
-			SLD_white_bay_vgr_in("vgr_in")
 			SLD_white_bay_mqtt_in("mqtt_in")
+			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_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_mqtt_in("mqtt_in")
+			SLD_red_bay_vgr_out("vgr_out")
 			SLD_red_bay_vgr_in("vgr_in")
 			SLD_red_bay_sld_in("sld_in")
-			SLD_red_bay_vgr_out("vgr_out")
 			SLD_red_bay_mqtt_out("mqtt_out")
+			SLD_red_bay_mqtt_in("mqtt_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_in("vgr_in")
-			SLD_blue_bay_sld_in("sld_in")
-			SLD_blue_bay_mqtt_in("mqtt_in")
 			SLD_blue_bay_mqtt_out("mqtt_out")
+			SLD_blue_bay_sld_in("sld_in")
+			SLD_blue_bay_vgr_in("vgr_in")
 			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_vgr_in ~~~ SLD_blue_bay_mqtt_out
 		end
@@ -240,19 +240,20 @@ flowchart LR
 	SLD_red_in --> SLD_red_bay_vgr_in
 	SLD_blue_in --> SLD_blue_bay_vgr_in
 	subgraph DSO
-		DSO_inp("inp")
-		DSO_mqtt_out("mqtt_out")
 		DSO_out("out")
 		DSO_mqtt_in("mqtt_in")
+		DSO_inp("inp")
+		DSO_mqtt_out("mqtt_out")
 		DSO_inp ~~~ DSO_out
 		DSO_mqtt_in ~~~ DSO_mqtt_out
 	end
+	DSO_out --> Collector_inp
 	DSO_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MQTTControlUnit
-		MQTTControlUnit_mqtt_out("mqtt_out")
 		MQTTControlUnit_REALTIME_INTERRUPT("REALTIME_INTERRUPT")
 		MQTTControlUnit_mqtt_in("mqtt_in")
 		MQTTControlUnit_REALTIME_OBSERVED("REALTIME_OBSERVED")
+		MQTTControlUnit_mqtt_out("mqtt_out")
 		MQTTControlUnit_mqtt_in ~~~ MQTTControlUnit_mqtt_out
 		MQTTControlUnit_REALTIME_INTERRUPT ~~~ MQTTControlUnit_REALTIME_OBSERVED
 	end
@@ -266,4 +267,7 @@ flowchart LR
 	MQTTControlUnit_mqtt_out --> SLD_mqtt_in
 	MQTTControlUnit_mqtt_out --> DSO_mqtt_in
 	MQTTControlUnit_REALTIME_OBSERVED --> FischertechnikFactory_REALTIME_OBSERVED
+	subgraph Collector
+		Collector_inp("inp")
+	end
 ```

+ 3 - 1
simulator/realtime_simulation.py

@@ -144,8 +144,10 @@ class MQTTSimulationBridge:
                 time.sleep(1)
         except KeyboardInterrupt:
             logger.info("Simulation stopped")
-            logger.info("Collected inventory:")
+            logger.info("HBW inventory:")
             logger.info(self.sim.model.get_inventory())
+            logger.info("Collector contents:")
+            logger.info(self.sim.model.collector.get_contents())
 
 
 if __name__ == "__main__":