diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json index f3083323..46f4ec36 100644 --- a/.github/linters/.jscpd.json +++ b/.github/linters/.jscpd.json @@ -4,7 +4,8 @@ "consoleFull" ], "ignore": [ - "**/__snapshots__/**" + "**/__snapshots__/**", + "**/mqtt_test/schemas/**" ], "absolute": true } \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e5b3eb3..a57907f0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,155 +1,166 @@ --- -name: Build - -on: - push: - pull_request: - branches: [main, development] - merge_group: - -jobs: - build-linux: - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04] - build_type: [Debug, Release] - arch: ["386", "amd64"] - runs-on: ${{matrix.os}} - env: - CMAKE_INSTALL_PREFIX: ${{ github.workspace }}/install - steps: - - name: Checkout Code - uses: actions/checkout@v3.5.3 - with: - path: UmatiDashboardOpcUaClient - submodules: recursive - fetch-depth: 0 - - name: Build UmatiDashboardOpcUaClient with dependencies - # yamllint disable rule:line-length - run: | - #! /bin/bash - LIBCPP_VERSION="$(dpkg -s libstdc++6 | grep Version | awk '{match($0,"[0-9]+.[0-9].[0-9]",a)}END{print a[0]}')" # yamllint disable-line rule:line-length - mkdir -p build - cd build || exit - #shellcheck disable=SC2296 - cmake ../UmatiDashboardOpcUaClient/.github/ -DCMAKE_INSTALL_PREFIX:PATH="${{ env.CMAKE_INSTALL_PREFIX }}" \ - -DCMAKE_BUILD_TYPE="${{matrix.build_type}}" -DPAHO_WITH_SSL=1 -DBUILD_DEB_PACKAGE:BOOL=1 -DDEB_PACKAGE_LIBCPP_VERSION="$LIBCPP_VERSION" - cmake --build . -j - cd Dashboard-Client-build - make package - # yamllint enable rule:line-length - - name: Run Tests - run: | - #! /bin/bash - cd build/Dashboard-Client-build || exit - ctest -V -C "${{matrix.build_type}}" - - name: Run integration test cacert_test - run: | - #! /bin/bash - cd UmatiDashboardOpcUaClient/Tests/integration_tests/cacert_test - ./genCerts.sh - cp "${{ github.workspace }}/build/Dashboard-Client-build/Tests/TestCaCertificate" . - docker-compose up -d - ./evaluateTest.sh - - name: Upload Artefacts - uses: actions/upload-artifact@v3 - with: - name: UmatiDashboardOpcUaClient-${{matrix.build_type}}-${{matrix.os}}-${{matrix.arch}} - path: | - ${{ env.CMAKE_INSTALL_PREFIX }}/bin - ${{ env.CMAKE_INSTALL_PREFIX }}/share/DashboardOpcUaClient - - name: Upload Debian package - uses: actions/upload-artifact@v3 - with: - name: dashboardopcuaclient-${{matrix.build_type}}-${{matrix.os}}-${{matrix.arch}}.deb - path: | - ${{ env.CMAKE_INSTALL_PREFIX }}/*.deb - build-windows: - strategy: - matrix: - os: [windows-2019, windows-2022] - build_type: [Debug, Release] - arch: ["386", "amd64"] - runs-on: ${{matrix.os}} - env: - CMAKE_INSTALL_PREFIX: ${{ github.workspace }}/install - steps: - - name: Checkout Code - uses: actions/checkout@v3.5.3 - with: - path: UmatiDashboardOpcUaClient - submodules: recursive - fetch-depth: 0 - - name: Build UmatiDashboardOpcUaClient with dependencies - shell: pwsh - # yamllint disable rule:line-length - run: | - #shellcheck disable=SC1073,SC1050,SC1141,SC1083,SC1072,SC2296 - $matrixarch = "${{matrix.arch}}" - if($matrixarch -eq "386"){$arch="Win32"} - if($matrixarch -eq "amd64"){$arch="x64"} - mkdir -p build - cd build || exit - cmake ../UmatiDashboardOpcUaClient/.github/ -DCMAKE_INSTALL_PREFIX:PATH=${{ env.CMAKE_INSTALL_PREFIX }} -A "$arch" -DPAHO_WITH_SSL=1 - cmake --build . -j --config ${{matrix.build_type}} - # yamllint enable rule:line-length - - name: Run Tests - shell: pwsh - run: | - cd build/Dashboard-Client-build || exit - ctest -V -C ${{matrix.build_type}} - - name: Download CA Certs - shell: pwsh - run: | - cd ${{ env.CMAKE_INSTALL_PREFIX }} - mkdir -p bin/certs - curl https://curl.se/ca/cacert.pem -o bin/certs/cacert.pem -s - - name: Upload Artefacts - uses: actions/upload-artifact@v3 - with: - name: UmatiDashboardOpcUaClient-${{matrix.build_type}}-${{matrix.os}}-${{matrix.arch}} - path: | - ${{ env.CMAKE_INSTALL_PREFIX }}/bin - ${{ env.CMAKE_INSTALL_PREFIX }}/share/DashboardOpcUaClient - docker: - runs-on: ubuntu-22.04 - env: - # Check if this is not a pull request and GITHUB_TOKEN is set - # As all env variables are strings, you need to compaire against "== 'true'" (not "== true") - IS_NOT_PR: ${{ !github.head_ref && true }} - steps: - - name: Checkout Code - uses: actions/checkout@v3.5.3 - with: - path: UmatiDashboardOpcUaClient - submodules: recursive - fetch-depth: 0 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2.2.0 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.9.1 - - name: PrepareReg Names - # yamllint disable rule:line-length - run: | - #! /bin/bash - #shellcheck disable=SC2296 - echo IMAGE_REPOSITORY="$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV" - echo IMAGE_TAG="$(echo "${{ github.ref }}" | tr '[:upper:]' '[:lower:]' | awk '{sub(/([^\/]*\/){2}/,""); gsub(/\/|_/, "-")}1')" >> "$GITHUB_ENV" - # yamllint enable rule:line-length - - name: Login to GitHub Container Registry - uses: docker/login-action@v2.2.0 - if: env.IS_NOT_PR == 'true' && ${{ github.actor }} != 'dependabot' - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build Docker Release - uses: docker/build-push-action@v4.1.1 - with: - file: "./UmatiDashboardOpcUaClient/Dockerfile" - context: ./UmatiDashboardOpcUaClient - platforms: linux/amd64 - push: ${{env.IS_NOT_PR == 'true'}} - tags: | - ghcr.io/${{ env.IMAGE_REPOSITORY }}:${{ env.IMAGE_TAG }} - provenance: false + name: Build + + on: + push: + pull_request: + branches: [main, development] + merge_group: + + jobs: + build-linux: + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04] + build_type: [Debug, Release] + arch: ["386", "amd64"] + runs-on: ${{matrix.os}} + env: + CMAKE_INSTALL_PREFIX: ${{ github.workspace }}/install + steps: + - name: Checkout Code + uses: actions/checkout@v3.5.3 + with: + path: UmatiDashboardOpcUaClient + submodules: recursive + fetch-depth: 0 + - name: Build UmatiDashboardOpcUaClient with dependencies + # yamllint disable rule:line-length + run: | + #! /bin/bash + LIBCPP_VERSION="$(dpkg -s libstdc++6 | grep Version | awk '{match($0,"[0-9]+.[0-9].[0-9]",a)}END{print a[0]}')" # yamllint disable-line rule:line-length + mkdir -p build + cd build || exit + #shellcheck disable=SC2296 + cmake ../UmatiDashboardOpcUaClient/.github/ -DCMAKE_INSTALL_PREFIX:PATH="${{ env.CMAKE_INSTALL_PREFIX }}" \ + -DCMAKE_BUILD_TYPE="${{matrix.build_type}}" -DPAHO_WITH_SSL=1 -DBUILD_DEB_PACKAGE:BOOL=1 -DDEB_PACKAGE_LIBCPP_VERSION="$LIBCPP_VERSION" + cmake --build . -j + cd Dashboard-Client-build + make package + # yamllint enable rule:line-length + - name: Run Tests + run: | + #! /bin/bash + cd build/Dashboard-Client-build || exit + ctest -V -C "${{matrix.build_type}}" + - name: Run integration test cacert_test + run: | + #! /bin/bash + cd UmatiDashboardOpcUaClient/Tests/integration/cacert_test || exit + ./genCerts.sh + cp "${{ github.workspace }}/build/Dashboard-Client-build/Tests/unit/TestCaCertificate" . + docker-compose up -d + ./evaluateTest.sh + - name: Upload Artefacts + uses: actions/upload-artifact@v3 + with: + name: UmatiDashboardOpcUaClient-${{matrix.build_type}}-${{matrix.os}}-${{matrix.arch}} + path: | + ${{ env.CMAKE_INSTALL_PREFIX }}/bin + ${{ env.CMAKE_INSTALL_PREFIX }}/share/DashboardOpcUaClient + - name: Upload Debian package + uses: actions/upload-artifact@v3 + with: + name: dashboardopcuaclient-${{matrix.build_type}}-${{matrix.os}}-${{matrix.arch}}.deb + path: | + ${{ env.CMAKE_INSTALL_PREFIX }}/*.deb + build-windows: + strategy: + matrix: + os: [windows-2019, windows-2022] + build_type: [Debug, Release] + arch: ["386", "amd64"] + runs-on: ${{matrix.os}} + env: + CMAKE_INSTALL_PREFIX: ${{ github.workspace }}/install + steps: + - name: Checkout Code + uses: actions/checkout@v3.5.3 + with: + path: UmatiDashboardOpcUaClient + submodules: recursive + fetch-depth: 0 + - name: Build UmatiDashboardOpcUaClient with dependencies + shell: pwsh + # yamllint disable rule:line-length + run: | + #shellcheck disable=SC1073,SC1050,SC1141,SC1083,SC1072,SC2296 + $matrixarch = "${{matrix.arch}}" + if($matrixarch -eq "386"){$arch="Win32"} + if($matrixarch -eq "amd64"){$arch="x64"} + mkdir -p build + cd build || exit + cmake ../UmatiDashboardOpcUaClient/.github/ -DCMAKE_INSTALL_PREFIX:PATH=${{ env.CMAKE_INSTALL_PREFIX }} -A "$arch" -DPAHO_WITH_SSL=1 + cmake --build . -j --config ${{matrix.build_type}} + # yamllint enable rule:line-length + - name: Run Tests + shell: pwsh + run: | + cd build/Dashboard-Client-build || exit + ctest -V -C ${{matrix.build_type}} + - name: Download CA Certs + shell: pwsh + run: | + cd ${{ env.CMAKE_INSTALL_PREFIX }} + mkdir -p bin/certs + curl https://curl.se/ca/cacert.pem -o bin/certs/cacert.pem -s + - name: Upload Artefacts + uses: actions/upload-artifact@v3 + with: + name: UmatiDashboardOpcUaClient-${{matrix.build_type}}-${{matrix.os}}-${{matrix.arch}} + path: | + ${{ env.CMAKE_INSTALL_PREFIX }}/bin + ${{ env.CMAKE_INSTALL_PREFIX }}/share/DashboardOpcUaClient + docker: + runs-on: ubuntu-22.04 + env: + # Check if this is not a pull request and GITHUB_TOKEN is set + # As all env variables are strings, you need to compaire against "== 'true'" (not "== true") + IS_NOT_PR: ${{ !github.head_ref && true }} + steps: + - name: Checkout Code + uses: actions/checkout@v3.5.3 + with: + path: UmatiDashboardOpcUaClient + submodules: recursive + fetch-depth: 0 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2.2.0 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.9.1 + - name: PrepareReg Names + # yamllint disable rule:line-length + run: | + #! /bin/bash + #shellcheck disable=SC2296 + echo IMAGE_REPOSITORY="$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV" + echo IMAGE_TAG="$(echo "${{ github.ref }}" | tr '[:upper:]' '[:lower:]' | awk '{sub(/([^\/]*\/){2}/,""); gsub(/\/|_/, "-")}1')" >> "$GITHUB_ENV" + # yamllint enable rule:line-length + - name: Login to GitHub Container Registry + uses: docker/login-action@v2.2.0 + if: env.IS_NOT_PR == 'true' && ${{ github.actor }} != 'dependabot' + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build Docker Release + uses: docker/build-push-action@v4.1.1 + with: + file: "./UmatiDashboardOpcUaClient/Dockerfile" + context: ./UmatiDashboardOpcUaClient + platforms: linux/amd64 + push: ${{env.IS_NOT_PR == 'true'}} + tags: | + ghcr.io/${{ env.IMAGE_REPOSITORY }}:${{ env.IMAGE_TAG }} + provenance: false + - name: Run integration test mqtt + if: ${{env.IS_NOT_PR == 'true'}} + run : | + #! /bin/bash + python -m pip install --upgrade pip + cd ./UmatiDashboardOpcUaClient/Tests/integration/mqtt_test || exit + pip install -r requirements.txt + docker compose up -d + ./waitForContainer.sh + python -m unittest discover test_mqtt_sampleserver + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 344ff6b1..8d126955 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,7 @@ add_subdirectory(OpcUaClient) add_subdirectory(MachineObserver) message("### opcua_dashboardclient: Adding test directory") -add_subdirectory(Tests) +add_subdirectory(Tests/unit) message("### opcua_dashboardclient: Adding the executable c++ file") find_package(Threads) diff --git a/README.md b/README.md index afaeee22..8040b2bc 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ Follow these instructions to use the client as a testing tool for your implement For the local instance testing you need to run your own MQTT Broker and a MQTT Client. See [MQTT Doc](doc/MQTT.md) for more information and instructions +[Here](example/ShowcaseDeployment/) is an docker-compose example including a mqtt broker, a umati Sample Server and the gateway. The example contains also the need configuration for the samples. + ### Usage for connecting a server to the dashboard Follow these instructions to use the client for connecting your local OPC UA Server to the umati.app Dashboard: [umati Dashboard Connection](doc/usage_for_dashboard.md) diff --git a/Tests/integration_tests/cacert_test/.gitignore b/Tests/integration/cacert_test/.gitignore similarity index 100% rename from Tests/integration_tests/cacert_test/.gitignore rename to Tests/integration/cacert_test/.gitignore diff --git a/Tests/integration_tests/cacert_test/ConfigurationCa.json b/Tests/integration/cacert_test/ConfigurationCa.json similarity index 100% rename from Tests/integration_tests/cacert_test/ConfigurationCa.json rename to Tests/integration/cacert_test/ConfigurationCa.json diff --git a/Tests/integration_tests/cacert_test/ConfigurationCa2.json b/Tests/integration/cacert_test/ConfigurationCa2.json similarity index 100% rename from Tests/integration_tests/cacert_test/ConfigurationCa2.json rename to Tests/integration/cacert_test/ConfigurationCa2.json diff --git a/Tests/integration_tests/cacert_test/docker-compose.yml b/Tests/integration/cacert_test/docker-compose.yml similarity index 100% rename from Tests/integration_tests/cacert_test/docker-compose.yml rename to Tests/integration/cacert_test/docker-compose.yml diff --git a/Tests/integration_tests/cacert_test/evaluateTest.sh b/Tests/integration/cacert_test/evaluateTest.sh similarity index 100% rename from Tests/integration_tests/cacert_test/evaluateTest.sh rename to Tests/integration/cacert_test/evaluateTest.sh diff --git a/Tests/integration_tests/cacert_test/genCerts.sh b/Tests/integration/cacert_test/genCerts.sh similarity index 100% rename from Tests/integration_tests/cacert_test/genCerts.sh rename to Tests/integration/cacert_test/genCerts.sh diff --git a/Tests/integration_tests/cacert_test/mosquitto.conf b/Tests/integration/cacert_test/mosquitto.conf similarity index 100% rename from Tests/integration_tests/cacert_test/mosquitto.conf rename to Tests/integration/cacert_test/mosquitto.conf diff --git a/Tests/integration_tests/cacert_test/test_entrypoint.sh b/Tests/integration/cacert_test/test_entrypoint.sh similarity index 100% rename from Tests/integration_tests/cacert_test/test_entrypoint.sh rename to Tests/integration/cacert_test/test_entrypoint.sh diff --git a/Tests/integration/mqtt_test/configuration.json b/Tests/integration/mqtt_test/configuration.json new file mode 100644 index 00000000..a0bc3f8f --- /dev/null +++ b/Tests/integration/mqtt_test/configuration.json @@ -0,0 +1,305 @@ +{ + "OpcUa": { + "Endpoint": "opc.tcp://opcuaserver:4840", + "Username": "", + "Password": "", + "Security": 1 + }, + "Mqtt": { + "Hostname": "mqtt_broker", + "Port": 1883, + "Username": "umati/mqtt_test", + "Password": "", + "Prefix": "umati/v2", + "ClientId": "umati/mqtt_test", + "Protocol": "tcp" + }, + "MachineCacheFile": "MachineCache_datahub.json", + "MachinesFilter": [ ], + "ObjectTypeNamespaces": [ + "http://opcfoundation.org/UA/", + "http://opcfoundation.org/UA/IA/", + "http://opcfoundation.org/UA/DI/", + "http://opcfoundation.org/UA/Dictionary/IRDI", + "http://opcfoundation.org/UA/Machinery/", + "http://opcfoundation.org/UA/Machinery/ProcessValues/", + "http://opcfoundation.org/UA/Machinery/Result/", + "http://opcfoundation.org/UA/MachineTool/", + "http://opcfoundation.org/UA/PADIM/", + "http://opcfoundation.org/UA/AdditiveManufacturing/NodeSet2/", + "http://opcfoundation.org/UA/Woodworking/", + "http://opcfoundation.org/UA/Robotics/", + "http://opcfoundation.org/UA/IJT/", + "http://opcfoundation.org/GMS/", + "http://opcfoundation.org/UA/PlasticsRubber/GeneralTypes/", + "http://opcfoundation.org/UA/PlasticsRubber/IMM2MES/", + "http://opcfoundation.org/UA/PlasticsRubber/TCD/", + "http://opcfoundation.org/UA/PlasticsRubber/HotRunner/", + "http://opcfoundation.org/UA/PlasticsRubber/LDS/", + "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/GeneralTypes/", + "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/HaulOff/", + "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Corrugator/", + "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Extruder/", + "http://opcfoundation.org/UA/PlasticsRubber/umati/generic/", + "http://opcfoundation.org/UA/PlasticsRubber/umati/OPC40079ForUmati/", + "http://opcfoundation.org/UA/PlasticsRubber/ImmToRobot/", + "http://opcfoundation.org/UA/Glass/Flat/", + "http://lenord.de/umati/MiniCODER/" + ], + "NamespaceInformations": [ + { + "Namespace": "http://opcfoundation.org/UA/Robotics/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/Robotics/", + "Id": "i=1004", + "BaseTypeLevel": 0, + "$comment": "MotionDeviceType" + }, + { + "Uri": "http://opcfoundation.org/UA/Robotics/", + "Id": "i=1003", + "BaseTypeLevel": 0, + "$comment": "ControllerType" + }, + { + "Uri": "http://opcfoundation.org/UA/Robotics/", + "Id": "i=1002", + "BaseTypeLevel": 0, + "$comment": "MotionDeviceSystemType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "BaseTypeLevel": 0, + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/IJT/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/IJT/", + "Id": "i=1005", + "$comment": "TighteningSystemType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/MachineTool-Prototyping/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/MachineTool-Prototyping/", + "Id": "i=1014", + "$comment": "MachineToolType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/MachineTool-Prototyping/", + "Id": "i=1012", + "$comment": "MachineToolIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/MachineTool/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/MachineTool/", + "Id": "i=13", + "$comment": "MachineToolType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/MachineTool/", + "Id": "i=11", + "$comment": "MachineToolIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/Woodworking/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/Woodworking/", + "Id": "i=2", + "$comment": "WwMachineType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/GMS/", + "Types": [ + { + "Uri": "http://opcfoundation.org/GMS/", + "Id": "i=1002", + "$comment": "GMSType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/GMS/", + "Id": "i=1011", + "$comment": "GMSIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/umati/generic/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/umati/generic/", + "Id": "i=1002", + "$comment": "UmatiPlasticsRubberGenericType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/IMM2MES/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/IMM2MES/", + "Id": "i=1007", + "$comment": "IMM_MES_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Extruder/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Extruder/", + "Id": "i=1015", + "$comment": "Extruder_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Corrugator/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Corrugator/", + "Id": "i=1003", + "$comment": "Corrugator_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/LDS/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/LDS/", + "Id": "i=1007", + "$comment": "LDS_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/HotRunner/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/HotRunner/", + "Id": "i=1010", + "$comment": "HRD_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/TCD/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/TCD/", + "Id": "i=1012", + "$comment": "TCD_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/umati/OPC40079ForUmati/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/umati/OPC40079ForUmati/", + "Id": "i=1003", + "$comment": "ImmRobotCellType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/Glass/Flat/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/Glass/Flat/", + "Id": "i=1015", + "$comment": "GlassMachineType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Glass/Flat/", + "Id": "i=1020", + "$comment": "GlassMachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/AdditiveManufacturing/NodeSet2/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/AdditiveManufacturing/NodeSet2/", + "Id": "i=1031", + "$comment": "AdditiveManufacturingType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/AdditiveManufacturing/NodeSet2/", + "Id": "i=1028", + "$comment": "MachineIdentificationAMType" + } + } + ] +} diff --git a/Tests/integration/mqtt_test/docker-compose.yml b/Tests/integration/mqtt_test/docker-compose.yml new file mode 100644 index 00000000..09195a74 --- /dev/null +++ b/Tests/integration/mqtt_test/docker-compose.yml @@ -0,0 +1,20 @@ +--- +version: '3.1' +services: + mqtt_broker: + image: eclipse-mosquitto + volumes: + - ./mosquitto.conf:/mosquitto/config/mosquitto.conf + ports: + - 1883:1883 + opcuaserver: + image: ghcr.io/umati/sample-server:develop + ports: + - 4840:4840 + gateway: + depends_on: + - mqtt_broker + - opcuaserver + image: ghcr.io/${IMAGE_REPOSITORY}:${IMAGE_TAG} + volumes: + - ./configuration.json:/app/configuration.json diff --git a/Tests/integration/mqtt_test/mosquitto.conf b/Tests/integration/mqtt_test/mosquitto.conf new file mode 100644 index 00000000..324b1915 --- /dev/null +++ b/Tests/integration/mqtt_test/mosquitto.conf @@ -0,0 +1,7 @@ +listener 1883 +protocol mqtt + +log_dest stdout +log_type all + +allow_anonymous true diff --git a/Tests/integration/mqtt_test/requirements.txt b/Tests/integration/mqtt_test/requirements.txt new file mode 100644 index 00000000..f52dd97b --- /dev/null +++ b/Tests/integration/mqtt_test/requirements.txt @@ -0,0 +1,3 @@ +jsonschema>=4.17.3 +paho-mqtt>=1.6.1 + diff --git a/Tests/integration/mqtt_test/schemas/SampleServer/BaseMachineTool.json b/Tests/integration/mqtt_test/schemas/SampleServer/BaseMachineTool.json new file mode 100644 index 00000000..07b242eb --- /dev/null +++ b/Tests/integration/mqtt_test/schemas/SampleServer/BaseMachineTool.json @@ -0,0 +1,372 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "Identification": { + "type": "object", + "properties": { + "Manufacturer": { + "type": "object", + "properties": { + "locale": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "locale", + "text" + ] + }, + "ProductInstanceUri": { + "type": "string" + }, + "SerialNumber": { + "type": "string" + } + }, + "required": [ + "Manufacturer", + "ProductInstanceUri", + "SerialNumber" + ] + }, + "Monitoring": { + "type": "object", + "properties": { + "": { + "type": "object", + "properties": { + "Channel 1": { + "type": "object", + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "ChannelMode": { + "type": "integer" + }, + "ChannelState": { + "type": "integer" + }, + "FeedOverride": { + "type": "object", + "properties": { + "properties": { + "type": "object", + "properties": { + "EURange": { + "type": "object", + "properties": { + "High": { + "type": "number" + }, + "Low": { + "type": "number" + } + }, + "required": [ + "High", + "Low" + ] + }, + "EngineeringUnits": { + "type": "object", + "properties": { + "Description": { + "type": "object", + "properties": { + "locale": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "locale", + "text" + ] + }, + "DisplayName": { + "type": "object", + "properties": { + "locale": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "locale", + "text" + ] + }, + "NamespaceUri": { + "type": "string" + }, + "UnitId": { + "type": "integer" + } + }, + "required": [ + "Description", + "DisplayName", + "NamespaceUri", + "UnitId" + ] + } + }, + "required": [ + "EURange", + "EngineeringUnits" + ] + }, + "value": { + "type": "number" + } + }, + "required": [ + "properties", + "value" + ] + }, + "Name": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "ChannelMode", + "ChannelState", + "FeedOverride", + "Name" + ] + } + }, + "required": [ + "Channel 1" + ] + }, + "MachineTool": { + "type": "object", + "properties": { + "OperationMode": { + "type": "integer" + }, + "PowerOnDuration": { + "type": "integer" + } + }, + "required": [ + "OperationMode", + "PowerOnDuration" + ] + }, + "Stacklight": { + "type": "object", + "properties": { + "": { + "type": "object", + "properties": { + "Light 0": { + "type": "object", + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "IsPartOfBase": { + "type": "boolean" + }, + "NumberInList": { + "type": "integer" + }, + "SignalColor": { + "type": "integer" + }, + "SignalMode": { + "type": "integer" + }, + "SignalOn": { + "type": "boolean" + } + }, + "required": [ + "$TypeDefinition", + "IsPartOfBase", + "NumberInList", + "SignalColor", + "SignalMode", + "SignalOn" + ] + }, + "Light 1": { + "type": "object", + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "IsPartOfBase": { + "type": "boolean" + }, + "NumberInList": { + "type": "integer" + }, + "SignalColor": { + "type": "integer" + }, + "SignalMode": { + "type": "integer" + }, + "SignalOn": { + "type": "boolean" + } + }, + "required": [ + "$TypeDefinition", + "IsPartOfBase", + "NumberInList", + "SignalColor", + "SignalMode", + "SignalOn" + ] + }, + "Light 2": { + "type": "object", + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "IsPartOfBase": { + "type": "boolean" + }, + "NumberInList": { + "type": "integer" + }, + "SignalColor": { + "type": "integer" + }, + "SignalMode": { + "type": "integer" + }, + "SignalOn": { + "type": "boolean" + } + }, + "required": [ + "$TypeDefinition", + "IsPartOfBase", + "NumberInList", + "SignalColor", + "SignalMode", + "SignalOn" + ] + } + }, + "required": [ + "Light 0", + "Light 1", + "Light 2" + ] + }, + "NodeVersion": { + "type": "string" + }, + "StacklightMode": { + "type": "integer" + } + }, + "required": [ + "", + "NodeVersion", + "StacklightMode" + ] + } + }, + "required": [ + "", + "MachineTool", + "Stacklight" + ] + }, + "Production": { + "type": "object", + "properties": { + "ActiveProgram": { + "type": "object", + "properties": { + "Name": { + "type": "string" + }, + "NumberInList": { + "type": "integer" + }, + "State": { + "type": "object", + "properties": { + "CurrentState": { + "type": "object", + "properties": { + "properties": { + "type": "object", + "properties": { + "Id": { + "type": "integer" + }, + "Number": { + "type": "integer" + } + }, + "required": [ + "Id", + "Number" + ] + }, + "value": { + "type": "object", + "properties": { + "locale": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "locale", + "text" + ] + } + }, + "required": [ + "properties", + "value" + ] + } + }, + "required": [ + "CurrentState" + ] + } + }, + "required": [ + "Name", + "NumberInList", + "State" + ] + } + }, + "required": [ + "ActiveProgram" + ] + } + }, + "required": [ + "Identification", + "Monitoring", + "Production" + ] +} \ No newline at end of file diff --git a/Tests/integration/mqtt_test/schemas/SampleServer/FullMachineTool.json b/Tests/integration/mqtt_test/schemas/SampleServer/FullMachineTool.json new file mode 100644 index 00000000..13d78235 --- /dev/null +++ b/Tests/integration/mqtt_test/schemas/SampleServer/FullMachineTool.json @@ -0,0 +1,1211 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/Welcome4", + "definitions": { + "Welcome4": { + "type": "object", + "additionalProperties": false, + "properties": { + "Equipment": { + "$ref": "#/definitions/Equipment" + }, + "Identification": { + "$ref": "#/definitions/Identification" + }, + "Monitoring": { + "$ref": "#/definitions/Monitoring" + }, + "Notification": { + "$ref": "#/definitions/Notification" + }, + "Production": { + "$ref": "#/definitions/Production" + } + }, + "required": [ + "Equipment", + "Identification", + "Monitoring", + "Notification", + "Production" + ], + "title": "Welcome4" + }, + "Equipment": { + "type": "object", + "additionalProperties": false, + "properties": { + "Tools": { + "$ref": "#/definitions/Tools" + } + }, + "required": [ + "Tools" + ], + "title": "Equipment" + }, + "Tools": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/ToolsTool" + }, + "NodeVersion": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "", + "NodeVersion" + ], + "title": "Tools" + }, + "ToolsTool": { + "type": "object", + "additionalProperties": false, + "properties": { + "Multi 1": { + "$ref": "#/definitions/Multi1" + }, + "Tool1": { + "$ref": "#/definitions/Tool1" + } + }, + "required": [ + "Multi 1", + "Tool1" + ], + "title": "ToolsTool" + }, + "Multi1": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "": { + "$ref": "#/definitions/Multi1_" + ], + "title": "Multi1" + }, + "Multi1_": { + "$ref": "#/definitions/ToolLifeEntry" + } + }, + "required": [ + "" + ], + "title": "ToolLife" + }, + "ToolLifeEntry": { + "type": "object", + "additionalProperties": false, + "properties": { + "Rotations": { + "$ref": "#/definitions/Rotations" + } + }, + "required": [ + "Rotations" + ], + "title": "ToolLifeEntry" + }, + "Rotations": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "properties": { + "$ref": "#/definitions/RotationsProperties" + }, + "value": { + "type": "integer" + } + }, + "required": [ + "$TypeDefinition", + "properties", + "value" + ], + "title": "Rotations" + }, + "RotationsProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "EngineeringUnits": { + "$ref": "#/definitions/EngineeringUnits" + }, + "Indication": { + "type": "integer" + }, + "IsCountingUp": { + "type": "boolean" + }, + "LimitValue": { + "type": "integer" + } + }, + "required": [ + "EngineeringUnits", + "Indication", + "IsCountingUp", + "LimitValue" + ], + "title": "RotationsProperties" + }, + "EngineeringUnits": { + "type": "object", + "additionalProperties": false, + "properties": { + "Description": { + "$ref": "#/definitions/ComponentName" + }, + "DisplayName": { + "$ref": "#/definitions/ComponentName" + }, + "NamespaceUri": { + "type": "string" + }, + "UnitId": { + "type": "integer" + } + }, + "required": [ + "Description", + "DisplayName", + "NamespaceUri", + "UnitId" + ], + "title": "EngineeringUnits" + }, + "ComponentName": { + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "$ref": "#/definitions/Locale" + }, + "text": { + "type": "string" + } + }, + "required": [ + "locale", + "text" + ], + "title": "ComponentName" + }, + "Identification": { + "type": "object", + "additionalProperties": false, + "properties": { + "ComponentName": { + "$ref": "#/definitions/ComponentName" + }, + "DeviceClass": { + "type": "string" + }, + "Location": { + "type": "string" + }, + "Manufacturer": { + "$ref": "#/definitions/ComponentName" + }, + "Model": { + "$ref": "#/definitions/ComponentName" + }, + "MonthOfConstruction": { + "type": "integer" + }, + "ProductCode": { + "type": "string" + }, + "ProductInstanceUri": { + "type": "string", + "format": "uri", + "qt-uri-protocols": [ + "http" + ] + }, + "SerialNumber": { + "type": "string" + }, + "SoftwareIdentification": { + "$ref": "#/definitions/SoftwareIdentification" + }, + "SoftwareRevision": { + "type": "string" + }, + "YearOfConstruction": { + "type": "integer" + } + }, + "required": [ + "ComponentName", + "DeviceClass", + "Location", + "Manufacturer", + "Model", + "MonthOfConstruction", + "ProductCode", + "ProductInstanceUri", + "SerialNumber", + "SoftwareIdentification", + "SoftwareRevision", + "YearOfConstruction" + ], + "title": "Identification" + }, + "SoftwareIdentification": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/SoftwareItem" + } + }, + "required": [ + "" + ], + "title": "SoftwareIdentification" + }, + "SoftwareItem": { + "type": "object", + "additionalProperties": false, + "properties": { + "OS": { + "$ref": "#/definitions/OS" + } + }, + "required": [ + "OS" + ], + "title": "SoftwareItem" + }, + "OS": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "Identifier": { + "type": "string" + }, + "SoftwareRevision": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "Identifier", + "SoftwareRevision" + ], + "title": "OS" + }, + "Monitoring": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/MonitoredElement" + }, + "MachineTool": { + "$ref": "#/definitions/MachineTool" + }, + "Stacklight": { + "$ref": "#/definitions/Stacklight" + } + }, + "required": [ + "", + "MachineTool", + "Stacklight" + ], + "title": "Monitoring" + }, + "MonitoredElement": { + "type": "object", + "additionalProperties": false, + "properties": { + "Channel 1": { + "$ref": "#/definitions/Channel" + }, + "Channel 2": { + "$ref": "#/definitions/Channel" + }, + "Channel 3": { + "$ref": "#/definitions/Channel" + }, + "Channel 4": { + "$ref": "#/definitions/Channel" + }, + "EDM": { + "$ref": "#/definitions/Edm" + }, + "Laser": { + "$ref": "#/definitions/Laser" + }, + "Spindle 1": { + "$ref": "#/definitions/Spindle1" + } + }, + "required": [ + "Channel 1", + "Channel 2", + "Channel 3", + "Channel 4", + "EDM", + "Laser", + "Spindle 1" + ], + "title": "MonitoredElement" + }, + "Channel": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "ChannelMode": { + "type": "integer" + }, + "ChannelState": { + "type": "integer" + }, + "FeedOverride": { + "$ref": "#/definitions/Override" + }, + "Name": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "ChannelMode", + "ChannelState", + "FeedOverride", + "Name" + ], + "title": "Channel" + }, + "Override": { + "type": "object", + "additionalProperties": false, + "properties": { + "properties": { + "$ref": "#/definitions/FeedOverrideProperties" + }, + "value": { + "type": "number" + } + }, + "required": [ + "properties", + "value" + ], + "title": "Override" + }, + "FeedOverrideProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "EURange": { + "$ref": "#/definitions/EURange" + }, + "EngineeringUnits": { + "$ref": "#/definitions/EngineeringUnits" + } + }, + "required": [ + "EURange", + "EngineeringUnits" + ], + "title": "FeedOverrideProperties" + }, + "EURange": { + "type": "object", + "additionalProperties": false, + "properties": { + "High": { + "type": "number" + }, + "Low": { + "type": "number" + } + }, + "required": [ + "High", + "Low" + ], + "title": "EURange" + }, + "Edm": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "EDMGeneratorState": { + "type": "integer" + }, + "IsOn": { + "type": "boolean" + }, + "Name": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "EDMGeneratorState", + "IsOn", + "Name" + ], + "title": "Edm" + }, + "Laser": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "ControllerIsOn": { + "type": "boolean" + }, + "LaserState": { + "type": "integer" + }, + "Name": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "ControllerIsOn", + "LaserState", + "Name" + ], + "title": "Laser" + }, + "Spindle1": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "IsRotating": { + "type": "boolean" + }, + "IsUsedAsAxis": { + "type": "boolean" + }, + "Name": { + "type": "string" + }, + "Override": { + "$ref": "#/definitions/Override" + } + }, + "required": [ + "$TypeDefinition", + "IsRotating", + "IsUsedAsAxis", + "Name", + "Override" + ], + "title": "Spindle1" + }, + "MachineTool": { + "type": "object", + "additionalProperties": false, + "properties": { + "OperationMode": { + "type": "integer" + }, + "PowerOnDuration": { + "type": "integer" + } + }, + "required": [ + "OperationMode", + "PowerOnDuration" + ], + "title": "MachineTool" + }, + "Stacklight": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/StacklightOrderedObject" + }, + "NodeVersion": { + "type": "string" + }, + "StacklightMode": { + "type": "integer" + } + }, + "required": [ + "", + "NodeVersion", + "StacklightMode" + ], + "title": "Stacklight" + }, + "StacklightOrderedObject": { + "type": "object", + "additionalProperties": false, + "properties": { + "Light 0": { + "$ref": "#/definitions/Light" + }, + "Light 1": { + "$ref": "#/definitions/Light" + }, + "Light 2": { + "$ref": "#/definitions/Light" + }, + "Light 3": { + "$ref": "#/definitions/Light" + } + }, + "required": [ + "Light 0", + "Light 1", + "Light 2", + "Light 3" + ], + "title": "StacklightOrderedObject" + }, + "Light": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "IsPartOfBase": { + "type": "boolean" + }, + "NumberInList": { + "type": "integer" + }, + "SignalColor": { + "type": "integer" + }, + "SignalMode": { + "type": "integer" + }, + "SignalOn": { + "type": "boolean" + } + }, + "required": [ + "$TypeDefinition", + "IsPartOfBase", + "NumberInList", + "SignalColor", + "SignalMode", + "SignalOn" + ], + "title": "Light" + }, + "Notification": { + "type": "object", + "additionalProperties": false, + "properties": { + "Prognoses": { + "$ref": "#/definitions/Prognoses" + } + }, + "required": [ + "Prognoses" + ], + "title": "Notification" + }, + "Prognoses": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/Prognosis" + }, + "NodeVersion": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "", + "NodeVersion" + ], + "title": "Prognoses" + }, + "Prognosis": { + "type": "object", + "additionalProperties": false, + "properties": { + "Maintenance": { + "$ref": "#/definitions/Maintenance" + }, + "Manual": { + "$ref": "#/definitions/Maintenance" + }, + "PartLoad": { + "$ref": "#/definitions/Load" + }, + "PartUnLoad": { + "$ref": "#/definitions/Load" + }, + "ProcessChangeover": { + "$ref": "#/definitions/Maintenance" + }, + "ProductionJobEnd": { + "$ref": "#/definitions/ProductionJobEnd" + }, + "ToolChange": { + "$ref": "#/definitions/Maintenance" + }, + "ToolLoad": { + "$ref": "#/definitions/Load" + }, + "ToolUnLoad": { + "$ref": "#/definitions/Load" + }, + "UtilityChange": { + "$ref": "#/definitions/UtilityChange" + } + }, + "required": [ + "Maintenance", + "Manual", + "PartLoad", + "PartUnLoad", + "ProcessChangeover", + "ProductionJobEnd", + "ToolChange", + "ToolLoad", + "ToolUnLoad", + "UtilityChange" + ], + "title": "Prognosis" + }, + "Maintenance": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "Activity": { + "$ref": "#/definitions/ComponentName" + }, + "PredictedTime": { + "type": "string" + }, + "Location": { + "$ref": "#/definitions/ComponentName" + } + }, + "required": [ + "$TypeDefinition", + "PredictedTime" + ], + "title": "Maintenance" + }, + "Load": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "Location": { + "$ref": "#/definitions/ComponentName" + }, + "PartName": { + "type": "string" + }, + "PredictedTime": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "Location", + "PredictedTime" + ], + "title": "Load" + }, + "ProductionJobEnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "PredictedTime": { + "type": "string" + }, + "SourceIdentifier": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "PredictedTime", + "SourceIdentifier" + ], + "title": "ProductionJobEnd" + }, + "UtilityChange": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "PredictedTime": { + "type": "string" + }, + "UtilityName": { + "type": "string" + } + }, + "required": [ + "$TypeDefinition", + "PredictedTime", + "UtilityName" + ], + "title": "UtilityChange" + }, + "Production": { + "type": "object", + "additionalProperties": false, + "properties": { + "ActiveProgram": { + "$ref": "#/definitions/ActiveProgram" + }, + "ProductionPlan": { + "$ref": "#/definitions/ProductionPlan" + } + }, + "required": [ + "ActiveProgram", + "ProductionPlan" + ], + "title": "Production" + }, + "ActiveProgram": { + "type": "object", + "additionalProperties": false, + "properties": { + "JobIdentifier": { + "type": "string" + }, + "JobNodeId": { + "type": "integer" + }, + "Name": { + "type": "string" + }, + "NumberInList": { + "type": "integer" + }, + "State": { + "$ref": "#/definitions/State" + } + }, + "required": [ + "JobIdentifier", + "JobNodeId", + "Name", + "NumberInList", + "State" + ], + "title": "ActiveProgram" + }, + "State": { + "type": "object", + "additionalProperties": false, + "properties": { + "CurrentState": { + "$ref": "#/definitions/CurrentState" + } + }, + "required": [ + "CurrentState" + ], + "title": "State" + }, + "CurrentState": { + "type": "object", + "additionalProperties": false, + "properties": { + "properties": { + "$ref": "#/definitions/CurrentStateProperties" + }, + "value": { + "$ref": "#/definitions/ComponentName" + } + }, + "required": [ + "properties", + "value" + ], + "title": "CurrentState" + }, + "CurrentStateProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "Id": { + "type": "integer" + }, + "Number": { + "type": "integer" + } + }, + "required": [ + "Id", + "Number" + ], + "title": "CurrentStateProperties" + }, + "ProductionPlan": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/ProductionPlanOrderedObject" + }, + "NodeVersion": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "", + "NodeVersion" + ], + "title": "ProductionPlan" + }, + "ProductionPlanOrderedObject": { + "type": "object", + "additionalProperties": false, + "properties": { + "MyJob 1": { + "$ref": "#/definitions/MyJob1" + } + }, + "required": [ + "MyJob 1" + ], + "title": "ProductionPlanOrderedObject" + }, + "MyJob1": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "Identifier": { + "type": "string" + }, + "NumberInList": { + "type": "integer" + }, + "PartSets": { + "$ref": "#/definitions/PartSets" + }, + "RunsCompleted": { + "type": "integer" + }, + "RunsPlanned": { + "$ref": "#/definitions/RunsPlanned" + }, + "State": { + "$ref": "#/definitions/State" + } + }, + "required": [ + "$TypeDefinition", + "Identifier", + "NumberInList", + "PartSets", + "RunsCompleted", + "RunsPlanned", + "State" + ], + "title": "MyJob1" + }, + "PartSets": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/PartSet" + } + }, + "required": [ + "" + ], + "title": "PartSets" + }, + "PartSet": { + "type": "object", + "additionalProperties": false, + "properties": { + "Set1": { + "$ref": "#/definitions/Set1" + } + }, + "required": [ + "Set1" + ], + "title": "PartSet" + }, + "Set1": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "ContainsMixedParts": { + "type": "boolean" + }, + "PartsCompletedPerRun": { + "type": "integer" + }, + "PartsPerRun": { + "$ref": "#/definitions/PartsPerRun" + }, + "PartsPlannedPerRun": { + "type": "integer" + } + }, + "required": [ + "$TypeDefinition", + "ContainsMixedParts", + "PartsCompletedPerRun", + "PartsPerRun", + "PartsPlannedPerRun" + ], + "title": "Set1" + }, + "PartsPerRun": { + "type": "object", + "additionalProperties": false, + "properties": { + "": { + "$ref": "#/definitions/Part" + } + }, + "required": [ + "" + ], + "title": "PartsPerRun" + }, + "Part": { + "type": "object", + "additionalProperties": false, + "properties": { + "Part 1": { + "$ref": "#/definitions/Part1_Class" + }, + "Part 2": { + "$ref": "#/definitions/Part1_Class" + }, + "Part 3": { + "$ref": "#/definitions/Part1_Class" + }, + "Part 4": { + "$ref": "#/definitions/Part1_Class" + }, + "Part 5": { + "$ref": "#/definitions/Part1_Class" + } + }, + "required": [ + "Part 1", + "Part 2", + "Part 3", + "Part 4", + "Part 5" + ], + "title": "Part" + }, + "Part1_Class": { + "type": "object", + "additionalProperties": false, + "properties": { + "$TypeDefinition": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "PartQuality": { + "type": "integer" + }, + "ProcessIrregularity": { + "type": "integer" + } + }, + "required": [ + "$TypeDefinition", + "Name", + "PartQuality", + "ProcessIrregularity" + ], + "title": "Part1_Class" + }, + "RunsPlanned": { + "type": "object", + "additionalProperties": false, + "properties": { + "properties": { + "$ref": "#/definitions/RunsPlannedProperties" + }, + "value": { + "type": "integer" + } + }, + "required": [ + "properties", + "value" + ], + "title": "RunsPlanned" + }, + "RunsPlannedProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "IsValid": { + "type": "boolean" + } + }, + "required": [ + "IsValid" + ], + "title": "RunsPlannedProperties" + }, + "Locale": { + "type": "string", + "enum": [ + "", + "en" + ], + "title": "Locale" + } + } +} diff --git a/Tests/integration/mqtt_test/test_mqtt_sampleserver.py b/Tests/integration/mqtt_test/test_mqtt_sampleserver.py new file mode 100644 index 00000000..f86b4041 --- /dev/null +++ b/Tests/integration/mqtt_test/test_mqtt_sampleserver.py @@ -0,0 +1,137 @@ +""" +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Copyright 2023 (c) Sebastian Friedl, FVA GmbH / interop4X(for umati and VDW e.V.) +""" + +import json +import time +import unittest +from typing import Any, Optional + +import jsonschema +import paho.mqtt.client as mqtt + + +class TestMqttSampleServer(unittest.TestCase): + client = mqtt.Client() + + @classmethod + def setUpClass(cls) -> None: + """ + A class method called before tests in an individual class are run. + Here we establish the MQTT client connection. + """ + ret = None + for i in range(0, 120): + try: + ret = cls.client.connect("localhost", 1883, 60) + except ConnectionRefusedError: + print(f"Try to connect to mqtt broker {i} times") + time.sleep(1) + assert ret == 0 + + @classmethod + def tearDownClass(cls) -> None: + """ + A class method called after all tests in an individual class have run. + Here we disconnect the MQTT client connection. + """ + ret = cls.client.disconnect() + assert ret == 0 + + def test_clientOnline_status(self): + """Tests if the client online status message is as expected.""" + received_msg = self.receive_message("umati/v2/umati/mqtt_test/clientOnline") + self.assertEqual(received_msg, b"1") + + def test_BaseMachineTool(self) -> None: + """ + This test function test if the BaseMachineTool is send correct to the mqtt broker + """ + # Use the helper method to receive the message as JSON + topic = "umati/v2/umati/mqtt_test/MachineToolType/nsu=http:_2F_2Fexample.com_2FBasicMachineTool_2F;i=66382" + json_msg = self.receive_message_as_json(topic) + + # Load the JSON schema from a file. + with open("schemas/SampleServer/BaseMachineTool.json", "r") as f: + schema = json.load(f) + + # Validate the received message against the JSON schema. + # In case of validation errors, the jsonschema.validate() function will raise an exception. + try: + jsonschema.validate(instance=json_msg, schema=schema) + except jsonschema.exceptions.ValidationError as e: + print(e) + self.fail(f"Message is of {topic} is not correct!") + + def test_FullMachineTool(self) -> None: + """ + This test function test if the FullMachineTool is send correct to the mqtt broker + """ + # Use the helper method to receive the message as JSON + topic = "umati/v2/umati/mqtt_test/MachineToolType/nsu=http:_2F_2Fexample.com_2FFullMachineTool_2F;i=66382" + json_msg = self.receive_message_as_json(topic) + + # Load the JSON schema from a file. + # you can use https://codebeautify.org/json-to-json-schema-generator to generate a schema from a example json + with open("schemas/SampleServer/FullMachineTool.json", "r") as f: + schema = json.load(f) + + # Validate the received message against the JSON schema. + # In case of validation errors, the jsonschema.validate() function will raise an exception. + try: + jsonschema.validate(instance=json_msg, schema=schema) + except jsonschema.exceptions.ValidationError as e: + print(e) + self.fail(f"Message is of {topic} is not correct!") + + def receive_message(self, topic: str, timeout: int = 10) -> Any: + """ + This helper function subscribes to a topic and waits for a message or until the timeout. + It then returns the received message as bytes. + + Parameters: + - topic: the MQTT topic to subscribe to. + - timeout: the amount of time in seconds to wait for a message. Default is 10 seconds. + + Returns: + - The payload of the received MQTT message as bytes. + """ + received_msg = None + + def on_message(client, userdata, msg): + nonlocal received_msg + if msg.topic == topic: + received_msg = msg.payload + + self.client.on_message = on_message + self.client.subscribe(topic) + timeout_time = time.time() + timeout + while received_msg is None and time.time() < timeout_time: + self.client.loop() + return received_msg + + def receive_message_as_json(self, topic: str, timeout: int = 10) -> dict: + """ + This helper function uses receive_message() to get a message, + then it decodes and deserializes it from JSON to a Python dictionary and returns it. + + Parameters: + - topic: the MQTT topic to subscribe to. + - timeout: the amount of time in seconds to wait for a message. Default is 10 seconds. + + Returns: + - The payload of the received MQTT message as a Python dictionary. + """ + received_msg: Optional[bytes] = self.receive_message(topic, timeout) + if received_msg is not None: + return json.loads(received_msg.decode("utf-8")) + else: + return {} + + +if __name__ == "__main__": + unittest.main() diff --git a/Tests/integration/mqtt_test/waitForContainer.sh b/Tests/integration/mqtt_test/waitForContainer.sh new file mode 100755 index 00000000..56d7811b --- /dev/null +++ b/Tests/integration/mqtt_test/waitForContainer.sh @@ -0,0 +1,10 @@ +#!/bin/bash +NEXT_WAITTIME=0 +WAITTIME_LIMIT_SEC=600 +while [[ "$(docker logs mqtt_test-gateway-1 | grep -c "nsu=http:_2F_2Fexample.com_2FFullMachineTool_2F")" != "2" && "$NEXT_WAITTIME" != "$WAITTIME_LIMIT_SEC" ]] +do + echo "Waiting for test container to become ready since ${NEXT_WAITTIME}s..." + sleep 5 + NEXT_WAITTIME=$((NEXT_WAITTIME + 5)) +done +sleep 5 \ No newline at end of file diff --git a/Tests/CMakeLists.txt b/Tests/unit/CMakeLists.txt similarity index 100% rename from Tests/CMakeLists.txt rename to Tests/unit/CMakeLists.txt diff --git a/Tests/TestCaCertificate.cpp b/Tests/unit/TestCaCertificate.cpp similarity index 100% rename from Tests/TestCaCertificate.cpp rename to Tests/unit/TestCaCertificate.cpp diff --git a/Tests/TestConverter.cpp b/Tests/unit/TestConverter.cpp similarity index 100% rename from Tests/TestConverter.cpp rename to Tests/unit/TestConverter.cpp diff --git a/Tests/TestIdEncode.cpp b/Tests/unit/TestIdEncode.cpp similarity index 100% rename from Tests/TestIdEncode.cpp rename to Tests/unit/TestIdEncode.cpp diff --git a/Tests/data/Configuration.json b/Tests/unit/data/Configuration.json similarity index 100% rename from Tests/data/Configuration.json rename to Tests/unit/data/Configuration.json diff --git a/Tests/data/Configuration2.json b/Tests/unit/data/Configuration2.json similarity index 100% rename from Tests/data/Configuration2.json rename to Tests/unit/data/Configuration2.json diff --git a/Tests/data/MachineCacheTest.json b/Tests/unit/data/MachineCacheTest.json similarity index 100% rename from Tests/data/MachineCacheTest.json rename to Tests/unit/data/MachineCacheTest.json diff --git a/Tests/testconfigurationjsonfile.cpp b/Tests/unit/testconfigurationjsonfile.cpp similarity index 100% rename from Tests/testconfigurationjsonfile.cpp rename to Tests/unit/testconfigurationjsonfile.cpp diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 069f2a88..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -version: "3.3" -services: - opcua_client: - build: . - volumes: - - "./configuration.json:/app/configuration.json" diff --git a/example/ShowcaseDeployment/configuration.json b/example/ShowcaseDeployment/configuration.json new file mode 100644 index 00000000..3191803b --- /dev/null +++ b/example/ShowcaseDeployment/configuration.json @@ -0,0 +1,334 @@ +{ + "OpcUa": { + "Endpoint": "opc.tcp://opcuaserver:4840", + "Username": "", + "Password": "", + "Security": 1 + }, + "Mqtt": { + "Hostname": "mqtt_broker", + "Port": 1883, + "Username": "umati/mqtt_test", + "Password": "", + "Prefix": "umati/v2", + "ClientId": "umati/mqtt_test", + "Protocol": "tcp" + }, + "MachineCacheFile": "MachineCache_datahub.json", + "MachinesFilter": [ ], + "ObjectTypeNamespaces": [ + "http://opcfoundation.org/UA/", + "http://opcfoundation.org/UA/IA/", + "http://opcfoundation.org/UA/DI/", + "http://opcfoundation.org/UA/Machinery/", + "http://opcfoundation.org/UA/Machinery/Result/", + "http://opcfoundation.org/UA/MachineTool/", + "http://opcfoundation.org/UA/TTD/" + ], + "NamespaceInformations": [ + { + "Namespace": "http://opcfoundation.org/UA/Robotics/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/Robotics/", + "Id": "i=1004", + "BaseTypeLevel": 0, + "$comment": "MotionDeviceType" + }, + { + "Uri": "http://opcfoundation.org/UA/Robotics/", + "Id": "i=1003", + "BaseTypeLevel": 0, + "$comment": "ControllerType" + }, + { + "Uri": "http://opcfoundation.org/UA/Robotics/", + "Id": "i=1002", + "BaseTypeLevel": 0, + "$comment": "MotionDeviceSystemType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "BaseTypeLevel": 0, + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/IJT/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/IJT/", + "Id": "i=1005", + "$comment": "TighteningSystemType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/MachineTool-Prototyping/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/MachineTool-Prototyping/", + "Id": "i=1014", + "$comment": "MachineToolType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/MachineTool-Prototyping/", + "Id": "i=1012", + "$comment": "MachineToolIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/MachineTool/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/MachineTool/", + "Id": "i=13", + "$comment": "MachineToolType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/MachineTool/", + "Id": "i=11", + "$comment": "MachineToolIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/Woodworking/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/Woodworking/", + "Id": "i=2", + "$comment": "WwMachineType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1009", + "$comment": "SurfaceTechnologCoatingSystemType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1006", + "$comment": "SurfaceTecnologyObjectType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1010", + "$comment": "SurfaceTechnologyDosingSystemType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1011", + "$comment": "SurfaceTechnologyFilterSystemType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1005", + "$comment": "SurfaceTechnologyPipeType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1008", + "$comment": "SurfaceTechnologyPumpType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1004", + "$comment": "SurfaceTechnologyValveType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1003", + "$comment": "SurfaceTechnologyVesselType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/GMS/", + "Types": [ + { + "Uri": "http://opcfoundation.org/GMS/", + "Id": "i=1002", + "$comment": "GMSType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/GMS/", + "Id": "i=1011", + "$comment": "GMSIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/umati/generic/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/umati/generic/", + "Id": "i=1002", + "$comment": "UmatiPlasticsRubberGenericType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/IMM2MES/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/IMM2MES/", + "Id": "i=1007", + "$comment": "IMM_MES_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Extruder/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Extruder/", + "Id": "i=1015", + "$comment": "Extruder_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Corrugator/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Corrugator/", + "Id": "i=1003", + "$comment": "Corrugator_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/LDS/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/LDS/", + "Id": "i=1007", + "$comment": "LDS_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/HotRunner/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/HotRunner/", + "Id": "i=1010", + "$comment": "HRD_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/TCD/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/TCD/", + "Id": "i=1012", + "$comment": "TCD_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/umati/OPC40079ForUmati/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/umati/OPC40079ForUmati/", + "Id": "i=1003", + "$comment": "ImmRobotCellType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/Glass/Flat/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/Glass/Flat/", + "Id": "i=1015", + "$comment": "GlassMachineType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Glass/Flat/", + "Id": "i=1020", + "$comment": "GlassMachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/AdditiveManufacturing/NodeSet2/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/AdditiveManufacturing/NodeSet2/", + "Id": "i=1031", + "$comment": "AdditiveManufacturingType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/AdditiveManufacturing/NodeSet2/", + "Id": "i=1028", + "$comment": "MachineIdentificationAMType" + } + } + ] +} diff --git a/example/ShowcaseDeployment/docker-compose.yml b/example/ShowcaseDeployment/docker-compose.yml new file mode 100644 index 00000000..afdcad42 --- /dev/null +++ b/example/ShowcaseDeployment/docker-compose.yml @@ -0,0 +1,22 @@ +--- +version: '3.1' +services: + mqtt_broker: + image: eclipse-mosquitto + volumes: + - ./mosquitto.conf:/mosquitto/config/mosquitto.conf + ports: + - 1883:1883 + opcuaserver: + image: ghcr.io/umati/sample-server:develop + ports: + - 4840:4840 + gateway: + depends_on: + - mqtt_broker + - opcuaserver + build: + context: "../../" + dockerfile: "Dockerfile" + volumes: + - ./configuration.json:/app/configuration.json diff --git a/example/ShowcaseDeployment/mosquitto.conf b/example/ShowcaseDeployment/mosquitto.conf new file mode 100644 index 00000000..324b1915 --- /dev/null +++ b/example/ShowcaseDeployment/mosquitto.conf @@ -0,0 +1,7 @@ +listener 1883 +protocol mqtt + +log_dest stdout +log_type all + +allow_anonymous true