TI40

**Технології індустрії 4.0. Лабораторний практикум. ** Автор і лектор: Олександр Пупена

<- до лаборних робіт на основну сторінку курсу
   

Лабораторна робота №9. Основи роботи з Grafana.

Завдання лабораторної роботи навчитися основам роботи з Grafana у парі з базою даних часових рядів Influxdb.

Порядок виконання лабораторної роботи

1.Створення безкоштовного облікового запису на Grafana Cloud Portal

Рис.1. Портал Grafana Cloud Portal

Рис.2. Форма реєстрації на порталі Grafana

image-20231120141224406

image-20231120142357564

Рис.3. Запуск Grafana з порталу Grafana Cloud Portal

image-20231120144412785

2.Створення джерела даних в Grafana

2.1. Генерація API токена для доступу до бакету “firstbucket” в Influxdb з Grafana

Рис.4. Налаштування токена доступу

Запис.1. Генерація API токена для доступу до бакету “firstbucket”

2.2. Створення джерела даних Influxdb в Grafana

image-20231120145149486

Рис.5. Налаштування Grafana

image-20231120145420085

image-20231120224254147

Запис.2. Створення джерела даних в Grafana

У попередній лабораторній роботі в якості перегляду даних в InfluxDB використоувалася мова fluxQL (SQL подібна мова запитів). У цій та наступній роботі використоуватиметься більша потужна мова Flux.

image-20231120150141337

Запис.3. Створення джерела даних в Grafana

Після цього, якщо підключення вдалося, виведеться напис: “datasource is working. 1 buckets found”

image-20231120225052403

2.3. Відновлення запису даних в influxdb

3.Створення першого Dashboard

3.1. Створення загальної директорії для зберігання дашбоардів лабораторної роботи

image-20231120225535879

3.2. Створення дашбоарду

Після створення директорії, за замовчуванням, відкриваються дашбоарди, які зберігаються в даній папці. Оскільки вона пуста, пропонується створити новий дашбоард.

image-20231120230417848

image-20231120230655863

Рис.6. Збереження дашбоарду

3.3. Навігація в Grafana

Для навігації між різними директоріями та дашбоардами використовується головний пункт меню Dashboards

3.4. Редагування інформаційної панелі (Panel)

image-20231120231413076

Рис.7. Перехід в редагування панелі

3.4.1. Налаштування діапазону відображення та періодичності

image-20231120231959965

Рис.8. Редагування панелі

image-20231120232251679

Рис.9. Редагування панелі Data Source

from(bucket: "firstbucket")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r["_measurement"] == "Building")
  |> filter(fn: (r) => r["_field"] == "Temperature")
  |> filter(fn: (r) => r["device"] == "AirConditioner_1" or r["device"] == "AirConditioner_10" or r["device"] == "AirConditioner_2" or r["device"] == "AirConditioner_3" or r["device"] == "AirConditioner_4" or r["device"] == "AirConditioner_5" or r["device"] == "AirConditioner_6" or r["device"] == "AirConditioner_7" or r["device"] == "AirConditioner_8" or r["device"] == "AirConditioner_9")
  |> aggregateWindow(every: 1m, fn: mean, createEmpty: false)

Детальніше мова flux буде розглядатися в наступній лабораторній роботі. Тут трохи прокоментуємо що саме відбувається в цьому коді. Спочатку з бакета firstbucket вибираються дані, які далі обробляються в конвеєрній обробці, яка позначається |>. Спочатку вказується діапазон вибірки по часу, потім йде фільтрація даних за службовим полем _measurement, field, та тегом device (один з вказаних по or). З вибраними даними відбувається агрегація, де за кожну хвилину розраховується середнє і видається результат.

image-20231120232741146

Рис.10. Редагування панелі

image-20231120233028056

image-20231120233216673

Запис.4. Збереження дашбоарду

image-20231120233452354

Рис.11. Період оновлення дашбоарду

3.4.2. Налаштування зовнішнього вигляду графіків

image-20231120235014125

Запис.5. Налаштування властивості override

image-20231120235456065

3.4.3. Робота з панеллю в режимі виконання

Запис.6. Зміна часового діапазону для відображення

3.5. Налаштування змінної дашбоарду для динамічного вибору кондиціонера для відображення на панелі (аналогічно до змінних Influxdb)

3.6.1. Ознайомлення із можливостями Explore

Для відлагодження запитів до баз даних, Grafana надає окремий інструмент Explore.

image-20231121085839640

Рис.15. Explore

Рис.16. Explore

import "influxdata/influxdb/v1"
v1.tagValues(bucket: "firstbucket", tag: "device", predicate: (r) => true, start: -1y)

Рис.16. Результат запиту

3.6.2. Створення змінної device дашбоарду Main

Змінні дають можливість використовувати змінні властивості, якими оперує користувач. У цьому пункті буде створена змінна з іменем device, перелік значень якої буде зчитуватися з бакета, як це було зроблено вище. Далі користувач зможе вибирати одне або кілька значень для фільтрування вибірки.

image-20231121090519605

import "influxdata/influxdb/v1"
v1.tagValues(bucket: "firstbucket", tag: "device", predicate: (r) => true, start: -1y)

image-20231121090806363

Рис.17. Налаштування опцій запиту для змінної дашбоарду

3.6.3 Використання змінної device в запиті
|> filter(fn: (r) => r["device"] == "AirConditioner_1" or r["device"] == "AirConditioner_10" or r["device"] == "AirConditioner_2" or r["device"] == "AirConditioner_3" or r["device"] == "AirConditioner_4" or r["device"] == "AirConditioner_5" or r["device"] == "AirConditioner_6" or r["device"] == "AirConditioner_7" or r["device"] == "AirConditioner_8" or r["device"] == "AirConditioner_9")

на

|> filter(fn: (r) => r["device"]  =~ /^${device:regex}$/ )

Звернення до змінної реалізується за допомогою $+назва змінної. Щоб забезпечити множинний вибір девайсів для відображення на графіку, окрім звернення до змінної безпосередньо – застосували регулярний вираз.

Запис.8. Перевірка роботи змінних дашбоарду

3.7. Налаштування візуалізації Stat для відображення останнього значення температури в числовому вигляді

image-20231121092539476

Рис.18. Дашбоард Main

4.Створення дашбоарду журнал подій – Events

4.1. Організація запису змін станів кондиціонерів в Influxdb

[
    {
        "id": "40ebca3949a03fc6",
        "type": "inject",
        "z": "42cdb79f439ee463",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payloadType": "date",
        "x": 110,
        "y": 200,
        "wires": [
            [
                "deed70ad5094bdfe"
            ]
        ]
    },
    {
        "id": "deed70ad5094bdfe",
        "type": "function",
        "z": "42cdb79f439ee463",
        "name": "multiplesubscribe",
        "func": "msg.payload=10000;\nfor(i=1;i<11;i++){\n   msg.topic=`ns=3;s=AirConditioner_${i}.State`;\n   node.send(msg);\n    }\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 290,
        "y": 200,
        "wires": [
            [
                "1c7613ca498e131c"
            ]
        ]
    },
    {
        "id": "1c7613ca498e131c",
        "type": "OpcUa-Client",
        "z": "42cdb79f439ee463",
        "endpoint": "501a96153c7c8d57",
        "action": "subscribe",
        "deadbandtype": "a",
        "deadbandvalue": 1,
        "time": "1",
        "timeUnit": "s",
        "certificate": "n",
        "localfile": "",
        "localkeyfile": "",
        "securitymode": "None",
        "securitypolicy": "None",
        "folderName4PKI": "",
        "name": "",
        "x": 500,
        "y": 200,
        "wires": [
            [
                "500d758fa01f947b"
            ]
        ]
    },
    {
        "id": "500d758fa01f947b",
        "type": "switch",
        "z": "42cdb79f439ee463",
        "name": "",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "ns=3;s=AirConditioner",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 615,
        "y": 200,
        "wires": [
            [
                "8c90ebf4b87e9109"
            ]
        ],
        "l": false
    },
    {
        "id": "8c90ebf4b87e9109",
        "type": "function",
        "z": "42cdb79f439ee463",
        "name": "",
        "func": "let str=\"\";\nlet obj={};\nstr=msg.topic.replace(\"ns=3;s=\",\"\");\nobj.device=str.replace(\".State\",\"\");\nobj.fields={State:msg.payload};\nobj.timestamp=msg.serverTimestamp;\nmsg={};\nmsg.payload=obj;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 675,
        "y": 200,
        "wires": [
            [
                "a153c3bec6acf22a"
            ]
        ],
        "l": false
    },
    {
        "id": "a153c3bec6acf22a",
        "type": "join",
        "z": "42cdb79f439ee463",
        "name": "",
        "mode": "custom",
        "build": "array",
        "property": "payload",
        "propertyType": "msg",
        "key": "topic",
        "joiner": "\\n",
        "joinerType": "str",
        "accumulate": false,
        "timeout": "1",
        "count": "",
        "reduceRight": false,
        "reduceExp": "",
        "reduceInit": "",
        "reduceInitType": "num",
        "reduceFixup": "",
        "x": 775,
        "y": 200,
        "wires": [
            [
                "0ba540df0fcba817"
            ]
        ],
        "l": false
    },
    {
        "id": "0ba540df0fcba817",
        "type": "function",
        "z": "42cdb79f439ee463",
        "name": "msgtoinflux",
        "func": "let datastore=[];\nconst State = new Map([\n  [0, 'Вимкнено'],\n  [1, 'Увімкнено'],\n  ]);\nclass sample {\n  constructor(payload) {\n    this.data={\n        measurement:'Building_log',\n        tags: {\n            device: payload.device,\n        },\n        fields: {message:payload.device+\" \"+State.get(payload.fields.State)},\n        timestamp: new Date(payload.timestamp).getTime()\n    }\n  }\n}\nfor (let i=0; i<msg.payload.length;i++){\n    dataobj=new sample(msg.payload[i]);\n    datastore.push(dataobj.data);\n}\nmsg.payload = {\n    bucket:'firstbucket',\n    precision: 'ms',\n    data:datastore,\n};\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 930,
        "y": 200,
        "wires": [
            [
                "260f8e645e064f0c"
            ]
        ]
    },
    {
        "id": "501a96153c7c8d57",
        "type": "OpcUa-Endpoint",
        "endpoint": " opc.tcp://DESKTOP-E871FG5:48010",
        "secpol": "None",
        "secmode": "None",
        "login": false
    }
]

Рис.19. Нові вузли для node-red

Імпортовані вузли забезпечуються опитування стану 10-ти кондиціонерів і запис цього стану у вигляді текстового повідомлення в той самий бакет Influxdb (firstbucket) проте в інший measurementBuilding_log.

4.2. Створення дашбоарду Events

Рис.20. Посилання на дашбоард Main

&var-device=AirConditioner_1&var-device=AirConditioner_10&var-device=AirConditioner_2

from(bucket: "firstbucket")
  |> range(start: -1y)
  |> filter(fn: (r) => r["_measurement"] == "Building_log")
  |> filter(fn: (r) => r["_field"] == "message")
  |> group(columns:["_field"])
  |> sort(columns: ["_time"], desc: true) 
  |> drop(columns:["_measurement"])

Рис.21. Data link комірки таблиці

Запис.10. Генерація подій (вкл/викл кондиціонер)

5.Створення анотацій

5.1. Створення анотації вручну

Анотації дозволяють робити помітки на графіку з прив’язкою до часу, щоб забезпечити контекст.

Запис.11. Ручне створення анотацій

5.2. Створення автоматичних анотацій з прив’язкою до подій з журналу подій

from(bucket: "firstbucket")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r["_measurement"] == "Building_log")
  |> filter(fn: (r) => r["device"] =~ /^${device:regex}$/)
  |> filter(fn: (r) => r["_field"] == "message")

6.Означення стартового дашбоарду

Рис.22. Додавання дашбоарду до улюблених

Таким чином дашбоард додано до улюблених.

image-20231121115028713

image-20231121115214836

Рис.23. Налаштування акаунта

7.Повноекранний режим

image-20231121115508536

image-20231121120718016

Рис.24. Перехід в повноекранний режим