Browse Source

Add configuration based on .env files

anfeny 3 months ago
parent
commit
636bbb1578

+ 46 - 0
README.md

@@ -82,6 +82,52 @@ This will:
 
 - Launch the DEVS simulator in realtime-mode
 
+
+## Environment Configuration
+
+Both the **dashboard** and **simulator** use a `.env` file to configure MQTT broker addresses and logging settings. These files are automatically loaded when the app runs.
+
+Each subproject (`dashboard/` and `simulator/`) contains its own `.env` file.
+
+### How to configure
+
+1. Copy the example file in each subproject:
+   ```bash
+   cp dashboard/.env.example dashboard/.env
+   cp simulator/.env.example simulator/.env
+   ```
+
+2. Edit the values as needed
+
+Each .env file is automatically loaded at runtime by the corresponding Python code. No additional changes to launch scripts are required.
+
+| Variable         | Description                                           |
+| ---------------- | ----------------------------------------------------- |
+| `MQTT_REAL_HOST` | IP or hostname of the *real factory's* MQTT broker    |
+| `MQTT_REAL_PORT` | Port number of the real broker (default `1883`)       |
+| `MQTT_SIM_HOST`  | IP or hostname of the *simulation* MQTT broker        |
+| `MQTT_SIM_PORT`  | Port number of the simulation broker (default `1883`) |
+| `LOGGING`        | Set to `1` to enable debug logging (optional)         |
+
+### Example `.env` file
+
+```ini
+# Real system MQTT broker
+MQTT_REAL_HOST=192.168.0.10
+MQTT_REAL_PORT=1883
+
+# Simulation MQTT broker
+MQTT_SIM_HOST=127.0.0.1
+MQTT_SIM_PORT=1883
+
+# Logging (0 or 1)
+LOGGING=0
+```
+
+> **Reminder**: The .env file is duplicated across both dashboard/ and simulator/ — keep them in >sync if you make changes.
+
+
+
 ## Project Structure
 
 ```bash

+ 10 - 0
dashboard/.env.example

@@ -0,0 +1,10 @@
+# Real system MQTT broker
+MQTT_REAL_HOST=192.168.0.10
+MQTT_REAL_PORT=1883
+
+# Simulation MQTT broker
+MQTT_SIM_HOST=127.0.0.1
+MQTT_SIM_PORT=1883
+
+# Logging (0 or 1)
+LOGGING=0

+ 14 - 0
dashboard/README.md

@@ -28,6 +28,20 @@ class Config:
 
 You can run the flask server which runs the main program using the included script `run.sh`.
 
+## Configuration
+
+This project uses a `.env` file for runtime configuration.
+
+To set it up, copy the example file:
+
+```bash
+cp .env.example .env
+```
+
+You can edit the broker addresses or logging settings inside.
+
+For a full explanation of the variables, see the [see the main project README](../README.md)
+
 
 ## Testing
 

+ 28 - 15
dashboard/config.py

@@ -1,17 +1,30 @@
-from src.utils.app_modes import AppMode
+import os
+from dataclasses import dataclass
+from dotenv import load_dotenv
 
-# Production Configuration
-# class Config:
-#     MQTT_REAL_HOST = '192.168.0.10' # MQTT broker IP of real system (which you want to monitor)
-#     MQTT_REAL_PORT = 1883
-#     MQTT_SIM_HOST = '127.0.0.1' # MQTT broker IP of simulated system
-#     MQTT_SIM_PORT = 1883
-#     LOGGING = False # Enable logging mqtt messages to a file
+# Load variables from .env file (looks in current dir by default)
+load_dotenv()
 
-# Test Configuration - treats simulation as real
-class Config:
-    MQTT_REAL_HOST = '127.0.0.1' # MQTT broker IP of real system (which you want to monitor)
-    MQTT_REAL_PORT = 1884
-    MQTT_SIM_HOST = '127.0.0.1' # MQTT broker IP of simulated system
-    MQTT_SIM_PORT = 1883
-    LOGGING = False
+@dataclass
+class BrokerCfg:
+    HOST: str
+    PORT: int
+
+@dataclass
+class AppConfig:
+    MQTT_REAL: BrokerCfg
+    MQTT_SIM: BrokerCfg
+    LOGGING: bool = False
+
+def load_config() -> AppConfig:
+    return AppConfig(
+        MQTT_REAL=BrokerCfg(
+            HOST=os.getenv("MQTT_REAL_HOST", "192.168.0.10"),
+            PORT=int(os.getenv("MQTT_REAL_PORT", 1883))
+        ),
+        MQTT_SIM=BrokerCfg(
+            HOST=os.getenv("MQTT_SIM_HOST", "127.0.0.1"),
+            PORT=int(os.getenv("MQTT_SIM_PORT", 1883))
+        ),
+        LOGGING=os.getenv("LOGGING", "0") == "1"
+    )

+ 2 - 1
dashboard/requirements.txt

@@ -1,4 +1,5 @@
 flask
 flask-socketio
 paho-mqtt
-loguru
+loguru
+python-dotenv

+ 11 - 8
dashboard/src/__init__.py

@@ -2,22 +2,25 @@ from flask import Flask
 from flask_socketio import SocketIO
 
 from .utils.mqtt_handler import MQTTHandler
-from .utils.app_modes import AppMode
-from config import Config
+from config import load_config
+
+CFG = load_config()
 
 # Create a SocketIO instance that will be bound to the app later
 socketio = SocketIO()
 
 def create_app():
     app = Flask(__name__)
-    app.config.from_object(Config)
     socketio.init_app(app)
     
-    # Initialize the MQTT handler and store it in app config for easy access
-    mqtt_handler_real = MQTTHandler(socketio, origin='real', host=app.config['MQTT_REAL_HOST'], port=app.config['MQTT_REAL_PORT'], logging=app.config['LOGGING'])
-    mqtt_handler_sim = MQTTHandler(socketio, origin='sim', host=app.config['MQTT_SIM_HOST'], port=app.config['MQTT_SIM_PORT'], logging=app.config['LOGGING'])
-    app.config['mqtt_handler_real'] = mqtt_handler_real
-    app.config['mqtt_handler_sim'] = mqtt_handler_sim
+    # Store config object directly
+    app.config_object = CFG
+
+    # Setup MQTT handlers
+    mqtt_real = MQTTHandler(socketio, origin='real', host=CFG.MQTT_REAL.HOST, port=CFG.MQTT_REAL.PORT, logging=CFG.LOGGING)
+    mqtt_sim = MQTTHandler(socketio, origin='sim', host=CFG.MQTT_SIM.HOST, port=CFG.MQTT_SIM.PORT, logging=CFG.LOGGING)
+
+    app.mqtt_handlers = {'real': mqtt_real, 'sim': mqtt_sim}
     
     # Register the blueprint for routes
     from . import routes

+ 5 - 5
dashboard/src/routes.py

@@ -10,15 +10,15 @@ def root():
 
 @bp.route('/dashboard/sim')
 def sim_dashboard():
-    return render_template("dashboards/sim.html", mode=AppMode.SIM.value, config=current_app.config)
+    return render_template("dashboards/sim.html", mode=AppMode.SIM.value, config=current_app.config_object)
 
 @bp.route('/dashboard/monitor')
 def monitor_dashboard():
-    return render_template("dashboards/monitor.html", mode=AppMode.REAL.value, config=current_app.config)
+    return render_template("dashboards/monitor.html", mode=AppMode.REAL.value, config=current_app.config_object)
 
 @bp.route('/dashboard/hybrid')
 def hybrid_dashboard():
-    return render_template("dashboards/hybrid.html", mode=AppMode.HYBRID.value, config=current_app.config)
+    return render_template("dashboards/hybrid.html", mode=AppMode.HYBRID.value, config=current_app.config_object)
 
 
 @bp.route('/api/publish', methods=['POST'])
@@ -28,9 +28,9 @@ def publish():
     target = request.form.get('target', 'sim')
     
     if target == 'real':
-        mqtt_handler = current_app.config['mqtt_handler_real']
+        mqtt_handler = current_app.mqtt_handlers['real']
     elif target == 'sim':
-        mqtt_handler = current_app.config['mqtt_handler_sim']
+        mqtt_handler = current_app.mqtt_handlers['sim']
     else:
         return 'Invalid target specified', 400
     mqtt_handler.publish(topic, message)

+ 2 - 2
dashboard/src/templates/index.html

@@ -32,12 +32,12 @@
                 <ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0"></ul>
                 {% if mode == 'sim' or mode == 'hybrid' %}
                 <div class="navbar-text me-2" title="MQTT simulation address">
-                    <h5 class="mb-0"><span class="badge rounded-pill text-bg-success">SIM: {{ config['MQTT_SIM_HOST'] }}:{{ config['MQTT_SIM_PORT']}}</span></h5>
+                    <h5 class="mb-0"><span class="badge rounded-pill text-bg-success">SIM: {{ config.MQTT_SIM.HOST }}:{{ config.MQTT_SIM.PORT}}</span></h5>
                 </div>
                 {% endif %}
                 {% if mode == 'real'  or mode == 'hybrid' %}
                 <div class="navbar-text me-2" title="MQTT real address">
-                    <h5 class="mb-0"><span class="badge rounded-pill text-bg-success">REAL: {{ config['MQTT_SIM_HOST'] }}:{{ config['MQTT_SIM_PORT']}}</span></h5>
+                    <h5 class="mb-0"><span class="badge rounded-pill text-bg-success">REAL: {{ config.MQTT_REAL.HOST }}:{{ config.MQTT_REAL.PORT}}</span></h5>
                 </div>
                 {% endif %}
                 <div class="btn-group">

+ 10 - 0
simulator/.env.example

@@ -0,0 +1,10 @@
+# Real system MQTT broker
+MQTT_REAL_HOST=192.168.0.10
+MQTT_REAL_PORT=1883
+
+# Simulation MQTT broker
+MQTT_SIM_HOST=127.0.0.1
+MQTT_SIM_PORT=1883
+
+# Logging (0 or 1)
+LOGGING=0

+ 15 - 0
simulator/README.md

@@ -10,6 +10,21 @@ packages.
 - `PythonPDEVS` 2.4.2 (delivered in repo)
 - `loguru` 0.7.3
 - `paho-mqtt` 2.1.0
+- `python-dotenv` 1.1.0
+
+## Configuration
+
+This project uses a `.env` file for runtime configuration.
+
+To set it up, copy the example file:
+
+```bash
+cp .env.example .env
+```
+
+You can edit the broker addresses or logging settings inside.
+
+For a full explanation of the variables, see the [see the main project README](../README.md)
 
 
 ## Flowchart Example

+ 30 - 0
simulator/config.py

@@ -0,0 +1,30 @@
+import os
+from dataclasses import dataclass
+from dotenv import load_dotenv
+
+# Load variables from .env file (looks in current dir by default)
+load_dotenv()
+
+@dataclass
+class BrokerCfg:
+    HOST: str
+    PORT: int
+
+@dataclass
+class AppConfig:
+    MQTT_REAL: BrokerCfg
+    MQTT_SIM: BrokerCfg
+    LOGGING: bool = False
+
+def load_config() -> AppConfig:
+    return AppConfig(
+        MQTT_REAL=BrokerCfg(
+            HOST=os.getenv("MQTT_REAL_HOST", "192.168.0.10"),
+            PORT=int(os.getenv("MQTT_REAL_PORT", 1883))
+        ),
+        MQTT_SIM=BrokerCfg(
+            HOST=os.getenv("MQTT_SIM_HOST", "127.0.0.1"),
+            PORT=int(os.getenv("MQTT_SIM_PORT", 1883))
+        ),
+        LOGGING=os.getenv("LOGGING", "0") == "1"
+    )

+ 34 - 34
simulator/flowchart.md

@@ -4,28 +4,28 @@ 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_out("out")
 		DSI_inp("inp")
 		DSI_vgr_in("vgr_in")
 		DSI_mqtt_out("mqtt_out")
 		DSI_mqtt_in("mqtt_in")
+		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_mqtt_out("mqtt_out")
-		ReaderStation_nfc_out("nfc_out")
 		ReaderStation_color_in("color_in")
-		ReaderStation_nfc_in("nfc_in")
+		ReaderStation_mqtt_out("mqtt_out")
 		ReaderStation_color_out("color_out")
+		ReaderStation_nfc_in("nfc_in")
+		ReaderStation_nfc_out("nfc_out")
 		ReaderStation_nfc_in ~~~ ReaderStation_nfc_out
 		ReaderStation_color_in ~~~ ReaderStation_color_out
 	end
@@ -33,24 +33,24 @@ flowchart LR
 	ReaderStation_color_out --> VacuumGripper_color_sensor_in
 	ReaderStation_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph VacuumGripper
-		VacuumGripper_sld_red_in("sld_red_in")
-		VacuumGripper_sld_blue_out("sld_blue_out")
 		VacuumGripper_color_sensor_in("color_sensor_in")
-		VacuumGripper_nfc_out("nfc_out")
-		VacuumGripper_ct_out("ct_out")
-		VacuumGripper_mqtt_out("mqtt_out")
 		VacuumGripper_sld_red_out("sld_red_out")
 		VacuumGripper_nfc_in("nfc_in")
-		VacuumGripper_ct_in("ct_in")
-		VacuumGripper_mqtt_in("mqtt_in")
-		VacuumGripper_color_sensor_out("color_sensor_out")
+		VacuumGripper_ct_out("ct_out")
+		VacuumGripper_dsi_out("dsi_out")
 		VacuumGripper_dso_out("dso_out")
+		VacuumGripper_mpo_out("mpo_out")
+		VacuumGripper_sld_blue_out("sld_blue_out")
+		VacuumGripper_ct_in("ct_in")
 		VacuumGripper_dsi_in("dsi_in")
 		VacuumGripper_sld_white_in("sld_white_in")
-		VacuumGripper_mpo_out("mpo_out")
-		VacuumGripper_dsi_out("dsi_out")
+		VacuumGripper_nfc_out("nfc_out")
 		VacuumGripper_sld_white_out("sld_white_out")
 		VacuumGripper_sld_blue_in("sld_blue_in")
+		VacuumGripper_color_sensor_out("color_sensor_out")
+		VacuumGripper_mqtt_in("mqtt_in")
+		VacuumGripper_mqtt_out("mqtt_out")
+		VacuumGripper_sld_red_in("sld_red_in")
 		VacuumGripper_dsi_in ~~~ VacuumGripper_dsi_out
 		VacuumGripper_ct_in ~~~ VacuumGripper_ct_out
 		VacuumGripper_nfc_in ~~~ VacuumGripper_nfc_out
@@ -71,11 +71,11 @@ 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_mqtt_out("mqtt_out")
 		Transporter_left_in("left_in")
+		Transporter_mqtt_out("mqtt_out")
 		Transporter_right_in("right_in")
-		Transporter_right_out("right_out")
 		Transporter_left_in ~~~ Transporter_right_out
 		Transporter_right_in ~~~ Transporter_left_out
 	end
@@ -83,11 +83,11 @@ flowchart LR
 	Transporter_left_out --> HighBayWarehouse_inp
 	Transporter_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph HighBayWarehouse
+		HighBayWarehouse_mqtt_out("mqtt_out")
 		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
@@ -101,15 +101,15 @@ flowchart LR
 	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_mqtt_out("mqtt_out")
 		MPO_vgr_in ~~~ MPO_mqtt_out
 		subgraph MPO_oven
+			MPO_oven_gripper_in("gripper_in")
 			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_vgr_in ~~~ MPO_oven_gripper_out
 			MPO_oven_gripper_in ~~~ MPO_oven_mqtt_out
 		end
@@ -128,10 +128,10 @@ flowchart LR
 		MPO_gripper_table_out --> MPO_saw_gripper_in
 		MPO_gripper_mqtt_out --> MPO_mqtt_out
 		subgraph MPO_saw
+			MPO_saw_conveyor_out("conveyor_out")
 			MPO_saw_gripper_in("gripper_in")
 			MPO_saw_mqtt_out("mqtt_out")
 			MPO_saw_gripper_out("gripper_out")
-			MPO_saw_conveyor_out("conveyor_out")
 			MPO_saw_gripper_in ~~~ MPO_saw_gripper_out
 		end
 		MPO_saw_gripper_out --> MPO_gripper_table_in
@@ -143,13 +143,14 @@ flowchart LR
 	MPO_vgr_in --> MPO_oven_vgr_in
 	subgraph Conveyor
 		Conveyor_mqtt_out("mqtt_out")
-		Conveyor_out("out")
 		Conveyor_inp("inp")
+		Conveyor_out("out")
 		Conveyor_inp ~~~ Conveyor_out
 	end
 	Conveyor_out --> SLD_inp
 	Conveyor_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph SLD
+		SLD_red_out("red_out")
 		SLD_blue_out("blue_out")
 		SLD_mqtt_out("mqtt_out")
 		SLD_inp("inp")
@@ -157,17 +158,16 @@ flowchart LR
 		SLD_red_in("red_in")
 		SLD_blue_in("blue_in")
 		SLD_white_out("white_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
 		subgraph SLD_conveyor
+			SLD_conveyor_out_red("out_red")
 			SLD_conveyor_out_blue("out_blue")
 			SLD_conveyor_mqtt_out("mqtt_out")
 			SLD_conveyor_inp("inp")
 			SLD_conveyor_out_white("out_white")
-			SLD_conveyor_out_red("out_red")
 			SLD_conveyor_inp ~~~ SLD_conveyor_out_white
 		end
 		SLD_conveyor_out_white --> SLD_white_bay_sld_in
@@ -175,29 +175,29 @@ 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_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_vgr_out("vgr_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_out("vgr_out")
+			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_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_vgr_in("vgr_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
@@ -214,19 +214,19 @@ 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_mqtt_in("mqtt_in")
 		DSO_out("out")
+		DSO_mqtt_out("mqtt_out")
+		DSO_inp("inp")
 		DSO_inp ~~~ DSO_out
 		DSO_mqtt_in ~~~ DSO_mqtt_out
 	end
 	DSO_mqtt_out --> MQTTControlUnit_mqtt_in
 	subgraph MQTTControlUnit
+		MQTTControlUnit_mqtt_out("mqtt_out")
+		MQTTControlUnit_REALTIME_OBSERVED("REALTIME_OBSERVED")
 		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

+ 2 - 1
simulator/requirements.txt

@@ -1,2 +1,3 @@
 loguru
-paho-mqtt
+paho-mqtt
+python-dotenv