diff --git a/examples/BaSyxDatabridge/README.md b/examples/BaSyxDatabridge/README.md new file mode 100644 index 000000000..b73db0236 --- /dev/null +++ b/examples/BaSyxDatabridge/README.md @@ -0,0 +1,42 @@ +# BaSyx Databridge Example Setup + +This example showcases the usage of the BaSyx Databridge. The BaSyx Databridge is a service that allows the exchange of data between assets and the AAS. +Here, an example MQTT client for an environmental sensor is used to send data via the BaSyx Databridge to an AAS located in the BaSyx AAS Environment. + +## How to run the BaSyx Databridge Example + +1. Open a terminal in this folder +2. Run the following command to start the BaSyx containers: + +```bash +docker-compose up -d +``` + +> To run the example containers, you need to have Docker installed on your device. + +## View the working Example + +To see the working example, open the [BaSyx AAS Web UI](http://localhost:3000) and navigate to the `SensorExampleAAS`. You can see the data coming from the MQTT client in the `SensorData` submodel. +To see updates in real-time, active the `Auto-Sync` feature in the AAS Web UI (see top right corner of the UI). + +## Where to find the configuration + +### BaSyx Databridge + +The configuration for the BaSyx Databridge can be found in the `databridge` folder. There you can find the `routes.config` file, which defines the routes from the assets data source to the AAS data sink. +The data source configuration can be found in the `mqttconsumer.json` file, and the data sink configuration can be found in the `aasserver.json` file. +There are also data transformers defined for extracting the data from the MQTT message. +For more details on how to configure the BaSyx Databridge, please refer to the [BaSyx Databridge documentation](https://wiki.basyx.org/en/latest/content/user_documentation/basyx_components/databridge/index.html). + +### MQTT Client + +The configuration for the MQTT client can be found in the `mqtt-publisher` folder. It is a small Python script that publishes data to the MQTT broker. + +### MQTT Broker + +The MQTT brokers configuration can be found in the `mosquitto` folder. The configuration is defined in the `config/mosquitto.conf` file. + +### BaSyx Components + +The configuration for the BaSyx components can be found in the `basyx` folder. +The AAS used in this example is located in the `aas` folder. diff --git a/examples/BaSyxDatabridge/aas/SensorExampleComplete.aasx b/examples/BaSyxDatabridge/aas/SensorExampleComplete.aasx new file mode 100644 index 000000000..33e68fe50 Binary files /dev/null and b/examples/BaSyxDatabridge/aas/SensorExampleComplete.aasx differ diff --git a/examples/BaSyxDatabridge/basyx/aas-env.properties b/examples/BaSyxDatabridge/basyx/aas-env.properties new file mode 100644 index 000000000..3d3d08d9d --- /dev/null +++ b/examples/BaSyxDatabridge/basyx/aas-env.properties @@ -0,0 +1,8 @@ +server.port=8081 +basyx.backend=InMemory +basyx.environment=file:aas +basyx.cors.allowed-origins=* +basyx.cors.allowed-methods=GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD +basyx.aasrepository.feature.registryintegration=http://aas-registry:8080 +basyx.submodelrepository.feature.registryintegration=http://sm-registry:8080 +basyx.externalurl=http://localhost:8081 diff --git a/examples/BaSyxDatabridge/basyx/aas-registry.yml b/examples/BaSyxDatabridge/basyx/aas-registry.yml new file mode 100644 index 000000000..4fac8db3a --- /dev/null +++ b/examples/BaSyxDatabridge/basyx/aas-registry.yml @@ -0,0 +1,4 @@ +basyx: + cors: + allowed-origins: '*' + allowed-methods: GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD diff --git a/examples/BaSyxDatabridge/basyx/sm-registry.yml b/examples/BaSyxDatabridge/basyx/sm-registry.yml new file mode 100644 index 000000000..4fac8db3a --- /dev/null +++ b/examples/BaSyxDatabridge/basyx/sm-registry.yml @@ -0,0 +1,4 @@ +basyx: + cors: + allowed-origins: '*' + allowed-methods: GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD diff --git a/examples/BaSyxDatabridge/databridge/AirQualityTransformer.jsonata b/examples/BaSyxDatabridge/databridge/AirQualityTransformer.jsonata new file mode 100644 index 000000000..af93b795f --- /dev/null +++ b/examples/BaSyxDatabridge/databridge/AirQualityTransformer.jsonata @@ -0,0 +1 @@ +airQuality \ No newline at end of file diff --git a/examples/BaSyxDatabridge/databridge/HumidityTransformer.jsonata b/examples/BaSyxDatabridge/databridge/HumidityTransformer.jsonata new file mode 100644 index 000000000..6506bbd58 --- /dev/null +++ b/examples/BaSyxDatabridge/databridge/HumidityTransformer.jsonata @@ -0,0 +1 @@ +humidity \ No newline at end of file diff --git a/examples/BaSyxDatabridge/databridge/TemperatureTransformer.jsonata b/examples/BaSyxDatabridge/databridge/TemperatureTransformer.jsonata new file mode 100644 index 000000000..4d81bb0f8 --- /dev/null +++ b/examples/BaSyxDatabridge/databridge/TemperatureTransformer.jsonata @@ -0,0 +1 @@ +temperature \ No newline at end of file diff --git a/examples/BaSyxDatabridge/databridge/aasserver.json b/examples/BaSyxDatabridge/databridge/aasserver.json new file mode 100644 index 000000000..2d6509135 --- /dev/null +++ b/examples/BaSyxDatabridge/databridge/aasserver.json @@ -0,0 +1,20 @@ +[ + { + "uniqueId": "TemperatureAAS", + "submodelEndpoint": "http://host.docker.internal:8081/submodels/YjdlNTA2NzgtZGY3Mi00ODAxLWI3YzAtNzI2NTNkMmU0NzI0", + "idShortPath": "TemperatureValue", + "api": "DotAAS-V3" + }, + { + "uniqueId": "HumidityAAS", + "submodelEndpoint": "http://host.docker.internal:8081/submodels/YjdlNTA2NzgtZGY3Mi00ODAxLWI3YzAtNzI2NTNkMmU0NzI0", + "idShortPath": "HumidityValue", + "api": "DotAAS-V3" + }, + { + "uniqueId": "AirQualityAAS", + "submodelEndpoint": "http://host.docker.internal:8081/submodels/YjdlNTA2NzgtZGY3Mi00ODAxLWI3YzAtNzI2NTNkMmU0NzI0", + "idShortPath": "AirQualityValue", + "api": "DotAAS-V3" + } +] diff --git a/examples/BaSyxDatabridge/databridge/jsonatatransformer.json b/examples/BaSyxDatabridge/databridge/jsonatatransformer.json new file mode 100644 index 000000000..691253839 --- /dev/null +++ b/examples/BaSyxDatabridge/databridge/jsonatatransformer.json @@ -0,0 +1,20 @@ +[ + { + "uniqueId": "TemperatureTransformer", + "queryPath": "TemperatureTransformer.jsonata", + "inputType": "JsonString", + "outputType": "JsonString" + }, + { + "uniqueId": "HumidityTransformer", + "queryPath": "HumidityTransformer.jsonata", + "inputType": "JsonString", + "outputType": "JsonString" + }, + { + "uniqueId": "AirQualityTransformer", + "queryPath": "AirQualityTransformer.jsonata", + "inputType": "JsonString", + "outputType": "JsonString" + } +] \ No newline at end of file diff --git a/examples/BaSyxDatabridge/databridge/mqttconsumer.json b/examples/BaSyxDatabridge/databridge/mqttconsumer.json new file mode 100644 index 000000000..5a6f7c890 --- /dev/null +++ b/examples/BaSyxDatabridge/databridge/mqttconsumer.json @@ -0,0 +1,8 @@ +[ + { + "uniqueId": "EnvironmentalSensorMQTT", + "serverUrl": "mosquitto", + "serverPort": 1883, + "topic": "EnvironmentalSensor/CombinedData" + } +] diff --git a/examples/BaSyxDatabridge/databridge/routes.json b/examples/BaSyxDatabridge/databridge/routes.json new file mode 100644 index 000000000..887c84db5 --- /dev/null +++ b/examples/BaSyxDatabridge/databridge/routes.json @@ -0,0 +1,22 @@ +[ + { + "datasource": "EnvironmentalSensorMQTT", + "transformers": [ + "TemperatureTransformer", + "HumidityTransformer", + "AirQualityTransformer" + ], + "datasinks": [ + "TemperatureAAS", + "HumidityAAS", + "AirQualityAAS" + ], + "datasinkMappingConfiguration": + { + "TemperatureAAS": ["TemperatureTransformer"], + "HumidityAAS": ["HumidityTransformer"], + "AirQualityAAS": ["AirQualityTransformer"] + }, + "trigger": "event" + } +] diff --git a/examples/BaSyxDatabridge/docker-compose.yml b/examples/BaSyxDatabridge/docker-compose.yml new file mode 100644 index 000000000..01a180222 --- /dev/null +++ b/examples/BaSyxDatabridge/docker-compose.yml @@ -0,0 +1,89 @@ +services: + # AAS Environment + aas-env: + image: eclipsebasyx/aas-environment:2.0.0-SNAPSHOT + container_name: aas-env + volumes: + - ./aas:/application/aas + - ./basyx/aas-env.properties:/application/application.properties + ports: + - '8081:8081' + restart: always + depends_on: + aas-registry: + condition: service_healthy + sm-registry: + condition: service_healthy + + # AAS Registry + aas-registry: + image: eclipsebasyx/aas-registry-log-mem:2.0.0-SNAPSHOT + container_name: aas-registry + ports: + - '8082:8080' + volumes: + - ./basyx/aas-registry.yml:/workspace/config/application.yml + restart: always + + # Submodel Registry + sm-registry: + image: eclipsebasyx/submodel-registry-log-mem:2.0.0-SNAPSHOT + container_name: sm-registry + ports: + - '8083:8080' + volumes: + - ./basyx/sm-registry.yml:/workspace/config/application.yml + restart: always + + # AAS Web UI + aas-web-ui: + image: eclipsebasyx/aas-gui:v2-241006 + container_name: aas-ui + ports: + - '3000:3000' + environment: + AAS_REGISTRY_PATH: http://localhost:8082/shell-descriptors + SUBMODEL_REGISTRY_PATH: http://localhost:8083/submodel-descriptors + AAS_REPO_PATH: http://localhost:8081/shells + SUBMODEL_REPO_PATH: http://localhost:8081/submodels + CD_REPO_PATH: http://localhost:8081/concept-descriptions + restart: always + depends_on: + aas-env: + condition: service_healthy + + # MQTT Broker + mosquitto: + image: eclipse-mosquitto:2.0.15 + container_name: mosquitto + ports: + - 1883:1883 + volumes: + - ./mosquitto:/mosquitto + healthcheck: + test: ["CMD-SHELL", mosquitto_sub -p 1883 -t 'topic' -C 1 -E -i probe -W 3] + interval: 5s + retries: 3 + start_period: 1s + timeout: 10s + restart: always + + # MQTT Publisher (for testing) + mqtt-publisher: + build: ./mqtt-publisher + container_name: mqtt-publisher + depends_on: + - mosquitto + restart: always + + # DataBridge + databridge: + image: eclipsebasyx/databridge:1.0.0-SNAPSHOT + container_name: databridge + volumes: + - "./databridge:/usr/share/config" + depends_on: + - mosquitto + - aas-env + restart: always + diff --git a/examples/BaSyxDatabridge/mosquitto/config/mosquitto.conf b/examples/BaSyxDatabridge/mosquitto/config/mosquitto.conf new file mode 100644 index 000000000..d05bdebdb --- /dev/null +++ b/examples/BaSyxDatabridge/mosquitto/config/mosquitto.conf @@ -0,0 +1,23 @@ +# Default listener port for unencrypted connections (usually 1883) +listener 1883 0.0.0.0 + +# Path to the directory where persistence information is stored. +# Remove or comment out to disable persistence. +persistence true +persistence_location /mosquitto/data/ + +# Log settings +log_dest file /mosquitto/log/mosquitto.log +log_type all + +# Connection settings +# Setting for maximum concurrent connections. -1 means unlimited. +max_connections -1 + +# Security settings +# Uncomment and set these for enabling username-password authentication. +#allow_anonymous false +#password_file /mosquitto/config/mosquitto_passwd + +# Other settings like SSL/TLS, ACLs, etc., can also be configured as needed. +allow_anonymous true diff --git a/examples/BaSyxDatabridge/mqtt-publisher/Dockerfile b/examples/BaSyxDatabridge/mqtt-publisher/Dockerfile new file mode 100644 index 000000000..9883c13ca --- /dev/null +++ b/examples/BaSyxDatabridge/mqtt-publisher/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.8-slim + +RUN pip install paho-mqtt + +COPY publisher.py /publisher.py + +CMD ["python", "/publisher.py"] \ No newline at end of file diff --git a/examples/BaSyxDatabridge/mqtt-publisher/publisher.py b/examples/BaSyxDatabridge/mqtt-publisher/publisher.py new file mode 100644 index 000000000..9a56afbd5 --- /dev/null +++ b/examples/BaSyxDatabridge/mqtt-publisher/publisher.py @@ -0,0 +1,47 @@ +import time +import random +import json +import paho.mqtt.client as mqtt + +# MQTT settings +broker_address = "mosquitto" +port = 1883 +base_topic = "EnvironmentalSensor/" + +# Connect to MQTT Broker +client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, "Client") +client.connect(broker_address, port) + +# Start the network loop in a separate thread +client.loop_start() + +try: + + while True: + # Generate different types of dynamic data + temperatureValue = random.uniform(0, 30) + humidityValue = random.uniform(0.0, 100.0) + airQualityValue = random.uniform(0.0, 1000.0) + + # Create a dictionary with all values + data = { + "temperature": temperatureValue, + "humidity": humidityValue, + "airQuality": airQualityValue + } + + # Convert the dictionary to a JSON string + json_data = json.dumps(data) + + # Publish dynamic data to respective subtopics + client.publish(base_topic + "Temperature", temperatureValue) + client.publish(base_topic + "Humidity", humidityValue) + client.publish(base_topic + "AirQuality", airQualityValue) + client.publish(base_topic + "CombinedData", json_data) + + # Wait for a short period before publishing the next set of values + time.sleep(1) +except KeyboardInterrupt: + # Stop the network loop if the script is interrupted + client.loop_stop() + client.disconnect()