From 5a9d6baa22c8a93145e8893eed1fc4fbfa2040b4 Mon Sep 17 00:00:00 2001 From: PEQSPC Date: Sat, 3 Jan 2026 18:14:20 +0000 Subject: [PATCH 1/5] Remove obsolete configuration files and add onboarding scripts for local development setup with Kubernetes --- .../workflows/publish-docker-image.yaml | 3 +- .gitignore | 4 +- iot-simulator-api/.dockerignore | 9 + iot-simulator-api/Dockerfile | 20 + iot-simulator-api/src/__init__.py | 0 iot-simulator-api/src/database.py | 22 +- iot-simulator-api/src/db/__init__.py | 0 iot-simulator-api/src/{ => db}/simulations.db | Bin 16384 -> 16384 bytes iot-simulator-api/src/main.py | 450 +++++++---------- iot-simulator-api/src/requirements.txt | 58 ++- iot-simulator-api/test-mqtt/index.html | 24 - iot-simulator-api/test-mqtt/my-config.json | 2 +- k8s/api-deployment.yaml | 26 + k8s/api-service.yaml | 13 + k8s/rbac-config.yaml | 29 ++ .../config/README_INDUSTRIAL_FACTORY.md | 286 ----------- .../config/README_MULTIPLE_DEVICES.md | 260 ---------- mqtt-simulator-master/config/settings.json | 63 +-- .../config/settings_azure.json | 52 -- .../settings_azure_multiple_devices.json | 52 -- .../config/settings_industrial_factory.json | 455 ------------------ .../settings_industrial_factory_azure.json | 454 ----------------- .../config/settings_mqtt_localhost.json | 49 -- .../mqtt-simulator/__init__.py | 0 mqtt-simulator-master/mqtt-simulator/main.py | 39 +- mqtt-simulator-master/requirements.txt | 4 +- onboarding-k8s/README.md | 133 +++++ onboarding-k8s/setup-dev.sh | 72 +++ 28 files changed, 600 insertions(+), 1979 deletions(-) rename {mqtt-simulator-master/.github => .github}/workflows/publish-docker-image.yaml (97%) create mode 100644 iot-simulator-api/.dockerignore create mode 100644 iot-simulator-api/Dockerfile create mode 100644 iot-simulator-api/src/__init__.py create mode 100644 iot-simulator-api/src/db/__init__.py rename iot-simulator-api/src/{ => db}/simulations.db (78%) delete mode 100644 iot-simulator-api/test-mqtt/index.html create mode 100644 k8s/api-deployment.yaml create mode 100644 k8s/api-service.yaml create mode 100644 k8s/rbac-config.yaml delete mode 100644 mqtt-simulator-master/config/README_INDUSTRIAL_FACTORY.md delete mode 100644 mqtt-simulator-master/config/README_MULTIPLE_DEVICES.md delete mode 100644 mqtt-simulator-master/config/settings_azure.json delete mode 100644 mqtt-simulator-master/config/settings_azure_multiple_devices.json delete mode 100644 mqtt-simulator-master/config/settings_industrial_factory.json delete mode 100644 mqtt-simulator-master/config/settings_industrial_factory_azure.json delete mode 100644 mqtt-simulator-master/config/settings_mqtt_localhost.json create mode 100644 mqtt-simulator-master/mqtt-simulator/__init__.py create mode 100644 onboarding-k8s/README.md create mode 100644 onboarding-k8s/setup-dev.sh diff --git a/mqtt-simulator-master/.github/workflows/publish-docker-image.yaml b/.github/workflows/publish-docker-image.yaml similarity index 97% rename from mqtt-simulator-master/.github/workflows/publish-docker-image.yaml rename to .github/workflows/publish-docker-image.yaml index 04da487..1739253 100644 --- a/mqtt-simulator-master/.github/workflows/publish-docker-image.yaml +++ b/.github/workflows/publish-docker-image.yaml @@ -1,9 +1,10 @@ +# ON TEST FILE name: Publish Docker image to ghcr.io on: push: branches: - - "master" + - main jobs: docker: diff --git a/.gitignore b/.gitignore index ad16eab..c5d4fbf 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,8 @@ wheels/ venv/ ENV/ env/ - +myenv/ +venv.bak/ # Environment variables .env @@ -38,3 +39,4 @@ env/ *.swo *~ api/src/simulations.db +iot-simulator-api/src/simulations.db diff --git a/iot-simulator-api/.dockerignore b/iot-simulator-api/.dockerignore new file mode 100644 index 0000000..0b04632 --- /dev/null +++ b/iot-simulator-api/.dockerignore @@ -0,0 +1,9 @@ +node_modules +.git +.env +.DS_Store +__pycache__ +*.pyc +.vscode +*.log +db/ \ No newline at end of file diff --git a/iot-simulator-api/Dockerfile b/iot-simulator-api/Dockerfile new file mode 100644 index 0000000..109fa84 --- /dev/null +++ b/iot-simulator-api/Dockerfile @@ -0,0 +1,20 @@ +# Python 3.11 slim image +FROM python:3.11-slim + +# Set working directory +WORKDIR /code + +# Copy requirements first to leverage Docker cache +COPY ./src/requirements.txt /code/requirements.txt + +# Install dependencies +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# Copy source code +COPY ./src /code + +# Expose port 80 +EXPOSE 80 + +# Run with standard FastAPI command (Production mode) +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] \ No newline at end of file diff --git a/iot-simulator-api/src/__init__.py b/iot-simulator-api/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/iot-simulator-api/src/database.py b/iot-simulator-api/src/database.py index c4888af..44f9e9f 100644 --- a/iot-simulator-api/src/database.py +++ b/iot-simulator-api/src/database.py @@ -2,8 +2,13 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime +import os -SQLALCHEMY_DATABASE_URL = "sqlite:///./simulations.db" +# Ensure the 'db' folder exists, otherwise sqlite fails +if not os.path.exists("./db"): + os.makedirs("./db") + +SQLALCHEMY_DATABASE_URL = "sqlite:///./db/simulations.db" engine = create_engine( SQLALCHEMY_DATABASE_URL, @@ -17,10 +22,15 @@ class Simulation(Base): id = Column(Integer, primary_key=True, index=True) simulation_id = Column(String, unique=True, index=True, nullable=False) + + # In K8s, this will store the Pod Name (e.g., "sim-abc-123") container_id = Column(String, nullable=True) - config_path = Column(String, nullable=False) - config_json = Column(Text, nullable=False) # JSON como texto - status = Column(String, default="running") # running, stopped, failed, expired + + + config_path = Column(String, nullable=True) + + config_json = Column(Text, nullable=False) # JSON stored as text + status = Column(String, default="running") created_at = Column(DateTime, default=datetime.utcnow) stopped_at = Column(DateTime, nullable=True) expires_at = Column(DateTime, nullable=False) @@ -29,10 +39,10 @@ class Simulation(Base): def __repr__(self): return f"" -# Criar tabelas +# Create tables Base.metadata.create_all(bind=engine) -# Dependency para obter sessão +# Dependency to get session def get_db(): db = SessionLocal() try: diff --git a/iot-simulator-api/src/db/__init__.py b/iot-simulator-api/src/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/iot-simulator-api/src/simulations.db b/iot-simulator-api/src/db/simulations.db similarity index 78% rename from iot-simulator-api/src/simulations.db rename to iot-simulator-api/src/db/simulations.db index 8d1acf37953d563ce073185efcd29278b92c0817..7169de81b7d2b0698ea9a73b56707518508828d2 100644 GIT binary patch delta 253 zcmZo@U~Fh$oFL7}GEv4^n1w;Ftdp04fq|L#Bm>`SK7Zbmn*|lZd6^rTnJ3Hh88fm> z4(9V`iej34h*zDFdGae>Bd(?(Ms{&!WyY55$!qxLOb+DV%*M!jn}PQ>Q0GNn=uK7<=9s@D~RDFHm7Y L9&Tn?PMF02_F+9v delta 1156 zcmb7@OKTKC6otE|Czw}HCkTop6XImyLetj!(YqK#G-5Cyh!KYFdPo9!Ct@NYGhz^T z>TF%Pb|ZsJAsct*59ALBS-B8bqE=6gNYc;=C^I!D&T`&7bPj`9o z!{?#rc|o}4mEM&4!>wj-&#kaU z{HN=U_odg3b5T#@ZuGXh*jD$#YUft_`L_DI`a;{rX1N%8<$B1UUF#be8S#a&L?VPO zK#E}~(iCRKovZ2F*btmu`+ba`rSwmLI4yRvMSoQ&4Zqxq>t zqY*%K7@}ds1`$dqNGOgaBoyEyiW6lF1xNA(CGSC)^1^0+a1gm8yKj^wX)3g}*b0F% zgP~y(0jNw$mCZ1hno@33WU>?+Yg5N;72u3O0f>-*5li7dGgw%d(~GOqE#~Hr^2fgs z!jlQHNC1oxz>e`SA(jbXG2xsuc3jDOAd_sfziNvQ@7+KAQHo&<}g_M}M7f=Z?gjgb+ySxv$#;UXG!OCI_u<@fj=F{elfz|)Qgb9TS zAhBT5nQ{=uDJX#;hD34#jw!7k&LG?z@PA`oZ$;OnlT;CEFoQM)5^};V1sbZ%0XjdO zOG-Fo3oy5-Q4}enEQc;IniEBgv)#&+%6&}gB8_nMpE1EncESXM1mlL!F&&_KVTB7i~v7(dX!Evv=@AG};}M|4eH!FWj8AVjSd!f_s)1x`J{? op5pvt_Ac7>qVLhS=u5QgF4~FCv<1FjEarO< - - - - - - -

MQTT Test

-
- - - \ No newline at end of file diff --git a/iot-simulator-api/test-mqtt/my-config.json b/iot-simulator-api/test-mqtt/my-config.json index b96ec4f..dcb6a3e 100644 --- a/iot-simulator-api/test-mqtt/my-config.json +++ b/iot-simulator-api/test-mqtt/my-config.json @@ -1,5 +1,5 @@ { - "BROKER_URL": "test.mosquitto.org", + "BROKER_URL": "broker.hivemq.com", "BROKER_PORT": 1883, "TIME_INTERVAL": 2, "TOPICS": [{ diff --git a/k8s/api-deployment.yaml b/k8s/api-deployment.yaml new file mode 100644 index 0000000..351195a --- /dev/null +++ b/k8s/api-deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: manager-api + namespace: iot-sims +spec: + replicas: 3 + selector: + matchLabels: + app: manager-api + template: + metadata: + labels: + app: manager-api + spec: + serviceAccountName: sim-manager-sa # <--- CRITICAL: Connects to RBAC + containers: + - name: api + image: peqspc/manager-api:dev # <--- Matches the image you built + imagePullPolicy: Never # <--- CRITICAL: Uses local image + ports: + - containerPort: 80 + env: + # This tells the API which simulator image to use when creating pods + - name: SIMULATOR_IMAGE + value: "ghcr.io/damascenorafael/mqtt-simulator:sha-a73a2e8" \ No newline at end of file diff --git a/k8s/api-service.yaml b/k8s/api-service.yaml new file mode 100644 index 0000000..c1ae9e0 --- /dev/null +++ b/k8s/api-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: manager-api-service + namespace: iot-sims +spec: + type: ClusterIP + selector: + app: manager-api + ports: + - protocol: TCP + port: 80 # Port inside the cluster + targetPort: 80 # Port the container listens on \ No newline at end of file diff --git a/k8s/rbac-config.yaml b/k8s/rbac-config.yaml new file mode 100644 index 0000000..44f3584 --- /dev/null +++ b/k8s/rbac-config.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: sim-manager-sa + namespace: iot-sims +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pod-manager-role + namespace: iot-sims +rules: +- apiGroups: [""] + resources: ["pods", "pods/log"] + verbs: ["create", "get", "list", "delete", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: sim-manager-binding + namespace: iot-sims +subjects: +- kind: ServiceAccount + name: sim-manager-sa + namespace: iot-sims +roleRef: + kind: Role + name: pod-manager-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/mqtt-simulator-master/config/README_INDUSTRIAL_FACTORY.md b/mqtt-simulator-master/config/README_INDUSTRIAL_FACTORY.md deleted file mode 100644 index 56bc8fc..0000000 --- a/mqtt-simulator-master/config/README_INDUSTRIAL_FACTORY.md +++ /dev/null @@ -1,286 +0,0 @@ -# Industrial Factory Simulation Configuration - -This configuration simulates a complete industrial manufacturing facility with realistic sensor data across all production stages. - -## Factory Overview - -The simulation covers **10 major areas** of a modern industrial facility: - -### 1. **Raw Material Intake** (`factory/rawmaterial/intake`) -- **Update Interval**: 15 seconds -- **Metrics**: - - Material received (kg) - - Quality grade (85-100%) - - Storage level (20-95%) -- **Use Case**: Monitor incoming raw materials, quality control, inventory management - -### 2. **Production Lines** (`factory/production/line/1-3`) -- **Update Interval**: 5 seconds (real-time monitoring) -- **3 Parallel Production Lines** with metrics: - - Machine temperature (65-85°C) - - Pressure (2.0-6.5 bar) - - Speed (800-1500 RPM) - - Vibration (0.5-4.5 mm/s) - for predictive maintenance - - Production count (cumulative) - - Defect count - - Power consumption (15-45 kW) -- **Use Case**: Real-time production monitoring, predictive maintenance, quality tracking - -### 3. **Assembly Stations** (`factory/assembly/station/1-5`) -- **Update Interval**: 8 seconds -- **5 Assembly Stations** with metrics: - - Assembly count (cumulative) - - Cycle time (45-90 seconds) - - Robot position (X/Y coordinates) - - Tool usage percentage - - Error count -- **Use Case**: Robot tracking, process optimization, bottleneck detection - -### 4. **Quality Inspection** (`factory/quality/inspection/1-2`) -- **Update Interval**: 10 seconds -- **2 Inspection Stations** with metrics: - - Inspected units (cumulative) - - Pass rate (92-99.9%) - - Rejected units - - Dimension tolerance (0.01-0.15 mm) - - Surface quality score (70-100) -- **Use Case**: Quality control, defect tracking, process improvement - -### 5. **Packaging Line** (`factory/packaging/line1`) -- **Update Interval**: 12 seconds -- **Metrics**: - - Packages completed (cumulative) - - Packaging speed (20-60 units/min) - - Label errors - - Material waste (kg) - - Conveyor speed (10-30 m/min) -- **Use Case**: Packaging efficiency, waste reduction, throughput monitoring - -### 6. **Warehouse Storage** (`factory/warehouse/storage`) -- **Update Interval**: 20 seconds -- **Metrics**: - - Finished goods inventory (500-10,000 units) - - Storage utilization (40-95%) - - Temperature control (18-24°C) - - Humidity (35-65%) - - Outbound shipments (cumulative) -- **Use Case**: Inventory management, environmental control, logistics - -### 7. **Energy Monitoring** (`factory/energy/monitoring`) -- **Update Interval**: 10 seconds -- **Metrics**: - - Total power consumption (150-450 kW) - - Voltage (380-400V) - - Current (200-800A) - - Power factor (0.85-0.98) - - Daily consumption (cumulative kWh) -- **Use Case**: Energy management, cost optimization, sustainability - -### 8. **HVAC System** (`factory/hvac/system`) -- **Update Interval**: 15 seconds -- **Metrics**: - - Ambient temperature (20-26°C) - - Target temperature (21-23°C) - - Humidity (40-60%) - - Air quality index (20-80) - - Filter pressure drop (50-250 Pa) -- **Use Case**: Environmental control, worker comfort, equipment protection - -### 9. **Maintenance Alerts** (`factory/maintenance/alerts`) -- **Update Interval**: 30 seconds -- **Metrics**: - - Active alerts count - - Preventive maintenance due - - Machine uptime (85-99.5%) - - MTBF - Mean Time Between Failures (200-2000 hours) -- **Use Case**: Preventive maintenance, downtime reduction, reliability tracking - -## Data Characteristics - -### Sensor Behavior - -The simulator uses realistic sensor patterns: - -- **RETAIN_PROBABILITY**: How often values stay stable (simulates steady-state operation) -- **INCREASE_PROBABILITY**: Tendency for cumulative counters to increase -- **MAX_STEP**: Maximum change between readings (simulates realistic sensor noise) - -### Example Behaviors - -**Production Counter** (cumulative metric): -- INCREASE_PROBABILITY: 0.95 (almost always increasing) -- RETAIN_PROBABILITY: 0.3 (changes frequently) -- Result: Steadily increasing production count with occasional pauses - -**Temperature** (stable metric): -- RETAIN_PROBABILITY: 0.85 (stable most of the time) -- MAX_STEP: 1.5°C (small variations) -- Result: Stable temperature with minor fluctuations - -**Defects** (rare events): -- INCREASE_PROBABILITY: 0.15 (rarely increases) -- RETAIN_PROBABILITY: 0.9 (usually stays the same) -- Result: Occasional defects, mostly zero - -## Usage - -### For Local MQTT Broker - -```bash -cd mqtt-simulator-master/mqtt-simulator -python main.py -f ../config/settings_industrial_factory.json -v -``` - -### For Azure IoT Hub - -1. Update the connection string in `settings_industrial_factory_azure.json`: - ```json - "AZURE_CONNECTION_STRING": "HostName=your-hub.azure-devices.net;DeviceId=factory-001;SharedAccessKey=..." - ``` - -2. Run the simulator: - ```bash - python main.py -f ../config/settings_industrial_factory_azure.json -v - ``` - -## Data Volume - -**Total Topics**: 18 telemetry streams -- 1 Raw material intake -- 3 Production lines -- 5 Assembly stations -- 2 Quality inspection stations -- 1 Packaging line -- 1 Warehouse -- 1 Energy monitoring -- 1 HVAC system -- 1 Maintenance alerts - -**Message Frequency**: -- Fastest: Production lines (every 5 seconds) -- Slowest: Maintenance alerts (every 30 seconds) -- **Average**: ~100 messages per minute across all topics - -## Monitoring Examples - -### MQTT Subscription - -Monitor specific areas: -```bash -# All factory data -mosquitto_sub -t "factory/#" -v - -# Production lines only -mosquitto_sub -t "factory/production/#" -v - -# Quality metrics -mosquitto_sub -t "factory/quality/#" -v - -# Energy and utilities -mosquitto_sub -t "factory/energy/#" -t "factory/hvac/#" -v -``` - -### Azure IoT Hub Monitoring - -```bash -# Monitor all telemetry -az iot hub monitor-events --hub-name your-hub-name - -# Filter by topic property -az iot hub monitor-events --hub-name your-hub-name --properties anno sys -``` - -## Dashboard Visualization Ideas - -### Production Dashboard -- Real-time production count from all lines -- Defect rates and quality metrics -- Machine temperature and vibration trends -- Overall Equipment Effectiveness (OEE) - -### Energy Dashboard -- Real-time power consumption -- Daily/weekly energy trends -- Power factor monitoring -- Cost estimation - -### Quality Dashboard -- Pass/fail rates from inspection -- Defect trending -- Dimension tolerance distribution -- Surface quality scores - -### Maintenance Dashboard -- Active alerts by severity -- Machine uptime percentages -- MTBF trending -- Preventive maintenance schedule - -## Customization - -To adjust the simulation: - -1. **Change update frequency**: Modify `TIME_INTERVAL` (in seconds) -2. **Adjust value ranges**: Modify `MIN_VALUE`, `MAX_VALUE` -3. **Change variability**: Adjust `MAX_STEP` -4. **Make more stable**: Increase `RETAIN_PROBABILITY` -5. **Add more production lines**: Increase `RANGE_END` - -### Example: Add a 4th production line - -Change: -```json -"PREFIX": "factory/production/line", -"RANGE_START": 1, -"RANGE_END": 3, -``` - -To: -```json -"PREFIX": "factory/production/line", -"RANGE_START": 1, -"RANGE_END": 4, -``` - -## Real-World Use Cases - -1. **Training & Education**: Learn IoT/IIoT concepts without physical hardware -2. **Dashboard Development**: Test visualization tools with realistic data -3. **Algorithm Testing**: Develop anomaly detection, predictive maintenance -4. **Cloud Integration**: Test Azure IoT, AWS IoT, or other platforms -5. **Load Testing**: Simulate high-volume industrial data streams -6. **Demo & Presentations**: Show industrial IoT capabilities - -## Anomaly Simulation - -To simulate anomalies, you can modify the configuration: - -**High temperature alert** (production line): -```json -"machine_temperature_celsius": { - "MIN_VALUE": 80.0, // Increased from 65 - "MAX_VALUE": 95.0, // Increased from 85 -} -``` - -**Quality issues**: -```json -"pass_rate_percent": { - "MIN_VALUE": 75.0, // Decreased from 92 - "MAX_VALUE": 90.0, // Decreased from 99.9 -} -``` - -**High vibration** (predictive maintenance): -```json -"vibration_mm_s": { - "MIN_VALUE": 3.0, // Increased from 0.5 - "MAX_VALUE": 8.0, // Increased from 4.5 -} -``` - -## Support - -For issues or questions about the industrial factory simulation, refer to: -- Main README: `mqtt-simulator-master/README.md` -- Azure Integration: `mqtt-simulator-master/README_AZURE_INTEGRATION.md` diff --git a/mqtt-simulator-master/config/README_MULTIPLE_DEVICES.md b/mqtt-simulator-master/config/README_MULTIPLE_DEVICES.md deleted file mode 100644 index 2f2d146..0000000 --- a/mqtt-simulator-master/config/README_MULTIPLE_DEVICES.md +++ /dev/null @@ -1,260 +0,0 @@ -# Azure IoT Hub - Multiple Device Connections - -## Overview - -When using Azure IoT Hub, each device ID can only have **ONE active connection** at a time. If you try to use the same device ID for multiple publishers, you'll get `ConnectionDroppedError` because Azure disconnects the previous connection when a new one is established. - -## The Solution: Multiple Device IDs - -Use the `AZURE_DEVICE_CONNECTIONS` configuration to map each topic to its own unique device ID. - -## Configuration Format - -### Option 1: Multiple Devices (Recommended for multiple topics) - -```json -{ - "BROKER_TYPE": "azure", - "AZURE_DEVICE_CONNECTIONS": { - "linha_producao/estacao/1": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao1;SharedAccessKey=...", - "linha_producao/estacao/2": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao2;SharedAccessKey=...", - "linha_producao/estacao/3": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao3;SharedAccessKey=..." - }, - "TOPICS": [ - { - "TYPE": "multiple", - "PREFIX": "linha_producao/estacao", - "RANGE_START": 1, - "RANGE_END": 3, - ... - } - ] -} -``` - -### Option 2: Single Device (Backwards compatible, only for single topic) - -```json -{ - "BROKER_TYPE": "azure", - "AZURE_CONNECTION_STRING": "HostName=iothub-ipca.azure-devices.net;DeviceId=device1;SharedAccessKey=...", - "TOPICS": [ - { - "TYPE": "single", - "NAME": "factory/sensor1", - ... - } - ] -} -``` - -## Setting Up Multiple Devices in Azure IoT Hub - -### Step 1: Create Devices in Azure Portal - -1. Go to [Azure Portal](https://portal.azure.com) -2. Navigate to your IoT Hub -3. Go to **Devices** under "Device management" -4. Click **+ Add Device** -5. Create each device: - - Device ID: `estacao1` - - Authentication: Symmetric key (auto-generate) - - Click **Save** -6. Repeat for `estacao2`, `estacao3`, etc. - -### Step 2: Get Connection Strings - -For each device: -1. Click on the device name -2. Copy the **Primary Connection String** -3. It will look like: - ``` - HostName=iothub-ipca.azure-devices.net;DeviceId=estacao1;SharedAccessKey=abc123... - ``` - -### Step 3: Update Configuration - -Update `settings_azure.json` with your connection strings: - -```json -{ - "BROKER_TYPE": "azure", - "AZURE_DEVICE_CONNECTIONS": { - "linha_producao/estacao/1": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao1;SharedAccessKey=YOUR_KEY_1", - "linha_producao/estacao/2": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao2;SharedAccessKey=YOUR_KEY_2", - "linha_producao/estacao/3": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao3;SharedAccessKey=YOUR_KEY_3" - }, - "TOPICS": [...] -} -``` - -## Using Azure CLI - -You can also create devices and get connection strings using Azure CLI: - -### Create Devices - -```bash -# Create device -az iot hub device-identity create --hub-name iothub-ipca --device-id estacao1 - -# Get connection string -az iot hub device-identity connection-string show --hub-name iothub-ipca --device-id estacao1 -``` - -### Create Multiple Devices (Bash script) - -```bash -#!/bin/bash -HUB_NAME="iothub-ipca" - -for i in {1..3}; do - DEVICE_ID="estacao$i" - echo "Creating device: $DEVICE_ID" - az iot hub device-identity create --hub-name $HUB_NAME --device-id $DEVICE_ID - - echo "Connection string for $DEVICE_ID:" - az iot hub device-identity connection-string show --hub-name $HUB_NAME --device-id $DEVICE_ID --output tsv - echo "" -done -``` - -### Create Multiple Devices (PowerShell script) - -```powershell -$HubName = "iothub-ipca" - -1..3 | ForEach-Object { - $DeviceId = "estacao$_" - Write-Host "Creating device: $DeviceId" - az iot hub device-identity create --hub-name $HubName --device-id $DeviceId - - Write-Host "Connection string for ${DeviceId}:" - az iot hub device-identity connection-string show --hub-name $HubName --device-id $DeviceId --output tsv - Write-Host "" -} -``` - -## Topic to Device Mapping - -The system automatically maps topics to devices based on the exact topic URL: - -| Topic URL | Device ID | Connection String Used | -|-----------|-----------|------------------------| -| `linha_producao/estacao/1` | `estacao1` | `AZURE_DEVICE_CONNECTIONS["linha_producao/estacao/1"]` | -| `linha_producao/estacao/2` | `estacao2` | `AZURE_DEVICE_CONNECTIONS["linha_producao/estacao/2"]` | -| `linha_producao/estacao/3` | `estacao3` | `AZURE_DEVICE_CONNECTIONS["linha_producao/estacao/3"]` | - -**Important**: The topic URL must **exactly match** the key in `AZURE_DEVICE_CONNECTIONS`. - -## Running the Simulator - -```bash -cd mqtt-simulator-master/mqtt-simulator -python main.py -f ../config/settings_azure_multiple_devices.json -v -``` - -**Expected output:** -``` -Using Azure IoT Hub publisher with 3 device connections -Starting: linha_producao/estacao/1 ... -Starting: linha_producao/estacao/2 ... -Starting: linha_producao/estacao/3 ... -Connected to Azure IoT Hub for topic: linha_producao/estacao/1 -Connected to Azure IoT Hub for topic: linha_producao/estacao/2 -Connected to Azure IoT Hub for topic: linha_producao/estacao/3 -[12:30:45] Telemetry sent to Azure IoT Hub: linha_producao/estacao/1 -[12:30:45] Telemetry sent to Azure IoT Hub: linha_producao/estacao/2 -[12:30:45] Telemetry sent to Azure IoT Hub: linha_producao/estacao/3 -``` - -## Monitoring - -Monitor telemetry from all devices: - -```bash -# All devices -az iot hub monitor-events --hub-name iothub-ipca - -# Specific device -az iot hub monitor-events --hub-name iothub-ipca --device-id estacao1 -``` - -## Industrial Factory Example - -For the industrial factory simulation with multiple production areas: - -```json -{ - "BROKER_TYPE": "azure", - "AZURE_DEVICE_CONNECTIONS": { - "factory/rawmaterial/intake": "HostName=...;DeviceId=rawmaterial;SharedAccessKey=...", - "factory/production/line/1": "HostName=...;DeviceId=production-line1;SharedAccessKey=...", - "factory/production/line/2": "HostName=...;DeviceId=production-line2;SharedAccessKey=...", - "factory/production/line/3": "HostName=...;DeviceId=production-line3;SharedAccessKey=...", - "factory/assembly/station/1": "HostName=...;DeviceId=assembly-st1;SharedAccessKey=...", - "factory/assembly/station/2": "HostName=...;DeviceId=assembly-st2;SharedAccessKey=...", - "factory/assembly/station/3": "HostName=...;DeviceId=assembly-st3;SharedAccessKey=...", - "factory/assembly/station/4": "HostName=...;DeviceId=assembly-st4;SharedAccessKey=...", - "factory/assembly/station/5": "HostName=...;DeviceId=assembly-st5;SharedAccessKey=...", - "factory/quality/inspection/1": "HostName=...;DeviceId=quality-insp1;SharedAccessKey=...", - "factory/quality/inspection/2": "HostName=...;DeviceId=quality-insp2;SharedAccessKey=...", - "factory/packaging/line1": "HostName=...;DeviceId=packaging;SharedAccessKey=...", - "factory/warehouse/storage": "HostName=...;DeviceId=warehouse;SharedAccessKey=...", - "factory/energy/monitoring": "HostName=...;DeviceId=energy;SharedAccessKey=...", - "factory/hvac/system": "HostName=...;DeviceId=hvac;SharedAccessKey=...", - "factory/maintenance/alerts": "HostName=...;DeviceId=maintenance;SharedAccessKey=..." - }, - "TOPICS": [...] -} -``` - -## Troubleshooting - -### Error: "No Azure connection string found for topic" - -**Problem**: The topic URL doesn't match any key in `AZURE_DEVICE_CONNECTIONS`. - -**Solution**: Ensure the topic URL exactly matches: -- Check for trailing slashes -- Verify the PREFIX and RANGE generate the correct topic names -- Use `-v` verbose mode to see exact topic URLs being used - -### Error: "ConnectionDroppedError: Unexpected disconnection" - -**Problem**: Multiple publishers trying to use the same device ID. - -**Solution**: -1. Verify each topic has its own unique device ID -2. Check you're not running multiple instances of the simulator -3. Ensure no other applications are using the same device IDs - -### Error: "Either AZURE_CONNECTION_STRING or AZURE_DEVICE_CONNECTIONS is required" - -**Problem**: No Azure connection configuration provided. - -**Solution**: Add either: -- `AZURE_DEVICE_CONNECTIONS` (for multiple devices), or -- `AZURE_CONNECTION_STRING` (for single device) - -## Best Practices - -1. **Use descriptive device IDs**: `production-line1`, not `device1` -2. **Organize by function**: Group related devices logically -3. **Document mappings**: Keep a reference of which device ID maps to which physical sensor/line -4. **Security**: Never commit connection strings to Git - use environment variables or Azure Key Vault in production -5. **Testing**: Test with a single device first, then scale up - -## Cost Considerations - -- **Azure IoT Hub Free Tier**: 8,000 messages/day, up to 500 devices -- **Standard Tier**: Charged per message, unlimited devices -- Consider your message frequency × number of devices to estimate costs -- Example: 3 devices × 100 messages/day = 300 messages/day (within free tier) - -## See Also - -- [README_AZURE_INTEGRATION.md](../README_AZURE_INTEGRATION.md) - Main Azure integration guide -- [README_INDUSTRIAL_FACTORY.md](README_INDUSTRIAL_FACTORY.md) - Industrial factory simulation -- [Azure IoT Hub Pricing](https://azure.microsoft.com/pricing/details/iot-hub/) -- [Azure IoT Hub Documentation](https://docs.microsoft.com/azure/iot-hub/) diff --git a/mqtt-simulator-master/config/settings.json b/mqtt-simulator-master/config/settings.json index 9471f0b..dcb6a3e 100644 --- a/mqtt-simulator-master/config/settings.json +++ b/mqtt-simulator-master/config/settings.json @@ -1,49 +1,20 @@ { - "BROKER_TYPE": "mqtt", - "BROKER_URL": "localhost", + "BROKER_URL": "broker.hivemq.com", "BROKER_PORT": 1883, - "TOPICS": [ - { - "TYPE": "multiple", - "PREFIX": "linha_producao/estacao", - "RANGE_START": 1, - "RANGE_END": 3, - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "producao", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 80, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "paragem", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 30, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "stock", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 100, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.3, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "defeitos", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 5, - "MAX_STEP": 1, - "RETAIN_PROBABILITY": 0.8 - } - ] - } - ] + "TIME_INTERVAL": 2, + "TOPICS": [{ + "TYPE": "multiple", + "PREFIX": "fabrica/sensor", + "RANGE_START": 1, + "RANGE_END": 3, + "DATA": [{ + "NAME": "temperatura", + "TYPE": "float", + "MIN_VALUE": 20, + "MAX_VALUE": 35, + "MAX_STEP": 1.5, + "INCREASE_PROBABILITY": 0.6 + }] + }], + "duration_minutes": 2 } \ No newline at end of file diff --git a/mqtt-simulator-master/config/settings_azure.json b/mqtt-simulator-master/config/settings_azure.json deleted file mode 100644 index 8dc9062..0000000 --- a/mqtt-simulator-master/config/settings_azure.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "BROKER_TYPE": "azure", - "AZURE_DEVICE_CONNECTIONS": { - "linha_producao/estacao/1": "HostName=your-hub-name.azure-devices.net;DeviceId=estacao1;SharedAccessKey=your-key-1", - "linha_producao/estacao/2": "HostName=your-hub-name.azure-devices.net;DeviceId=estacao2;SharedAccessKey=your-key-2", - "linha_producao/estacao/3": "HostName=your-hub-name.azure-devices.net;DeviceId=estacao3;SharedAccessKey=your-key-3" - }, - "TOPICS": [ - { - "TYPE": "multiple", - "PREFIX": "linha_producao/estacao", - "RANGE_START": 1, - "RANGE_END": 3, - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "producao", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 80, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "paragem", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 30, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "stock", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 100, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.3, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "defeitos", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 5, - "MAX_STEP": 1, - "RETAIN_PROBABILITY": 0.8 - } - ] - } - ] -} diff --git a/mqtt-simulator-master/config/settings_azure_multiple_devices.json b/mqtt-simulator-master/config/settings_azure_multiple_devices.json deleted file mode 100644 index 8c6033c..0000000 --- a/mqtt-simulator-master/config/settings_azure_multiple_devices.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "BROKER_TYPE": "azure", - "AZURE_DEVICE_CONNECTIONS": { - "linha_producao/estacao/1": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao1;SharedAccessKey=your-key-1", - "linha_producao/estacao/2": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao2;SharedAccessKey=your-key-2", - "linha_producao/estacao/3": "HostName=iothub-ipca.azure-devices.net;DeviceId=estacao3;SharedAccessKey=your-key-3" - }, - "TOPICS": [ - { - "TYPE": "multiple", - "PREFIX": "linha_producao/estacao", - "RANGE_START": 1, - "RANGE_END": 3, - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "producao", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 80, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "paragem", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 30, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "stock", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 100, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.3, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "defeitos", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 5, - "MAX_STEP": 1, - "RETAIN_PROBABILITY": 0.8 - } - ] - } - ] -} diff --git a/mqtt-simulator-master/config/settings_industrial_factory.json b/mqtt-simulator-master/config/settings_industrial_factory.json deleted file mode 100644 index b4beb09..0000000 --- a/mqtt-simulator-master/config/settings_industrial_factory.json +++ /dev/null @@ -1,455 +0,0 @@ -{ - "BROKER_TYPE": "mqtt", - "BROKER_URL": "localhost", - "BROKER_PORT": 1883, - "TOPICS": [ - { - "TYPE": "single", - "NAME": "factory/rawmaterial/intake", - "TIME_INTERVAL": 15, - "DATA": [ - { - "NAME": "material_received_kg", - "TYPE": "int", - "MIN_VALUE": 100, - "MAX_VALUE": 500, - "MAX_STEP": 50, - "RETAIN_PROBABILITY": 0.6 - }, - { - "NAME": "quality_grade", - "TYPE": "float", - "MIN_VALUE": 85.0, - "MAX_VALUE": 100.0, - "MAX_STEP": 2.0, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "storage_level_percent", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 95, - "MAX_STEP": 5, - "INCREASE_PROBABILITY": 0.4, - "RETAIN_PROBABILITY": 0.7 - } - ] - }, - { - "TYPE": "multiple", - "PREFIX": "factory/production/line", - "RANGE_START": 1, - "RANGE_END": 3, - "TIME_INTERVAL": 5, - "DATA": [ - { - "NAME": "machine_temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 65.0, - "MAX_VALUE": 85.0, - "MAX_STEP": 1.5, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "pressure_bar", - "TYPE": "float", - "MIN_VALUE": 2.0, - "MAX_VALUE": 6.5, - "MAX_STEP": 0.3, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "speed_rpm", - "TYPE": "int", - "MIN_VALUE": 800, - "MAX_VALUE": 1500, - "MAX_STEP": 50, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "vibration_mm_s", - "TYPE": "float", - "MIN_VALUE": 0.5, - "MAX_VALUE": 4.5, - "MAX_STEP": 0.4, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "production_count", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 10000, - "MAX_STEP": 15, - "INCREASE_PROBABILITY": 0.95, - "RETAIN_PROBABILITY": 0.3 - }, - { - "NAME": "defect_count", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 50, - "MAX_STEP": 2, - "INCREASE_PROBABILITY": 0.15, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "power_consumption_kw", - "TYPE": "float", - "MIN_VALUE": 15.0, - "MAX_VALUE": 45.0, - "MAX_STEP": 2.5, - "RETAIN_PROBABILITY": 0.7 - } - ] - }, - { - "TYPE": "multiple", - "PREFIX": "factory/assembly/station", - "RANGE_START": 1, - "RANGE_END": 5, - "TIME_INTERVAL": 8, - "DATA": [ - { - "NAME": "assembly_count", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 5000, - "MAX_STEP": 8, - "INCREASE_PROBABILITY": 0.9, - "RETAIN_PROBABILITY": 0.4 - }, - { - "NAME": "cycle_time_seconds", - "TYPE": "float", - "MIN_VALUE": 45.0, - "MAX_VALUE": 90.0, - "MAX_STEP": 3.0, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "robot_position_x", - "TYPE": "float", - "MIN_VALUE": -500.0, - "MAX_VALUE": 500.0, - "MAX_STEP": 50.0, - "RETAIN_PROBABILITY": 0.5 - }, - { - "NAME": "robot_position_y", - "TYPE": "float", - "MIN_VALUE": -300.0, - "MAX_VALUE": 300.0, - "MAX_STEP": 30.0, - "RETAIN_PROBABILITY": 0.5 - }, - { - "NAME": "tools_usage_percent", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 100, - "MAX_STEP": 5, - "INCREASE_PROBABILITY": 0.6, - "RETAIN_PROBABILITY": 0.65 - }, - { - "NAME": "errors", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 10, - "MAX_STEP": 1, - "INCREASE_PROBABILITY": 0.1, - "RETAIN_PROBABILITY": 0.95 - } - ] - }, - { - "TYPE": "multiple", - "PREFIX": "factory/quality/inspection", - "RANGE_START": 1, - "RANGE_END": 2, - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "inspected_units", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 3000, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.85, - "RETAIN_PROBABILITY": 0.5 - }, - { - "NAME": "pass_rate_percent", - "TYPE": "float", - "MIN_VALUE": 92.0, - "MAX_VALUE": 99.9, - "MAX_STEP": 0.8, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "rejected_units", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 100, - "MAX_STEP": 3, - "INCREASE_PROBABILITY": 0.2, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "dimension_tolerance_mm", - "TYPE": "float", - "MIN_VALUE": 0.01, - "MAX_VALUE": 0.15, - "MAX_STEP": 0.02, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "surface_quality_score", - "TYPE": "int", - "MIN_VALUE": 70, - "MAX_VALUE": 100, - "MAX_STEP": 3, - "RETAIN_PROBABILITY": 0.8 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/packaging/line1", - "TIME_INTERVAL": 12, - "DATA": [ - { - "NAME": "packages_completed", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 2000, - "MAX_STEP": 12, - "INCREASE_PROBABILITY": 0.9, - "RETAIN_PROBABILITY": 0.4 - }, - { - "NAME": "packaging_speed_units_min", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 60, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "label_errors", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 15, - "MAX_STEP": 1, - "INCREASE_PROBABILITY": 0.15, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "material_waste_kg", - "TYPE": "float", - "MIN_VALUE": 0.0, - "MAX_VALUE": 25.0, - "MAX_STEP": 1.5, - "INCREASE_PROBABILITY": 0.4, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "conveyor_speed_m_min", - "TYPE": "float", - "MIN_VALUE": 10.0, - "MAX_VALUE": 30.0, - "MAX_STEP": 2.0, - "RETAIN_PROBABILITY": 0.8 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/warehouse/storage", - "TIME_INTERVAL": 20, - "DATA": [ - { - "NAME": "finished_goods_units", - "TYPE": "int", - "MIN_VALUE": 500, - "MAX_VALUE": 10000, - "MAX_STEP": 50, - "INCREASE_PROBABILITY": 0.5, - "RETAIN_PROBABILITY": 0.6 - }, - { - "NAME": "storage_utilization_percent", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 95, - "MAX_STEP": 5, - "INCREASE_PROBABILITY": 0.45, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 18.0, - "MAX_VALUE": 24.0, - "MAX_STEP": 0.5, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "humidity_percent", - "TYPE": "int", - "MIN_VALUE": 35, - "MAX_VALUE": 65, - "MAX_STEP": 3, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "outbound_shipments", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 500, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.6, - "RETAIN_PROBABILITY": 0.5 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/energy/monitoring", - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "total_power_kw", - "TYPE": "float", - "MIN_VALUE": 150.0, - "MAX_VALUE": 450.0, - "MAX_STEP": 20.0, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "voltage_v", - "TYPE": "float", - "MIN_VALUE": 380.0, - "MAX_VALUE": 400.0, - "MAX_STEP": 2.0, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "current_a", - "TYPE": "float", - "MIN_VALUE": 200.0, - "MAX_VALUE": 800.0, - "MAX_STEP": 30.0, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "power_factor", - "TYPE": "float", - "MIN_VALUE": 0.85, - "MAX_VALUE": 0.98, - "MAX_STEP": 0.02, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "daily_consumption_kwh", - "TYPE": "float", - "MIN_VALUE": 0.0, - "MAX_VALUE": 10000.0, - "MAX_STEP": 50.0, - "INCREASE_PROBABILITY": 0.95, - "RETAIN_PROBABILITY": 0.3 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/hvac/system", - "TIME_INTERVAL": 15, - "DATA": [ - { - "NAME": "ambient_temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 20.0, - "MAX_VALUE": 26.0, - "MAX_STEP": 0.8, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "target_temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 21.0, - "MAX_VALUE": 23.0, - "MAX_STEP": 0.2, - "RETAIN_PROBABILITY": 0.95 - }, - { - "NAME": "humidity_percent", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 60, - "MAX_STEP": 2, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "air_quality_index", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 80, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "filter_pressure_drop_pa", - "TYPE": "int", - "MIN_VALUE": 50, - "MAX_VALUE": 250, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.7, - "RETAIN_PROBABILITY": 0.6 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/maintenance/alerts", - "TIME_INTERVAL": 30, - "DATA": [ - { - "NAME": "active_alerts", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 20, - "MAX_STEP": 2, - "INCREASE_PROBABILITY": 0.2, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "preventive_maintenance_due", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 10, - "MAX_STEP": 1, - "INCREASE_PROBABILITY": 0.3, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "machine_uptime_percent", - "TYPE": "float", - "MIN_VALUE": 85.0, - "MAX_VALUE": 99.5, - "MAX_STEP": 1.0, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "mtbf_hours", - "TYPE": "int", - "MIN_VALUE": 200, - "MAX_VALUE": 2000, - "MAX_STEP": 50, - "INCREASE_PROBABILITY": 0.6, - "RETAIN_PROBABILITY": 0.7 - } - ] - } - ] -} diff --git a/mqtt-simulator-master/config/settings_industrial_factory_azure.json b/mqtt-simulator-master/config/settings_industrial_factory_azure.json deleted file mode 100644 index f78f9e3..0000000 --- a/mqtt-simulator-master/config/settings_industrial_factory_azure.json +++ /dev/null @@ -1,454 +0,0 @@ -{ - "BROKER_TYPE": "azure", - "AZURE_CONNECTION_STRING": "HostName=your-hub-name.azure-devices.net;DeviceId=factory-001;SharedAccessKey=your-shared-access-key", - "TOPICS": [ - { - "TYPE": "single", - "NAME": "factory/rawmaterial/intake", - "TIME_INTERVAL": 15, - "DATA": [ - { - "NAME": "material_received_kg", - "TYPE": "int", - "MIN_VALUE": 100, - "MAX_VALUE": 500, - "MAX_STEP": 50, - "RETAIN_PROBABILITY": 0.6 - }, - { - "NAME": "quality_grade", - "TYPE": "float", - "MIN_VALUE": 85.0, - "MAX_VALUE": 100.0, - "MAX_STEP": 2.0, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "storage_level_percent", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 95, - "MAX_STEP": 5, - "INCREASE_PROBABILITY": 0.4, - "RETAIN_PROBABILITY": 0.7 - } - ] - }, - { - "TYPE": "multiple", - "PREFIX": "factory/production/line", - "RANGE_START": 1, - "RANGE_END": 3, - "TIME_INTERVAL": 5, - "DATA": [ - { - "NAME": "machine_temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 65.0, - "MAX_VALUE": 85.0, - "MAX_STEP": 1.5, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "pressure_bar", - "TYPE": "float", - "MIN_VALUE": 2.0, - "MAX_VALUE": 6.5, - "MAX_STEP": 0.3, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "speed_rpm", - "TYPE": "int", - "MIN_VALUE": 800, - "MAX_VALUE": 1500, - "MAX_STEP": 50, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "vibration_mm_s", - "TYPE": "float", - "MIN_VALUE": 0.5, - "MAX_VALUE": 4.5, - "MAX_STEP": 0.4, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "production_count", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 10000, - "MAX_STEP": 15, - "INCREASE_PROBABILITY": 0.95, - "RETAIN_PROBABILITY": 0.3 - }, - { - "NAME": "defect_count", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 50, - "MAX_STEP": 2, - "INCREASE_PROBABILITY": 0.15, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "power_consumption_kw", - "TYPE": "float", - "MIN_VALUE": 15.0, - "MAX_VALUE": 45.0, - "MAX_STEP": 2.5, - "RETAIN_PROBABILITY": 0.7 - } - ] - }, - { - "TYPE": "multiple", - "PREFIX": "factory/assembly/station", - "RANGE_START": 1, - "RANGE_END": 5, - "TIME_INTERVAL": 8, - "DATA": [ - { - "NAME": "assembly_count", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 5000, - "MAX_STEP": 8, - "INCREASE_PROBABILITY": 0.9, - "RETAIN_PROBABILITY": 0.4 - }, - { - "NAME": "cycle_time_seconds", - "TYPE": "float", - "MIN_VALUE": 45.0, - "MAX_VALUE": 90.0, - "MAX_STEP": 3.0, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "robot_position_x", - "TYPE": "float", - "MIN_VALUE": -500.0, - "MAX_VALUE": 500.0, - "MAX_STEP": 50.0, - "RETAIN_PROBABILITY": 0.5 - }, - { - "NAME": "robot_position_y", - "TYPE": "float", - "MIN_VALUE": -300.0, - "MAX_VALUE": 300.0, - "MAX_STEP": 30.0, - "RETAIN_PROBABILITY": 0.5 - }, - { - "NAME": "tools_usage_percent", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 100, - "MAX_STEP": 5, - "INCREASE_PROBABILITY": 0.6, - "RETAIN_PROBABILITY": 0.65 - }, - { - "NAME": "errors", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 10, - "MAX_STEP": 1, - "INCREASE_PROBABILITY": 0.1, - "RETAIN_PROBABILITY": 0.95 - } - ] - }, - { - "TYPE": "multiple", - "PREFIX": "factory/quality/inspection", - "RANGE_START": 1, - "RANGE_END": 2, - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "inspected_units", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 3000, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.85, - "RETAIN_PROBABILITY": 0.5 - }, - { - "NAME": "pass_rate_percent", - "TYPE": "float", - "MIN_VALUE": 92.0, - "MAX_VALUE": 99.9, - "MAX_STEP": 0.8, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "rejected_units", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 100, - "MAX_STEP": 3, - "INCREASE_PROBABILITY": 0.2, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "dimension_tolerance_mm", - "TYPE": "float", - "MIN_VALUE": 0.01, - "MAX_VALUE": 0.15, - "MAX_STEP": 0.02, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "surface_quality_score", - "TYPE": "int", - "MIN_VALUE": 70, - "MAX_VALUE": 100, - "MAX_STEP": 3, - "RETAIN_PROBABILITY": 0.8 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/packaging/line1", - "TIME_INTERVAL": 12, - "DATA": [ - { - "NAME": "packages_completed", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 2000, - "MAX_STEP": 12, - "INCREASE_PROBABILITY": 0.9, - "RETAIN_PROBABILITY": 0.4 - }, - { - "NAME": "packaging_speed_units_min", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 60, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "label_errors", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 15, - "MAX_STEP": 1, - "INCREASE_PROBABILITY": 0.15, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "material_waste_kg", - "TYPE": "float", - "MIN_VALUE": 0.0, - "MAX_VALUE": 25.0, - "MAX_STEP": 1.5, - "INCREASE_PROBABILITY": 0.4, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "conveyor_speed_m_min", - "TYPE": "float", - "MIN_VALUE": 10.0, - "MAX_VALUE": 30.0, - "MAX_STEP": 2.0, - "RETAIN_PROBABILITY": 0.8 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/warehouse/storage", - "TIME_INTERVAL": 20, - "DATA": [ - { - "NAME": "finished_goods_units", - "TYPE": "int", - "MIN_VALUE": 500, - "MAX_VALUE": 10000, - "MAX_STEP": 50, - "INCREASE_PROBABILITY": 0.5, - "RETAIN_PROBABILITY": 0.6 - }, - { - "NAME": "storage_utilization_percent", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 95, - "MAX_STEP": 5, - "INCREASE_PROBABILITY": 0.45, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 18.0, - "MAX_VALUE": 24.0, - "MAX_STEP": 0.5, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "humidity_percent", - "TYPE": "int", - "MIN_VALUE": 35, - "MAX_VALUE": 65, - "MAX_STEP": 3, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "outbound_shipments", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 500, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.6, - "RETAIN_PROBABILITY": 0.5 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/energy/monitoring", - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "total_power_kw", - "TYPE": "float", - "MIN_VALUE": 150.0, - "MAX_VALUE": 450.0, - "MAX_STEP": 20.0, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "voltage_v", - "TYPE": "float", - "MIN_VALUE": 380.0, - "MAX_VALUE": 400.0, - "MAX_STEP": 2.0, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "current_a", - "TYPE": "float", - "MIN_VALUE": 200.0, - "MAX_VALUE": 800.0, - "MAX_STEP": 30.0, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "power_factor", - "TYPE": "float", - "MIN_VALUE": 0.85, - "MAX_VALUE": 0.98, - "MAX_STEP": 0.02, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "daily_consumption_kwh", - "TYPE": "float", - "MIN_VALUE": 0.0, - "MAX_VALUE": 10000.0, - "MAX_STEP": 50.0, - "INCREASE_PROBABILITY": 0.95, - "RETAIN_PROBABILITY": 0.3 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/hvac/system", - "TIME_INTERVAL": 15, - "DATA": [ - { - "NAME": "ambient_temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 20.0, - "MAX_VALUE": 26.0, - "MAX_STEP": 0.8, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "target_temperature_celsius", - "TYPE": "float", - "MIN_VALUE": 21.0, - "MAX_VALUE": 23.0, - "MAX_STEP": 0.2, - "RETAIN_PROBABILITY": 0.95 - }, - { - "NAME": "humidity_percent", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 60, - "MAX_STEP": 2, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "air_quality_index", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 80, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.75 - }, - { - "NAME": "filter_pressure_drop_pa", - "TYPE": "int", - "MIN_VALUE": 50, - "MAX_VALUE": 250, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.7, - "RETAIN_PROBABILITY": 0.6 - } - ] - }, - { - "TYPE": "single", - "NAME": "factory/maintenance/alerts", - "TIME_INTERVAL": 30, - "DATA": [ - { - "NAME": "active_alerts", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 20, - "MAX_STEP": 2, - "INCREASE_PROBABILITY": 0.2, - "RETAIN_PROBABILITY": 0.85 - }, - { - "NAME": "preventive_maintenance_due", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 10, - "MAX_STEP": 1, - "INCREASE_PROBABILITY": 0.3, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "machine_uptime_percent", - "TYPE": "float", - "MIN_VALUE": 85.0, - "MAX_VALUE": 99.5, - "MAX_STEP": 1.0, - "RETAIN_PROBABILITY": 0.8 - }, - { - "NAME": "mtbf_hours", - "TYPE": "int", - "MIN_VALUE": 200, - "MAX_VALUE": 2000, - "MAX_STEP": 50, - "INCREASE_PROBABILITY": 0.6, - "RETAIN_PROBABILITY": 0.7 - } - ] - } - ] -} diff --git a/mqtt-simulator-master/config/settings_mqtt_localhost.json b/mqtt-simulator-master/config/settings_mqtt_localhost.json deleted file mode 100644 index 24fee52..0000000 --- a/mqtt-simulator-master/config/settings_mqtt_localhost.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "BROKER_TYPE": "mqtt", - "BROKER_URL": "localhost", - "BROKER_PORT": 1883, - "TOPICS": [ - { - "TYPE": "multiple", - "PREFIX": "linha_producao/estacao", - "RANGE_START": 1, - "RANGE_END": 3, - "TIME_INTERVAL": 10, - "DATA": [ - { - "NAME": "producao", - "TYPE": "int", - "MIN_VALUE": 40, - "MAX_VALUE": 80, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "paragem", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 30, - "MAX_STEP": 5, - "RETAIN_PROBABILITY": 0.9 - }, - { - "NAME": "stock", - "TYPE": "int", - "MIN_VALUE": 20, - "MAX_VALUE": 100, - "MAX_STEP": 10, - "INCREASE_PROBABILITY": 0.3, - "RETAIN_PROBABILITY": 0.7 - }, - { - "NAME": "defeitos", - "TYPE": "int", - "MIN_VALUE": 0, - "MAX_VALUE": 5, - "MAX_STEP": 1, - "RETAIN_PROBABILITY": 0.8 - } - ] - } - ] -} diff --git a/mqtt-simulator-master/mqtt-simulator/__init__.py b/mqtt-simulator-master/mqtt-simulator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mqtt-simulator-master/mqtt-simulator/main.py b/mqtt-simulator-master/mqtt-simulator/main.py index 4dc264d..eb505fd 100644 --- a/mqtt-simulator-master/mqtt-simulator/main.py +++ b/mqtt-simulator-master/mqtt-simulator/main.py @@ -2,6 +2,8 @@ import signal import sys import time +import os +import json from json import JSONDecodeError from pathlib import Path @@ -11,19 +13,50 @@ from utils.print_validation_error import print_validation_error from utils.read_publishers import read_publishers - def default_settings() -> Path: base_folder = Path(__file__).resolve().parent.parent settings_file = base_folder / "config/settings.json" return settings_file - def is_valid_file(arg: str) -> Path: settings_file = Path(arg) if not settings_file.is_file(): raise argparse.ArgumentTypeError(f"argument -f/--file: can't open '{arg}'") return settings_file +# ========================================== +# 1. KUBERNETES CONFIG LOADER +# ========================================== +# Checks if K8s injected config via Env Var. +# If so, writes it to the default file location. +env_config = os.getenv("SIMULATOR_CONFIG_JSON") +if env_config: + try: + print("[INIT] Found SIMULATOR_CONFIG_JSON environment variable.") + # Parse to ensure it's valid JSON + config_data = json.loads(env_config) + + # Define target path (Standard location) + target_path = default_settings() + + # Ensure folder exists + target_path.parent.mkdir(parents=True, exist_ok=True) + + # Write file + with open(target_path, "w") as f: + json.dump(config_data, f, indent=2) + + print(f"[INIT] Successfully wrote config to {target_path}") + + except json.JSONDecodeError as e: + print(f"[ERROR] Failed to parse SIMULATOR_CONFIG_JSON: {e}") + # We don't exit here; we let the file argument handler try its luck + except Exception as e: + print(f"[ERROR] Failed to write config file: {e}") + +# ========================================== +# 2. STANDARD ARGUMENT PARSING +# ========================================== parser = argparse.ArgumentParser() parser.add_argument( @@ -73,4 +106,4 @@ def signal_handler(sig, frame): except KeyboardInterrupt: print("\n\nShutting down gracefully...") simulator.stop() - time.sleep(2) + time.sleep(2) \ No newline at end of file diff --git a/mqtt-simulator-master/requirements.txt b/mqtt-simulator-master/requirements.txt index 6d60fb2..2f9c6be 100644 --- a/mqtt-simulator-master/requirements.txt +++ b/mqtt-simulator-master/requirements.txt @@ -1,3 +1,3 @@ -paho-mqtt==2.1.0 -pydantic==2.11.5 +paho-mqtt==1.6.1 +pydantic==2.12.0 azure-iot-device==2.14.0 diff --git a/onboarding-k8s/README.md b/onboarding-k8s/README.md new file mode 100644 index 0000000..5bc0a70 --- /dev/null +++ b/onboarding-k8s/README.md @@ -0,0 +1,133 @@ +## Local Development Onboarding Guide (WSL2) + +This guide explains how to spin up the ** Simulation-as-a-Service** platform locally using the `setup-dev.sh` automation script. + +### 1. Prerequisites (Windows + WSL2) + +Ensure the following are installed and running before executing the script. + +* **WSL2:** (Ubuntu 20.04 or 22.04 recommended). +* **Docker Desktop for Windows:** +* *Crucial Setting:* Go to Settings > Resources > WSL Integration > Enable integration for your specific distro (e.g., Ubuntu). + + +* **Kubectl:** The Kubernetes command-line tool. +* *Install in WSL:* `curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl` + + +* **Kind:** Kubernetes in Docker. +* *Install in WSL:* `curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 && chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind` + + + +--- + +### 2. Project Setup + +Ensure your repository is cloned inside the **WSL filesystem** (e.g., `~/projects/ProjetoISIv1`), not the Windows mounted drive (`/mnt/c/...`), for significantly better Docker performance. + +**Required Directory Structure:** + +```text +/PEQSPC/ProjetoISIv1 +├── setup-dev.sh # The automation script +├── app/ # Manager API code +├── simulator/ # Simulator code +└── k8s-static/ # Kubernetes YAMLs + +``` + +--- + +### 3. Running the Environment + +1. **Open Terminal:** Launch your WSL terminal (Ubuntu). +2. **Navigate to Repo:** +```bash +cd ~/path/to/PEQSPC/ProjetoISIv1 + +``` + + +3. **Make Executable:** +```bash +chmod +x setup-dev.sh + +``` + + +4. **Run Script:** +```bash +./setup-dev.sh + +``` + + + +**What the script does:** + +1. Creates a Kind cluster named `isi-dev-cluster`. +2. Builds local Docker images for the API and Simulator. +3. Loads those images directly into the cluster nodes (bypassing Docker Hub). +4. Applies RBAC (permissions) and deploys the API. + +--- + +### 4. Verification & Usage + +Once the script finishes with `✅ Setup Complete!`: + +**A. Check Status** + +```bash +kubectl get pods -n iot-sims +# You should see 'manager-api-xxxxx' running. + +``` + +**B. Access the API (Port Forward)** +Run this command in a **separate** WSL terminal window to open the tunnel: + +```bash +kubectl port-forward svc/manager-api-service 5000:80 -n iot-sims + +``` + +**C. Create a Simulation** +Open **PowerShell** (Windows) or stay in **WSL** and run: + +```bash +curl -X POST http://localhost:5000/create-sim \ + -H "Content-Type: application/json" \ + -d '{"device_id": "test-unit-01", "duration": 60}' + +``` + +**D. See the Result** +Back in WSL: + +```bash +kubectl get pods -n iot-sims --watch + +``` + +You will see a new pod `sim-test-unit-01-xxxx` spin up, run, and eventually terminate automatically. + +--- + +### Common Troubleshooting + +* **Error: "docker: command not found"** +* *Fix:* Docker Desktop is not running or WSL integration is disabled in Docker Settings. + + +* **Error: "ImagePullBackOff"** +* *Fix:* The script failed to load the image. Run `kind load docker-image peqspc/manager-api:dev --name isi-dev-cluster` manually. + + +* **Slow Network/IO** +* *Fix:* Ensure you are running strictly inside the Linux filesystem (`/home/user/...`), not `/mnt/c/Users/...`. + + + +Would you like the `test-curl.sh` script to automate step 4C? \ No newline at end of file diff --git a/onboarding-k8s/setup-dev.sh b/onboarding-k8s/setup-dev.sh new file mode 100644 index 0000000..c437c0f --- /dev/null +++ b/onboarding-k8s/setup-dev.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# ========================================== +# Local Dev Onboarding Script +# ========================================== +CLUSTER_NAME="isi-dev-cluster" +IMAGE_REGISTRY="peqspc" +API_IMAGE="manager-api:dev" +SIM_IMAGE="iot-simulator:dev" + +echo "Starting Onboarding for ISIv1 SaaS..." + +# 1. Check Prerequisites +command -v docker >/dev/null 2>&1 || { echo "❌ Docker is required but not installed. Aborting." >&2; exit 1; } +command -v kind >/dev/null 2>&1 || { echo "❌ Kind is required but not installed. Aborting." >&2; exit 1; } +command -v kubectl >/dev/null 2>&1 || { echo "❌ Kubectl is required but not installed. Aborting." >&2; exit 1; } + +# 2. Cleanup Old Cluster (Optional) +if kind get clusters | grep -q "^$CLUSTER_NAME$"; then + read -p "Cluster '$CLUSTER_NAME' already exists. Delete and recreate? (y/n) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + kind delete cluster --name $CLUSTER_NAME + else + echo "Example: Keeping existing cluster..." + fi +fi + +# 3. Create Kind Cluster +if ! kind get clusters | grep -q "^$CLUSTER_NAME$"; then + echo "Creating Kind Cluster..." + kind create cluster --name $CLUSTER_NAME +fi + +# 4. Build Docker Images (Local Build) + echo "Building Manager API Image..." + docker build -t $IMAGE_REGISTRY/$API_IMAGE ./app + + echo "Building IoT Simulator Image..." + docker build -t $IMAGE_REGISTRY/$SIM_IMAGE ./simulator + +# 5. Load Images into Kind (No need to push to DockerHub for local dev) +echo "Loading images into Kind nodes..." +kind load docker-image $IMAGE_REGISTRY/$API_IMAGE --name $CLUSTER_NAME +kind load docker-image $IMAGE_REGISTRY/$SIM_IMAGE --name $CLUSTER_NAME + +# 6. Apply Infrastructure +echo "Applying Kubernetes Manifests..." + +# Create Namespace +kubectl --context kind-$CLUSTER_NAME create namespace iot-sims --dry-run=client -o yaml | kubectl apply -f - + +# Apply RBAC (Crucial for API to create Pods) +kubectl --context kind-$CLUSTER_NAME apply -f k8s/rbac-config.yaml + +# Apply API Deployment & Service +kubectl --context kind-$CLUSTER_NAME apply -f k8s/api-deployment.yaml +kubectl --context kind-$CLUSTER_NAME apply -f k8s/api-service.yaml +# 7. Install ArgoCD (Optional - for Production Parity) +echo "Installing ArgoCD (for later use)..." +kubectl --context kind-$CLUSTER_NAME create namespace argocd --dry-run=client -o yaml | kubectl apply -f - +kubectl --context kind-$CLUSTER_NAME apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml + +# 8. Status & Instructions +echo "" +echo "=========================================" +echo " Setup Complete!" +echo "=========================================" +echo "To access your API from Windows:" +echo " kubectl port-forward svc/manager-api-service 5000:80 -n iot-sims" +echo "" +echo "Then send a POST request to: http://localhost:5000/create-sim" \ No newline at end of file From dc5cd4165df60694ea3d4eb3158a3f4fcd6861f4 Mon Sep 17 00:00:00 2001 From: PEQSPC Date: Sat, 3 Jan 2026 18:42:26 +0000 Subject: [PATCH 2/5] remove azure iot device code because of conflit librarys --- .github/workflows/publish-docker-image.yaml | 2 +- mqtt-simulator-master/.dockerignore | 11 + mqtt-simulator-master/Dockerfile | 2 +- mqtt-simulator-master/config/settings.json | 33 +-- .../mqtt-simulator/azure_publisher.py | 235 ------------------ .../mqtt-simulator/utils/read_publishers.py | 37 +-- mqtt-simulator-master/requirements.txt | 5 +- 7 files changed, 54 insertions(+), 271 deletions(-) delete mode 100644 mqtt-simulator-master/mqtt-simulator/azure_publisher.py diff --git a/.github/workflows/publish-docker-image.yaml b/.github/workflows/publish-docker-image.yaml index 1739253..3907ecb 100644 --- a/.github/workflows/publish-docker-image.yaml +++ b/.github/workflows/publish-docker-image.yaml @@ -1,5 +1,5 @@ # ON TEST FILE -name: Publish Docker image to ghcr.io +name: Publish Docker images to ghcr.io on: push: diff --git a/mqtt-simulator-master/.dockerignore b/mqtt-simulator-master/.dockerignore index 9badeae..1ee09b0 100644 --- a/mqtt-simulator-master/.dockerignore +++ b/mqtt-simulator-master/.dockerignore @@ -2,3 +2,14 @@ LICENSE *.md docs venv +env +__pycache__ +*.pyc +*.pyo +*.pyd +*.pyds +.DS_Store +.git +.gitignore +.vscode +docker-compose.override.yml diff --git a/mqtt-simulator-master/Dockerfile b/mqtt-simulator-master/Dockerfile index 0754c71..8291cb4 100644 --- a/mqtt-simulator-master/Dockerfile +++ b/mqtt-simulator-master/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13-alpine +FROM python:3.11-slim WORKDIR /usr/src/app diff --git a/mqtt-simulator-master/config/settings.json b/mqtt-simulator-master/config/settings.json index dcb6a3e..8ba9f6c 100644 --- a/mqtt-simulator-master/config/settings.json +++ b/mqtt-simulator-master/config/settings.json @@ -2,19 +2,22 @@ "BROKER_URL": "broker.hivemq.com", "BROKER_PORT": 1883, "TIME_INTERVAL": 2, - "TOPICS": [{ - "TYPE": "multiple", - "PREFIX": "fabrica/sensor", - "RANGE_START": 1, - "RANGE_END": 3, - "DATA": [{ - "NAME": "temperatura", - "TYPE": "float", - "MIN_VALUE": 20, - "MAX_VALUE": 35, - "MAX_STEP": 1.5, - "INCREASE_PROBABILITY": 0.6 - }] - }], - "duration_minutes": 2 + "TOPICS": [ + { + "TYPE": "multiple", + "PREFIX": "fabrica/sensor", + "RANGE_START": 1, + "RANGE_END": 3, + "DATA": [ + { + "NAME": "temperatura", + "TYPE": "float", + "MIN_VALUE": 20, + "MAX_VALUE": 35, + "MAX_STEP": 1.5, + "INCREASE_PROBABILITY": 0.6 + } + ] + } + ] } \ No newline at end of file diff --git a/mqtt-simulator-master/mqtt-simulator/azure_publisher.py b/mqtt-simulator-master/mqtt-simulator/azure_publisher.py deleted file mode 100644 index 154cc21..0000000 --- a/mqtt-simulator-master/mqtt-simulator/azure_publisher.py +++ /dev/null @@ -1,235 +0,0 @@ -""" -Azure IoT Hub Publisher - -This module provides an Azure IoT Hub publisher that mimics the MQTT Publisher -interface but sends telemetry to Azure IoT Hub instead of a traditional MQTT broker. -""" - -import asyncio -import json -import threading -import time -from typing import Any - -from azure.iot.device.aio import IoTHubDeviceClient -from azure.iot.device import Message -from settings_classes import BrokerSettings, ClientSettings, DataSettings - - -class AzurePublisher(threading.Thread): - """ - Azure IoT Hub Publisher that sends telemetry data to Azure IoT Hub. - - This class maintains the same interface as the MQTT Publisher but uses - Azure IoT Device SDK instead of paho-mqtt. - """ - - def __init__( - self, - broker_settings: BrokerSettings, - topic_url: str, - topic_data: list[DataSettings], - topic_payload_root: dict[str, Any], - client_settings: ClientSettings, - is_verbose: bool, - ): - threading.Thread.__init__(self) - # Set as daemon thread to allow clean program exit - self.daemon = True - - self.broker_settings = broker_settings - self.topic_url = topic_url # Will be used as message property "topic" - self.topic_data = topic_data - self.topic_payload_root = topic_payload_root - self.client_settings = client_settings - self.is_verbose = is_verbose - - self.loop = False - self.payload: dict[str, Any] | None = None - self.client: IoTHubDeviceClient | None = None - self.event_loop: asyncio.AbstractEventLoop | None = None - - def create_client(self) -> IoTHubDeviceClient: - """Create Azure IoT Hub device client with appropriate settings.""" - # Get the connection string for this specific topic - connection_string = self.broker_settings.get_azure_connection_string(self.topic_url) - - if not connection_string: - raise ValueError( - f"No Azure connection string found for topic: {self.topic_url}. " - f"Check AZURE_DEVICE_CONNECTIONS or AZURE_CONNECTION_STRING in settings." - ) - - if self.broker_settings.azure_model_id: - client = IoTHubDeviceClient.create_from_connection_string( - connection_string, - product_info=self.broker_settings.azure_model_id - ) - else: - client = IoTHubDeviceClient.create_from_connection_string( - connection_string - ) - - # The SDK handles reconnections automatically, but we add our own retry logic - return client - - async def connect_async(self): - """Async connection to Azure IoT Hub.""" - self.client = self.create_client() - await self.client.connect() - print(f"Connected to Azure IoT Hub for topic: {self.topic_url}") - - async def disconnect_async(self): - """Async disconnection from Azure IoT Hub.""" - if self.client: - try: - await self.client.shutdown() - print(f"Disconnected from Azure IoT Hub for topic: {self.topic_url}") - except Exception as e: - # Ignore errors during shutdown (e.g., if already disconnected) - pass - - async def send_telemetry_async(self, payload: dict[str, Any]): - """ - Send telemetry to Azure IoT Hub asynchronously. - - Args: - payload: Telemetry data to send - - Raises: - RuntimeError: If client is not connected - Exception: If message sending fails - """ - if not self.client: - raise RuntimeError("Client not connected") - - # Create message with JSON payload - message = Message(json.dumps(payload)) - message.content_encoding = "utf-8" - message.content_type = "application/json" - - # Add topic as custom property to maintain compatibility with MQTT structure - message.custom_properties["topic"] = self.topic_url - - # Send message with timeout - try: - await asyncio.wait_for( - self.client.send_message(message), - timeout=30.0 # 30 second timeout - ) - - # Log publish event - on_publish_log = f"[{time.strftime('%H:%M:%S')}] Telemetry sent to Azure IoT Hub: {self.topic_url}" - if self.is_verbose: - on_publish_log += f"\n\t[payload] {json.dumps(payload)}" - print(on_publish_log) - - except asyncio.TimeoutError: - raise Exception(f"Timeout sending message to Azure IoT Hub for {self.topic_url}") - except Exception as e: - raise Exception(f"Failed to send message: {str(e)}") - - async def publish_loop_async(self): - """Main async publishing loop with reconnection handling.""" - max_retries = 3 - retry_delay = 5 - - # Initial connection with retries - for attempt in range(max_retries): - try: - await self.connect_async() - break - except Exception as e: - if attempt < max_retries - 1: - print(f"Connection attempt {attempt + 1} failed: {e}. Retrying in {retry_delay}s...") - await asyncio.sleep(retry_delay) - else: - print(f"Failed to connect after {max_retries} attempts: {e}") - return - - try: - while self.loop: - self.payload = self.generate_payload() - if self.payload is None: - break - - try: - await self.send_telemetry_async(self.payload) - except Exception as e: - print(f"Error sending telemetry to {self.topic_url}: {e}") - # Try to reconnect on send failure - try: - print(f"Attempting to reconnect for {self.topic_url}...") - # Shutdown old client - if self.client: - await self.client.shutdown() - await asyncio.sleep(2) - # Create and connect new client - await self.connect_async() - print(f"Reconnected successfully for {self.topic_url}") - except Exception as reconnect_error: - print(f"Reconnection failed for {self.topic_url}: {reconnect_error}") - # Continue loop to retry on next iteration - - await asyncio.sleep(self.client_settings.time_interval) - - finally: - await self.disconnect_async() - - def run(self): - """ - Thread run method that creates an event loop and runs the async publisher. - """ - self.loop = True - - # Create new event loop for this thread - self.event_loop = asyncio.new_event_loop() - asyncio.set_event_loop(self.event_loop) - - try: - # Run the async publish loop - self.event_loop.run_until_complete(self.publish_loop_async()) - except Exception as e: - print(f"Error in Azure publisher: {e}") - finally: - # Cancel all pending tasks before closing the loop - try: - pending = asyncio.all_tasks(self.event_loop) - for task in pending: - task.cancel() - # Give tasks a chance to cancel - if pending: - self.event_loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True)) - except Exception: - pass - finally: - try: - self.event_loop.close() - except Exception: - pass - - def stop(self): - """Stop the publisher.""" - self.loop = False - - def generate_payload(self) -> dict[str, Any] | None: - """ - Generate payload from data settings. - - Returns: - Dictionary with generated data or None if no data is active - """ - payload: dict[str, Any] = {} - payload.update(self.topic_payload_root) - has_data_active = False - - for data in self.topic_data: - if data.get_is_active(): - has_data_active = True - payload[data.name] = data.generate_value() - - if not has_data_active: - self.stop() - return None - - return payload diff --git a/mqtt-simulator-master/mqtt-simulator/utils/read_publishers.py b/mqtt-simulator-master/mqtt-simulator/utils/read_publishers.py index 81c545e..ccd8597 100644 --- a/mqtt-simulator-master/mqtt-simulator/utils/read_publishers.py +++ b/mqtt-simulator-master/mqtt-simulator/utils/read_publishers.py @@ -3,8 +3,13 @@ from typing import Any from publisher import Publisher -from azure_publisher import AzurePublisher -from settings_classes import BrokerSettings, ClientSettings, DataSettings, DataSettingsFactory, TopicSettingsFactory +from settings_classes import ( + BrokerSettings, + ClientSettings, + DataSettings, + DataSettingsFactory, + TopicSettingsFactory, +) def read_publishers(settings_file: Path, is_verbose: bool) -> list[Publisher]: @@ -16,38 +21,37 @@ def load_topic_data(topic_data_object: list[dict[str, Any]]) -> list[DataSetting return topic_data publishers: list[Publisher] = [] - default_client_settings = ClientSettings(CLEAN_SESSION=True, RETAIN=False, QOS=2, TIME_INTERVAL=10) + default_client_settings = ClientSettings( + CLEAN_SESSION=True, + RETAIN=False, + QOS=2, + TIME_INTERVAL=10, + ) + with open(settings_file, encoding="utf-8") as json_file: json_object = json.load(json_file) + broker_settings = BrokerSettings.model_validate(json_object) broker_client_settings = ClientSettings.model_validate(json_object).resolve_with_default( default=default_client_settings ) - # Determine which publisher class to use based on broker type - PublisherClass = AzurePublisher if broker_settings.is_azure_enabled() else Publisher - - if broker_settings.is_azure_enabled(): - if broker_settings.azure_device_connections: - device_count = len(broker_settings.azure_device_connections) - print(f"Using Azure IoT Hub publisher with {device_count} device connections") - else: - print(f"Using Azure IoT Hub publisher (single connection)") - else: - print(f"Using MQTT publisher (broker: {broker_settings.url}:{broker_settings.port})") + # 🔹 MQTT only (Azure disabled) + print(f"Using MQTT publisher (broker: {broker_settings.url}:{broker_settings.port})") # read each configured topic for topic_object in json_object.get("TOPICS"): client_settings = ClientSettings.model_validate(topic_object).resolve_with_default( default=broker_client_settings ) + topic_settings = TopicSettingsFactory.create(topic_object) topic_data_object = topic_object.get("DATA") + for topic_url in topic_settings.topic_urls(): - # each topic_url should have different data_settings instances topic_data = load_topic_data(topic_data_object) publishers.append( - PublisherClass( + Publisher( broker_settings, topic_url, topic_data, @@ -56,4 +60,5 @@ def load_topic_data(topic_data_object: list[dict[str, Any]]) -> list[DataSetting is_verbose, ) ) + return publishers diff --git a/mqtt-simulator-master/requirements.txt b/mqtt-simulator-master/requirements.txt index 2f9c6be..5057cb8 100644 --- a/mqtt-simulator-master/requirements.txt +++ b/mqtt-simulator-master/requirements.txt @@ -1,3 +1,2 @@ -paho-mqtt==1.6.1 -pydantic==2.12.0 -azure-iot-device==2.14.0 +paho-mqtt==2.1.0 +pydantic==2.11.5 From 749d27a57eba342bdc0902ceaf296c34aa6b60c4 Mon Sep 17 00:00:00 2001 From: PEQSPC Date: Sat, 3 Jan 2026 19:01:38 +0000 Subject: [PATCH 3/5] feat: Implement Docker image publishing workflow and update image references for Kubernetes deployment --- .github/workflows/publish-docker-image.yaml | 44 ------- .github/workflows/publish-docker-images.yaml | 67 ++++++++++ .github/workflows/restrict-main.yml | 2 +- iot-simulator-api/src/main.py | 2 +- k8s/api-deployment.yaml | 6 +- mqtt-simulator-master/.gitignore | 125 ------------------- mqtt-simulator-master/LICENSE | 21 ---- 7 files changed, 72 insertions(+), 195 deletions(-) delete mode 100644 .github/workflows/publish-docker-image.yaml create mode 100644 .github/workflows/publish-docker-images.yaml delete mode 100644 mqtt-simulator-master/.gitignore delete mode 100644 mqtt-simulator-master/LICENSE diff --git a/.github/workflows/publish-docker-image.yaml b/.github/workflows/publish-docker-image.yaml deleted file mode 100644 index 3907ecb..0000000 --- a/.github/workflows/publish-docker-image.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# ON TEST FILE -name: Publish Docker images to ghcr.io - -on: - push: - branches: - - main - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository }} - tags: | - type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - type=sha - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to GHCR - if: github.event_name != 'pull_request' - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/publish-docker-images.yaml b/.github/workflows/publish-docker-images.yaml new file mode 100644 index 0000000..d8eb032 --- /dev/null +++ b/.github/workflows/publish-docker-images.yaml @@ -0,0 +1,67 @@ +name: Publish Docker images to GitHub Container Registry + +on: + push: + branches: + - main + +env: + REGISTRY: ghcr.io + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + strategy: + fail-fast: false + matrix: + include: + # Configuration for the API + - context: ./iot-simulator-api + image-name: manager-api + dockerfile: ./iot-simulator-api/Dockerfile + + # Configuration for the Simulator + # (Assuming 'mqtt-simulator-master' is the folder name) + - context: ./mqtt-simulator-master + image-name: mqtt-simulator + dockerfile: ./mqtt-simulator-master/Dockerfile + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + # This creates the full image name: ghcr.io/username/image-name + images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ matrix.image-name }} + tags: | + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + type=sha + type=ref,event=branch + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.context }} + file: ${{ matrix.dockerfile }} + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # Enable multi-arch builds if needed (optional) + # platforms: linux/amd64,linux/arm64 \ No newline at end of file diff --git a/.github/workflows/restrict-main.yml b/.github/workflows/restrict-main.yml index fd626f0..40b3517 100644 --- a/.github/workflows/restrict-main.yml +++ b/.github/workflows/restrict-main.yml @@ -12,6 +12,6 @@ jobs: - name: Ensure PR is from develop run: | if [ "${{ github.head_ref }}" != "develop" ]; then - echo "❌ Pull requests to main must come from develop" + echo " Pull requests to main must come from develop" exit 1 fi diff --git a/iot-simulator-api/src/main.py b/iot-simulator-api/src/main.py index 8825428..d7dddf6 100644 --- a/iot-simulator-api/src/main.py +++ b/iot-simulator-api/src/main.py @@ -125,7 +125,7 @@ async def create_simulation(config: SimulationConfig, db: Session = Depends(get_ "containers": [{ "name": "simulator", # Use your public image or local dev image - "image": "ghcr.io/damascenorafael/mqtt-simulator:sha-a73a2e8", + "image": "ghcr.io/peqspc/mqtt-simulator:latest", "imagePullPolicy": "IfNotPresent", # We inject the config via an Environment Variable instead of a file volume # This is much simpler for K8s than managing temporary PVCs or ConfigMaps diff --git a/k8s/api-deployment.yaml b/k8s/api-deployment.yaml index 351195a..b4a66da 100644 --- a/k8s/api-deployment.yaml +++ b/k8s/api-deployment.yaml @@ -13,11 +13,11 @@ spec: labels: app: manager-api spec: - serviceAccountName: sim-manager-sa # <--- CRITICAL: Connects to RBAC + serviceAccountName: sim-manager-sa containers: - name: api - image: peqspc/manager-api:dev # <--- Matches the image you built - imagePullPolicy: Never # <--- CRITICAL: Uses local image + image: peqspc/manager-api:latest + imagePullPolicy: Always ports: - containerPort: 80 env: diff --git a/mqtt-simulator-master/.gitignore b/mqtt-simulator-master/.gitignore deleted file mode 100644 index f717486..0000000 --- a/mqtt-simulator-master/.gitignore +++ /dev/null @@ -1,125 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/mqtt-simulator-master/LICENSE b/mqtt-simulator-master/LICENSE deleted file mode 100644 index 5416d88..0000000 --- a/mqtt-simulator-master/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Rafael Damasceno - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From c61151727799dbc81d0e8c52b5715e50cf146c5d Mon Sep 17 00:00:00 2001 From: PEQSPC Date: Mon, 5 Jan 2026 19:25:05 +0000 Subject: [PATCH 4/5] feat: Add 'develop' branch to Docker image publishing workflow triggers --- .github/workflows/publish-docker-images.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-docker-images.yaml b/.github/workflows/publish-docker-images.yaml index d8eb032..0352496 100644 --- a/.github/workflows/publish-docker-images.yaml +++ b/.github/workflows/publish-docker-images.yaml @@ -4,6 +4,7 @@ on: push: branches: - main + - develop env: REGISTRY: ghcr.io From 1f2ae10afadf5aeab6bccfcca52aa983d404c108 Mon Sep 17 00:00:00 2001 From: PEQSPC Date: Mon, 5 Jan 2026 23:04:18 +0000 Subject: [PATCH 5/5] feat: Update Kubernetes deployment and configuration for simulator image usage --- iot-simulator-api/src/db/simulations.db | Bin 16384 -> 16384 bytes iot-simulator-api/src/main.py | 12 +++++++++--- iot-simulator-api/test-mqtt/my-config.json | 2 +- k8s/api-deployment.yaml | 8 +++++--- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/iot-simulator-api/src/db/simulations.db b/iot-simulator-api/src/db/simulations.db index 7169de81b7d2b0698ea9a73b56707518508828d2..598966d3012fbd1f3aa94b6064373c912ed6b01d 100644 GIT binary patch delta 301 zcmZo@U~Fh$oFL7}Hc`fzk!@qb5`Hdb&WQ}Xx4E}*PTbho%&FX>#LUAWtIE(WDecJM z$e5OzW|3l$np~Wjs|#aq{wI5l(ce_T$jHjb%*w!2&(gry#MH>hz{pJ3z);r!B4dG& zVQNugLN?UQ%+l1r!rT&MD2xp@R0pEfz{<#6&%o5e#K;uQP@s)48K%v*Wi^?&nE5|2 z@c-fe#{Xfnpu!`5IaX#yMrLLZ$;8CW%*n~cz`(%Fe~W?t3;%QeTR=5u_|JwaT;$z+TUL{ajgkKk1OFeOz