├── src └── solution │ ├── HandsOn_1 │ ├── node_red │ │ ├── node-red │ │ │ ├── flows_cred.json │ │ │ ├── package.json │ │ │ ├── Dockerfile.example │ │ │ ├── settings.js │ │ │ └── flows.json │ │ ├── .env │ │ └── docker-compose.yml │ ├── my_edge_app │ │ ├── data-analytics │ │ │ ├── requirements.txt │ │ │ ├── Dockerfile.example │ │ │ └── program │ │ │ │ ├── app.py │ │ │ │ └── data_analytics.py │ │ ├── .env │ │ ├── docker-compose_Edge.yml │ │ ├── docker-compose.yml │ │ └── influxDB │ │ │ └── drive_kpi_calc_dashboard.json │ ├── mqtt_broker_mosquitto │ │ ├── .env │ │ └── docker-compose.yml │ └── IE Flow Creator Flows │ │ └── flow_data_gen.json │ └── HandsOn_2 │ ├── my_edge_app │ ├── data-analytics │ │ ├── requirements.txt │ │ ├── Dockerfile.example │ │ └── program │ │ │ ├── app.py │ │ │ └── data_analytics.py │ ├── .env │ ├── influxdb │ │ ├── influxdb.env │ │ ├── Dockerfile.example │ │ └── init_influxdb.sh │ ├── docker-compose_Edge.yml │ └── docker-compose.yml │ ├── mqtt_broker_mosquitto │ ├── .env │ └── docker-compose.yml │ ├── example │ └── docker-compose.web.yml │ ├── tracee │ └── docker-compose.yml │ └── IE Flow Creator Flows │ └── flow_data_S7_Connector.json ├── docs └── Picture_5_3_Architecture_IED.png ├── .gitignore ├── LICENSE.txt ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── README.md └── CODE_OF_CONDUCT.md /src/solution/HandsOn_1/node_red/node-red/flows_cred.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/data-analytics/requirements.txt: -------------------------------------------------------------------------------- 1 | paho-mqtt==1.5.0 2 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/data-analytics/requirements.txt: -------------------------------------------------------------------------------- 1 | paho-mqtt==1.5.0 2 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/node_red/.env: -------------------------------------------------------------------------------- 1 | 2 | http_proxy="" 3 | https_proxy="" 4 | no_proxy=localhost,127.0.0.1 5 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/mqtt_broker_mosquitto/.env: -------------------------------------------------------------------------------- 1 | MQTT_VERSION=1.6.14 2 | http_proxy="" 3 | https_proxy="" 4 | no_proxy=localhost,127.0.0.1 5 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/mqtt_broker_mosquitto/.env: -------------------------------------------------------------------------------- 1 | MQTT_VERSION=1.6.14 2 | http_proxy="" 3 | https_proxy="" 4 | no_proxy=localhost,127.0.0.1 5 | -------------------------------------------------------------------------------- /docs/Picture_5_3_Architecture_IED.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/industrial-edge/Developer-Guide-Hands-on-App/HEAD/docs/Picture_5_3_Architecture_IED.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /src/solution/HandsOn_1/opcua_server/opcua_server_simulator/node_modules 2 | /src/solution/HandsOn_2/opcua_server/opcua_server_simulator/node_modules 3 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/.env: -------------------------------------------------------------------------------- 1 | BASE_IMAGE=python:3.9.2-alpine3.13 2 | INFLUXDB_VERSION=2.4-alpine 3 | INFLUXDB_DB=edgedb 4 | INFLUXDB_DATA_INDEX_VERSION=tsi1 5 | http_proxy="" 6 | https_proxy="" 7 | no_proxy=localhost,127.0.0.1 8 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/.env: -------------------------------------------------------------------------------- 1 | BASE_IMAGE=python:3.9.2-alpine3.13 2 | INFLUXDB_VERSION=2.2-alpine 3 | INFLUXDB_DB=edgedb 4 | INFLUXDB_DATA_INDEX_VERSION=tsi1 5 | http_proxy="" 6 | https_proxy="" 7 | no_proxy=localhost,127.0.0.1 8 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/example/docker-compose.web.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | 3 | services: 4 | web: 5 | image: nginx 6 | volumes: 7 | - ./templates:/etc/nginx/templates 8 | ports: 9 | - "8080:80" 10 | environment: 11 | - NGINX_HOST=foobar.com 12 | - NGINX_PORT=80 13 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/influxdb/influxdb.env: -------------------------------------------------------------------------------- 1 | # Default Environment Variables for InfluxDB initialization 2 | 3 | DOCKER_INFLUXDB_INIT_MODE=setup 4 | DOCKER_INFLUXDB_INIT_USERNAME=admin 5 | DOCKER_INFLUXDB_INIT_PASSWORD=adminadmin 6 | DOCKER_INFLUXDB_INIT_ORG=siemens 7 | DOCKER_INFLUXDB_INIT_BUCKET=edgedb 8 | DOCKER_INFLUXDB_INIT_RETENTION=1w 9 | DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=testtoken -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/influxdb/Dockerfile.example: -------------------------------------------------------------------------------- 1 | FROM influxdb:2.2-alpine 2 | 3 | ENV DOCKER_INFLUXDB_INIT_MODE=setup 4 | ENV DOCKER_INFLUXDB_INIT_USERNAME=admin 5 | ENV DOCKER_INFLUXDB_INIT_PASSWORD=adminadmin 6 | ENV DOCKER_INFLUXDB_INIT_ORG=siemens 7 | ENV DOCKER_INFLUXDB_INIT_BUCKET=edgedb 8 | ENV DOCKER_INFLUXDB_INIT_RETENTION=1w 9 | ENV DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=testtoken 10 | ENV CONFIG_NAME=TEST 11 | 12 | ADD init_influxdb.sh /init_influxdb.sh 13 | ENTRYPOINT ["/init_influxdb.sh"] -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/data-analytics/Dockerfile.example: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | FROM ${BASE_IMAGE} 3 | 4 | RUN adduser -S nonroot 5 | # install all requirements from requirements.txt 6 | COPY requirements.txt / 7 | RUN pip install -r /requirements.txt; rm -f /requirements.txt 8 | 9 | 10 | # Set the working directory to /app 11 | WORKDIR /app 12 | # Copy the current dir into the container at /app 13 | COPY ./program/* /app/ 14 | 15 | USER nonroot 16 | # Run app.py when the container launches 17 | CMD ["python", "-u", "-m", "app"] 18 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/data-analytics/Dockerfile.example: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | FROM ${BASE_IMAGE} 3 | 4 | RUN adduser -S nonroot 5 | # install all requirements from requirements.txt 6 | COPY requirements.txt / 7 | RUN pip install -r /requirements.txt; rm -f /requirements.txt 8 | 9 | 10 | # Set the working directory to /app 11 | WORKDIR /app 12 | # Copy the current dir into the container at /app 13 | COPY ./program/* /app/ 14 | 15 | USER nonroot 16 | # Run app.py when the container launches 17 | CMD ["python", "-u", "-m", "app"] 18 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/tracee/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | 3 | services: 4 | tracee: 5 | container_name: tracee 6 | pid: host 7 | privileged: true 8 | volumes: 9 | - '/etc/os-release:/etc/os-release-host:ro' 10 | environment: 11 | - LIBBPFGO_OSRELEASE_FILE=/etc/os-release-host 12 | - TRACEE_EBPF_ONLY=1 13 | logging: 14 | driver: json-file 15 | command: 16 | - --trace container=new 17 | - --trace event=cap_capable 18 | image: 'aquasec/tracee:0.7.0' 19 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/node_red/node-red/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-docker", 3 | "version": "2.2.2", 4 | "description": "A visual tool for wiring the Internet of Things", 5 | "homepage": "http://nodered.org", 6 | "license": "Apache-2.0", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/node-red/node-red-docker.git" 10 | }, 11 | "main": "node_modules/node-red/red/red.js", 12 | "scripts": { 13 | "start": "node $NODE_OPTIONS node_modules/node-red/red.js -v $FLOWS" 14 | }, 15 | "dependencies": { 16 | "node-red": "2.2.2", 17 | "node-red-contrib-influxdb": "0.6.1", 18 | "node-red-contrib-opcua": "0.2.289" 19 | }, 20 | "engines": { 21 | "node": ">=12" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/node_red/node-red/Dockerfile.example: -------------------------------------------------------------------------------- 1 | ARG NODE_VERSION=16 2 | FROM node:${NODE_VERSION} 3 | 4 | # Home directory for Node-RED application source code. 5 | RUN mkdir -p /usr/src/node-red 6 | 7 | # User data directory, contains flows, config and nodes. 8 | RUN mkdir /data 9 | RUN mkdir /data/csv 10 | RUN chmod -R 777 /data/csv 11 | 12 | WORKDIR /usr/src/node-red 13 | 14 | # package.json contains Node-RED NPM module and node dependencies 15 | COPY package.json /usr/src/node-red/ 16 | COPY flows.json /data/flows.json 17 | COPY flows_cred.json /data/flows_cred.json 18 | COPY settings.js /data/settings.js 19 | 20 | RUN npm install 21 | 22 | # User configuration directory volume 23 | EXPOSE 1880 24 | 25 | # Environment variable holding file path for flows configuration 26 | ENV FLOWS=flows.json 27 | ENV NODE_PATH=/usr/src/node-red/node_modules:/data/node_modules 28 | 29 | CMD ["npm", "start", "--", "--userDir", "/data"] 30 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Siemens 2023 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: Bug Report 1 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Issue-template 11 | 12 | ## Summary 13 | 14 | Add summary of the found bug 15 | 16 | ## Version of the used components 17 | 18 | Which versions of the Industrial Edge components did you use? 19 | e.g. 20 | 21 | - Industrial Edge Device: V1.2.0-56 22 | - Performance Insight: V1.2.0 23 | - ... 24 | 25 | ## Steps to reproduce 26 | 27 | Please describe in detail the steps to reproduce the described behaviour 28 | 29 | ## What is the current bug behavior? 30 | 31 | Describe what actually happens 32 | 33 | ## What is the expected correct behavior? 34 | 35 | How should the app behave? 36 | 37 | ## Relevant logs and/or screenshots 38 | 39 | Please add relevant screenshots and logs 40 | 41 | ## Line of code 42 | 43 | Please refer to the dedicated line of code/ description if possible 44 | 45 | Example how to refer to a line of code: 46 | 47 | https://github.com/industrial-edge/how-to-template/blob/f195f788dc2c2572aaf822202b08fa3681e868a1/docker-compose.yml#L1-L3 48 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/data-analytics/program/app.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Siemens 2021 2 | # This file is subject to the terms and conditions of the MIT License. 3 | # See LICENSE file in the top-level directory. 4 | 5 | ''' Main python module for Data Analytics service ''' 6 | import time 7 | import os 8 | import sys 9 | import logging 10 | import data_analytics 11 | 12 | MAIN_LOOP_SLEEP_TIME = 0.5 13 | 14 | 15 | def main(): 16 | 17 | """ Initialize data-analytics """ 18 | 19 | # configures basic logger 20 | logger = logging.getLogger( __name__ ) 21 | logger.setLevel(logging.INFO) 22 | handler = logging.StreamHandler(sys.stdout) 23 | handler.setLevel(logging.INFO) 24 | formatter = logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(message)s') 25 | handler.setFormatter(formatter) 26 | logger.addHandler(handler) 27 | 28 | 29 | logger.info('Starting data-analytics service ...') 30 | analytics = data_analytics.DataAnalyzer(logger.name) 31 | analytics.handle_data() 32 | 33 | while True: 34 | time.sleep(MAIN_LOOP_SLEEP_TIME) 35 | 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/data-analytics/program/app.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Siemens 2021 2 | # This file is subject to the terms and conditions of the MIT License. 3 | # See LICENSE file in the top-level directory. 4 | 5 | ''' Main python module for Data Analytics service ''' 6 | import time 7 | import os 8 | import sys 9 | import logging 10 | import data_analytics 11 | 12 | MAIN_LOOP_SLEEP_TIME = 0.5 13 | 14 | 15 | def main(): 16 | 17 | """ Initialize data-analytics """ 18 | 19 | # configures basic logger 20 | logger = logging.getLogger( __name__ ) 21 | logger.setLevel(logging.INFO) 22 | handler = logging.StreamHandler(sys.stdout) 23 | handler.setLevel(logging.INFO) 24 | formatter = logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(message)s') 25 | handler.setFormatter(formatter) 26 | logger.addHandler(handler) 27 | 28 | 29 | logger.info('Starting data-analytics service ...') 30 | analytics = data_analytics.DataAnalyzer(logger.name) 31 | analytics.handle_data() 32 | 33 | while True: 34 | time.sleep(MAIN_LOOP_SLEEP_TIME) 35 | 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/influxdb/init_influxdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -e 3 | 4 | # Load secrets from various management sources 5 | # Use them to configure the influxdb database 6 | 7 | DOCKER_INFLUXDB_INIT_MODE=${DOCKER_INFLUXDB_INIT_MODE} 8 | DOCKER_INFLUXDB_INIT_USERNAME=${DOCKER_INFLUXDB_INIT_USERNAME} 9 | DOCKER_INFLUXDB_INIT_PASSWORD=${DOCKER_INFLUXDB_INIT_PASSWORD} 10 | DOCKER_INFLUXDB_INIT_ORG=${DOCKER_INFLUXDB_INIT_ORG} 11 | DOCKER_INFLUXDB_INIT_BUCKET=${DOCKER_INFLUXDB_INIT_BUCKET} 12 | DOCKER_INFLUXDB_INIT_RETENTION=${DOCKER_INFLUXDB_INIT_RETENTION} 13 | DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN} 14 | CONFIG_NAME=${CONFIG_NAME} 15 | # Setup InfluxDB using the `setup` command 16 | influx setup --name ${CONFIG_NAME} --bucket ${DOCKER_INFLUXDB_INIT_BUCKET} -t ${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN} -o ${DOCKER_INFLUXDB_INIT_ORG} --username="${DOCKER_INFLUXDB_INIT_USERNAME}" --password="${DOCKER_INFLUXDB_INIT_PASSWORD}" --host=http://influxdb:8086 -f 17 | 18 | ## Custom Setup 19 | CUSTOM_BUCKET=coreData 20 | CUSTOM_ORG=coreOrg 21 | CUSTOM_USER=edgeCoreUser 22 | CUSTOM_PASSWORD=edgeCoreUsersPassword 23 | 24 | # Create a Custom Organization 25 | influx org create -n ${CUSTOM_ORG} --host=http://influxdb:8086 -t ${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN} 26 | 27 | # Create a Custom Bucket, in the Org with retention policy of 24 hours 28 | influx bucket create -n ${CUSTOM_BUCKET} -o ${CUSTOM_ORG} -r 24h --host=http://influxdb:8086 -t ${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN} 29 | 30 | # Create a Custom User 31 | influx user create -n ${CUSTOM_USER} -p ${CUSTOM_PASSWORD} -o ${CUSTOM_ORG} --host=http://influxdb:8086 -t ${DOCKER_INFLUXDB_INIT_ADMIN_TOKEN} -------------------------------------------------------------------------------- /src/solution/HandsOn_1/mqtt_broker_mosquitto/docker-compose.yml: -------------------------------------------------------------------------------- 1 | ### Docker Compose File for MQTT Broker - Replacement of IE Databus ### 2 | # This docker-compose file creates a preconfigured MQTT Broker container without authentication 3 | 4 | 5 | version: '2.4' # docker-compose version is set to 2.4 6 | 7 | services: 8 | 9 | mqtt-broker: 10 | image: eclipse-mosquitto:$MQTT_VERSION # define image to pull from docker hub if not already on your machine available 11 | container_name: ie-databus # Name of MQTT broker container 12 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 13 | logging: # allow logging 14 | options: # we use best pactice here as limiting file size and rolling mechanism 15 | max-size: "10m" # File size is 10MB 16 | max-file: "2" # only 2 files created before rolling mechanism applies 17 | volumes: # mount volume from host 18 | - mosquitto:/mosquitto:ro # set to read-only volume 19 | ports: # expose of ports and publish 20 | - "33083:1883" # map containers default MQTT port (1883) to host's port 33083 21 | networks: # define networks connected to container 'mqtt-broker' 22 | proxy-redirect: # Name of the network 23 | 24 | ###### NETWORK CONFIG ###### 25 | networks: # Network interface configuration 26 | proxy-redirect: # Reference 'proxy-redirect' as predefined network 27 | name: proxy-redirect 28 | driver: bridge 29 | 30 | ###### VOLUMES ###### 31 | volumes: # Volumes for containers 32 | mosquitto: -------------------------------------------------------------------------------- /src/solution/HandsOn_2/mqtt_broker_mosquitto/docker-compose.yml: -------------------------------------------------------------------------------- 1 | ### Docker Compose File for MQTT Broker - Replacement of IE Databus ### 2 | # This docker-compose file creates a preconfigured MQTT Broker container without authentication 3 | 4 | 5 | version: '2.4' # docker-compose version is set to 2.4 6 | 7 | services: 8 | 9 | mqtt-broker: 10 | image: eclipse-mosquitto:$MQTT_VERSION # define image to pull from docker hub if not already on your machine available 11 | container_name: ie-databus # Name of MQTT broker container 12 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 13 | logging: # allow logging 14 | options: # we use best pactice here as limiting file size and rolling mechanism 15 | max-size: "10m" # File size is 10MB 16 | max-file: "2" # only 2 files created before rolling mechanism applies 17 | volumes: # mount volume from host 18 | - mosquitto:/mosquitto:ro # set to read-only volume 19 | ports: # expose of ports and publish 20 | - "33083:1883" # map containers default MQTT port (1883) to host's port 33083 21 | networks: # define networks connected to container 'mqtt-broker' 22 | proxy-redirect: # Name of the network 23 | 24 | ###### NETWORK CONFIG ###### 25 | networks: # Network interface configuration 26 | proxy-redirect: # Reference 'proxy-redirect' as predefined network 27 | name: proxy-redirect 28 | driver: bridge 29 | 30 | ###### VOLUMES ###### 31 | volumes: # Volumes for containers 32 | mosquitto: -------------------------------------------------------------------------------- /src/solution/HandsOn_1/node_red/docker-compose.yml: -------------------------------------------------------------------------------- 1 | ### Docker Compose File for node-red - Replacement of Southbound and SIMATIC Flow Creator ### 2 | # This docker-compose file creates a preconfigured NodeRed container MQTT connection 3 | 4 | 5 | version: '2.4' # docker-compose version is set to 2.4 6 | 7 | services: 8 | 9 | ###### NODE-RED ###### 10 | nodered: 11 | build: # Configuration applied at build time 12 | context: ./node-red # Relative Path to node-red from this docker-compose file containing Dockerfile 13 | args: # Args variables available only at build-time 14 | no_proxy: $no_proxy 15 | http_proxy: $http_proxy # Proxy url's from environment 16 | https_proxy: $https_proxy 17 | image: nodered:v0.0.1 # Name of the built image 18 | container_name: nodered # Name of the node-red container 19 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 20 | environment: # Environment variables available at container run-time 21 | http_proxy: $http_proxy # Proxy url's from environment 22 | https_proxy: $https_proxy 23 | logging: # allow logging 24 | options: # we use best pactice here as limiting file size and rolling mechanism 25 | max-size: "10m" # File size is 10MB 26 | max-file: "2" # only 2 files created before rolling mechanism applies 27 | ports: # expose of ports and publish 28 | - "33080:1880" # map containers port 33080 to host's port 1880 29 | networks: # define networks connected to container 'data-analytics' 30 | proxy-redirect: # Name of the network 31 | external_links: # Dependencie on other container 32 | - influxdb # Wait for start of container 'influxdb' 33 | 34 | ####### NETWORK CONFIG ###### 35 | networks: # Network interface configuration 36 | proxy-redirect: # Reference 'proxy-redirect' as predefined network 37 | external: # Note: Please create the network manually as it is preexisting on Industrial Edge Device 38 | name: proxy-redirect 39 | driver: bridge 40 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/docker-compose_Edge.yml: -------------------------------------------------------------------------------- 1 | ### Docker Compose File for my Industrial Edge App ### 2 | # This docker-compose file creates a preconfigured 3 | # * Data Analytics container based in Python with Mqtt Connection 4 | # * InfluxDB Container for Storage of Time Series data adn visualization 5 | 6 | version: '2.4' # docker-compose version is set to 2.4 7 | 8 | services: 9 | 10 | ###### DATA-ANALYTICS ###### 11 | data-analytics: 12 | image: data-analytics:v0.0.1 # Name of the built image 13 | container_name: data-analytics # Name of the data-analytics container 14 | mem_limit: 350m 15 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 16 | logging: # allow logging 17 | options: # we use best pactice here as limiting file size and rolling mechanism 18 | max-size: "10m" # File size is 10MB 19 | max-file: "2" # only 2 files created before rolling mechanism applies 20 | driver: json-file 21 | networks: # define networks connected to container 'data-analytics' 22 | proxy-redirect: # Name of the network 23 | 24 | ##### INFLUXDB ###### 25 | influxdb: 26 | image: influxdb:2.4-alpine # Define image to pull from docker hub if not already on your machine available 27 | container_name: influxdb # Name of the influx-db container 28 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 29 | mem_limit: 1400m 30 | environment: # Environment variables available at container run-time 31 | - DOCKER_INFLUXDB_INIT_MODE=setup 32 | - DOCKER_INFLUXDB_INIT_USERNAME=edge 33 | - DOCKER_INFLUXDB_INIT_PASSWORD=edgeadmin 34 | - DOCKER_INFLUXDB_INIT_ORG=siemens 35 | - DOCKER_INFLUXDB_INIT_BUCKET=edgedb 36 | - DOCKER_INFLUXDB_INIT_RETENTION=1w 37 | - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=testtoken 38 | logging: # allow logging 39 | options: # we use best pactice here as limiting file size and rolling mechanism 40 | max-size: "10m" # File size is 10MB 41 | max-file: "2" # only 2 files created before rolling mechanism applies 42 | driver: json-file 43 | volumes: # mount volume from host 44 | - db-backup:/var/lib/influxdb # mount named volume 'db-backup' to host's path to /var/lib/influxdb 45 | ports: # expose of ports and publish 46 | - "38086:8086" # map containers port 8086 to host's port 38086 47 | networks: # define networks connected to container 'influxdb' 48 | proxy-redirect: # Name of the network 49 | 50 | ###### NETWORK CONFIG ###### 51 | networks: # Network interface configuration 52 | proxy-redirect: # Reference 'proxy-redirect' as predefined network 53 | external: # Note: Already preexisting on Industrial Edge Device 54 | name: proxy-redirect 55 | driver: bridge 56 | 57 | ###### VOLUMES ###### 58 | volumes: # Volumes for containers 59 | db-backup: 60 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/docker-compose_Edge.yml: -------------------------------------------------------------------------------- 1 | ### Docker Compose File for my Industrial Edge App ### 2 | # This docker-compose file creates a preconfigured 3 | # * Data Analytics container based in Python with Mqtt Connection 4 | # * InfluxDB Container for Storage of Time Series data 5 | 6 | version: '2.4' # docker-compose version is set to 2.4 7 | 8 | services: 9 | 10 | ###### DATA-ANALYTICS ###### 11 | data-analytics: 12 | image: data-analytics:v0.0.1 # Name of the built image 13 | container_name: data-analytics # Name of the data-analytics container 14 | mem_limit: 350m 15 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 16 | logging: # allow logging 17 | options: # we use best pactice here as limiting file size and rolling mechanism 18 | max-size: "10m" # File size is 10MB 19 | max-file: "2" # only 2 files created before rolling mechanism applies 20 | driver: json-file 21 | networks: # define networks connected to container 'data-analytics' 22 | proxy-redirect: # Name of the network 23 | 24 | ##### INFLUXDB ###### 25 | influxdb: 26 | image: influxdb:2.2-alpine # Define image to pull from docker hub if not already on your machine available 27 | container_name: influxdb # Name of the influx-db container 28 | environment: 29 | INFLUXD_LOG_LEVEL: debug 30 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 31 | mem_limit: 350m 32 | logging: # allow logging 33 | options: # we use best pactice here as limiting file size and rolling mechanism 34 | max-size: "10m" # File size is 10MB 35 | max-file: "2" # only 2 files created before rolling mechanism applies 36 | driver: json-file 37 | volumes: # mount volume from host 38 | - db-backup:/var/lib/influxdb # mount named volume 'db-backup' to host's path to /var/lib/influxdb 39 | ports: # expose of ports and publish 40 | - "38086:8086" # map containers port 8086 to host's port 8086 41 | healthcheck: 42 | test: "exit 0" 43 | networks: # define networks connected to container 'influxdb' 44 | proxy-redirect: # Name of the network 45 | 46 | init-db: 47 | image: init-db:v0.0.1 # Docker Init Container to Setup the Main InfluxDB v2.2 Container 48 | mem_limit: 100m 49 | depends_on: 50 | influxdb: 51 | condition: service_healthy 52 | networks: # define networks connected to container 'influxdb' 53 | proxy-redirect: # Name of the network 54 | 55 | ###### NETWORK CONFIG ###### 56 | networks: # Network interface configuration 57 | proxy-redirect: # Reference 'proxy-redirect' as predefined network 58 | external: # Note: Already preexisting on Industrial Edge Device 59 | name: proxy-redirect 60 | driver: bridge 61 | 62 | ###### VOLUMES ###### 63 | volumes: # Volumes for containers 64 | db-backup: 65 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | ### Docker Compose File for my Industrial Edge App ### 2 | # This docker-compose file creates a preconfigured 3 | # * Data Analytics container based in Python with Mqtt Connection 4 | # * InfluxDB Container for Storage of Time Series data and visualization 5 | 6 | version: '2.4' # docker-compose version is set to 2.4 7 | 8 | services: 9 | 10 | ###### DATA-ANALYTICS ###### 11 | data-analytics: 12 | build: # Configuration applied at build time 13 | context: ./data-analytics # Relative Path to data-analytics from this docker-compose file containing Dockerfile 14 | args: # Args variables available only at build-time 15 | BASE_IMAGE: $BASE_IMAGE # 16 | image: data-analytics:v0.0.1 # Name of the built image 17 | container_name: data-analytics # Name of the data-analytics container 18 | mem_limit: 350m 19 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 20 | logging: # allow logging 21 | options: # we use best pactice here as limiting file size and rolling mechanism 22 | max-size: "10m" # File size is 10MB 23 | max-file: "2" # only 2 files created before rolling mechanism applies 24 | networks: # define networks connected to container 'data-analytics' 25 | proxy-redirect: # Name of the network 26 | 27 | ##### INFLUXDB ###### 28 | init-db: 29 | build: 30 | context: ./influxdb 31 | image: init-db:v0.0.1 # Docker Init Container to Setup the Main InfluxDB v2.2 Container 32 | depends_on: 33 | influxdb: 34 | condition: service_healthy 35 | networks: # define networks connected to container 'influxdb' 36 | proxy-redirect: # Name of the network 37 | 38 | influxdb: 39 | image: influxdb:$INFLUXDB_VERSION # Define image to pull from docker hub if not already on your machine available 40 | container_name: influxdb # Name of the influx-db container 41 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 42 | mem_limit: 350m 43 | logging: # allow logging 44 | options: # we use best pactice here as limiting file size and rolling mechanism 45 | max-size: "10m" # File size is 10MB 46 | max-file: "2" # only 2 files created before rolling mechanism applies 47 | driver: json-file 48 | ports: # expose of ports and publish 49 | - "38086:8086" # map containers port 8086 to host's port 8086 50 | volumes: # mount volume from host 51 | - db-backup:/var/lib/influxdb # mount named volume 'db-backup' to host's path to /var/lib/influxdb 52 | healthcheck: 53 | test: "exit 0" 54 | networks: # define networks connected to container 'influxdb' 55 | proxy-redirect: # Name of the network 56 | 57 | ###### NETWORK CONFIG ###### 58 | networks: # Network interface configuration 59 | proxy-redirect: # Reference 'proxy-redirect' as predefined network 60 | external: 61 | name: proxy-redirect 62 | driver: bridge 63 | 64 | ###### VOLUMES ###### 65 | volumes: # Volumes for containers 66 | db-backup: 67 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | ### Docker Compose File for my Industrial Edge App ### 2 | # This docker-compose file creates a preconfigured 3 | # * Data Analytics container based in Python with Mqtt Connection 4 | # * InfluxDB Container for Storage of Time Series data and visualization 5 | 6 | version: '2.4' # docker-compose version is set to 2.4 7 | 8 | services: 9 | 10 | ###### DATA-ANALYTICS ###### 11 | data-analytics: 12 | build: # Configuration applied at build time 13 | context: ./data-analytics # Relative Path to data-analytics from this docker-compose file containing Dockerfile 14 | args: # Args variables available only at build-time 15 | BASE_IMAGE: $BASE_IMAGE # 16 | http_proxy: $http_proxy # Proxy url's from environment 17 | https_proxy: $https_proxy 18 | image: data-analytics:v0.0.1 # Name of the built image 19 | container_name: data-analytics # Name of the data-analytics container 20 | mem_limit: 350m 21 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 22 | environment: # Environment variables available at container run-time 23 | http_proxy: $http_proxy # Proxy url's from environment 24 | https_proxy: $https_proxy 25 | logging: # allow logging 26 | options: # we use best pactice here as limiting file size and rolling mechanism 27 | max-size: "10m" # File size is 10MB 28 | max-file: "2" # only 2 files created before rolling mechanism applies 29 | networks: # define networks connected to container 'data-analytics' 30 | proxy-redirect: # Name of the network 31 | 32 | ##### INFLUXDB ###### 33 | influxdb: 34 | image: influxdb:$INFLUXDB_VERSION # Define image to pull from docker hub if not already on your machine available 35 | container_name: influxdb # Name of the influx-db container 36 | restart: unless-stopped # always restarts (see overview page 12 Industrial Edge Developer Guide) 37 | mem_limit: 1400m 38 | environment: # Environment variables available at container run-time 39 | - DOCKER_INFLUXDB_INIT_MODE=setup 40 | - DOCKER_INFLUXDB_INIT_USERNAME=edge 41 | - DOCKER_INFLUXDB_INIT_PASSWORD=edgeadmin 42 | - DOCKER_INFLUXDB_INIT_ORG=siemens 43 | - DOCKER_INFLUXDB_INIT_BUCKET=edgedb 44 | - DOCKER_INFLUXDB_INIT_RETENTION=1w 45 | - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=testtoken 46 | logging: # allow logging 47 | options: # we use best pactice here as limiting file size and rolling mechanism 48 | max-size: "10m" # File size is 10MB 49 | max-file: "2" # only 2 files created before rolling mechanism applies 50 | volumes: # mount volume from host 51 | - db-backup:/var/lib/influxdb # mount named volume 'db-backup' to host's path to /var/lib/influxdb 52 | ports: # expose of ports and publish 53 | - "38086:8086" # map containers port 8086 to host's port 38086 54 | networks: # define networks connected to container 'influxdb' 55 | proxy-redirect: # Name of the network 56 | 57 | ###### NETWORK CONFIG ###### 58 | networks: # Network interface configuration 59 | proxy-redirect: # Reference 'proxy-redirect' as predefined network 60 | external: 61 | name: proxy-redirect 62 | driver: bridge 63 | 64 | ###### VOLUMES ###### 65 | volumes: # Volumes for containers 66 | db-backup: 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # My first Industrial Edge App - App Developer Guide 2 | 3 | Creating a first Industrial Edge App on a development environment to deploy it to an Industrial Edge Device based on App Developer Guide. 4 | 5 | - [My first Industrial Edge App - App Developer Guide](#my-first-industrial-edge-app---app-developer-guide) 6 | - [Prerequisites](#prerequisites) 7 | - [Installation](#installation) 8 | - [Description](#description) 9 | - [Documentation](#documentation) 10 | - [Contribution](#contribution) 11 | - [License and Legal Information](#license-and-legal-information) 12 | - [Disclaimer](#disclaimer) 13 | 14 | ## Prerequisites 15 | 16 | Prerequisites are in the App Developer Guide which is available on [industrial-edge.io](https://docs.eu1.edge.siemens.cloud/develop_an_application/developer_guide/00_Overview.html). It contains description of the requirements as well as the step-by-step description how to work with this Developer Guide repository. 17 | 18 | ## Installation 19 | 20 | If you would like to run the solution of this app you need to rename all files called "Dockerfile.example" to Dockerfile. These Dockerfiles are just an example how you could implement it. 21 | 22 | ## Description 23 | 24 | As the example app will cover the most common use case in the Industrial Edge environment, the app on the Industrial Edge Device will look like the architectural overview in the figure below. The goal of the app will be to collect, process and store data from an OPC UA Server, which provides data from a PLC. 25 | 26 | ![Overview of app architecture](./docs/Picture_5_3_Architecture_IED.png) 27 | 28 | The app contains three parts – the connectivity to collect the data from the OPC UA Server by system apps, the IE Databus for distributions of the data and the process, storing and visualization of data in the Edge App. 29 | 30 | 1. The **IE Databus** based on MQTT is responsible for distributing data to certain topics, that are filled by system or custom apps by publishing and subscribing to these topics. 31 | 2. To receive the data from the OPC UA server, which is providing data from a PLC, the **OPC UA Connector connectivity** is used. OPC UA Connector is a system app, that publishes the data to IE Databus. Another system app, the SIMATIC Flow Creator, consumes the data from the OPC UA Connector topics on the IE Databus. The data is preprocessed in the SIMATIC Flow Creator before being published on the IE Databus again. 32 | 3. The developed **data analytics container** with Python is consuming the preprocessed data on the topics from the SIMATIC Flow Creator. The Python data analytics performs calculations and evaluations and returns the results as KPIs back to the IE Databus. To handle the IE Databus publishes and subscriptions, the data analytics container requires a MQTT client. 33 | 4. The **SIMATIC Flow Creator** consumes the analyzed data again. The SIMATIC Flow Creator persistently stores the (raw) and analyzed data in InfluxDB. 34 | 5. The **InfluxDB** is a time series database which is optimized for fast, high-availability storage and retrieval of time series data. It stores both the data transmitted by the OPC UA server to the app and the analyzed data. 35 | 6. The data stored in the database can be queried and graphed in dashboards to format them and present them in meaningful and easy to understand way. There are many types of dashboards to choose from including those that come with InfluxDB or other open source projects like Grafana. In this application, the native **InfluxDB Dashboards** are leveraged for basic data visualization. 36 | 37 | ## Documentation 38 | 39 | - Here is a link to the [industrial-edge.io](https://docs.eu1.edge.siemens.cloud/develop_an_application/developer_guide/00_Overview.html) where the App Developer Guide of this application example can be found. 40 | - You can find further documentation and help in the following links 41 | - [Industrial Edge Hub](https://iehub.eu1.edge.siemens.cloud/#/documentation) 42 | - [Industrial Edge Forum](https://www.siemens.com/industrial-edge-forum) 43 | - [Industrial Edge landing page](http://siemens.com/industrial-edge) 44 | 45 | ## Contribution 46 | 47 | Thank you for your interest in contributing. Anybody is free to report bugs, unclear documentation, and other problems regarding this repository in the Issues section. 48 | Additionally everybody is free to propose any changes to this repository using Pull Requests. 49 | 50 | If you haven't previously signed the [Siemens Contributor License Agreement](https://cla-assistant.io/industrial-edge/) (CLA), the system will automatically prompt you to do so when you submit your Pull Request. This can be conveniently done through the CLA Assistant's online platform. Once the CLA is signed, your Pull Request will automatically be cleared and made ready for merging if all other test stages succeed. 51 | 52 | ## License and Legal Information 53 | 54 | Please read the [Legal information](LICENSE.txt). 55 | 56 | ## Disclaimer 57 | 58 | IMPORTANT - PLEASE READ CAREFULLY: 59 | 60 | This documentation describes how you can download and set up containers which consist of or contain third-party software. By following this documentation you agree that using such third-party software is done at your own discretion and risk. No advice or information, whether oral or written, obtained by you from us or from this documentation shall create any warranty for the third-party software. Additionally, by following these descriptions or using the contents of this documentation, you agree that you are responsible for complying with all third party licenses applicable to such third-party software. All product names, logos, and brands are property of their respective owners. All third-party company, product and service names used in this documentation are for identification purposes only. Use of these names, logos, and brands does not imply endorsement. 61 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | industrialedge.industry@siemens.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/data-analytics/program/data_analytics.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Siemens 2021 2 | # This file is subject to the terms and conditions of the MIT License. 3 | # See LICENSE file in the top-level directory. 4 | 5 | """Module Data Analytics. 6 | 7 | This module consists of DataGenerator class and also the function to generate 8 | bivariate normal distributed datasets. 9 | 10 | """ 11 | 12 | import paho.mqtt.client as mqtt 13 | import sys 14 | import logging 15 | import statistics 16 | import json 17 | 18 | BROKER_ADDRESS='ie-databus' 19 | BROKER_PORT=1883 20 | MICRO_SERVICE_NAME = 'data-analytics' 21 | """ Broker user and password for authtentification""" 22 | USERNAME='edge' 23 | PASSWORD='edge' 24 | 25 | class DataAnalyzer(): 26 | """ 27 | Data Analyzer connects to mqtt broker and waits for new 28 | input data to calculate KPIs. 29 | 30 | """ 31 | 32 | def __init__(self, logger_parent): 33 | """ Starts the instantiated object with a proper logger """ 34 | 35 | logger_name = '{}.{}'.format(logger_parent,__name__) 36 | self.logger = logging.getLogger(logger_name) 37 | self.client = mqtt.Client(MICRO_SERVICE_NAME) 38 | self.client.on_connect = self.on_connect 39 | self.client.on_disconnect = self.on_disconnect 40 | self.client.on_subscribe = self.on_subscribe 41 | self.client.on_message = self.on_message 42 | self.topic_callback = dict() 43 | 44 | def on_connect(self, client, userdata, flags, rc): 45 | self.logger.info('Connected successfully to broker, response code {}'.format(rc)) 46 | 47 | def on_disconnect(self, client, userdata, rc): 48 | if rc != 0: 49 | self.logger.warning('Connection ended unexpectedly from broker, error code {}'.format(rc)) 50 | 51 | 52 | def on_subscribe(self, client, userdata, mid, granted_qos): 53 | 54 | self.logger.info('successfully subscribed ') 55 | 56 | def on_message(self, client, userdata, message): 57 | self.logger.info('New message received on topic: {}'.format(message.topic)) 58 | # print(message.payload) 59 | # load = message.payload 60 | new_msg = json.loads(message.payload) 61 | self.logger.info('new message: {}'.format(new_msg)) 62 | try: 63 | self.topic_callback[message.topic](new_msg) 64 | except Exception as err: 65 | self.logger.error('An error ocurred while hanlding new message of {}: {}'.format(message.topic, err)) 66 | 67 | def subscribe(self, topic, callback): 68 | """ Subscribes to given topic, assigning a callback function that 69 | handles the received payload 70 | 71 | :topic: string with the topic to subscribe 72 | :callback: function to assign the payload received 73 | """ 74 | self.topic_callback.update({topic:callback}) 75 | self.client.subscribe(topic) 76 | 77 | # Callback function for MQTT topic 'StandardKpis' 78 | def standard_kpis(self, payload): 79 | values = [key['_value'] for key in payload] 80 | name = [key['_measurement'] for key in payload] 81 | self.logger.info('name is: {}'.format(name)) 82 | # Calculate standard KPIs 83 | result = { 84 | 'mean_result' : statistics.mean(values), 85 | 'median_result' : statistics.median(values), 86 | 'stddev_result' : statistics.stdev(values), 87 | 'name' : payload[0]['_measurement'], 88 | } 89 | self.logger.info('mean calculated: {}'.format(statistics.mean(values))) 90 | self.logger.info('median calculated: {}'.format(statistics.median(values))) 91 | self.logger.info('stddev calculated: {} \n ======='.format(statistics.stdev(values))) 92 | # publish results back on MQTT topic 'StandardKpiResult' 93 | self.client.publish(topic='StandardKpiResult', payload=json.dumps(result)) 94 | return 95 | 96 | # Callback function for MQTT topic 'Mean' subscription 97 | def power_mean(self, payload): 98 | self.logger.info('calculating power mean...') 99 | 100 | current_values = [item['_value'] for item in payload['current_drive3_batch']] 101 | voltage_values = [item['_value'] for item in payload['voltage_drive3_batch']] 102 | # Calculate mean of power 103 | power_batch_sum = sum([current*voltage for current, voltage in zip(current_values,voltage_values)]) 104 | 105 | power_mean = round((power_batch_sum/payload['sample_number']),2) 106 | self.logger.info("power mean result: {}\n".format(power_mean)) 107 | 108 | result = { 109 | 'power_mean_result' : power_mean, 110 | 'name' : 'powerdrive3_mean', 111 | } 112 | # publish result back on MQTT topic 'MeanResult' 113 | self.client.publish(topic='MeanResult', payload=json.dumps(result)) 114 | return 115 | 116 | def handle_data(self): 117 | """ 118 | Starts the connection to MQTT broker and subscribe to respective 119 | topics. 120 | 121 | """ 122 | 123 | self.logger.info('Preparing Mqtt Connection') 124 | try: 125 | self.client.username_pw_set(USERNAME, PASSWORD) 126 | self.client.connect(BROKER_ADDRESS) 127 | self.client.loop_start() 128 | self.logger.info('Subscribe to topic StandardKpis') 129 | self.subscribe(topic='StandardKpis', callback=self.standard_kpis) 130 | self.logger.info('Subscripe to topic Mean') 131 | self.subscribe(topic='Mean', callback=self.power_mean) 132 | self.logger.info('Finished subscription to topics') 133 | 134 | 135 | except Exception as e: 136 | self.logger.error(str(e)) 137 | -------------------------------------------------------------------------------- /src/solution/HandsOn_2/my_edge_app/data-analytics/program/data_analytics.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Siemens 2021 2 | # This file is subject to the terms and conditions of the MIT License. 3 | # See LICENSE file in the top-level directory. 4 | 5 | """Module Data Analytics. 6 | 7 | This module consists of DataGenerator class and also the function to generate 8 | bivariate normal distributed datasets. 9 | 10 | """ 11 | 12 | import paho.mqtt.client as mqtt 13 | import sys 14 | import logging 15 | import statistics 16 | import json 17 | 18 | BROKER_ADDRESS='ie-databus' 19 | BROKER_PORT=1883 20 | MICRO_SERVICE_NAME = 'data-analytics' 21 | """ Broker user and password for authtentification""" 22 | USERNAME='edge' 23 | PASSWORD='edge' 24 | 25 | class DataAnalyzer(): 26 | """ 27 | Data Analyzer connects to mqtt broker and waits for new 28 | input data to calculate KPIs. 29 | 30 | """ 31 | 32 | def __init__(self, logger_parent): 33 | """ Starts the instantiated object with a proper logger """ 34 | 35 | logger_name = '{}.{}'.format(logger_parent,__name__) 36 | self.logger = logging.getLogger(logger_name) 37 | self.client = mqtt.Client(MICRO_SERVICE_NAME) 38 | self.client.on_connect = self.on_connect 39 | self.client.on_disconnect = self.on_disconnect 40 | self.client.on_subscribe = self.on_subscribe 41 | self.client.on_message = self.on_message 42 | self.topic_callback = dict() 43 | 44 | def on_connect(self, client, userdata, flags, rc): 45 | self.logger.info('Connected successfully to broker, response code {}'.format(rc)) 46 | 47 | def on_disconnect(self, client, userdata, rc): 48 | if rc != 0: 49 | self.logger.warning('Connection ended unexpectedly from broker, error code {}'.format(rc)) 50 | 51 | 52 | def on_subscribe(self, client, userdata, mid, granted_qos): 53 | 54 | self.logger.info('successfully subscribed ') 55 | 56 | def on_message(self, client, userdata, message): 57 | self.logger.info('New message received on topic: {}'.format(message.topic)) 58 | # print(message.payload) 59 | # load = message.payload 60 | new_msg = json.loads(message.payload) 61 | self.logger.info('new message: {}'.format(new_msg)) 62 | try: 63 | self.topic_callback[message.topic](new_msg) 64 | except Exception as err: 65 | self.logger.error('An error ocurred while hanlding new message of {}: {}'.format(message.topic, err)) 66 | 67 | def subscribe(self, topic, callback): 68 | """ Subscribes to given topic, assigning a callback function that 69 | handles the received payload 70 | 71 | :topic: string with the topic to subscribe 72 | :callback: function to assign the payload received 73 | """ 74 | self.topic_callback.update({topic:callback}) 75 | self.client.subscribe(topic) 76 | 77 | # Callback function for MQTT topic 'StandardKpis' 78 | def standard_kpis(self, payload): 79 | values = [key['_value'] for key in payload] 80 | name = [key['_measurement'] for key in payload] 81 | self.logger.info('name is: {}'.format(name)) 82 | # Calculate standard KPIs 83 | result = { 84 | 'mean_result' : statistics.mean(values), 85 | 'median_result' : statistics.median(values), 86 | 'stddev_result' : statistics.stdev(values), 87 | 'name' : payload[0]['_measurement'], 88 | } 89 | self.logger.info('mean calculated: {}'.format(statistics.mean(values))) 90 | self.logger.info('median calculated: {}'.format(statistics.median(values))) 91 | self.logger.info('stddev calculated: {} \n ======='.format(statistics.stdev(values))) 92 | # publish results back on MQTT topic 'StandardKpiResult' 93 | self.client.publish(topic='StandardKpiResult', payload=json.dumps(result)) 94 | return 95 | 96 | # Callback function for MQTT topic 'Mean' subscription 97 | def power_mean(self, payload): 98 | self.logger.info('calculating power mean...') 99 | 100 | current_values = [item['_value'] for item in payload['current_drive3_batch']] 101 | voltage_values = [item['_value'] for item in payload['voltage_drive3_batch']] 102 | # Calculate mean of power 103 | power_batch_sum = sum([current*voltage for current, voltage in zip(current_values,voltage_values)]) 104 | 105 | power_mean = round((power_batch_sum/payload['sample_number']),2) 106 | self.logger.info("power mean result: {}\n".format(power_mean)) 107 | 108 | result = { 109 | 'power_mean_result' : power_mean, 110 | 'name' : 'powerdrive3_mean', 111 | } 112 | # publish result back on MQTT topic 'MeanResult' 113 | self.client.publish(topic='MeanResult', payload=json.dumps(result)) 114 | return 115 | 116 | def handle_data(self): 117 | """ 118 | Starts the connection to MQTT broker and subscribe to respective 119 | topics. 120 | 121 | """ 122 | 123 | self.logger.info('Preparing Mqtt Connection') 124 | try: 125 | self.client.username_pw_set(USERNAME, PASSWORD) 126 | self.client.connect(BROKER_ADDRESS) 127 | self.client.loop_start() 128 | self.logger.info('Subscribe to topic StandardKpis') 129 | self.subscribe(topic='StandardKpis', callback=self.standard_kpis) 130 | self.logger.info('Subscripe to topic Mean') 131 | self.subscribe(topic='Mean', callback=self.power_mean) 132 | self.logger.info('Finished subscription to topics') 133 | 134 | 135 | except Exception as e: 136 | self.logger.error(str(e)) 137 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/node_red/node-red/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright JS Foundation and other contributors, http://js.foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | // The `https` setting requires the `fs` module. Uncomment the following 18 | // to make it available: 19 | //var fs = require("fs"); 20 | 21 | module.exports = { 22 | // the tcp port that the Node-RED web server is listening on 23 | uiPort: process.env.PORT || 1880, 24 | 25 | // By default, the Node-RED UI accepts connections on all IPv4 interfaces. 26 | // To listen on all IPv6 addresses, set uiHost to "::", 27 | // The following property can be used to listen on a specific interface. For 28 | // example, the following would only allow connections from the local machine. 29 | //uiHost: "127.0.0.1", 30 | 31 | // Retry time in milliseconds for MQTT connections 32 | mqttReconnectTime: 15000, 33 | 34 | // Retry time in milliseconds for Serial port connections 35 | serialReconnectTime: 15000, 36 | 37 | // Retry time in milliseconds for TCP socket connections 38 | //socketReconnectTime: 10000, 39 | 40 | // Timeout in milliseconds for TCP server socket connections 41 | // defaults to no timeout 42 | //socketTimeout: 120000, 43 | 44 | // Timeout in milliseconds for HTTP request connections 45 | // defaults to 120 seconds 46 | //httpRequestTimeout: 120000, 47 | 48 | // The maximum length, in characters, of any message sent to the debug sidebar tab 49 | debugMaxLength: 1000, 50 | 51 | // The maximum number of messages nodes will buffer internally as part of their 52 | // operation. This applies across a range of nodes that operate on message sequences. 53 | // defaults to no limit. A value of 0 also means no limit is applied. 54 | //nodeMaxMessageBufferLength: 0, 55 | 56 | // To disable the option for using local files for storing keys and certificates in the TLS configuration 57 | // node, set this to true 58 | //tlsConfigDisableLocalFiles: true, 59 | 60 | // Colourise the console output of the debug node 61 | //debugUseColors: true, 62 | 63 | // The file containing the flows. If not set, it defaults to flows_.json 64 | flowFile: 'flows.json', 65 | 66 | // To enabled pretty-printing of the flow within the flow file, set the following 67 | // property to true: 68 | //flowFilePretty: true, 69 | 70 | // By default, credentials are encrypted in storage using a generated key. To 71 | // specify your own secret, set the following property. 72 | // If you want to disable encryption of credentials, set this property to false. 73 | // Note: once you set this property, do not change it - doing so will prevent 74 | // node-red from being able to decrypt your existing credentials and they will be 75 | // lost. 76 | // credentialSecret: 77 | 78 | // By default, all user data is stored in the Node-RED install directory. To 79 | // use a different location, the following property can be used 80 | //userDir: '/home/nol/.node-red/', 81 | 82 | // Node-RED scans the `nodes` directory in the install directory to find nodes. 83 | // The following property can be used to specify an additional directory to scan. 84 | //nodesDir: '/home/nol/.node-red/nodes', 85 | 86 | // By default, the Node-RED UI is available at http://localhost:1880/ 87 | // The following property can be used to specify a different root path. 88 | // If set to false, this is disabled. 89 | //httpAdminRoot: '/admin', 90 | 91 | // Some nodes, such as HTTP In, can be used to listen for incoming http requests. 92 | // By default, these are served relative to '/'. The following property 93 | // can be used to specifiy a different root path. If set to false, this is 94 | // disabled. 95 | //httpNodeRoot: '/red-nodes', 96 | 97 | // The following property can be used in place of 'httpAdminRoot' and 'httpNodeRoot', 98 | // to apply the same root to both parts. 99 | //httpRoot: '/red', 100 | 101 | // When httpAdminRoot is used to move the UI to a different root path, the 102 | // following property can be used to identify a directory of static content 103 | // that should be served at http://localhost:1880/. 104 | //httpStatic: '/home/nol/node-red-static/', 105 | 106 | // The maximum size of HTTP request that will be accepted by the runtime api. 107 | // Default: 5mb 108 | //apiMaxLength: '5mb', 109 | 110 | // If you installed the optional node-red-dashboard you can set it's path 111 | // relative to httpRoot 112 | //ui: { path: "ui" }, 113 | 114 | // Securing Node-RED 115 | // ----------------- 116 | // To password protect the Node-RED editor and admin API, the following 117 | // property can be used. See http://nodered.org/docs/security.html for details. 118 | //adminAuth: { 119 | // type: "credentials", 120 | // users: [{ 121 | // username: "admin", 122 | // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", 123 | // permissions: "*" 124 | // }] 125 | //}, 126 | 127 | // To password protect the node-defined HTTP endpoints (httpNodeRoot), or 128 | // the static content (httpStatic), the following properties can be used. 129 | // The pass field is a bcrypt hash of the password. 130 | // See http://nodered.org/docs/security.html#generating-the-password-hash 131 | //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, 132 | //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, 133 | 134 | // The following property can be used to enable HTTPS 135 | // See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener 136 | // for details on its contents. 137 | // See the comment at the top of this file on how to load the `fs` module used by 138 | // this setting. 139 | // 140 | //https: { 141 | // key: fs.readFileSync('privatekey.pem'), 142 | // cert: fs.readFileSync('certificate.pem') 143 | //}, 144 | 145 | // The following property can be used to cause insecure HTTP connections to 146 | // be redirected to HTTPS. 147 | //requireHttps: true 148 | 149 | // The following property can be used to disable the editor. The admin API 150 | // is not affected by this option. To disable both the editor and the admin 151 | // API, use either the httpRoot or httpAdminRoot properties 152 | //disableEditor: false, 153 | 154 | // The following property can be used to configure cross-origin resource sharing 155 | // in the HTTP nodes. 156 | // See https://github.com/troygoode/node-cors#configuration-options for 157 | // details on its contents. The following is a basic permissive set of options: 158 | //httpNodeCors: { 159 | // origin: "*", 160 | // methods: "GET,PUT,POST,DELETE" 161 | //}, 162 | 163 | // If you need to set an http proxy please set an environment variable 164 | // called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. 165 | // For example - http_proxy=http://myproxy.com:8080 166 | // (Setting it here will have no effect) 167 | // You may also specify no_proxy (or NO_PROXY) to supply a comma separated 168 | // list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk 169 | 170 | // The following property can be used to add a custom middleware function 171 | // in front of all http in nodes. This allows custom authentication to be 172 | // applied to all http in nodes, or any other sort of common request processing. 173 | //httpNodeMiddleware: function(req,res,next) { 174 | // // Handle/reject the request, or pass it on to the http in node by calling next(); 175 | // // Optionally skip our rawBodyParser by setting this to true; 176 | // //req.skipRawBodyParser = true; 177 | // next(); 178 | //}, 179 | 180 | // The following property can be used to verify websocket connection attempts. 181 | // This allows, for example, the HTTP request headers to be checked to ensure 182 | // they include valid authentication information. 183 | //webSocketNodeVerifyClient: function(info) { 184 | // // 'info' has three properties: 185 | // // - origin : the value in the Origin header 186 | // // - req : the HTTP request 187 | // // - secure : true if req.connection.authorized or req.connection.encrypted is set 188 | // // 189 | // // The function should return true if the connection should be accepted, false otherwise. 190 | // // 191 | // // Alternatively, if this function is defined to accept a second argument, callback, 192 | // // it can be used to verify the client asynchronously. 193 | // // The callback takes three arguments: 194 | // // - result : boolean, whether to accept the connection or not 195 | // // - code : if result is false, the HTTP error status to return 196 | // // - reason: if result is false, the HTTP reason string to return 197 | //}, 198 | 199 | // Anything in this hash is globally available to all functions. 200 | // It is accessed as context.global. 201 | // eg: 202 | // functionGlobalContext: { os:require('os') } 203 | // can be accessed in a function block as: 204 | // context.global.os 205 | 206 | functionGlobalContext: { 207 | // os:require('os'), 208 | // jfive:require("johnny-five"), 209 | // j5board:require("johnny-five").Board({repl:false}) 210 | }, 211 | 212 | // The following property can be used to order the categories in the editor 213 | // palette. If a node's category is not in the list, the category will get 214 | // added to the end of the palette. 215 | // If not set, the following default order is used: 216 | //paletteCategories: ['subflows', 'input', 'output', 'function', 'social', 'mobile', 'storage', 'analysis', 'advanced'], 217 | 218 | // Configure the logging output 219 | logging: { 220 | // Only console logging is currently supported 221 | console: { 222 | // Level of logging to be recorded. Options are: 223 | // fatal - only those errors which make the application unusable should be recorded 224 | // error - record errors which are deemed fatal for a particular request + fatal errors 225 | // warn - record problems which are non fatal + errors + fatal errors 226 | // info - record information about the general running of the application + warn + error + fatal errors 227 | // debug - record information which is more verbose than info + info + warn + error + fatal errors 228 | // trace - record very detailed logging + debug + info + warn + error + fatal errors 229 | // off - turn off all logging (doesn't affect metrics or audit) 230 | level: "debug", 231 | // Whether or not to include metric events in the log output 232 | metrics: false, 233 | // Whether or not to include audit events in the log output 234 | audit: false 235 | } 236 | }, 237 | 238 | // Customising the editor 239 | editorTheme: { 240 | projects: { 241 | // To enable the Projects feature, set this value to true 242 | enabled: false 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/solution/HandsOn_1/my_edge_app/influxDB/drive_kpi_calc_dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "version": "1", 4 | "type": "dashboard", 5 | "name": "Drive KPI Calc Dashboard-Template", 6 | "description": "template created from dashboard: Drive KPI Calc Dashboard" 7 | }, 8 | "content": { 9 | "data": { 10 | "type": "dashboard", 11 | "attributes": { 12 | "name": "Drive KPI Calc Dashboard", 13 | "description": "Factory Line Drives Overview" 14 | }, 15 | "relationships": { 16 | "label": { 17 | "data": [] 18 | }, 19 | "cell": { 20 | "data": [ 21 | { 22 | "type": "cell", 23 | "id": "0a27ee95195b5000" 24 | }, 25 | { 26 | "type": "cell", 27 | "id": "0a27ee951adb5000" 28 | }, 29 | { 30 | "type": "cell", 31 | "id": "0a27ee951b1b5000" 32 | }, 33 | { 34 | "type": "cell", 35 | "id": "0a27ee951b9b5000" 36 | }, 37 | { 38 | "type": "cell", 39 | "id": "0a27ee951bdb5000" 40 | } 41 | ] 42 | }, 43 | "variable": { 44 | "data": [] 45 | } 46 | } 47 | }, 48 | "included": [ 49 | { 50 | "id": "0a27ee95195b5000", 51 | "type": "cell", 52 | "attributes": { 53 | "x": 0, 54 | "y": 0, 55 | "w": 12, 56 | "h": 3 57 | }, 58 | "relationships": { 59 | "view": { 60 | "data": { 61 | "type": "view", 62 | "id": "0a27ee95195b5000" 63 | } 64 | } 65 | } 66 | }, 67 | { 68 | "id": "0a27ee951adb5000", 69 | "type": "cell", 70 | "attributes": { 71 | "x": 0, 72 | "y": 7, 73 | "w": 6, 74 | "h": 5 75 | }, 76 | "relationships": { 77 | "view": { 78 | "data": { 79 | "type": "view", 80 | "id": "0a27ee951adb5000" 81 | } 82 | } 83 | } 84 | }, 85 | { 86 | "id": "0a27ee951b1b5000", 87 | "type": "cell", 88 | "attributes": { 89 | "x": 0, 90 | "y": 3, 91 | "w": 6, 92 | "h": 4 93 | }, 94 | "relationships": { 95 | "view": { 96 | "data": { 97 | "type": "view", 98 | "id": "0a27ee951b1b5000" 99 | } 100 | } 101 | } 102 | }, 103 | { 104 | "id": "0a27ee951b9b5000", 105 | "type": "cell", 106 | "attributes": { 107 | "x": 6, 108 | "y": 7, 109 | "w": 6, 110 | "h": 5 111 | }, 112 | "relationships": { 113 | "view": { 114 | "data": { 115 | "type": "view", 116 | "id": "0a27ee951b9b5000" 117 | } 118 | } 119 | } 120 | }, 121 | { 122 | "id": "0a27ee951bdb5000", 123 | "type": "cell", 124 | "attributes": { 125 | "x": 6, 126 | "y": 3, 127 | "w": 6, 128 | "h": 4 129 | }, 130 | "relationships": { 131 | "view": { 132 | "data": { 133 | "type": "view", 134 | "id": "0a27ee951bdb5000" 135 | } 136 | } 137 | } 138 | }, 139 | { 140 | "type": "view", 141 | "id": "0a27ee95195b5000", 142 | "attributes": { 143 | "name": "Name this Cell", 144 | "properties": { 145 | "shape": "chronograf-v2", 146 | "type": "markdown", 147 | "note": "# Power Drive KPI Dashboard\n---\n**Designed to have an overview about the drives in the factory line**\n\nCustomer Dashboard" 148 | } 149 | } 150 | }, 151 | { 152 | "type": "view", 153 | "id": "0a27ee951adb5000", 154 | "attributes": { 155 | "name": "KPI (Mean,Median,Standard dev) calculation for the PowerDrive2", 156 | "properties": { 157 | "shape": "chronograf-v2", 158 | "queries": [ 159 | { 160 | "text": "from(bucket: \"edgedb\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"POWERDRIVE2_STANDARD_KPIS\")\n |> filter(fn: (r) => r[\"_field\"] == \"mean\" or r[\"_field\"] == \"median\" or r[\"_field\"] == \"stddev\")\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n |> yield(name: \"last\")", 161 | "editMode": "advanced", 162 | "name": "", 163 | "builderConfig": { 164 | "buckets": [], 165 | "tags": [ 166 | { 167 | "key": "_measurement", 168 | "values": [], 169 | "aggregateFunctionType": "filter" 170 | } 171 | ], 172 | "functions": [ 173 | { 174 | "name": "mean" 175 | } 176 | ], 177 | "aggregateWindow": { 178 | "period": "auto", 179 | "fillValues": false 180 | } 181 | } 182 | } 183 | ], 184 | "axes": { 185 | "x": { 186 | "bounds": [ 187 | "", 188 | "" 189 | ], 190 | "label": "", 191 | "prefix": "", 192 | "suffix": "", 193 | "base": "10", 194 | "scale": "linear" 195 | }, 196 | "y": { 197 | "bounds": [ 198 | "0", 199 | "1000" 200 | ], 201 | "label": "", 202 | "prefix": "", 203 | "suffix": "", 204 | "base": "10", 205 | "scale": "linear" 206 | } 207 | }, 208 | "type": "xy", 209 | "staticLegend": { 210 | "colorizeRows": true, 211 | "heightRatio": 0.18478260869565216, 212 | "opacity": 1, 213 | "orientationThreshold": 100000000, 214 | "widthRatio": 1 215 | }, 216 | "geom": "line", 217 | "colors": [ 218 | { 219 | "id": "e7d7d0aa-7f11-4661-a9c6-f637d8c2e33a", 220 | "type": "scale", 221 | "hex": "#8F8AF4", 222 | "name": "Do Androids Dream of Electric Sheep?", 223 | "value": 0 224 | }, 225 | { 226 | "id": "5634dc28-b475-44d1-b420-26c7d518abcd", 227 | "type": "scale", 228 | "hex": "#A51414", 229 | "name": "Do Androids Dream of Electric Sheep?", 230 | "value": 0 231 | }, 232 | { 233 | "id": "235f2f13-9149-4857-a27b-f93c03e36f65", 234 | "type": "scale", 235 | "hex": "#F4CF31", 236 | "name": "Do Androids Dream of Electric Sheep?", 237 | "value": 0 238 | } 239 | ], 240 | "note": "", 241 | "showNoteWhenEmpty": false, 242 | "xColumn": "_time", 243 | "generateXAxisTicks": [], 244 | "xTotalTicks": 0, 245 | "xTickStart": 0, 246 | "xTickStep": 0, 247 | "yColumn": "_value", 248 | "generateYAxisTicks": [], 249 | "yTotalTicks": 0, 250 | "yTickStart": 0, 251 | "yTickStep": 0, 252 | "shadeBelow": true, 253 | "position": "overlaid", 254 | "timeFormat": "HH:mm:ss", 255 | "hoverDimension": "auto", 256 | "legendColorizeRows": true, 257 | "legendHide": false, 258 | "legendOpacity": 1, 259 | "legendOrientationThreshold": 100000000 260 | } 261 | } 262 | }, 263 | { 264 | "type": "view", 265 | "id": "0a27ee951b1b5000", 266 | "attributes": { 267 | "name": "Total Power Consumption", 268 | "properties": { 269 | "shape": "chronograf-v2", 270 | "type": "gauge", 271 | "queries": [ 272 | { 273 | "text": "from(bucket: \"edgedb\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"GEN_KPI_TotalPower\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n |> yield(name: \"last\")", 274 | "editMode": "advanced", 275 | "name": "", 276 | "builderConfig": { 277 | "buckets": [], 278 | "tags": [ 279 | { 280 | "key": "_measurement", 281 | "values": [], 282 | "aggregateFunctionType": "filter" 283 | } 284 | ], 285 | "functions": [ 286 | { 287 | "name": "mean" 288 | } 289 | ], 290 | "aggregateWindow": { 291 | "period": "auto", 292 | "fillValues": false 293 | } 294 | } 295 | } 296 | ], 297 | "prefix": "", 298 | "tickPrefix": "", 299 | "suffix": "", 300 | "tickSuffix": "W", 301 | "colors": [ 302 | { 303 | "id": "0", 304 | "type": "min", 305 | "hex": "#7CE490", 306 | "name": "honeydew", 307 | "value": 0 308 | }, 309 | { 310 | "id": "1", 311 | "type": "max", 312 | "hex": "#BF3D5E", 313 | "name": "ruby", 314 | "value": 2000 315 | } 316 | ], 317 | "decimalPlaces": { 318 | "isEnforced": true, 319 | "digits": 2 320 | }, 321 | "note": "", 322 | "showNoteWhenEmpty": false 323 | } 324 | } 325 | }, 326 | { 327 | "type": "view", 328 | "id": "0a27ee951b9b5000", 329 | "attributes": { 330 | "name": "Power Drive List", 331 | "properties": { 332 | "shape": "chronograf-v2", 333 | "type": "table", 334 | "queries": [ 335 | { 336 | "text": "from(bucket: \"edgedb\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"POWERDRIVE1_STANDARD_KPIS\" or r[\"_measurement\"] == \"POWERDRIVE2_STANDARD_KPIS\" or r[\"_measurement\"] == \"POWERDRIVE3_MEAN\")\n |> filter(fn: (r) => r[\"_field\"] == \"mean\" or r[\"_field\"] == \"median\" or r[\"_field\"] == \"stddev\")\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n |> yield(name: \"last\")", 337 | "editMode": "advanced", 338 | "name": "", 339 | "builderConfig": { 340 | "buckets": [], 341 | "tags": [ 342 | { 343 | "key": "_measurement", 344 | "values": [], 345 | "aggregateFunctionType": "filter" 346 | } 347 | ], 348 | "functions": [ 349 | { 350 | "name": "mean" 351 | } 352 | ], 353 | "aggregateWindow": { 354 | "period": "auto", 355 | "fillValues": false 356 | } 357 | } 358 | } 359 | ], 360 | "colors": [ 361 | { 362 | "id": "base", 363 | "type": "text", 364 | "hex": "#F48D38", 365 | "name": "tiger", 366 | "value": 0 367 | } 368 | ], 369 | "tableOptions": { 370 | "verticalTimeAxis": true, 371 | "sortBy": { 372 | "internalName": "", 373 | "displayName": "", 374 | "visible": false 375 | }, 376 | "wrapping": "", 377 | "fixFirstColumn": false 378 | }, 379 | "fieldOptions": [ 380 | { 381 | "internalName": "_start", 382 | "displayName": "Start time", 383 | "visible": false 384 | }, 385 | { 386 | "internalName": "_stop", 387 | "displayName": "Stop time", 388 | "visible": false 389 | }, 390 | { 391 | "internalName": "_time", 392 | "displayName": "Time", 393 | "visible": true 394 | }, 395 | { 396 | "internalName": "_value", 397 | "displayName": "Value", 398 | "visible": true 399 | }, 400 | { 401 | "internalName": "_measurement", 402 | "displayName": "Measurement name", 403 | "visible": true 404 | }, 405 | { 406 | "internalName": "_field", 407 | "displayName": "Field", 408 | "visible": true 409 | } 410 | ], 411 | "timeFormat": "YYYY/MM/DD HH:mm:ss", 412 | "decimalPlaces": { 413 | "isEnforced": false, 414 | "digits": 2 415 | }, 416 | "note": "", 417 | "showNoteWhenEmpty": false 418 | } 419 | } 420 | }, 421 | { 422 | "type": "view", 423 | "id": "0a27ee951bdb5000", 424 | "attributes": { 425 | "name": "Total Power - Mean", 426 | "properties": { 427 | "shape": "chronograf-v2", 428 | "queries": [ 429 | { 430 | "text": "from(bucket: \"edgedb\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"GEN_KPI_TotalPower\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", 431 | "editMode": "advanced", 432 | "name": "", 433 | "builderConfig": { 434 | "buckets": [], 435 | "tags": [ 436 | { 437 | "key": "_measurement", 438 | "values": [], 439 | "aggregateFunctionType": "filter" 440 | } 441 | ], 442 | "functions": [ 443 | { 444 | "name": "mean" 445 | } 446 | ], 447 | "aggregateWindow": { 448 | "period": "auto", 449 | "fillValues": false 450 | } 451 | } 452 | } 453 | ], 454 | "axes": { 455 | "x": { 456 | "bounds": [ 457 | "", 458 | "" 459 | ], 460 | "label": "", 461 | "prefix": "", 462 | "suffix": "", 463 | "base": "10", 464 | "scale": "linear" 465 | }, 466 | "y": { 467 | "bounds": [ 468 | "0", 469 | "2000" 470 | ], 471 | "label": "Power", 472 | "prefix": "", 473 | "suffix": "", 474 | "base": "10", 475 | "scale": "linear" 476 | } 477 | }, 478 | "type": "line-plus-single-stat", 479 | "staticLegend": { 480 | "colorizeRows": true, 481 | "opacity": 1, 482 | "orientationThreshold": -1, 483 | "widthRatio": 1 484 | }, 485 | "colors": [ 486 | { 487 | "id": "base", 488 | "type": "text", 489 | "hex": "#32B08C", 490 | "name": "viridian", 491 | "value": 0 492 | }, 493 | { 494 | "id": "8002dfd3-b78a-4a45-9d86-c0953ab11a36", 495 | "type": "scale", 496 | "hex": "#DA6FF1", 497 | "name": "Ectoplasm", 498 | "value": 0 499 | }, 500 | { 501 | "id": "36cb054f-8804-44f9-ae47-cdb4ec14273f", 502 | "type": "scale", 503 | "hex": "#00717A", 504 | "name": "Ectoplasm", 505 | "value": 0 506 | }, 507 | { 508 | "id": "4e5cc096-6648-4256-b4bc-c77fcd7897de", 509 | "type": "scale", 510 | "hex": "#ACFF76", 511 | "name": "Ectoplasm", 512 | "value": 0 513 | } 514 | ], 515 | "prefix": "", 516 | "suffix": "W", 517 | "decimalPlaces": { 518 | "isEnforced": true, 519 | "digits": 0 520 | }, 521 | "note": "", 522 | "showNoteWhenEmpty": false, 523 | "xColumn": "_time", 524 | "generateXAxisTicks": [], 525 | "xTotalTicks": 0, 526 | "xTickStart": 0, 527 | "xTickStep": 0, 528 | "yColumn": "_value", 529 | "generateYAxisTicks": [], 530 | "yTotalTicks": 0, 531 | "yTickStart": 0, 532 | "yTickStep": 0, 533 | "shadeBelow": true, 534 | "position": "overlaid", 535 | "timeFormat": "HH:mm:ss", 536 | "hoverDimension": "auto", 537 | "legendColorizeRows": true, 538 | "legendHide": false, 539 | "legendOpacity": 1, 540 | "legendOrientationThreshold": -1 541 | } 542 | } 543 | } 544 | ] 545 | }, 546 | "labels": [] 547 | } -------------------------------------------------------------------------------- /src/solution/HandsOn_1/IE Flow Creator Flows/flow_data_gen.json: -------------------------------------------------------------------------------- 1 | [{"id":"803455e.14d3d28","type":"tab","label":"Data Collection","disabled":false,"info":""},{"id":"56ac0cbb.978d6c","type":"tab","label":"KPI-Calc-Dummy","disabled":false,"info":""},{"id":"f7e739bb.370638","type":"tab","label":"KPI Estimation","disabled":false,"info":""},{"id":"b6ec4c8d.61eac","type":"mqtt-broker","name":"","broker":"mqtt-broker","port":"1883","clientid":"nodered-client","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"4ad3ce8d.582c5","type":"influxdb","hostname":"influxdb","port":"8086","protocol":"http","database":"edgedb","name":"","usetls":false,"tls":"","influxdbVersion":"2.0","url":"http://influxdb:8086","rejectUnauthorized":false},{"id":"99d8c9c3d94c327e","type":"OpcUa-Endpoint","endpoint":"opc.tcp://opc-server:4841","secpol":"Basic256","secmode":"SIGN","login":false},{"id":"a67248fc.148ff8","type":"comment","z":"803455e.14d3d28","name":"### Generate dummy data and storage in influxdb ###","info":"","x":300,"y":80,"wires":[]},{"id":"9e8378b0.74b62","type":"inject","z":"803455e.14d3d28","name":"n-samples","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":true,"onceDelay":0.1,"topic":"samples_number_n","payload":"20","payloadType":"num","x":230,"y":320,"wires":[["925a5c7.deaec2","6dc5f6c6.740bc8","43bc8a92.aad59c","1101d4e.caa782b"]]},{"id":"1101d4e.caa782b","type":"function","z":"803455e.14d3d28","name":"Write_KPI_PowerDrive1_Influxdb","func":"if (msg !== null) {\n mean = msg.payload;\n Beta = [{\n\n measurement: \"powerdrive1\",\n fields: {\n name: \"powerdrive1\",\n value: (Math.random() * (999.0 - 750.0 + 1) + 750.0),\n qualitycode: false\n //weitereTags: -100 //(optional) nur 4 Datatypes sind in der Influxdb akzeptiert: Int,Float,String, Bool\n },\n timestamp: new Date()\n }]\n // Arr.push(Beta);\n //}\n\n msg.payload = Beta;\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":240,"wires":[["bdcb1de8.cd129","6a295d91.59ab5c","edc207be.ce696"]]},{"id":"925a5c7.deaec2","type":"function","z":"803455e.14d3d28","name":"Write_KPI_PowerDrive2_Influxdb","func":"if (msg !== null) {\n mean = msg.payload;\n Beta = [{\n //values between 800 and 999 Watt\n measurement: \"powerdrive2\",\n fields: {\n name: \"powerdrive2\",\n value: (Math.random() * (999.0 - 800.0 + 1) + 800.0),\n qualitycode: false\n //weitereTags: -100 //(optional) nur 4 Datatypes sind in der Influxdb akzeptiert: Int,Float,String, Bool\n },\n timestamp: new Date()\n }]\n // Arr.push(Beta);\n //}\n\n msg.payload = Beta;\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":300,"wires":[["ef28ccb6.75c6e","6a295d91.59ab5c","edc207be.ce696"]]},{"id":"6dc5f6c6.740bc8","type":"function","z":"803455e.14d3d28","name":"Write_KPI_VoltageDrive3_Influxdb","func":"if (msg !== null) {\n mean = msg.payload;\n Beta = [{\n //values between 48 and 50 Volt\n measurement: \"voltagedrive3\",\n fields: {\n name: \"voltagedrive3\",\n value: (Math.random() * (50.0 - 48.0) + 48.0),\n qualitycode: false\n\n },\n ttimestamp: new Date()\n }]\n // Arr.push(Beta);\n //}\n\n msg.payload = Beta;\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":360,"wires":[["6a295d91.59ab5c","1fe4faea.f61d45","edc207be.ce696"]]},{"id":"43bc8a92.aad59c","type":"function","z":"803455e.14d3d28","name":"Write_KPI_CurrentDrive3_Influxdb","func":"if (msg !== null) {\n mean = msg.payload;\n Beta = [{\n //values between 18 and 20 Ampere\n measurement: \"currentdrive3\",\n fields: {\n name: \"currentdrive3\",\n value: (Math.random() * (20.0 - 18.0) + 18.0),\n qualitycode: false\n\n },\n timestamp: new Date()\n }]\n // Arr.push(Beta);\n //}\n\n msg.payload = Beta;\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":420,"wires":[["6a295d91.59ab5c","14aad25e.88d496","edc207be.ce696"]]},{"id":"6a295d91.59ab5c","type":"influxdb batch","z":"803455e.14d3d28","influxdb":"4ad3ce8d.582c5","precision":"","retentionPolicy":"","name":"write_data_influxdb","database":"","retentionPolicyV18Flux":"","org":"siemens","bucket":"edgedb","x":970,"y":320,"wires":[]},{"id":"edc207be.ce696","type":"link out","z":"803455e.14d3d28","name":"data_gen","links":["84d99c19.67892","554edde8.db9efc"],"x":925,"y":480,"wires":[]},{"id":"bdcb1de8.cd129","type":"debug","z":"803455e.14d3d28","name":"KPI-Result_PowerDrive1","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":990,"y":240,"wires":[]},{"id":"ef28ccb6.75c6e","type":"debug","z":"803455e.14d3d28","name":"KPI-Result_PowerDrive2","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":990,"y":280,"wires":[]},{"id":"1fe4faea.f61d45","type":"debug","z":"803455e.14d3d28","name":"KPI-Result_VoltageDrive3","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":990,"y":380,"wires":[]},{"id":"14aad25e.88d496","type":"debug","z":"803455e.14d3d28","name":"KPI-Result_CurrentDrive3","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":990,"y":420,"wires":[]},{"id":"1f383f974e86d387","type":"function","z":"803455e.14d3d28","d":true,"name":"store data","func":"\nmsgoutput={}\n//msgoutput.measurement = msg.topic.split(';')[1].slice(2);\n/*msgoutput.payload = {\n serverTimestamp: msg.serverTimestamp.toISOString(),\n value: Math.round(msg.payload * 1e2)/ 1e2,\n name: msgoutput.measurement,\n}*/\n\nBeta = [{\n measurement: msg.topic.split(';')[1].slice(2),\n fields:{\n name: msg.topic.split(';')[1].slice(2),\n value: Math.round(msg.payload * 1e2)/ 1e2,\n qualitycode: false\n },\n timestamp: new Date()\n}]\n\nmsgoutput.payload = Beta;\n\nreturn msgoutput;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":760,"wires":[["c41865e7d1104ade","52089d7cd8e56e3f","0c7cbde017b44877"]]},{"id":"b04a16e38b71dffd","type":"debug","z":"803455e.14d3d28","d":true,"name":"opc output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":770,"y":700,"wires":[]},{"id":"c41865e7d1104ade","type":"debug","z":"803455e.14d3d28","d":true,"name":"output to influx","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1010,"y":700,"wires":[]},{"id":"cbc4bb227efe36c6","type":"inject","z":"803455e.14d3d28","d":true,"name":"powerdrive2-Temp","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"ns=1;s=temperatur2;datatype=Float","payload":"1000","payloadType":"num","x":230,"y":840,"wires":[["63347d527e8207ff"]]},{"id":"8e359fccc4764064","type":"inject","z":"803455e.14d3d28","d":true,"name":"powerdrive1","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"ns=1;s=powerdrive1;datatype=Float","payload":"2000","payloadType":"num","x":250,"y":800,"wires":[["63347d527e8207ff"]]},{"id":"cc3560626b80a10b","type":"inject","z":"803455e.14d3d28","d":true,"name":"powerdrive2","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"ns=1;s=powerdrive2;datatype=Float","payload":"2000","payloadType":"num","x":250,"y":760,"wires":[["63347d527e8207ff"]]},{"id":"7ce4d28a7ad4d351","type":"inject","z":"803455e.14d3d28","d":true,"name":"currentdrive1","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"ns=1;s=currentdrive1;datatype=Float","payload":"1000","payloadType":"num","x":240,"y":720,"wires":[["63347d527e8207ff"]]},{"id":"959aea5922760439","type":"inject","z":"803455e.14d3d28","d":true,"name":"voltagedrive1","props":[{"p":"payload","v":"1000","vt":"num"},{"p":"topic","v":"ns=1;s=voltagedrive1;datatype=Double","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"ns=1;s=voltagedrive1;datatype=Double","payload":"1000","payloadType":"num","x":240,"y":680,"wires":[["63347d527e8207ff"]]},{"id":"63347d527e8207ff","type":"OpcUa-Client","z":"803455e.14d3d28","d":true,"endpoint":"99d8c9c3d94c327e","action":"subscribe","deadbandtype":"a","deadbandvalue":1,"time":"1","timeUnit":"s","certificate":"n","localfile":"","localkeyfile":"","name":"","x":542,"y":739,"wires":[["1f383f974e86d387","b04a16e38b71dffd"]]},{"id":"52089d7cd8e56e3f","type":"influxdb batch","z":"803455e.14d3d28","d":true,"influxdb":"4ad3ce8d.582c5","precision":"","retentionPolicy":"","name":"write_data_influxdb","database":"","retentionPolicyV18Flux":"","org":"siemens","bucket":"edgedb","x":1010,"y":780,"wires":[]},{"id":"f4c9ffa958ec9c1a","type":"comment","z":"803455e.14d3d28","name":"### Receive simulated data from a OPC UA Server Simulator and storage in influxdb ###","info":"","x":450,"y":620,"wires":[]},{"id":"0c7cbde017b44877","type":"link out","z":"803455e.14d3d28","d":true,"name":"data_gen","links":["84d99c19.67892","554edde8.db9efc"],"x":945,"y":840,"wires":[]},{"id":"4462f599.69bb0c","type":"comment","z":"56ac0cbb.978d6c","name":"Calculate Total Power Consumption","info":"","x":800,"y":60,"wires":[]},{"id":"33ca4fcc.54615","type":"function","z":"56ac0cbb.978d6c","name":"Query-List","func":"var a ='\"name\"'\n//msg.query=\"select * from powerdrive1 where \"+a+\"='powerdrive1'\"\nmsg.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive1\")\n |> last()`;\nreturn msg; \n/*\nCLI-Funktionen f?r die Influxdb (Auszug)\n\"select * from data1\"\n//Zeigt alle Daten im measuurement an\nSELECT * FROM data where time > '2018-08-09T08:20:39.96Z' and time <= now()\n//Anzeige der measuremebts in einem bestimmten Zeitraum\nselect * from data where value > 80 and value < 85 \n//Filterung der Daten nach Value \n*/","outputs":1,"noerr":0,"initialize":"","finalize":"","x":270,"y":160,"wires":[["2f2206bb.f13332"]]},{"id":"fba4a9a5.e82c18","type":"function","z":"56ac0cbb.978d6c","name":"Query-List","func":"var a ='\"name\"'\n//msg.query=\"select * from powerdrive2 where \"+a+\"='powerdrive2'\"\nmsg.query= `from(bucket: \"edgedb\")\n |> range(start: -5m)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive2\")\n |> last()`;\nreturn msg; \n\n\n/*\nCLI-Funktionen f?r die Influxdb (Auszug)\n\"select * from data1\"\n//Zeigt alle Daten im measuurement an\nSELECT * FROM data where time > '2018-08-09T08:20:39.96Z' and time <= now()\n//Anzeige der measuremebts in einem bestimmten Zeitraum\nselect * from data where value > 80 and value < 85 \n//Filterung der Daten nach Value \n*/\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":270,"y":220,"wires":[["ad1edda4.a7224"]]},{"id":"ad1edda4.a7224","type":"influxdb in","z":"56ac0cbb.978d6c","influxdb":"4ad3ce8d.582c5","name":"influxdb_query_power2","query":"","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":490,"y":220,"wires":[["47df6e05.45bc98","16716b3a.ae9815"]]},{"id":"47df6e05.45bc98","type":"function","z":"56ac0cbb.978d6c","name":"collect_last_power_values","func":"context.data = context.data || {};\n\nswitch (msg.payload[0]._measurement) {\n case \"powerdrive1\":\n context.data.power1 = msg.payload[2]._value;\n msg = null;\n break;\n case \"powerdrive2\":\n context.data.power2 = msg.payload[2]._value;\n msg = null;\n break;\n default:\n msg = null;\n break;\n}\n\nif (context.data.power1 != null && context.data.power2 != null) {\n msg2 = {};\n msg2 = context.data;\n\n context.data = null;\n return msg2;\n} else {\n return msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":770,"y":220,"wires":[["2fd23fd2.bd1b5","dc69781c.6bd958"]]},{"id":"e874b51b.e7df48","type":"function","z":"56ac0cbb.978d6c","name":"join_and_write_power_Influxdb","func":"total = msg.payload;\n\nBeta = [{\n measurement: \"GEN_KPI_TotalPower\",\n fields: {\n name: \"TotalPower\",\n value: total,\n //weitereTags: -100 //(optional) nur 4 Datatypes sind in der Influxdb akzeptiert: Int,Float,String, Bool\n },\n timestamp: new Date()\n}]\n// Arr.push(Beta);\n//}\n\nmsg.payload = Beta;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1270,"y":220,"wires":[["4d3e876.90160f8","9b10f05a.d8ee"]]},{"id":"9b10f05a.d8ee","type":"influxdb batch","z":"56ac0cbb.978d6c","influxdb":"4ad3ce8d.582c5","precision":"","retentionPolicy":"","name":"influxdb_write","database":"","retentionPolicyV18Flux":"","org":"siemens","bucket":"edgedb","x":1520,"y":180,"wires":[]},{"id":"2f2206bb.f13332","type":"influxdb in","z":"56ac0cbb.978d6c","influxdb":"4ad3ce8d.582c5","name":"influxdb_query_power1","query":"","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":490,"y":160,"wires":[["47df6e05.45bc98","16716b3a.ae9815"]]},{"id":"554edde8.db9efc","type":"link in","z":"56ac0cbb.978d6c","name":"","links":["3e5dc939.97a5b6","edc207be.ce696","0c7cbde017b44877"],"x":135,"y":220,"wires":[["33ca4fcc.54615","fba4a9a5.e82c18"]]},{"id":"2fd23fd2.bd1b5","type":"function","z":"56ac0cbb.978d6c","name":"sum_total_power","func":"var arr = [];\nvar mean = 0;\nvar sum = 0;\n\narr = Object.keys(msg).map(function(key) {\n return msg[key]\n})\narr.splice(-1, 1)\n\nfor (var i = 0; i < arr.length; i++) {\n sum = sum + arr[i];\n}\n// mean = sum / arr.length; \n\nmsg2 = {};\n//msg2.payload = (arr[0] + arr[1] + arr[2]) / arr.length; \n\nmsg2.payload = sum;\nreturn msg2;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1010,"y":220,"wires":[["e874b51b.e7df48"]]},{"id":"4d3e876.90160f8","type":"debug","z":"56ac0cbb.978d6c","name":"KPI-Power","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1510,"y":260,"wires":[]},{"id":"dc69781c.6bd958","type":"debug","z":"56ac0cbb.978d6c","name":"Last Power Values","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1010,"y":160,"wires":[]},{"id":"6eac8115.8634e","type":"debug","z":"56ac0cbb.978d6c","name":"KPI-Power","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1030,"y":300,"wires":[]},{"id":"16716b3a.ae9815","type":"function","z":"56ac0cbb.978d6c","name":"collect_last_power_values","func":"context.data = context.data || {};\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":380,"wires":[["6eac8115.8634e"]]},{"id":"13ee085f.be8d98","type":"influxdb in","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"query","query":"","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":850,"y":160,"wires":[["5e1cdcc5.cf69a4"]]},{"id":"69401cb2.ee9d44","type":"debug","z":"f7e739bb.370638","name":"count","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":610,"y":120,"wires":[]},{"id":"84d99c19.67892","type":"link in","z":"f7e739bb.370638","name":"input_raw_data","links":["edc207be.ce696","0c7cbde017b44877"],"x":55,"y":160,"wires":[["b64620ac.81fd3"]]},{"id":"c231c8cd.975aa","type":"influxdb in","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"query","query":"","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":450,"y":160,"wires":[["69401cb2.ee9d44","9d60b753.7fcfc"]]},{"id":"3a59b4bb.74773c","type":"function","z":"f7e739bb.370638","name":"COUNT","func":"msgQueryCount = {}\nmsgQueryCount.measurement = msg.payload[0].measurement\n//msgQueryCount.query = 'SELECT COUNT(\"value\") FROM ' + msg.payload[0].measurement\n\nmsgQueryCount.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive1\" or r[\"_measurement\"] == \"powerdrive2\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> count()`;\nreturn msgQueryCount;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":300,"y":160,"wires":[["c231c8cd.975aa"]]},{"id":"9d60b753.7fcfc","type":"function","z":"f7e739bb.370638","name":"GET_LAST_ENTRIES","func":"msgQueryGet = {}\nmsgQueryGet.measurement = msg.measurement\nif (msg.payload[0]._value >= 50) {\n //msgQueryGet.query = 'SELECT * FROM ' + msg.measurement + ' ORDER BY time DESC limit 50'\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"${msg.measurement}\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)\n |> group()`;\n return msgQueryGet;\n}\n\nelse {msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"${msg.measurement}\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> sort(columns: [\"_time\"], desc: true)\n |> group()`;\n return msgQueryGet;\n \n}\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":160,"wires":[["13ee085f.be8d98"]]},{"id":"c561e0d7.eb0998","type":"comment","z":"f7e739bb.370638","name":"### check if measurement has enough entries and get last 5 values if true","info":"","x":320,"y":20,"wires":[]},{"id":"7000bdea.5b3c8c","type":"mqtt out","z":"f7e739bb.370638","name":"StandardKpis","topic":"StandardKpis","qos":"","retain":"","broker":"b6ec4c8d.61eac","x":1220,"y":160,"wires":[]},{"id":"5e1cdcc5.cf69a4","type":"json","z":"f7e739bb.370638","name":"","property":"payload","action":"obj","pretty":false,"x":990,"y":160,"wires":[["408593f2.7ce534","7000bdea.5b3c8c"]]},{"id":"408593f2.7ce534","type":"debug","z":"f7e739bb.370638","name":"response_get","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1220,"y":220,"wires":[]},{"id":"51e1820.ff6d8fc","type":"comment","z":"f7e739bb.370638","name":"PowerDrive1 and PowerDrive2 last 50 entries","info":"","x":210,"y":80,"wires":[]},{"id":"cc9de0f5.cdc4e","type":"influxdb in","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"query","query":"","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":830,"y":400,"wires":[["67b35a2b.c82f54"]]},{"id":"b55ad1ad.1d31e8","type":"debug","z":"f7e739bb.370638","name":"mqtt output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1340,"y":460,"wires":[]},{"id":"6e9cf99d.f4721","type":"influxdb in","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"query","query":"from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"voltagedrive3\" and (r[\"_field\"] == \"value\"))\n |> count()","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":370,"y":400,"wires":[["e0680050.bd51a","d40d07c0.067dc8"]]},{"id":"e0680050.bd51a","type":"function","z":"f7e739bb.370638","name":"GET_LAST_ENTRIES","func":"// gets the value of n, if not yet set takes default value 50\nvar n = context.get('sample_n')|| 50;\n\n\n \n msgQueryGet = {}\n //msgQueryGet.measurement = msg.measurement\n msgQueryGet.measurement = 'voltagedrive3'\n if (msg.payload[0]._value >= n) {\n msgQueryGet.SampleNumber = n\n //msgQueryGet.query = 'SELECT \"value\" FROM voltagedrive3 ORDER BY time DESC limit ' + n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"voltagedrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)`;\n //msgQueryGet.query = 'SELECT \"value\" FROM ' + msg.measurement + ' limit ' + n\n return msgQueryGet;\n }\n else {\n msgQueryGet.SampleNumber = n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"voltagedrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)`;\n return msgQueryGet;\n }","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":600,"y":400,"wires":[["cc9de0f5.cdc4e","67cd84cb.b3012c"]]},{"id":"206e8e74.bad04a","type":"mqtt out","z":"f7e739bb.370638","name":"Mean","topic":"Mean","qos":"","retain":"","broker":"b6ec4c8d.61eac","x":1320,"y":500,"wires":[]},{"id":"587d20bd.56fc1","type":"comment","z":"f7e739bb.370638","name":"VoltageDrive3 dynamic sample of entries","info":"","x":490,"y":360,"wires":[]},{"id":"d73768e8.892c4","type":"influxdb in","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"query","query":"","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":810,"y":600,"wires":[["67b35a2b.c82f54"]]},{"id":"30279058.935348","type":"influxdb in","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"query","query":"from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"currentdrive3\" and (r[\"_field\"] == \"value\"))\n |> count()","rawOutput":false,"precision":"","retentionPolicy":"","org":"siemens","x":370,"y":600,"wires":[["e483dac2.b82e5"]]},{"id":"e483dac2.b82e5","type":"function","z":"f7e739bb.370638","name":"GET_LAST_ENTRIES","func":"\n\n// gets the value of n, if not yet set takes default value 50\nvar n = context.get('sample_n')|| 50;\n\n\n \n msgQueryGet = {}\n //msgQueryGet.measurement = msg.measurement\n msgQueryGet.measurement = 'currentdrive3'\n if (msg.payload[0]._value >= n) {\n msgQueryGet.SampleNumber = n\n //msgQueryGet.query = 'SELECT \"value\" FROM currentdrive3 ORDER BY time DESC limit ' + n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"currentdrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)`;\n //msgQueryGet.query = 'SELECT \"value\" FROM ' + msg.measurement + ' limit ' + n\n return msgQueryGet;\n }\n else {\n msgQueryGet.SampleNumber = n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"currentdrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)`;\n return msgQueryGet;\n }","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":600,"y":600,"wires":[["d73768e8.892c4"]]},{"id":"fe2bcb9b.e56fb8","type":"comment","z":"f7e739bb.370638","name":"CurrentDrive3 dynamic sample of entries","info":"","x":490,"y":640,"wires":[]},{"id":"b64620ac.81fd3","type":"switch","z":"f7e739bb.370638","name":"filter","property":"payload[0].measurement","propertyType":"msg","rules":[{"t":"eq","v":"powerdrive1","vt":"str"},{"t":"eq","v":"powerdrive2","vt":"str"},{"t":"eq","v":"voltagedrive3","vt":"str"},{"t":"eq","v":"currentdrive3","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":150,"y":160,"wires":[["3a59b4bb.74773c"],["3a59b4bb.74773c"],["6e9cf99d.f4721","4d240511.3cb51c"],["30279058.935348"]]},{"id":"67b35a2b.c82f54","type":"function","z":"f7e739bb.370638","name":"manual_join","func":"var tempo_cd3_batch = context.get('cd3_batch')||[];\nvar tempo_vd3_batch = context.get('vd3_batch')||[];\n\nfunction build_analytics_object(){\n data_obj = {\n sample_number : msg.SampleNumber,\n current_drive3_batch : tempo_cd3_batch,\n voltage_drive3_batch : tempo_vd3_batch,\n }\n return data_obj\n}\n\n\nif (msg.measurement == 'currentdrive3'){\n tempo_cd3_batch = msg.payload\n context.set('cd3_batch',tempo_cd3_batch)\n \n}\nelse if (msg.measurement == 'voltagedrive3') {\n tempo_vd3_batch = msg.payload\n context.set('vd3_batch',tempo_vd3_batch)\n}\n\nif (tempo_cd3_batch.length > 0 && tempo_vd3_batch.length >0 ) {\n py_obj={}\n py_obj.payload = build_analytics_object()\n tempo_cd3_batch = []\n tempo_vd3_batch = []\n context.set('cd3_batch', tempo_cd3_batch)\n context.set('vd3_batch', tempo_vd3_batch)\n \n return py_obj\n \n}\n\nelse{\n return\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1100,"y":500,"wires":[["b55ad1ad.1d31e8","206e8e74.bad04a"]]},{"id":"49c977da.a0dce","type":"mqtt in","z":"f7e739bb.370638","name":"","topic":"StandardKpiResult","qos":"2","datatype":"auto","broker":"b6ec4c8d.61eac","inputs":0,"x":130,"y":820,"wires":[["b119df7b.43a088"]]},{"id":"f85bea86.d12e9","type":"debug","z":"f7e739bb.370638","name":"response standard kpis","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1230,"y":780,"wires":[]},{"id":"b119df7b.43a088","type":"json","z":"f7e739bb.370638","name":"","property":"payload","action":"","pretty":false,"x":510,"y":820,"wires":[["fbd3d4ca.35021"]]},{"id":"fbd3d4ca.35021","type":"function","z":"f7e739bb.370638","name":"store data","func":"my_payload = {};\nmy_payload = msg.payload;\noutput_standardkpis = {}\noutput_standardkpis.measurement = my_payload.name.toUpperCase() + '_STANDARD_KPIS'\noutput_standardkpis.payload = {\n mean: Math.round(my_payload.mean_result * 1e2)/ 1e2,\n median: Math.round(my_payload.median_result * 1e2)/ 1e2,\n stddev: Math.round(my_payload.stddev_result * 1e2)/ 1e2,\n name: my_payload.name,\n}\n\nreturn output_standardkpis;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":870,"y":820,"wires":[["8736d8c4.6a8dd8","f85bea86.d12e9"]]},{"id":"8736d8c4.6a8dd8","type":"influxdb out","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"write_influxdb","measurement":"","precision":"","retentionPolicy":"","database":"","retentionPolicyV18Flux":"","org":"siemens","bucket":"edgedb","x":1200,"y":820,"wires":[]},{"id":"e09b497.2d66eb8","type":"mqtt in","z":"f7e739bb.370638","name":"","topic":"MeanResult","qos":"2","datatype":"auto","broker":"b6ec4c8d.61eac","inputs":0,"x":110,"y":960,"wires":[["9fe01ff4.3223f"]]},{"id":"e8bf4594.e0d068","type":"debug","z":"f7e739bb.370638","name":"response mean","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1210,"y":920,"wires":[]},{"id":"9fe01ff4.3223f","type":"json","z":"f7e739bb.370638","name":"","property":"payload","action":"","pretty":false,"x":510,"y":960,"wires":[["e9d5781f.cd3a1"]]},{"id":"e9d5781f.cd3a1","type":"function","z":"f7e739bb.370638","name":"store data","func":"my_payload = {};\nmy_payload = msg.payload;\noutput_pd1_mean = {}\noutput_pd1_mean.measurement = my_payload.name.toUpperCase()\noutput_pd1_mean.payload = {\n value: Math.round(my_payload.power_mean_result * 1e2)/ 1e2,\n name: my_payload.name,\n}\n\nreturn output_pd1_mean;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":870,"y":960,"wires":[["36636dbd.e80eda","e8bf4594.e0d068"]]},{"id":"36636dbd.e80eda","type":"influxdb out","z":"f7e739bb.370638","influxdb":"4ad3ce8d.582c5","name":"write_influxdb","measurement":"","precision":"","retentionPolicy":"","database":"","retentionPolicyV18Flux":"","org":"siemens","bucket":"edgedb","x":1200,"y":960,"wires":[]},{"id":"bb22a94d.81ee48","type":"comment","z":"f7e739bb.370638","name":"Data Analytics Results","info":"","x":540,"y":740,"wires":[]},{"id":"4d240511.3cb51c","type":"debug","z":"f7e739bb.370638","name":"mqtt output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":390,"y":280,"wires":[]},{"id":"d40d07c0.067dc8","type":"debug","z":"f7e739bb.370638","name":"mqtt output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":610,"y":300,"wires":[]},{"id":"67cd84cb.b3012c","type":"debug","z":"f7e739bb.370638","name":"mqtt output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":950,"y":300,"wires":[]}] -------------------------------------------------------------------------------- /src/solution/HandsOn_2/IE Flow Creator Flows/flow_data_S7_Connector.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "9f80fd1f.3bd908", 4 | "type": "tab", 5 | "label": "Data Collection", 6 | "disabled": false, 7 | "info": "msgoutput={}\nmsgoutput.measurement = msg.topic.split('/').pop;\nmsgoutput.payload = {\n serverTimestamp: msg.payload.ts.toISOString(),\n value: Math.round(msg.payload.val * 1e2)/ 1e2,\n name: msgoutput.measurement,\n}\n\nreturn msgoutput;" 8 | }, 9 | { 10 | "id": "7474ceb842992a1a", 11 | "type": "tab", 12 | "label": "KPI-Power-Calc", 13 | "disabled": false, 14 | "info": "" 15 | }, 16 | { 17 | "id": "1ff02d49d32ca74f", 18 | "type": "tab", 19 | "label": "KPI Estimation", 20 | "disabled": false, 21 | "info": "" 22 | }, 23 | { 24 | "id": "4ad3ce8d.582c5", 25 | "type": "influxdb", 26 | "hostname": "influxdb", 27 | "port": "8086", 28 | "protocol": "http", 29 | "database": "edgedb", 30 | "name": "", 31 | "usetls": false, 32 | "tls": "", 33 | "influxdbVersion": "2.0", 34 | "url": "http://influxdb:8086", 35 | "rejectUnauthorized": false 36 | }, 37 | { 38 | "id": "1ab7e97a.bda1e7", 39 | "type": "mqtt-broker", 40 | "name": "Databus", 41 | "broker": "ie-databus", 42 | "port": "1883", 43 | "clientid": "", 44 | "autoConnect": true, 45 | "usetls": false, 46 | "compatmode": false, 47 | "protocolVersion": "4", 48 | "keepalive": "60", 49 | "cleansession": true, 50 | "birthTopic": "", 51 | "birthQos": "0", 52 | "birthPayload": "", 53 | "birthMsg": {}, 54 | "closeTopic": "", 55 | "closeQos": "0", 56 | "closePayload": "", 57 | "closeMsg": {}, 58 | "willTopic": "", 59 | "willQos": "0", 60 | "willPayload": "", 61 | "willMsg": {}, 62 | "userProps": "", 63 | "sessionExpiry": "" 64 | }, 65 | { 66 | "id": "1b70cc5299eb3297", 67 | "type": "OpcUa-Endpoint", 68 | "endpoint": "opc.tcp://192.168.239.200:4840", 69 | "secpol": "None", 70 | "secmode": "SIGN", 71 | "none": false, 72 | "login": false, 73 | "usercert": false, 74 | "usercertificate": "", 75 | "userprivatekey": "" 76 | }, 77 | { 78 | "id": "38a14f21.7ff638", 79 | "type": "link out", 80 | "z": "9f80fd1f.3bd908", 81 | "name": "data_gen", 82 | "links": [ 83 | "684dd35d.22d42c", 84 | "70b8ec30.c1ee84", 85 | "be3b1e8c001db67a", 86 | "6464c1e5620091ab", 87 | "ecef8cf0f9a01c4b" 88 | ], 89 | "x": 1105, 90 | "y": 460, 91 | "wires": [] 92 | }, 93 | { 94 | "id": "20da19e9.da8926", 95 | "type": "mqtt in", 96 | "z": "9f80fd1f.3bd908", 97 | "name": "FA_Drives-Data source", 98 | "topic": "ie/d/j/simatic/v1/opcuac1/dp/r/FA_Drives/default", 99 | "qos": "2", 100 | "datatype": "auto", 101 | "broker": "1ab7e97a.bda1e7", 102 | "nl": false, 103 | "rap": false, 104 | "inputs": 0, 105 | "x": 220, 106 | "y": 280, 107 | "wires": [ 108 | [ 109 | "971f6ffe.7241f" 110 | ] 111 | ] 112 | }, 113 | { 114 | "id": "971f6ffe.7241f", 115 | "type": "function", 116 | "z": "9f80fd1f.3bd908", 117 | "name": "Convert S7 Connecor JSON to InfluxDB JSON", 118 | "func": "var id2NameMap = global.get(\"id2NameMap\")\nvar id2DataTypeMap = global.get(\"id2DataTypeMap\")\n\nlet msgoutput={}\n\nlet bulk = JSON.parse(msg.payload);\n//node.warn(bulk)\nmsgoutput.payload = new Array(bulk.vals.size);\n\nfor (var i = 0; i < bulk.vals.length; i++) {\n let record = bulk.vals[i]\n\n msgoutput.payload[i] = {\n measurement: id2NameMap.get(record.id),\n fields: {\n value: Math.round(record.val * 1e2)/ 1e2,\n name: id2NameMap.get(record.id),\n qualitycode: false\n },\n timestamp: new Date(record.ts)\n }\n \n}\n\nreturn msgoutput;", 119 | "outputs": 1, 120 | "noerr": 0, 121 | "initialize": "", 122 | "finalize": "", 123 | "libs": [], 124 | "x": 560, 125 | "y": 280, 126 | "wires": [ 127 | [ 128 | "d20bf630.ac7c98", 129 | "50556ca2.120a8c", 130 | "6d21e9a.2814a98" 131 | ] 132 | ] 133 | }, 134 | { 135 | "id": "93f44c93.8b63e", 136 | "type": "comment", 137 | "z": "9f80fd1f.3bd908", 138 | "name": "### Map variable id to variable name so that it is human readable ###", 139 | "info": "", 140 | "x": 370, 141 | "y": 40, 142 | "wires": [] 143 | }, 144 | { 145 | "id": "fdc0f327.a560a", 146 | "type": "mqtt in", 147 | "z": "9f80fd1f.3bd908", 148 | "name": "", 149 | "topic": "ie/m/j/simatic/v1/opcuac1/dp", 150 | "qos": "2", 151 | "datatype": "auto", 152 | "broker": "1ab7e97a.bda1e7", 153 | "nl": false, 154 | "rap": false, 155 | "inputs": 0, 156 | "x": 280, 157 | "y": 100, 158 | "wires": [ 159 | [ 160 | "b18c8995.ce3c7", 161 | "43428e95.c95d58" 162 | ] 163 | ] 164 | }, 165 | { 166 | "id": "b18c8995.ce3c7", 167 | "type": "function", 168 | "z": "9f80fd1f.3bd908", 169 | "name": "", 170 | "func": "var id2NameMap = global.get(\"id2NameMap\")\nvar id2DataTypeMap = global.get(\"id2DataTypeMap\")\n\nif(id2NameMap == undefined || id2DataTypeMap == undefined ){\n id2NameMap = new Map(); \n id2DataTypeMap = new Map(); \n}\n\nlet connectionName = \"FA_Drives\"; // Name of the connection\n\n\nlet m = JSON.parse(msg.payload);\n\n\n// Mapping ID -> Variable Name\n// Mapping ID -> Variable Data Type\nm.connections.forEach(connection => \n{\n if(connection.name == connectionName) {\n let datapointobjects = connection.dataPoints;\n \n datapointobjects.forEach( datapoint => {\n datapoint.dataPointDefinitions.forEach( definition => {\n id2NameMap.set(definition.id, definition.name) // Mapping ID -> Variable Name\n id2DataTypeMap.set(definition.id, definition.dataType) // Mapping ID -> Variable Data Type \n })\n \n })\n }\n \n})\n\nglobal.set(\"id2NameMap\", id2NameMap)\nglobal.set(\"id2DataTypeMap\", id2DataTypeMap)\n\nmsg.payload = id2NameMap\n\nreturn msg;", 171 | "outputs": 1, 172 | "noerr": 0, 173 | "initialize": "", 174 | "finalize": "", 175 | "libs": [], 176 | "x": 520, 177 | "y": 140, 178 | "wires": [ 179 | [ 180 | "43428e95.c95d58" 181 | ] 182 | ] 183 | }, 184 | { 185 | "id": "43428e95.c95d58", 186 | "type": "debug", 187 | "z": "9f80fd1f.3bd908", 188 | "name": "", 189 | "active": false, 190 | "tosidebar": true, 191 | "console": false, 192 | "tostatus": false, 193 | "complete": "false", 194 | "statusVal": "", 195 | "statusType": "auto", 196 | "x": 750, 197 | "y": 100, 198 | "wires": [] 199 | }, 200 | { 201 | "id": "5c1c9dcd.546984", 202 | "type": "comment", 203 | "z": "9f80fd1f.3bd908", 204 | "name": "### Retrieve data from OPC UA connector and replace variable ID with variable Name and puslish as Array ###", 205 | "info": "", 206 | "x": 500, 207 | "y": 220, 208 | "wires": [] 209 | }, 210 | { 211 | "id": "d20bf630.ac7c98", 212 | "type": "debug", 213 | "z": "9f80fd1f.3bd908", 214 | "name": "", 215 | "active": true, 216 | "tosidebar": true, 217 | "console": false, 218 | "tostatus": false, 219 | "complete": "false", 220 | "statusVal": "", 221 | "statusType": "auto", 222 | "x": 1030, 223 | "y": 280, 224 | "wires": [] 225 | }, 226 | { 227 | "id": "50556ca2.120a8c", 228 | "type": "influxdb batch", 229 | "z": "9f80fd1f.3bd908", 230 | "influxdb": "4ad3ce8d.582c5", 231 | "precision": "", 232 | "retentionPolicy": "", 233 | "name": "", 234 | "database": "database", 235 | "precisionV18FluxV20": "ms", 236 | "retentionPolicyV18Flux": "", 237 | "org": "siemens", 238 | "bucket": "edgedb", 239 | "x": 1030, 240 | "y": 360, 241 | "wires": [] 242 | }, 243 | { 244 | "id": "6d21e9a.2814a98", 245 | "type": "split", 246 | "z": "9f80fd1f.3bd908", 247 | "name": "Split S7 Array", 248 | "splt": "\\n", 249 | "spltType": "str", 250 | "arraySplt": "1", 251 | "arraySpltType": "len", 252 | "stream": false, 253 | "addname": "", 254 | "x": 860, 255 | "y": 480, 256 | "wires": [ 257 | [ 258 | "38a14f21.7ff638" 259 | ] 260 | ] 261 | }, 262 | { 263 | "id": "5baa825a.490d5c", 264 | "type": "comment", 265 | "z": "9f80fd1f.3bd908", 266 | "name": "### Connection error on MQTT nodes please do the following steps ###", 267 | "info": "", 268 | "x": 310, 269 | "y": 460, 270 | "wires": [] 271 | }, 272 | { 273 | "id": "6d514375.dddf04", 274 | "type": "comment", 275 | "z": "9f80fd1f.3bd908", 276 | "name": "### 1. Open configuration node view ###", 277 | "info": "", 278 | "x": 220, 279 | "y": 500, 280 | "wires": [] 281 | }, 282 | { 283 | "id": "fea16ee2.9b9548", 284 | "type": "comment", 285 | "z": "9f80fd1f.3bd908", 286 | "name": "### 2. Double-Click on Databus node and select tab security###", 287 | "info": "", 288 | "x": 290, 289 | "y": 540, 290 | "wires": [] 291 | }, 292 | { 293 | "id": "c946b764.ac1338", 294 | "type": "comment", 295 | "z": "9f80fd1f.3bd908", 296 | "name": "### 3. Enter user 'edge' and password 'edge' ###", 297 | "info": "", 298 | "x": 240, 299 | "y": 580, 300 | "wires": [] 301 | }, 302 | { 303 | "id": "3bea85d5.8166ea", 304 | "type": "comment", 305 | "z": "9f80fd1f.3bd908", 306 | "name": "### 4. Click 'Finish' and deploy flows again ###", 307 | "info": "", 308 | "x": 240, 309 | "y": 620, 310 | "wires": [] 311 | }, 312 | { 313 | "id": "32a48b8c.b60274", 314 | "type": "comment", 315 | "z": "9f80fd1f.3bd908", 316 | "name": "### Please wait till 50 values are retrived so that you can see the results in the Influx UI view ###", 317 | "info": "", 318 | "x": 390, 319 | "y": 420, 320 | "wires": [] 321 | }, 322 | { 323 | "id": "210128b3065fac4a", 324 | "type": "function", 325 | "z": "9f80fd1f.3bd908", 326 | "d": true, 327 | "name": "store data", 328 | "func": "\nmsgoutput={}\n//msgoutput.measurement = msg.topic.split(';')[1].slice(2);\n/*msgoutput.payload = {\n serverTimestamp: msg.serverTimestamp.toISOString(),\n value: Math.round(msg.payload * 1e2)/ 1e2,\n name: msgoutput.measurement,\n}*/\n\nBeta = [{\n measurement: msg.topic.split(';')[1].slice(2),\n fields:{\n name: msg.topic.split(';')[1].slice(2),\n value: Math.round(msg.payload * 1e2)/ 1e2,\n qualitycode: false\n },\n timestamp: new Date()\n}]\n\nmsgoutput.payload = Beta;\n\nreturn msgoutput;", 329 | "outputs": 1, 330 | "noerr": 0, 331 | "initialize": "", 332 | "finalize": "", 333 | "libs": [], 334 | "x": 740, 335 | "y": 880, 336 | "wires": [ 337 | [ 338 | "832cbcfb18a82ae0", 339 | "52a7853f068a139e", 340 | "82dbdbd7e8fb888b" 341 | ] 342 | ] 343 | }, 344 | { 345 | "id": "373c3ebdfc8962a1", 346 | "type": "debug", 347 | "z": "9f80fd1f.3bd908", 348 | "d": true, 349 | "name": "opc output", 350 | "active": false, 351 | "tosidebar": true, 352 | "console": false, 353 | "tostatus": false, 354 | "complete": "true", 355 | "targetType": "full", 356 | "x": 730, 357 | "y": 820, 358 | "wires": [] 359 | }, 360 | { 361 | "id": "832cbcfb18a82ae0", 362 | "type": "debug", 363 | "z": "9f80fd1f.3bd908", 364 | "d": true, 365 | "name": "output to influx", 366 | "active": false, 367 | "tosidebar": true, 368 | "console": false, 369 | "tostatus": false, 370 | "complete": "true", 371 | "targetType": "full", 372 | "x": 970, 373 | "y": 820, 374 | "wires": [] 375 | }, 376 | { 377 | "id": "5bd466858aba7401", 378 | "type": "inject", 379 | "z": "9f80fd1f.3bd908", 380 | "d": true, 381 | "name": "powerdrive2-Temp", 382 | "repeat": "", 383 | "crontab": "", 384 | "once": false, 385 | "onceDelay": 0.1, 386 | "topic": "ns=1;s=temperatur2;datatype=Float", 387 | "payload": "1000", 388 | "payloadType": "num", 389 | "x": 190, 390 | "y": 960, 391 | "wires": [ 392 | [ 393 | "0b315d77e029d17d" 394 | ] 395 | ] 396 | }, 397 | { 398 | "id": "c0f358edb13dbafc", 399 | "type": "inject", 400 | "z": "9f80fd1f.3bd908", 401 | "d": true, 402 | "name": "powerdrive1", 403 | "repeat": "", 404 | "crontab": "", 405 | "once": false, 406 | "onceDelay": 0.1, 407 | "topic": "ns=1;s=powerdrive1;datatype=Float", 408 | "payload": "2000", 409 | "payloadType": "num", 410 | "x": 210, 411 | "y": 920, 412 | "wires": [ 413 | [ 414 | "0b315d77e029d17d" 415 | ] 416 | ] 417 | }, 418 | { 419 | "id": "97a9776eb04e43d6", 420 | "type": "inject", 421 | "z": "9f80fd1f.3bd908", 422 | "d": true, 423 | "name": "powerdrive2", 424 | "repeat": "", 425 | "crontab": "", 426 | "once": false, 427 | "onceDelay": 0.1, 428 | "topic": "ns=1;s=powerdrive2;datatype=Float", 429 | "payload": "2000", 430 | "payloadType": "num", 431 | "x": 210, 432 | "y": 880, 433 | "wires": [ 434 | [ 435 | "0b315d77e029d17d" 436 | ] 437 | ] 438 | }, 439 | { 440 | "id": "ec42e72ae8ffaf5a", 441 | "type": "inject", 442 | "z": "9f80fd1f.3bd908", 443 | "d": true, 444 | "name": "currentdrive1", 445 | "repeat": "", 446 | "crontab": "", 447 | "once": false, 448 | "onceDelay": 0.1, 449 | "topic": "ns=1;s=currentdrive1;datatype=Float", 450 | "payload": "1000", 451 | "payloadType": "num", 452 | "x": 200, 453 | "y": 840, 454 | "wires": [ 455 | [ 456 | "0b315d77e029d17d" 457 | ] 458 | ] 459 | }, 460 | { 461 | "id": "aa17ff32ee7ed707", 462 | "type": "inject", 463 | "z": "9f80fd1f.3bd908", 464 | "d": true, 465 | "name": "voltagedrive1", 466 | "props": [ 467 | { 468 | "p": "payload", 469 | "v": "1000", 470 | "vt": "num" 471 | }, 472 | { 473 | "p": "topic", 474 | "v": "ns=1;s=voltagedrive1;datatype=Double", 475 | "vt": "str" 476 | } 477 | ], 478 | "repeat": "", 479 | "crontab": "", 480 | "once": false, 481 | "onceDelay": 0.1, 482 | "topic": "ns=1;s=voltagedrive1;datatype=Double", 483 | "payload": "1000", 484 | "payloadType": "num", 485 | "x": 200, 486 | "y": 800, 487 | "wires": [ 488 | [ 489 | "0b315d77e029d17d" 490 | ] 491 | ] 492 | }, 493 | { 494 | "id": "0b315d77e029d17d", 495 | "type": "OpcUa-Client", 496 | "z": "9f80fd1f.3bd908", 497 | "d": true, 498 | "endpoint": "1b70cc5299eb3297", 499 | "action": "subscribe", 500 | "deadbandtype": "a", 501 | "deadbandvalue": 1, 502 | "time": "1", 503 | "timeUnit": "s", 504 | "certificate": "n", 505 | "localfile": "", 506 | "localkeyfile": "", 507 | "folderName4PKI": "", 508 | "name": "", 509 | "x": 502, 510 | "y": 859, 511 | "wires": [ 512 | [ 513 | "210128b3065fac4a", 514 | "373c3ebdfc8962a1" 515 | ] 516 | ] 517 | }, 518 | { 519 | "id": "52a7853f068a139e", 520 | "type": "influxdb batch", 521 | "z": "9f80fd1f.3bd908", 522 | "d": true, 523 | "influxdb": "4ad3ce8d.582c5", 524 | "precision": "", 525 | "retentionPolicy": "", 526 | "name": "write_data_influxdb", 527 | "database": "", 528 | "retentionPolicyV18Flux": "", 529 | "org": "siemens", 530 | "bucket": "edgedb", 531 | "x": 970, 532 | "y": 900, 533 | "wires": [] 534 | }, 535 | { 536 | "id": "4233eb0a43d330f1", 537 | "type": "comment", 538 | "z": "9f80fd1f.3bd908", 539 | "name": "### For testing - Receive simulated data from a OPC UA Server ###", 540 | "info": "", 541 | "x": 340, 542 | "y": 740, 543 | "wires": [] 544 | }, 545 | { 546 | "id": "82dbdbd7e8fb888b", 547 | "type": "link out", 548 | "z": "9f80fd1f.3bd908", 549 | "d": true, 550 | "name": "data_gen", 551 | "mode": "link", 552 | "links": [ 553 | "be3b1e8c001db67a", 554 | "ecef8cf0f9a01c4b" 555 | ], 556 | "x": 905, 557 | "y": 960, 558 | "wires": [] 559 | }, 560 | { 561 | "id": "1ac34de4909180b7", 562 | "type": "comment", 563 | "z": "7474ceb842992a1a", 564 | "name": "Calculate Total Power Consumption", 565 | "info": "", 566 | "x": 800, 567 | "y": 60, 568 | "wires": [] 569 | }, 570 | { 571 | "id": "24d36f2e8f195da4", 572 | "type": "function", 573 | "z": "7474ceb842992a1a", 574 | "name": "Query-List", 575 | "func": "var a ='\"name\"'\n//msg.query=\"select * from powerdrive1 where \"+a+\"='powerdrive1'\"\nmsg.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive1\")\n |> last()`;\nreturn msg; \n/*\nCLI-Funktionen f?r die Influxdb (Auszug)\n\"select * from data1\"\n//Zeigt alle Daten im measuurement an\nSELECT * FROM data where time > '2018-08-09T08:20:39.96Z' and time <= now()\n//Anzeige der measuremebts in einem bestimmten Zeitraum\nselect * from data where value > 80 and value < 85 \n//Filterung der Daten nach Value \n*/", 576 | "outputs": 1, 577 | "noerr": 0, 578 | "initialize": "", 579 | "finalize": "", 580 | "x": 270, 581 | "y": 160, 582 | "wires": [ 583 | [ 584 | "a3698549a82e281c" 585 | ] 586 | ] 587 | }, 588 | { 589 | "id": "d91e47171d708254", 590 | "type": "function", 591 | "z": "7474ceb842992a1a", 592 | "name": "Query-List", 593 | "func": "var a ='\"name\"'\n//msg.query=\"select * from powerdrive2 where \"+a+\"='powerdrive2'\"\nmsg.query= `from(bucket: \"edgedb\")\n |> range(start: -5m)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive2\")\n |> last()`;\nreturn msg; \n\n\n/*\nCLI-Funktionen f?r die Influxdb (Auszug)\n\"select * from data1\"\n//Zeigt alle Daten im measuurement an\nSELECT * FROM data where time > '2018-08-09T08:20:39.96Z' and time <= now()\n//Anzeige der measuremebts in einem bestimmten Zeitraum\nselect * from data where value > 80 and value < 85 \n//Filterung der Daten nach Value \n*/\n", 594 | "outputs": 1, 595 | "noerr": 0, 596 | "initialize": "", 597 | "finalize": "", 598 | "libs": [], 599 | "x": 270, 600 | "y": 220, 601 | "wires": [ 602 | [ 603 | "e72876fc847d9707" 604 | ] 605 | ] 606 | }, 607 | { 608 | "id": "e72876fc847d9707", 609 | "type": "influxdb in", 610 | "z": "7474ceb842992a1a", 611 | "influxdb": "4ad3ce8d.582c5", 612 | "name": "influxdb_query_power2", 613 | "query": "", 614 | "rawOutput": false, 615 | "precision": "", 616 | "retentionPolicy": "", 617 | "org": "siemens", 618 | "x": 490, 619 | "y": 220, 620 | "wires": [ 621 | [ 622 | "14b0503b4d11a1f1", 623 | "d324e4d979f80857" 624 | ] 625 | ] 626 | }, 627 | { 628 | "id": "14b0503b4d11a1f1", 629 | "type": "function", 630 | "z": "7474ceb842992a1a", 631 | "name": "collect_last_power_values", 632 | "func": "context.data = context.data || {};\n\nswitch (msg.payload[0]._measurement) {\n case \"powerdrive1\":\n context.data.power1 = msg.payload[2]._value;\n msg = null;\n break;\n case \"powerdrive2\":\n context.data.power2 = msg.payload[2]._value;\n msg = null;\n break;\n default:\n msg = null;\n break;\n}\n\nif (context.data.power1 != null && context.data.power2 != null) {\n msg2 = {};\n msg2 = context.data;\n\n context.data = null;\n return msg2;\n} else {\n return msg;\n}", 633 | "outputs": 1, 634 | "noerr": 0, 635 | "initialize": "", 636 | "finalize": "", 637 | "libs": [], 638 | "x": 770, 639 | "y": 220, 640 | "wires": [ 641 | [ 642 | "16a3f64e6e4cb01f", 643 | "fd2ecb26787fa544" 644 | ] 645 | ] 646 | }, 647 | { 648 | "id": "797b84ead1704e99", 649 | "type": "function", 650 | "z": "7474ceb842992a1a", 651 | "name": "join_and_write_power_Influxdb", 652 | "func": "total = msg.payload;\n\nBeta = [{\n measurement: \"GEN_KPI_TotalPower\",\n fields: {\n name: \"TotalPower\",\n value: total,\n //weitereTags: -100 //(optional) nur 4 Datatypes sind in der Influxdb akzeptiert: Int,Float,String, Bool\n },\n timestamp: new Date()\n}]\n// Arr.push(Beta);\n//}\n\nmsg.payload = Beta;\nreturn msg;", 653 | "outputs": 1, 654 | "noerr": 0, 655 | "initialize": "", 656 | "finalize": "", 657 | "libs": [], 658 | "x": 1270, 659 | "y": 220, 660 | "wires": [ 661 | [ 662 | "5d341f7d40ab6696", 663 | "cf316c5feaf54b75" 664 | ] 665 | ] 666 | }, 667 | { 668 | "id": "cf316c5feaf54b75", 669 | "type": "influxdb batch", 670 | "z": "7474ceb842992a1a", 671 | "influxdb": "4ad3ce8d.582c5", 672 | "precision": "", 673 | "retentionPolicy": "", 674 | "name": "influxdb_write", 675 | "database": "", 676 | "retentionPolicyV18Flux": "", 677 | "org": "siemens", 678 | "bucket": "edgedb", 679 | "x": 1520, 680 | "y": 180, 681 | "wires": [] 682 | }, 683 | { 684 | "id": "a3698549a82e281c", 685 | "type": "influxdb in", 686 | "z": "7474ceb842992a1a", 687 | "influxdb": "4ad3ce8d.582c5", 688 | "name": "influxdb_query_power1", 689 | "query": "", 690 | "rawOutput": false, 691 | "precision": "", 692 | "retentionPolicy": "", 693 | "org": "siemens", 694 | "x": 490, 695 | "y": 160, 696 | "wires": [ 697 | [ 698 | "14b0503b4d11a1f1", 699 | "d324e4d979f80857" 700 | ] 701 | ] 702 | }, 703 | { 704 | "id": "ecef8cf0f9a01c4b", 705 | "type": "link in", 706 | "z": "7474ceb842992a1a", 707 | "name": "", 708 | "links": [ 709 | "38a14f21.7ff638", 710 | "82dbdbd7e8fb888b" 711 | ], 712 | "x": 135, 713 | "y": 220, 714 | "wires": [ 715 | [ 716 | "24d36f2e8f195da4", 717 | "d91e47171d708254" 718 | ] 719 | ] 720 | }, 721 | { 722 | "id": "16a3f64e6e4cb01f", 723 | "type": "function", 724 | "z": "7474ceb842992a1a", 725 | "name": "sum_total_power", 726 | "func": "var arr = [];\nvar mean = 0;\nvar sum = 0;\n\narr = Object.keys(msg).map(function(key) {\n return msg[key]\n})\narr.splice(-1, 1)\n\nfor (var i = 0; i < arr.length; i++) {\n sum = sum + arr[i];\n}\n// mean = sum / arr.length; \n\nmsg2 = {};\n//msg2.payload = (arr[0] + arr[1] + arr[2]) / arr.length; \n\nmsg2.payload = sum;\nreturn msg2;", 727 | "outputs": 1, 728 | "noerr": 0, 729 | "initialize": "", 730 | "finalize": "", 731 | "libs": [], 732 | "x": 1010, 733 | "y": 220, 734 | "wires": [ 735 | [ 736 | "797b84ead1704e99" 737 | ] 738 | ] 739 | }, 740 | { 741 | "id": "5d341f7d40ab6696", 742 | "type": "debug", 743 | "z": "7474ceb842992a1a", 744 | "name": "KPI-Power", 745 | "active": false, 746 | "tosidebar": true, 747 | "console": false, 748 | "tostatus": false, 749 | "complete": "payload", 750 | "targetType": "msg", 751 | "x": 1510, 752 | "y": 260, 753 | "wires": [] 754 | }, 755 | { 756 | "id": "fd2ecb26787fa544", 757 | "type": "debug", 758 | "z": "7474ceb842992a1a", 759 | "name": "Last Power Values", 760 | "active": false, 761 | "tosidebar": true, 762 | "console": false, 763 | "tostatus": false, 764 | "complete": "true", 765 | "targetType": "full", 766 | "x": 1010, 767 | "y": 160, 768 | "wires": [] 769 | }, 770 | { 771 | "id": "8b8c3adc7921a640", 772 | "type": "debug", 773 | "z": "7474ceb842992a1a", 774 | "name": "KPI-Power", 775 | "active": false, 776 | "tosidebar": true, 777 | "console": false, 778 | "tostatus": false, 779 | "complete": "payload", 780 | "targetType": "msg", 781 | "statusVal": "", 782 | "statusType": "auto", 783 | "x": 1030, 784 | "y": 300, 785 | "wires": [] 786 | }, 787 | { 788 | "id": "d324e4d979f80857", 789 | "type": "function", 790 | "z": "7474ceb842992a1a", 791 | "name": "collect_last_power_values", 792 | "func": "context.data = context.data || {};\n\nreturn msg", 793 | "outputs": 1, 794 | "noerr": 0, 795 | "initialize": "", 796 | "finalize": "", 797 | "libs": [], 798 | "x": 750, 799 | "y": 380, 800 | "wires": [ 801 | [ 802 | "8b8c3adc7921a640" 803 | ] 804 | ] 805 | }, 806 | { 807 | "id": "c17d056bfe661bfb", 808 | "type": "influxdb in", 809 | "z": "1ff02d49d32ca74f", 810 | "influxdb": "4ad3ce8d.582c5", 811 | "name": "query", 812 | "query": "", 813 | "rawOutput": false, 814 | "precision": "", 815 | "retentionPolicy": "", 816 | "org": "siemens", 817 | "x": 770, 818 | "y": 140, 819 | "wires": [ 820 | [ 821 | "97ada85a60986d3d" 822 | ] 823 | ] 824 | }, 825 | { 826 | "id": "be3b1e8c001db67a", 827 | "type": "link in", 828 | "z": "1ff02d49d32ca74f", 829 | "name": "input_raw_data", 830 | "links": [ 831 | "38a14f21.7ff638", 832 | "82dbdbd7e8fb888b" 833 | ], 834 | "x": 185, 835 | "y": 160, 836 | "wires": [ 837 | [ 838 | "be65079f10c41189" 839 | ] 840 | ] 841 | }, 842 | { 843 | "id": "efd211984f39259b", 844 | "type": "function", 845 | "z": "1ff02d49d32ca74f", 846 | "name": "GET_LAST_ENTRIES", 847 | "func": "let msgQueryGet = {}\nmsgQueryGet.measurement = msg.payload.measurement\n\nmsgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"${msgQueryGet.measurement}\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)\n |> group()`;\nreturn msgQueryGet;\n", 848 | "outputs": 1, 849 | "noerr": 0, 850 | "initialize": "", 851 | "finalize": "", 852 | "libs": [], 853 | "x": 560, 854 | "y": 140, 855 | "wires": [ 856 | [ 857 | "c17d056bfe661bfb" 858 | ] 859 | ] 860 | }, 861 | { 862 | "id": "22c5dc214c72eeb6", 863 | "type": "mqtt out", 864 | "z": "1ff02d49d32ca74f", 865 | "name": "StandardKpis", 866 | "topic": "StandardKpis", 867 | "qos": "", 868 | "retain": "", 869 | "respTopic": "", 870 | "contentType": "", 871 | "userProps": "", 872 | "correl": "", 873 | "expiry": "", 874 | "broker": "1ab7e97a.bda1e7", 875 | "x": 1180, 876 | "y": 140, 877 | "wires": [] 878 | }, 879 | { 880 | "id": "97ada85a60986d3d", 881 | "type": "json", 882 | "z": "1ff02d49d32ca74f", 883 | "name": "", 884 | "property": "payload", 885 | "action": "obj", 886 | "pretty": false, 887 | "x": 910, 888 | "y": 140, 889 | "wires": [ 890 | [ 891 | "98936ecf14b1220d", 892 | "22c5dc214c72eeb6" 893 | ] 894 | ] 895 | }, 896 | { 897 | "id": "98936ecf14b1220d", 898 | "type": "debug", 899 | "z": "1ff02d49d32ca74f", 900 | "name": "response_get", 901 | "active": false, 902 | "tosidebar": true, 903 | "console": false, 904 | "tostatus": false, 905 | "complete": "true", 906 | "targetType": "full", 907 | "statusVal": "", 908 | "statusType": "auto", 909 | "x": 1120, 910 | "y": 220, 911 | "wires": [] 912 | }, 913 | { 914 | "id": "ad4f6d543b736f83", 915 | "type": "comment", 916 | "z": "1ff02d49d32ca74f", 917 | "name": "PowerDrive1 and PowerDrive2 last 50 entries", 918 | "info": "", 919 | "x": 550, 920 | "y": 80, 921 | "wires": [] 922 | }, 923 | { 924 | "id": "b92d4778a8765068", 925 | "type": "influxdb in", 926 | "z": "1ff02d49d32ca74f", 927 | "influxdb": "4ad3ce8d.582c5", 928 | "name": "query", 929 | "query": "", 930 | "rawOutput": false, 931 | "precision": "", 932 | "retentionPolicy": "", 933 | "org": "siemens", 934 | "x": 730, 935 | "y": 340, 936 | "wires": [ 937 | [ 938 | "da5a71e0e50b6a2d" 939 | ] 940 | ] 941 | }, 942 | { 943 | "id": "3cc3c6c7bd32b8fd", 944 | "type": "debug", 945 | "z": "1ff02d49d32ca74f", 946 | "name": "mqtt output", 947 | "active": false, 948 | "tosidebar": true, 949 | "console": false, 950 | "tostatus": false, 951 | "complete": "true", 952 | "targetType": "full", 953 | "statusVal": "", 954 | "statusType": "auto", 955 | "x": 1230, 956 | "y": 520, 957 | "wires": [] 958 | }, 959 | { 960 | "id": "1a526d08fd3a5196", 961 | "type": "mqtt out", 962 | "z": "1ff02d49d32ca74f", 963 | "name": "Mean", 964 | "topic": "Mean", 965 | "qos": "", 966 | "retain": "", 967 | "respTopic": "", 968 | "contentType": "", 969 | "userProps": "", 970 | "correl": "", 971 | "expiry": "", 972 | "broker": "1ab7e97a.bda1e7", 973 | "x": 1290, 974 | "y": 400, 975 | "wires": [] 976 | }, 977 | { 978 | "id": "bcc37a8b4d68d059", 979 | "type": "comment", 980 | "z": "1ff02d49d32ca74f", 981 | "name": "VoltageDrive3 dynamic sample of entries", 982 | "info": "", 983 | "x": 560, 984 | "y": 260, 985 | "wires": [] 986 | }, 987 | { 988 | "id": "98d95adec7f11faf", 989 | "type": "influxdb in", 990 | "z": "1ff02d49d32ca74f", 991 | "influxdb": "4ad3ce8d.582c5", 992 | "name": "query", 993 | "query": "", 994 | "rawOutput": false, 995 | "precision": "", 996 | "retentionPolicy": "", 997 | "org": "siemens", 998 | "x": 750, 999 | "y": 500, 1000 | "wires": [ 1001 | [ 1002 | "da5a71e0e50b6a2d" 1003 | ] 1004 | ] 1005 | }, 1006 | { 1007 | "id": "8e801bd99a2e77b6", 1008 | "type": "comment", 1009 | "z": "1ff02d49d32ca74f", 1010 | "name": "CurrentDrive3 dynamic sample of entries", 1011 | "info": "", 1012 | "x": 560, 1013 | "y": 440, 1014 | "wires": [] 1015 | }, 1016 | { 1017 | "id": "be65079f10c41189", 1018 | "type": "switch", 1019 | "z": "1ff02d49d32ca74f", 1020 | "name": "filter", 1021 | "property": "payload.measurement", 1022 | "propertyType": "msg", 1023 | "rules": [ 1024 | { 1025 | "t": "eq", 1026 | "v": "powerdrive1", 1027 | "vt": "str" 1028 | }, 1029 | { 1030 | "t": "eq", 1031 | "v": "powerdrive2", 1032 | "vt": "str" 1033 | }, 1034 | { 1035 | "t": "eq", 1036 | "v": "voltagedrive3", 1037 | "vt": "str" 1038 | }, 1039 | { 1040 | "t": "eq", 1041 | "v": "currentdrive3", 1042 | "vt": "str" 1043 | } 1044 | ], 1045 | "checkall": "true", 1046 | "repair": false, 1047 | "outputs": 4, 1048 | "x": 290, 1049 | "y": 160, 1050 | "wires": [ 1051 | [ 1052 | "efd211984f39259b" 1053 | ], 1054 | [ 1055 | "efd211984f39259b" 1056 | ], 1057 | [ 1058 | "50952f79f64eb614" 1059 | ], 1060 | [ 1061 | "46b6e48d1b12352a" 1062 | ] 1063 | ] 1064 | }, 1065 | { 1066 | "id": "da5a71e0e50b6a2d", 1067 | "type": "function", 1068 | "z": "1ff02d49d32ca74f", 1069 | "name": "manual_join", 1070 | "func": "var tempo_cd3_batch = context.get('cd3_batch')||[];\nvar tempo_vd3_batch = context.get('vd3_batch')||[];\n\nfunction build_analytics_object(){\n let data_obj = {\n sample_number : msg.SampleNumber,\n current_drive3_batch : tempo_cd3_batch,\n voltage_drive3_batch : tempo_vd3_batch,\n }\n return data_obj\n}\n\n\nif (msg.measurement == 'currentdrive3'){\n tempo_cd3_batch = msg.payload\n context.set('cd3_batch',tempo_cd3_batch)\n \n}\nelse if (msg.measurement == 'voltagedrive3') {\n tempo_vd3_batch = msg.payload\n context.set('vd3_batch',tempo_vd3_batch)\n}\n\nif (tempo_cd3_batch.length > 0 && tempo_vd3_batch.length >0 ) {\n let py_obj={}\n py_obj.payload = build_analytics_object()\n tempo_cd3_batch = []\n tempo_vd3_batch = []\n context.set('cd3_batch', tempo_cd3_batch)\n context.set('vd3_batch', tempo_vd3_batch)\n \n return py_obj\n \n}\n\nelse{\n return\n}", 1071 | "outputs": 1, 1072 | "noerr": 0, 1073 | "initialize": "", 1074 | "finalize": "", 1075 | "libs": [], 1076 | "x": 990, 1077 | "y": 400, 1078 | "wires": [ 1079 | [ 1080 | "3cc3c6c7bd32b8fd", 1081 | "1a526d08fd3a5196" 1082 | ] 1083 | ] 1084 | }, 1085 | { 1086 | "id": "7bce0dac6af8be62", 1087 | "type": "mqtt in", 1088 | "z": "1ff02d49d32ca74f", 1089 | "name": "", 1090 | "topic": "StandardKpiResult", 1091 | "qos": "2", 1092 | "datatype": "auto", 1093 | "broker": "1ab7e97a.bda1e7", 1094 | "nl": false, 1095 | "rap": false, 1096 | "inputs": 0, 1097 | "x": 130, 1098 | "y": 820, 1099 | "wires": [ 1100 | [ 1101 | "78ccf7c99d23ca4d" 1102 | ] 1103 | ] 1104 | }, 1105 | { 1106 | "id": "bbcadf72240b8428", 1107 | "type": "debug", 1108 | "z": "1ff02d49d32ca74f", 1109 | "name": "response standard kpis", 1110 | "active": false, 1111 | "tosidebar": true, 1112 | "console": false, 1113 | "tostatus": false, 1114 | "complete": "true", 1115 | "targetType": "full", 1116 | "statusVal": "", 1117 | "statusType": "auto", 1118 | "x": 1230, 1119 | "y": 780, 1120 | "wires": [] 1121 | }, 1122 | { 1123 | "id": "78ccf7c99d23ca4d", 1124 | "type": "json", 1125 | "z": "1ff02d49d32ca74f", 1126 | "name": "", 1127 | "property": "payload", 1128 | "action": "", 1129 | "pretty": false, 1130 | "x": 510, 1131 | "y": 820, 1132 | "wires": [ 1133 | [ 1134 | "8f26ae08dabcb38e" 1135 | ] 1136 | ] 1137 | }, 1138 | { 1139 | "id": "8f26ae08dabcb38e", 1140 | "type": "function", 1141 | "z": "1ff02d49d32ca74f", 1142 | "name": "store data", 1143 | "func": "let my_payload = {};\nmy_payload = msg.payload;\nlet output_standardkpis = {}\noutput_standardkpis.measurement = my_payload.name.toUpperCase() + '_STANDARD_KPIS'\noutput_standardkpis.payload = {\n mean: Math.round(my_payload.mean_result * 1e2)/ 1e2,\n median: Math.round(my_payload.median_result * 1e2)/ 1e2,\n stddev: Math.round(my_payload.stddev_result * 1e2)/ 1e2,\n name: my_payload.name,\n}\n\nreturn output_standardkpis;", 1144 | "outputs": 1, 1145 | "noerr": 0, 1146 | "initialize": "", 1147 | "finalize": "", 1148 | "libs": [], 1149 | "x": 870, 1150 | "y": 820, 1151 | "wires": [ 1152 | [ 1153 | "2cdca5c6f88b0194", 1154 | "bbcadf72240b8428" 1155 | ] 1156 | ] 1157 | }, 1158 | { 1159 | "id": "2cdca5c6f88b0194", 1160 | "type": "influxdb out", 1161 | "z": "1ff02d49d32ca74f", 1162 | "influxdb": "4ad3ce8d.582c5", 1163 | "name": "write_influxdb", 1164 | "measurement": "", 1165 | "precision": "", 1166 | "retentionPolicy": "", 1167 | "database": "", 1168 | "retentionPolicyV18Flux": "", 1169 | "org": "siemens", 1170 | "bucket": "edgedb", 1171 | "x": 1200, 1172 | "y": 820, 1173 | "wires": [] 1174 | }, 1175 | { 1176 | "id": "d4be09607b515978", 1177 | "type": "mqtt in", 1178 | "z": "1ff02d49d32ca74f", 1179 | "name": "", 1180 | "topic": "MeanResult", 1181 | "qos": "2", 1182 | "datatype": "auto", 1183 | "broker": "1ab7e97a.bda1e7", 1184 | "nl": false, 1185 | "rap": false, 1186 | "inputs": 0, 1187 | "x": 110, 1188 | "y": 960, 1189 | "wires": [ 1190 | [ 1191 | "186ea90f99e29d7b" 1192 | ] 1193 | ] 1194 | }, 1195 | { 1196 | "id": "8153c1341ba5dcd2", 1197 | "type": "debug", 1198 | "z": "1ff02d49d32ca74f", 1199 | "name": "response mean", 1200 | "active": false, 1201 | "tosidebar": true, 1202 | "console": false, 1203 | "tostatus": false, 1204 | "complete": "true", 1205 | "targetType": "full", 1206 | "x": 1210, 1207 | "y": 920, 1208 | "wires": [] 1209 | }, 1210 | { 1211 | "id": "186ea90f99e29d7b", 1212 | "type": "json", 1213 | "z": "1ff02d49d32ca74f", 1214 | "name": "", 1215 | "property": "payload", 1216 | "action": "", 1217 | "pretty": false, 1218 | "x": 510, 1219 | "y": 960, 1220 | "wires": [ 1221 | [ 1222 | "742190e9e7e62ef6" 1223 | ] 1224 | ] 1225 | }, 1226 | { 1227 | "id": "742190e9e7e62ef6", 1228 | "type": "function", 1229 | "z": "1ff02d49d32ca74f", 1230 | "name": "store data", 1231 | "func": "my_payload = {};\nmy_payload = msg.payload;\noutput_pd1_mean = {}\noutput_pd1_mean.measurement = my_payload.name.toUpperCase()\noutput_pd1_mean.payload = {\n value: Math.round(my_payload.power_mean_result * 1e2)/ 1e2,\n name: my_payload.name,\n}\n\nreturn output_pd1_mean;", 1232 | "outputs": 1, 1233 | "noerr": 0, 1234 | "initialize": "", 1235 | "finalize": "", 1236 | "x": 870, 1237 | "y": 960, 1238 | "wires": [ 1239 | [ 1240 | "b1e47db6fb4093e9", 1241 | "8153c1341ba5dcd2" 1242 | ] 1243 | ] 1244 | }, 1245 | { 1246 | "id": "b1e47db6fb4093e9", 1247 | "type": "influxdb out", 1248 | "z": "1ff02d49d32ca74f", 1249 | "influxdb": "4ad3ce8d.582c5", 1250 | "name": "write_influxdb", 1251 | "measurement": "", 1252 | "precision": "", 1253 | "retentionPolicy": "", 1254 | "database": "", 1255 | "retentionPolicyV18Flux": "", 1256 | "org": "siemens", 1257 | "bucket": "edgedb", 1258 | "x": 1200, 1259 | "y": 960, 1260 | "wires": [] 1261 | }, 1262 | { 1263 | "id": "a18ffe23f5be216a", 1264 | "type": "comment", 1265 | "z": "1ff02d49d32ca74f", 1266 | "name": "Data Analytics Results", 1267 | "info": "", 1268 | "x": 540, 1269 | "y": 740, 1270 | "wires": [] 1271 | }, 1272 | { 1273 | "id": "50952f79f64eb614", 1274 | "type": "function", 1275 | "z": "1ff02d49d32ca74f", 1276 | "name": "GET_LAST_ENTRIES", 1277 | "func": "let msgQueryGet = {}\nmsgQueryGet.measurement = msg.payload.measurement\n\nmsgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"${msgQueryGet.measurement}\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)\n |> group()`;\nreturn msgQueryGet;\n", 1278 | "outputs": 1, 1279 | "noerr": 0, 1280 | "initialize": "", 1281 | "finalize": "", 1282 | "libs": [], 1283 | "x": 540, 1284 | "y": 340, 1285 | "wires": [ 1286 | [ 1287 | "b92d4778a8765068" 1288 | ] 1289 | ] 1290 | }, 1291 | { 1292 | "id": "46b6e48d1b12352a", 1293 | "type": "function", 1294 | "z": "1ff02d49d32ca74f", 1295 | "name": "GET_LAST_ENTRIES", 1296 | "func": "let msgQueryGet = {}\nmsgQueryGet.measurement = msg.payload.measurement\n\nmsgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"${msgQueryGet.measurement}\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)\n |> group()`;\nreturn msgQueryGet;\n", 1297 | "outputs": 1, 1298 | "noerr": 0, 1299 | "initialize": "", 1300 | "finalize": "", 1301 | "libs": [], 1302 | "x": 520, 1303 | "y": 500, 1304 | "wires": [ 1305 | [ 1306 | "98d95adec7f11faf" 1307 | ] 1308 | ] 1309 | } 1310 | ] -------------------------------------------------------------------------------- /src/solution/HandsOn_1/node_red/node-red/flows.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "803455e.14d3d28", 4 | "type": "tab", 5 | "label": "Data Collection", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "56ac0cbb.978d6c", 11 | "type": "tab", 12 | "label": "KPI-Calc-Dummy", 13 | "disabled": false, 14 | "info": "" 15 | }, 16 | { 17 | "id": "f7e739bb.370638", 18 | "type": "tab", 19 | "label": "KPI Estimation", 20 | "disabled": false, 21 | "info": "" 22 | }, 23 | { 24 | "id": "b6ec4c8d.61eac", 25 | "type": "mqtt-broker", 26 | "name": "", 27 | "broker": "mqtt-broker", 28 | "port": "1883", 29 | "clientid": "nodered-client", 30 | "usetls": false, 31 | "compatmode": true, 32 | "keepalive": "60", 33 | "cleansession": true, 34 | "birthTopic": "", 35 | "birthQos": "0", 36 | "birthPayload": "", 37 | "closeTopic": "", 38 | "closeQos": "0", 39 | "closePayload": "", 40 | "willTopic": "", 41 | "willQos": "0", 42 | "willPayload": "" 43 | }, 44 | { 45 | "id": "4ad3ce8d.582c5", 46 | "type": "influxdb", 47 | "hostname": "influxdb", 48 | "port": "8086", 49 | "protocol": "http", 50 | "database": "edgedb", 51 | "name": "", 52 | "usetls": false, 53 | "tls": "", 54 | "influxdbVersion": "2.0", 55 | "url": "http://influxdb:8086", 56 | "rejectUnauthorized": false, 57 | "credentials": { 58 | "username": "", 59 | "password": "", 60 | "token": "testtoken" 61 | } 62 | }, 63 | { 64 | "id": "99d8c9c3d94c327e", 65 | "type": "OpcUa-Endpoint", 66 | "endpoint": "opc.tcp://opc-server:4841", 67 | "secpol": "Basic256", 68 | "secmode": "SIGN", 69 | "none": true, 70 | "login": false, 71 | "usercert": false, 72 | "usercertificate": "", 73 | "userprivatekey": "" 74 | }, 75 | { 76 | "id": "a67248fc.148ff8", 77 | "type": "comment", 78 | "z": "803455e.14d3d28", 79 | "name": "### Generate dummy data and storage in influxdb ###", 80 | "info": "", 81 | "x": 300, 82 | "y": 80, 83 | "wires": [] 84 | }, 85 | { 86 | "id": "9e8378b0.74b62", 87 | "type": "inject", 88 | "z": "803455e.14d3d28", 89 | "name": "n-samples", 90 | "props": [ 91 | { 92 | "p": "payload" 93 | }, 94 | { 95 | "p": "topic", 96 | "vt": "str" 97 | } 98 | ], 99 | "repeat": "10", 100 | "crontab": "", 101 | "once": true, 102 | "onceDelay": 0.1, 103 | "topic": "samples_number_n", 104 | "payload": "20", 105 | "payloadType": "num", 106 | "x": 230, 107 | "y": 320, 108 | "wires": [ 109 | [ 110 | "925a5c7.deaec2", 111 | "6dc5f6c6.740bc8", 112 | "43bc8a92.aad59c", 113 | "1101d4e.caa782b" 114 | ] 115 | ] 116 | }, 117 | { 118 | "id": "1101d4e.caa782b", 119 | "type": "function", 120 | "z": "803455e.14d3d28", 121 | "name": "Write_KPI_PowerDrive1_Influxdb", 122 | "func": "if (msg!==null)\n{\nmean = msg.payload;\n Beta = [{\n\n measurement: \"powerdrive1\",\n fields:{\n name: \"powerdrive1\",\n value:(Math.random() * (999.0 - 750.0 + 1) + 750.0),\n qualitycode: false\n //weitereTags: -100 //(optional) nur 4 Datatypes sind in der Influxdb akzeptiert: Int,Float,String, Bool\n },\n timestamp: new Date()\n }]\n// Arr.push(Beta);\n//}\n\nmsg.payload = Beta;\n}\nreturn msg;", 123 | "outputs": 1, 124 | "noerr": 0, 125 | "x": 520, 126 | "y": 240, 127 | "wires": [ 128 | [ 129 | "bdcb1de8.cd129", 130 | "6a295d91.59ab5c", 131 | "edc207be.ce696" 132 | ] 133 | ] 134 | }, 135 | { 136 | "id": "925a5c7.deaec2", 137 | "type": "function", 138 | "z": "803455e.14d3d28", 139 | "name": "Write_KPI_PowerDrive2_Influxdb", 140 | "func": "if (msg!==null)\n{\nmean = msg.payload;\n Beta = [{\n//values between 800 and 999 Watt\n measurement: \"powerdrive2\",\n fields:{\n name: \"powerdrive2\",\n value:(Math.random() * (999.0 - 800.0 + 1) + 800.0),\n qualitycode: false\n //weitereTags: -100 //(optional) nur 4 Datatypes sind in der Influxdb akzeptiert: Int,Float,String, Bool\n },\n timestamp: new Date()\n }]\n// Arr.push(Beta);\n//}\n\nmsg.payload = Beta;\n}\nreturn msg;", 141 | "outputs": 1, 142 | "noerr": 0, 143 | "x": 520, 144 | "y": 300, 145 | "wires": [ 146 | [ 147 | "ef28ccb6.75c6e", 148 | "6a295d91.59ab5c", 149 | "edc207be.ce696" 150 | ] 151 | ] 152 | }, 153 | { 154 | "id": "6dc5f6c6.740bc8", 155 | "type": "function", 156 | "z": "803455e.14d3d28", 157 | "name": "Write_KPI_VoltageDrive3_Influxdb", 158 | "func": "if (msg!==null)\n{\nmean = msg.payload;\n Beta = [{\n//values between 48 and 50 Volt\n measurement: \"voltagedrive3\",\n fields:{\n name: \"voltagedrive3\",\n value:(Math.random() * (50.0 - 48.0) + 48.0),\n qualitycode: false\n \n },\n ttimestamp: new Date()\n }]\n// Arr.push(Beta);\n//}\n\nmsg.payload = Beta;\n}\nreturn msg;", 159 | "outputs": 1, 160 | "noerr": 0, 161 | "initialize": "", 162 | "finalize": "", 163 | "x": 520, 164 | "y": 360, 165 | "wires": [ 166 | [ 167 | "6a295d91.59ab5c", 168 | "1fe4faea.f61d45", 169 | "edc207be.ce696" 170 | ] 171 | ] 172 | }, 173 | { 174 | "id": "43bc8a92.aad59c", 175 | "type": "function", 176 | "z": "803455e.14d3d28", 177 | "name": "Write_KPI_CurrentDrive3_Influxdb", 178 | "func": "if (msg!==null)\n{\nmean = msg.payload;\n Beta = [{\n//values between 18 and 20 Ampere\n measurement: \"currentdrive3\",\n fields:{\n name: \"currentdrive3\",\n value:(Math.random() * (20.0 - 18.0) + 18.0),\n qualitycode: false\n \n },\n timestamp: new Date()\n }]\n// Arr.push(Beta);\n//}\n\nmsg.payload = Beta;\n}\nreturn msg;", 179 | "outputs": 1, 180 | "noerr": 0, 181 | "initialize": "", 182 | "finalize": "", 183 | "x": 520, 184 | "y": 420, 185 | "wires": [ 186 | [ 187 | "6a295d91.59ab5c", 188 | "14aad25e.88d496", 189 | "edc207be.ce696" 190 | ] 191 | ] 192 | }, 193 | { 194 | "id": "6a295d91.59ab5c", 195 | "type": "influxdb batch", 196 | "z": "803455e.14d3d28", 197 | "influxdb": "4ad3ce8d.582c5", 198 | "precision": "", 199 | "retentionPolicy": "", 200 | "name": "write_data_influxdb", 201 | "database": "", 202 | "retentionPolicyV18Flux": "", 203 | "org": "siemens", 204 | "bucket": "edgedb", 205 | "x": 970, 206 | "y": 320, 207 | "wires": [] 208 | }, 209 | { 210 | "id": "edc207be.ce696", 211 | "type": "link out", 212 | "z": "803455e.14d3d28", 213 | "name": "data_gen", 214 | "links": [ 215 | "84d99c19.67892", 216 | "554edde8.db9efc" 217 | ], 218 | "x": 925, 219 | "y": 480, 220 | "wires": [] 221 | }, 222 | { 223 | "id": "bdcb1de8.cd129", 224 | "type": "debug", 225 | "z": "803455e.14d3d28", 226 | "name": "KPI-Result_PowerDrive1", 227 | "active": false, 228 | "tosidebar": true, 229 | "console": false, 230 | "tostatus": false, 231 | "complete": "payload", 232 | "targetType": "msg", 233 | "x": 990, 234 | "y": 240, 235 | "wires": [] 236 | }, 237 | { 238 | "id": "ef28ccb6.75c6e", 239 | "type": "debug", 240 | "z": "803455e.14d3d28", 241 | "name": "KPI-Result_PowerDrive2", 242 | "active": false, 243 | "tosidebar": true, 244 | "console": false, 245 | "tostatus": false, 246 | "complete": "payload", 247 | "targetType": "msg", 248 | "x": 990, 249 | "y": 280, 250 | "wires": [] 251 | }, 252 | { 253 | "id": "1fe4faea.f61d45", 254 | "type": "debug", 255 | "z": "803455e.14d3d28", 256 | "name": "KPI-Result_VoltageDrive3", 257 | "active": false, 258 | "tosidebar": true, 259 | "console": false, 260 | "tostatus": false, 261 | "complete": "payload", 262 | "targetType": "msg", 263 | "statusVal": "", 264 | "statusType": "auto", 265 | "x": 990, 266 | "y": 380, 267 | "wires": [] 268 | }, 269 | { 270 | "id": "14aad25e.88d496", 271 | "type": "debug", 272 | "z": "803455e.14d3d28", 273 | "name": "KPI-Result_CurrentDrive3", 274 | "active": false, 275 | "tosidebar": true, 276 | "console": false, 277 | "tostatus": false, 278 | "complete": "payload", 279 | "targetType": "msg", 280 | "statusVal": "", 281 | "statusType": "auto", 282 | "x": 990, 283 | "y": 420, 284 | "wires": [] 285 | }, 286 | { 287 | "id": "1f383f974e86d387", 288 | "type": "function", 289 | "z": "803455e.14d3d28", 290 | "d": true, 291 | "name": "store data", 292 | "func": "\nmsgoutput={}\n//msgoutput.measurement = msg.topic.split(';')[1].slice(2);\n/*msgoutput.payload = {\n serverTimestamp: msg.serverTimestamp.toISOString(),\n value: Math.round(msg.payload * 1e2)/ 1e2,\n name: msgoutput.measurement,\n}*/\n\nBeta = [{\n measurement: msg.topic.split(';')[1].slice(2),\n fields:{\n name: msg.topic.split(';')[1].slice(2),\n value: Math.round(msg.payload * 1e2)/ 1e2,\n qualitycode: false\n },\n timestamp: new Date()\n}]\n\nmsgoutput.payload = Beta;\n\nreturn msgoutput;", 293 | "outputs": 1, 294 | "noerr": 0, 295 | "initialize": "", 296 | "finalize": "", 297 | "libs": [], 298 | "x": 780, 299 | "y": 760, 300 | "wires": [ 301 | [ 302 | "c41865e7d1104ade", 303 | "52089d7cd8e56e3f", 304 | "0c7cbde017b44877" 305 | ] 306 | ] 307 | }, 308 | { 309 | "id": "b04a16e38b71dffd", 310 | "type": "debug", 311 | "z": "803455e.14d3d28", 312 | "d": true, 313 | "name": "opc output", 314 | "active": false, 315 | "tosidebar": true, 316 | "console": false, 317 | "tostatus": false, 318 | "complete": "true", 319 | "targetType": "full", 320 | "x": 770, 321 | "y": 700, 322 | "wires": [] 323 | }, 324 | { 325 | "id": "c41865e7d1104ade", 326 | "type": "debug", 327 | "z": "803455e.14d3d28", 328 | "d": true, 329 | "name": "output to influx", 330 | "active": false, 331 | "tosidebar": true, 332 | "console": false, 333 | "tostatus": false, 334 | "complete": "true", 335 | "targetType": "full", 336 | "x": 1010, 337 | "y": 700, 338 | "wires": [] 339 | }, 340 | { 341 | "id": "cbc4bb227efe36c6", 342 | "type": "inject", 343 | "z": "803455e.14d3d28", 344 | "d": true, 345 | "name": "powerdrive2-Temp", 346 | "repeat": "", 347 | "crontab": "", 348 | "once": false, 349 | "onceDelay": 0.1, 350 | "topic": "ns=1;s=temperatur2;datatype=Float", 351 | "payload": "1000", 352 | "payloadType": "num", 353 | "x": 230, 354 | "y": 840, 355 | "wires": [ 356 | [ 357 | "63347d527e8207ff" 358 | ] 359 | ] 360 | }, 361 | { 362 | "id": "8e359fccc4764064", 363 | "type": "inject", 364 | "z": "803455e.14d3d28", 365 | "d": true, 366 | "name": "powerdrive1", 367 | "repeat": "", 368 | "crontab": "", 369 | "once": false, 370 | "onceDelay": 0.1, 371 | "topic": "ns=1;s=powerdrive1;datatype=Float", 372 | "payload": "2000", 373 | "payloadType": "num", 374 | "x": 250, 375 | "y": 800, 376 | "wires": [ 377 | [ 378 | "63347d527e8207ff" 379 | ] 380 | ] 381 | }, 382 | { 383 | "id": "cc3560626b80a10b", 384 | "type": "inject", 385 | "z": "803455e.14d3d28", 386 | "d": true, 387 | "name": "powerdrive2", 388 | "repeat": "", 389 | "crontab": "", 390 | "once": false, 391 | "onceDelay": 0.1, 392 | "topic": "ns=1;s=powerdrive2;datatype=Float", 393 | "payload": "2000", 394 | "payloadType": "num", 395 | "x": 250, 396 | "y": 760, 397 | "wires": [ 398 | [ 399 | "63347d527e8207ff" 400 | ] 401 | ] 402 | }, 403 | { 404 | "id": "7ce4d28a7ad4d351", 405 | "type": "inject", 406 | "z": "803455e.14d3d28", 407 | "d": true, 408 | "name": "currentdrive1", 409 | "repeat": "", 410 | "crontab": "", 411 | "once": false, 412 | "onceDelay": 0.1, 413 | "topic": "ns=1;s=currentdrive1;datatype=Float", 414 | "payload": "1000", 415 | "payloadType": "num", 416 | "x": 240, 417 | "y": 720, 418 | "wires": [ 419 | [ 420 | "63347d527e8207ff" 421 | ] 422 | ] 423 | }, 424 | { 425 | "id": "959aea5922760439", 426 | "type": "inject", 427 | "z": "803455e.14d3d28", 428 | "d": true, 429 | "name": "voltagedrive1", 430 | "props": [ 431 | { 432 | "p": "payload", 433 | "v": "1000", 434 | "vt": "num" 435 | }, 436 | { 437 | "p": "topic", 438 | "v": "ns=1;s=voltagedrive1;datatype=Double", 439 | "vt": "str" 440 | } 441 | ], 442 | "repeat": "", 443 | "crontab": "", 444 | "once": false, 445 | "onceDelay": 0.1, 446 | "topic": "ns=1;s=voltagedrive1;datatype=Double", 447 | "payload": "1000", 448 | "payloadType": "num", 449 | "x": 240, 450 | "y": 680, 451 | "wires": [ 452 | [ 453 | "63347d527e8207ff" 454 | ] 455 | ] 456 | }, 457 | { 458 | "id": "63347d527e8207ff", 459 | "type": "OpcUa-Client", 460 | "z": "803455e.14d3d28", 461 | "d": true, 462 | "endpoint": "99d8c9c3d94c327e", 463 | "action": "subscribe", 464 | "deadbandtype": "a", 465 | "deadbandvalue": 1, 466 | "time": "1", 467 | "timeUnit": "s", 468 | "certificate": "n", 469 | "localfile": "", 470 | "localkeyfile": "", 471 | "folderName4PKI": "", 472 | "name": "", 473 | "x": 542, 474 | "y": 739, 475 | "wires": [ 476 | [ 477 | "1f383f974e86d387", 478 | "b04a16e38b71dffd" 479 | ] 480 | ] 481 | }, 482 | { 483 | "id": "52089d7cd8e56e3f", 484 | "type": "influxdb batch", 485 | "z": "803455e.14d3d28", 486 | "d": true, 487 | "influxdb": "4ad3ce8d.582c5", 488 | "precision": "", 489 | "retentionPolicy": "", 490 | "name": "write_data_influxdb", 491 | "database": "", 492 | "retentionPolicyV18Flux": "", 493 | "org": "siemens", 494 | "bucket": "edgedb", 495 | "x": 1010, 496 | "y": 780, 497 | "wires": [] 498 | }, 499 | { 500 | "id": "f4c9ffa958ec9c1a", 501 | "type": "comment", 502 | "z": "803455e.14d3d28", 503 | "name": "### Receive simulated data from a OPC UA Server Simulator and storage in influxdb ###", 504 | "info": "", 505 | "x": 450, 506 | "y": 620, 507 | "wires": [] 508 | }, 509 | { 510 | "id": "0c7cbde017b44877", 511 | "type": "link out", 512 | "z": "803455e.14d3d28", 513 | "d": true, 514 | "name": "data_gen", 515 | "links": [ 516 | "84d99c19.67892", 517 | "554edde8.db9efc" 518 | ], 519 | "x": 945, 520 | "y": 840, 521 | "wires": [] 522 | }, 523 | { 524 | "id": "4462f599.69bb0c", 525 | "type": "comment", 526 | "z": "56ac0cbb.978d6c", 527 | "name": "Calculate Total Power Consumption", 528 | "info": "", 529 | "x": 800, 530 | "y": 60, 531 | "wires": [] 532 | }, 533 | { 534 | "id": "33ca4fcc.54615", 535 | "type": "function", 536 | "z": "56ac0cbb.978d6c", 537 | "name": "Query-List", 538 | "func": "var a ='\"name\"'\n//msg.query=\"select * from powerdrive1 where \"+a+\"='powerdrive1'\"\nmsg.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive1\")\n |> last()`;\nreturn msg; \n/*\nCLI-Funktionen f?r die Influxdb (Auszug)\n\"select * from data1\"\n//Zeigt alle Daten im measuurement an\nSELECT * FROM data where time > '2018-08-09T08:20:39.96Z' and time <= now()\n//Anzeige der measuremebts in einem bestimmten Zeitraum\nselect * from data where value > 80 and value < 85 \n//Filterung der Daten nach Value \n*/", 539 | "outputs": 1, 540 | "noerr": 0, 541 | "initialize": "", 542 | "finalize": "", 543 | "x": 270, 544 | "y": 160, 545 | "wires": [ 546 | [ 547 | "2f2206bb.f13332" 548 | ] 549 | ] 550 | }, 551 | { 552 | "id": "fba4a9a5.e82c18", 553 | "type": "function", 554 | "z": "56ac0cbb.978d6c", 555 | "name": "Query-List", 556 | "func": "var a ='\"name\"'\n//msg.query=\"select * from powerdrive2 where \"+a+\"='powerdrive2'\"\nmsg.query= `from(bucket: \"edgedb\")\n |> range(start: -5m)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive2\")\n |> last()`;\nreturn msg; \n\n\n/*\nCLI-Funktionen f?r die Influxdb (Auszug)\n\"select * from data1\"\n//Zeigt alle Daten im measuurement an\nSELECT * FROM data where time > '2018-08-09T08:20:39.96Z' and time <= now()\n//Anzeige der measuremebts in einem bestimmten Zeitraum\nselect * from data where value > 80 and value < 85 \n//Filterung der Daten nach Value \n*/\n", 557 | "outputs": 1, 558 | "noerr": 0, 559 | "initialize": "", 560 | "finalize": "", 561 | "x": 270, 562 | "y": 220, 563 | "wires": [ 564 | [ 565 | "ad1edda4.a7224" 566 | ] 567 | ] 568 | }, 569 | { 570 | "id": "ad1edda4.a7224", 571 | "type": "influxdb in", 572 | "z": "56ac0cbb.978d6c", 573 | "influxdb": "4ad3ce8d.582c5", 574 | "name": "influxdb_query_power2", 575 | "query": "", 576 | "rawOutput": false, 577 | "precision": "", 578 | "retentionPolicy": "", 579 | "org": "siemens", 580 | "x": 490, 581 | "y": 220, 582 | "wires": [ 583 | [ 584 | "47df6e05.45bc98", 585 | "16716b3a.ae9815" 586 | ] 587 | ] 588 | }, 589 | { 590 | "id": "47df6e05.45bc98", 591 | "type": "function", 592 | "z": "56ac0cbb.978d6c", 593 | "name": "collect_last_power_values", 594 | "func": "context.data = context.data || {};\n\nswitch (msg.payload[0]._measurement) \n{\n case \"powerdrive1\":\n context.data.power1 = msg.payload[2]._value;\n msg = null;\n break;\n case \"powerdrive2\":\n context.data.power2= msg.payload[2]._value;\n msg = null;\n break;\n default:\n msg = null;\n \tbreak;\n}\n\nif(context.data.power1 !== null && context.data.power2 !== null) \n{\n\tmsg2 = {};\n msg2 = context.data;\n\n context.data=null;\n\treturn msg2;\n} \nelse\n{\n \n return msg; \n}\n", 595 | "outputs": 1, 596 | "noerr": 0, 597 | "initialize": "", 598 | "finalize": "", 599 | "x": 770, 600 | "y": 220, 601 | "wires": [ 602 | [ 603 | "2fd23fd2.bd1b5", 604 | "dc69781c.6bd958" 605 | ] 606 | ] 607 | }, 608 | { 609 | "id": "e874b51b.e7df48", 610 | "type": "function", 611 | "z": "56ac0cbb.978d6c", 612 | "name": "join_and_write_power_Influxdb", 613 | "func": "total = msg.payload;\n\n\n Beta = [{\n measurement: \"GEN_KPI_TotalPower\",\n fields:{\n name: \"TotalPower\",\n value:total,\n //weitereTags: -100 //(optional) nur 4 Datatypes sind in der Influxdb akzeptiert: Int,Float,String, Bool\n },\n timestamp: new Date()\n }]\n// Arr.push(Beta);\n//}\n\nmsg.payload = Beta;\nreturn msg;", 614 | "outputs": 1, 615 | "noerr": 0, 616 | "x": 1270, 617 | "y": 220, 618 | "wires": [ 619 | [ 620 | "4d3e876.90160f8", 621 | "9b10f05a.d8ee" 622 | ] 623 | ] 624 | }, 625 | { 626 | "id": "9b10f05a.d8ee", 627 | "type": "influxdb batch", 628 | "z": "56ac0cbb.978d6c", 629 | "influxdb": "4ad3ce8d.582c5", 630 | "precision": "", 631 | "retentionPolicy": "", 632 | "name": "influxdb_write", 633 | "database": "", 634 | "retentionPolicyV18Flux": "", 635 | "org": "siemens", 636 | "bucket": "edgedb", 637 | "x": 1520, 638 | "y": 180, 639 | "wires": [] 640 | }, 641 | { 642 | "id": "2f2206bb.f13332", 643 | "type": "influxdb in", 644 | "z": "56ac0cbb.978d6c", 645 | "influxdb": "4ad3ce8d.582c5", 646 | "name": "influxdb_query_power1", 647 | "query": "", 648 | "rawOutput": false, 649 | "precision": "", 650 | "retentionPolicy": "", 651 | "org": "siemens", 652 | "x": 490, 653 | "y": 160, 654 | "wires": [ 655 | [ 656 | "47df6e05.45bc98", 657 | "16716b3a.ae9815" 658 | ] 659 | ] 660 | }, 661 | { 662 | "id": "554edde8.db9efc", 663 | "type": "link in", 664 | "z": "56ac0cbb.978d6c", 665 | "name": "", 666 | "links": [ 667 | "3e5dc939.97a5b6", 668 | "edc207be.ce696", 669 | "0c7cbde017b44877" 670 | ], 671 | "x": 135, 672 | "y": 220, 673 | "wires": [ 674 | [ 675 | "33ca4fcc.54615", 676 | "fba4a9a5.e82c18" 677 | ] 678 | ] 679 | }, 680 | { 681 | "id": "2fd23fd2.bd1b5", 682 | "type": "function", 683 | "z": "56ac0cbb.978d6c", 684 | "name": "sum_total_power", 685 | "func": "var arr = [];\nvar mean=0;\nvar sum=0;\n\narr = Object.keys(msg).map(function(key) {\n return msg[key]\n })\narr.splice(-1,1)\n\nfor(var i=0; i < arr.length ; i++){\n sum = sum + arr[i];\n}\n// mean = sum / arr.length; \n\nmsg2 = new Object();\n//msg2.payload = (arr[0] + arr[1] + arr[2]) / arr.length; \n\nmsg2.payload = sum;\nreturn msg2;", 686 | "outputs": 1, 687 | "noerr": 0, 688 | "x": 1010, 689 | "y": 220, 690 | "wires": [ 691 | [ 692 | "e874b51b.e7df48" 693 | ] 694 | ] 695 | }, 696 | { 697 | "id": "4d3e876.90160f8", 698 | "type": "debug", 699 | "z": "56ac0cbb.978d6c", 700 | "name": "KPI-Power", 701 | "active": false, 702 | "tosidebar": true, 703 | "console": false, 704 | "tostatus": false, 705 | "complete": "payload", 706 | "targetType": "msg", 707 | "x": 1510, 708 | "y": 260, 709 | "wires": [] 710 | }, 711 | { 712 | "id": "dc69781c.6bd958", 713 | "type": "debug", 714 | "z": "56ac0cbb.978d6c", 715 | "name": "Last Power Values", 716 | "active": false, 717 | "tosidebar": true, 718 | "console": false, 719 | "tostatus": false, 720 | "complete": "true", 721 | "targetType": "full", 722 | "x": 1010, 723 | "y": 160, 724 | "wires": [] 725 | }, 726 | { 727 | "id": "6eac8115.8634e", 728 | "type": "debug", 729 | "z": "56ac0cbb.978d6c", 730 | "name": "KPI-Power", 731 | "active": false, 732 | "tosidebar": true, 733 | "console": false, 734 | "tostatus": false, 735 | "complete": "payload", 736 | "targetType": "msg", 737 | "statusVal": "", 738 | "statusType": "auto", 739 | "x": 1030, 740 | "y": 300, 741 | "wires": [] 742 | }, 743 | { 744 | "id": "16716b3a.ae9815", 745 | "type": "function", 746 | "z": "56ac0cbb.978d6c", 747 | "name": "collect_last_power_values", 748 | "func": "context.data = context.data || {};\n\nreturn msg", 749 | "outputs": 1, 750 | "noerr": 0, 751 | "initialize": "", 752 | "finalize": "", 753 | "x": 750, 754 | "y": 380, 755 | "wires": [ 756 | [ 757 | "6eac8115.8634e" 758 | ] 759 | ] 760 | }, 761 | { 762 | "id": "13ee085f.be8d98", 763 | "type": "influxdb in", 764 | "z": "f7e739bb.370638", 765 | "influxdb": "4ad3ce8d.582c5", 766 | "name": "query", 767 | "query": "", 768 | "rawOutput": false, 769 | "precision": "", 770 | "retentionPolicy": "", 771 | "org": "siemens", 772 | "x": 850, 773 | "y": 160, 774 | "wires": [ 775 | [ 776 | "5e1cdcc5.cf69a4" 777 | ] 778 | ] 779 | }, 780 | { 781 | "id": "69401cb2.ee9d44", 782 | "type": "debug", 783 | "z": "f7e739bb.370638", 784 | "name": "count", 785 | "active": false, 786 | "tosidebar": true, 787 | "console": false, 788 | "tostatus": false, 789 | "complete": "true", 790 | "targetType": "full", 791 | "x": 610, 792 | "y": 120, 793 | "wires": [] 794 | }, 795 | { 796 | "id": "84d99c19.67892", 797 | "type": "link in", 798 | "z": "f7e739bb.370638", 799 | "name": "input_raw_data", 800 | "links": [ 801 | "edc207be.ce696", 802 | "0c7cbde017b44877" 803 | ], 804 | "x": 55, 805 | "y": 160, 806 | "wires": [ 807 | [ 808 | "b64620ac.81fd3" 809 | ] 810 | ] 811 | }, 812 | { 813 | "id": "c231c8cd.975aa", 814 | "type": "influxdb in", 815 | "z": "f7e739bb.370638", 816 | "influxdb": "4ad3ce8d.582c5", 817 | "name": "query", 818 | "query": "", 819 | "rawOutput": false, 820 | "precision": "", 821 | "retentionPolicy": "", 822 | "org": "siemens", 823 | "x": 450, 824 | "y": 160, 825 | "wires": [ 826 | [ 827 | "69401cb2.ee9d44", 828 | "9d60b753.7fcfc" 829 | ] 830 | ] 831 | }, 832 | { 833 | "id": "3a59b4bb.74773c", 834 | "type": "function", 835 | "z": "f7e739bb.370638", 836 | "name": "COUNT", 837 | "func": "msgQueryCount = {}\nmsgQueryCount.measurement = msg.payload[0].measurement\n//msgQueryCount.query = 'SELECT COUNT(\"value\") FROM ' + msg.payload[0].measurement\n\nmsgQueryCount.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"powerdrive1\" or r[\"_measurement\"] == \"powerdrive2\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> count()`;\nreturn msgQueryCount;", 838 | "outputs": 1, 839 | "noerr": 0, 840 | "initialize": "", 841 | "finalize": "", 842 | "x": 300, 843 | "y": 160, 844 | "wires": [ 845 | [ 846 | "c231c8cd.975aa" 847 | ] 848 | ] 849 | }, 850 | { 851 | "id": "9d60b753.7fcfc", 852 | "type": "function", 853 | "z": "f7e739bb.370638", 854 | "name": "GET_LAST_ENTRIES", 855 | "func": "msgQueryGet = {}\nmsgQueryGet.measurement = msg.measurement\nif (msg.payload[0]._value >= 50) {\n //msgQueryGet.query = 'SELECT * FROM ' + msg.measurement + ' ORDER BY time DESC limit 50'\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"${msg.measurement}\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)\n |> group()`;\n return msgQueryGet;\n}\n\nelse {msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"${msg.measurement}\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> sort(columns: [\"_time\"], desc: true)\n |> group()`;\n return msgQueryGet;\n \n}\n\n\n", 856 | "outputs": 1, 857 | "noerr": 0, 858 | "initialize": "", 859 | "finalize": "", 860 | "x": 660, 861 | "y": 160, 862 | "wires": [ 863 | [ 864 | "13ee085f.be8d98" 865 | ] 866 | ] 867 | }, 868 | { 869 | "id": "c561e0d7.eb0998", 870 | "type": "comment", 871 | "z": "f7e739bb.370638", 872 | "name": "### check if measurement has enough entries and get last 5 values if true", 873 | "info": "", 874 | "x": 320, 875 | "y": 20, 876 | "wires": [] 877 | }, 878 | { 879 | "id": "7000bdea.5b3c8c", 880 | "type": "mqtt out", 881 | "z": "f7e739bb.370638", 882 | "name": "StandardKpis", 883 | "topic": "StandardKpis", 884 | "qos": "", 885 | "retain": "", 886 | "broker": "b6ec4c8d.61eac", 887 | "x": 1220, 888 | "y": 160, 889 | "wires": [] 890 | }, 891 | { 892 | "id": "5e1cdcc5.cf69a4", 893 | "type": "json", 894 | "z": "f7e739bb.370638", 895 | "name": "", 896 | "property": "payload", 897 | "action": "obj", 898 | "pretty": false, 899 | "x": 990, 900 | "y": 160, 901 | "wires": [ 902 | [ 903 | "408593f2.7ce534", 904 | "7000bdea.5b3c8c" 905 | ] 906 | ] 907 | }, 908 | { 909 | "id": "408593f2.7ce534", 910 | "type": "debug", 911 | "z": "f7e739bb.370638", 912 | "name": "response_get", 913 | "active": false, 914 | "tosidebar": true, 915 | "console": false, 916 | "tostatus": false, 917 | "complete": "true", 918 | "targetType": "full", 919 | "statusVal": "", 920 | "statusType": "auto", 921 | "x": 1220, 922 | "y": 220, 923 | "wires": [] 924 | }, 925 | { 926 | "id": "51e1820.ff6d8fc", 927 | "type": "comment", 928 | "z": "f7e739bb.370638", 929 | "name": "PowerDrive1 and PowerDrive2 last 50 entries", 930 | "info": "", 931 | "x": 210, 932 | "y": 80, 933 | "wires": [] 934 | }, 935 | { 936 | "id": "cc9de0f5.cdc4e", 937 | "type": "influxdb in", 938 | "z": "f7e739bb.370638", 939 | "influxdb": "4ad3ce8d.582c5", 940 | "name": "query", 941 | "query": "", 942 | "rawOutput": false, 943 | "precision": "", 944 | "retentionPolicy": "", 945 | "org": "siemens", 946 | "x": 830, 947 | "y": 400, 948 | "wires": [ 949 | [ 950 | "67b35a2b.c82f54" 951 | ] 952 | ] 953 | }, 954 | { 955 | "id": "b55ad1ad.1d31e8", 956 | "type": "debug", 957 | "z": "f7e739bb.370638", 958 | "name": "mqtt output", 959 | "active": false, 960 | "tosidebar": true, 961 | "console": false, 962 | "tostatus": false, 963 | "complete": "true", 964 | "targetType": "full", 965 | "statusVal": "", 966 | "statusType": "auto", 967 | "x": 1340, 968 | "y": 460, 969 | "wires": [] 970 | }, 971 | { 972 | "id": "6e9cf99d.f4721", 973 | "type": "influxdb in", 974 | "z": "f7e739bb.370638", 975 | "influxdb": "4ad3ce8d.582c5", 976 | "name": "query", 977 | "query": "from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"voltagedrive3\" and (r[\"_field\"] == \"value\"))\n |> count()", 978 | "rawOutput": false, 979 | "precision": "", 980 | "retentionPolicy": "", 981 | "org": "siemens", 982 | "x": 370, 983 | "y": 400, 984 | "wires": [ 985 | [ 986 | "e0680050.bd51a", 987 | "d40d07c0.067dc8" 988 | ] 989 | ] 990 | }, 991 | { 992 | "id": "e0680050.bd51a", 993 | "type": "function", 994 | "z": "f7e739bb.370638", 995 | "name": "GET_LAST_ENTRIES", 996 | "func": "// gets the value of n, if not yet set takes default value 50\nvar n = context.get('sample_n')|| 50;\n\n\n \n msgQueryGet = {}\n //msgQueryGet.measurement = msg.measurement\n msgQueryGet.measurement = 'voltagedrive3'\n if (msg.payload[0]._value >= n) {\n msgQueryGet.SampleNumber = n\n //msgQueryGet.query = 'SELECT \"value\" FROM voltagedrive3 ORDER BY time DESC limit ' + n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"voltagedrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)`;\n //msgQueryGet.query = 'SELECT \"value\" FROM ' + msg.measurement + ' limit ' + n\n return msgQueryGet;\n }\n else {\n msgQueryGet.SampleNumber = n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"voltagedrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)`;\n return msgQueryGet;\n }", 997 | "outputs": 1, 998 | "noerr": 0, 999 | "initialize": "", 1000 | "finalize": "", 1001 | "x": 600, 1002 | "y": 400, 1003 | "wires": [ 1004 | [ 1005 | "cc9de0f5.cdc4e", 1006 | "67cd84cb.b3012c" 1007 | ] 1008 | ] 1009 | }, 1010 | { 1011 | "id": "206e8e74.bad04a", 1012 | "type": "mqtt out", 1013 | "z": "f7e739bb.370638", 1014 | "name": "Mean", 1015 | "topic": "Mean", 1016 | "qos": "", 1017 | "retain": "", 1018 | "broker": "b6ec4c8d.61eac", 1019 | "x": 1320, 1020 | "y": 500, 1021 | "wires": [] 1022 | }, 1023 | { 1024 | "id": "587d20bd.56fc1", 1025 | "type": "comment", 1026 | "z": "f7e739bb.370638", 1027 | "name": "VoltageDrive3 dynamic sample of entries", 1028 | "info": "", 1029 | "x": 490, 1030 | "y": 360, 1031 | "wires": [] 1032 | }, 1033 | { 1034 | "id": "d73768e8.892c4", 1035 | "type": "influxdb in", 1036 | "z": "f7e739bb.370638", 1037 | "influxdb": "4ad3ce8d.582c5", 1038 | "name": "query", 1039 | "query": "", 1040 | "rawOutput": false, 1041 | "precision": "", 1042 | "retentionPolicy": "", 1043 | "org": "siemens", 1044 | "x": 810, 1045 | "y": 600, 1046 | "wires": [ 1047 | [ 1048 | "67b35a2b.c82f54" 1049 | ] 1050 | ] 1051 | }, 1052 | { 1053 | "id": "30279058.935348", 1054 | "type": "influxdb in", 1055 | "z": "f7e739bb.370638", 1056 | "influxdb": "4ad3ce8d.582c5", 1057 | "name": "query", 1058 | "query": "from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"currentdrive3\" and (r[\"_field\"] == \"value\"))\n |> count()", 1059 | "rawOutput": false, 1060 | "precision": "", 1061 | "retentionPolicy": "", 1062 | "org": "siemens", 1063 | "x": 370, 1064 | "y": 600, 1065 | "wires": [ 1066 | [ 1067 | "e483dac2.b82e5" 1068 | ] 1069 | ] 1070 | }, 1071 | { 1072 | "id": "e483dac2.b82e5", 1073 | "type": "function", 1074 | "z": "f7e739bb.370638", 1075 | "name": "GET_LAST_ENTRIES", 1076 | "func": "\n\n// gets the value of n, if not yet set takes default value 50\nvar n = context.get('sample_n')|| 50;\n\n\n \n msgQueryGet = {}\n //msgQueryGet.measurement = msg.measurement\n msgQueryGet.measurement = 'currentdrive3'\n if (msg.payload[0]._value >= n) {\n msgQueryGet.SampleNumber = n\n //msgQueryGet.query = 'SELECT \"value\" FROM currentdrive3 ORDER BY time DESC limit ' + n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"currentdrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n: 50)`;\n //msgQueryGet.query = 'SELECT \"value\" FROM ' + msg.measurement + ' limit ' + n\n return msgQueryGet;\n }\n else {\n msgQueryGet.SampleNumber = n\n msgQueryGet.query= `from(bucket: \"edgedb\")\n |> range(start: -5h)\n |> filter(fn: (r) => r[\"_measurement\"] == \"currentdrive3\" and (r[\"_field\"] == \"value\"))\n |> sort(columns: [\"_time\"], desc: true)`;\n return msgQueryGet;\n }", 1077 | "outputs": 1, 1078 | "noerr": 0, 1079 | "initialize": "", 1080 | "finalize": "", 1081 | "x": 600, 1082 | "y": 600, 1083 | "wires": [ 1084 | [ 1085 | "d73768e8.892c4" 1086 | ] 1087 | ] 1088 | }, 1089 | { 1090 | "id": "fe2bcb9b.e56fb8", 1091 | "type": "comment", 1092 | "z": "f7e739bb.370638", 1093 | "name": "CurrentDrive3 dynamic sample of entries", 1094 | "info": "", 1095 | "x": 490, 1096 | "y": 640, 1097 | "wires": [] 1098 | }, 1099 | { 1100 | "id": "b64620ac.81fd3", 1101 | "type": "switch", 1102 | "z": "f7e739bb.370638", 1103 | "name": "filter", 1104 | "property": "payload[0].measurement", 1105 | "propertyType": "msg", 1106 | "rules": [ 1107 | { 1108 | "t": "eq", 1109 | "v": "powerdrive1", 1110 | "vt": "str" 1111 | }, 1112 | { 1113 | "t": "eq", 1114 | "v": "powerdrive2", 1115 | "vt": "str" 1116 | }, 1117 | { 1118 | "t": "eq", 1119 | "v": "voltagedrive3", 1120 | "vt": "str" 1121 | }, 1122 | { 1123 | "t": "eq", 1124 | "v": "currentdrive3", 1125 | "vt": "str" 1126 | } 1127 | ], 1128 | "checkall": "true", 1129 | "repair": false, 1130 | "outputs": 4, 1131 | "x": 150, 1132 | "y": 160, 1133 | "wires": [ 1134 | [ 1135 | "3a59b4bb.74773c" 1136 | ], 1137 | [ 1138 | "3a59b4bb.74773c" 1139 | ], 1140 | [ 1141 | "6e9cf99d.f4721", 1142 | "4d240511.3cb51c" 1143 | ], 1144 | [ 1145 | "30279058.935348" 1146 | ] 1147 | ] 1148 | }, 1149 | { 1150 | "id": "67b35a2b.c82f54", 1151 | "type": "function", 1152 | "z": "f7e739bb.370638", 1153 | "name": "manual_join", 1154 | "func": "var tempo_cd3_batch = context.get('cd3_batch')||[];\nvar tempo_vd3_batch = context.get('vd3_batch')||[];\n\nfunction build_analytics_object(){\n data_obj = {\n sample_number : msg.SampleNumber,\n current_drive3_batch : tempo_cd3_batch,\n voltage_drive3_batch : tempo_vd3_batch,\n }\n return data_obj\n}\n\n\nif (msg.measurement == 'currentdrive3'){\n tempo_cd3_batch = msg.payload\n context.set('cd3_batch',tempo_cd3_batch)\n \n}\nelse if (msg.measurement == 'voltagedrive3') {\n tempo_vd3_batch = msg.payload\n context.set('vd3_batch',tempo_vd3_batch)\n}\n\nif (tempo_cd3_batch.length > 0 && tempo_vd3_batch.length >0 ) {\n py_obj={}\n py_obj.payload = build_analytics_object()\n tempo_cd3_batch = []\n tempo_vd3_batch = []\n context.set('cd3_batch', tempo_cd3_batch)\n context.set('vd3_batch', tempo_vd3_batch)\n \n return py_obj\n \n}\n\nelse{\n return\n}", 1155 | "outputs": 1, 1156 | "noerr": 0, 1157 | "initialize": "", 1158 | "finalize": "", 1159 | "x": 1100, 1160 | "y": 500, 1161 | "wires": [ 1162 | [ 1163 | "b55ad1ad.1d31e8", 1164 | "206e8e74.bad04a" 1165 | ] 1166 | ] 1167 | }, 1168 | { 1169 | "id": "49c977da.a0dce", 1170 | "type": "mqtt in", 1171 | "z": "f7e739bb.370638", 1172 | "name": "", 1173 | "topic": "StandardKpiResult", 1174 | "qos": "2", 1175 | "datatype": "auto", 1176 | "broker": "b6ec4c8d.61eac", 1177 | "inputs": 0, 1178 | "x": 130, 1179 | "y": 820, 1180 | "wires": [ 1181 | [ 1182 | "b119df7b.43a088" 1183 | ] 1184 | ] 1185 | }, 1186 | { 1187 | "id": "f85bea86.d12e9", 1188 | "type": "debug", 1189 | "z": "f7e739bb.370638", 1190 | "name": "response standard kpis", 1191 | "active": false, 1192 | "tosidebar": true, 1193 | "console": false, 1194 | "tostatus": false, 1195 | "complete": "true", 1196 | "targetType": "full", 1197 | "statusVal": "", 1198 | "statusType": "auto", 1199 | "x": 1230, 1200 | "y": 780, 1201 | "wires": [] 1202 | }, 1203 | { 1204 | "id": "b119df7b.43a088", 1205 | "type": "json", 1206 | "z": "f7e739bb.370638", 1207 | "name": "", 1208 | "property": "payload", 1209 | "action": "", 1210 | "pretty": false, 1211 | "x": 510, 1212 | "y": 820, 1213 | "wires": [ 1214 | [ 1215 | "fbd3d4ca.35021" 1216 | ] 1217 | ] 1218 | }, 1219 | { 1220 | "id": "fbd3d4ca.35021", 1221 | "type": "function", 1222 | "z": "f7e739bb.370638", 1223 | "name": "store data", 1224 | "func": "my_payload = {};\nmy_payload = msg.payload;\noutput_standardkpis = {}\noutput_standardkpis.measurement = my_payload.name.toUpperCase() + '_STANDARD_KPIS'\noutput_standardkpis.payload = {\n mean: Math.round(my_payload.mean_result * 1e2)/ 1e2,\n median: Math.round(my_payload.median_result * 1e2)/ 1e2,\n stddev: Math.round(my_payload.stddev_result * 1e2)/ 1e2,\n name: my_payload.name,\n}\n\nreturn output_standardkpis;", 1225 | "outputs": 1, 1226 | "noerr": 0, 1227 | "x": 870, 1228 | "y": 820, 1229 | "wires": [ 1230 | [ 1231 | "8736d8c4.6a8dd8", 1232 | "f85bea86.d12e9" 1233 | ] 1234 | ] 1235 | }, 1236 | { 1237 | "id": "8736d8c4.6a8dd8", 1238 | "type": "influxdb out", 1239 | "z": "f7e739bb.370638", 1240 | "influxdb": "4ad3ce8d.582c5", 1241 | "name": "write_influxdb", 1242 | "measurement": "", 1243 | "precision": "", 1244 | "retentionPolicy": "", 1245 | "database": "", 1246 | "retentionPolicyV18Flux": "", 1247 | "org": "siemens", 1248 | "bucket": "edgedb", 1249 | "x": 1200, 1250 | "y": 820, 1251 | "wires": [] 1252 | }, 1253 | { 1254 | "id": "e09b497.2d66eb8", 1255 | "type": "mqtt in", 1256 | "z": "f7e739bb.370638", 1257 | "name": "", 1258 | "topic": "MeanResult", 1259 | "qos": "2", 1260 | "datatype": "auto", 1261 | "broker": "b6ec4c8d.61eac", 1262 | "inputs": 0, 1263 | "x": 110, 1264 | "y": 960, 1265 | "wires": [ 1266 | [ 1267 | "9fe01ff4.3223f" 1268 | ] 1269 | ] 1270 | }, 1271 | { 1272 | "id": "e8bf4594.e0d068", 1273 | "type": "debug", 1274 | "z": "f7e739bb.370638", 1275 | "name": "response mean", 1276 | "active": true, 1277 | "tosidebar": true, 1278 | "console": false, 1279 | "tostatus": false, 1280 | "complete": "true", 1281 | "targetType": "full", 1282 | "x": 1210, 1283 | "y": 920, 1284 | "wires": [] 1285 | }, 1286 | { 1287 | "id": "9fe01ff4.3223f", 1288 | "type": "json", 1289 | "z": "f7e739bb.370638", 1290 | "name": "", 1291 | "property": "payload", 1292 | "action": "", 1293 | "pretty": false, 1294 | "x": 510, 1295 | "y": 960, 1296 | "wires": [ 1297 | [ 1298 | "e9d5781f.cd3a1" 1299 | ] 1300 | ] 1301 | }, 1302 | { 1303 | "id": "e9d5781f.cd3a1", 1304 | "type": "function", 1305 | "z": "f7e739bb.370638", 1306 | "name": "store data", 1307 | "func": "my_payload = {};\nmy_payload = msg.payload;\noutput_pd1_mean = {}\noutput_pd1_mean.measurement = my_payload.name.toUpperCase()\noutput_pd1_mean.payload = {\n value: Math.round(my_payload.power_mean_result * 1e2)/ 1e2,\n name: my_payload.name,\n}\n\nreturn output_pd1_mean;", 1308 | "outputs": 1, 1309 | "noerr": 0, 1310 | "initialize": "", 1311 | "finalize": "", 1312 | "x": 870, 1313 | "y": 960, 1314 | "wires": [ 1315 | [ 1316 | "36636dbd.e80eda", 1317 | "e8bf4594.e0d068" 1318 | ] 1319 | ] 1320 | }, 1321 | { 1322 | "id": "36636dbd.e80eda", 1323 | "type": "influxdb out", 1324 | "z": "f7e739bb.370638", 1325 | "influxdb": "4ad3ce8d.582c5", 1326 | "name": "write_influxdb", 1327 | "measurement": "", 1328 | "precision": "", 1329 | "retentionPolicy": "", 1330 | "database": "", 1331 | "retentionPolicyV18Flux": "", 1332 | "org": "siemens", 1333 | "bucket": "edgedb", 1334 | "x": 1200, 1335 | "y": 960, 1336 | "wires": [] 1337 | }, 1338 | { 1339 | "id": "bb22a94d.81ee48", 1340 | "type": "comment", 1341 | "z": "f7e739bb.370638", 1342 | "name": "Data Analytics Results", 1343 | "info": "", 1344 | "x": 540, 1345 | "y": 740, 1346 | "wires": [] 1347 | }, 1348 | { 1349 | "id": "4d240511.3cb51c", 1350 | "type": "debug", 1351 | "z": "f7e739bb.370638", 1352 | "name": "mqtt output", 1353 | "active": false, 1354 | "tosidebar": true, 1355 | "console": false, 1356 | "tostatus": false, 1357 | "complete": "true", 1358 | "targetType": "full", 1359 | "statusVal": "", 1360 | "statusType": "auto", 1361 | "x": 390, 1362 | "y": 280, 1363 | "wires": [] 1364 | }, 1365 | { 1366 | "id": "d40d07c0.067dc8", 1367 | "type": "debug", 1368 | "z": "f7e739bb.370638", 1369 | "name": "mqtt output", 1370 | "active": false, 1371 | "tosidebar": true, 1372 | "console": false, 1373 | "tostatus": false, 1374 | "complete": "true", 1375 | "targetType": "full", 1376 | "statusVal": "", 1377 | "statusType": "auto", 1378 | "x": 610, 1379 | "y": 300, 1380 | "wires": [] 1381 | }, 1382 | { 1383 | "id": "67cd84cb.b3012c", 1384 | "type": "debug", 1385 | "z": "f7e739bb.370638", 1386 | "name": "mqtt output", 1387 | "active": false, 1388 | "tosidebar": true, 1389 | "console": false, 1390 | "tostatus": false, 1391 | "complete": "true", 1392 | "targetType": "full", 1393 | "statusVal": "", 1394 | "statusType": "auto", 1395 | "x": 950, 1396 | "y": 300, 1397 | "wires": [] 1398 | } 1399 | ] --------------------------------------------------------------------------------