История началась с того, что потребовалось создать демонстрационный проект SCADA на 50 000 тегов (точек или каналов) для потенциального клиента. Целью проекта было показать, что программный продукт SCADA достаточно производителен и удовлетворяет требованиям заказчика.

Хороший повод попробовать для демопроекта новое поколение SCADA-системы, одним из разработчиков которой является автор. Когда до демонстрации оставалось несколько дней, и неизбежность выполнения задачи стала очевидной, создаём новый проект. Добавляем в проект 1000 виртуальных устройств-симуляторов, генерирующих данные, создаём 50К+ каналов для хранения значений, запускаем… и на веб-клиенте созерцаем данные, которые обновляются один раз в несколько минут в неочевидной последовательности. О том, что было дальше, написана эта статья.

Аббревиатура SCADA достаточно известна и не требует расшифровки, при необходимости справочную информацию можно найти по ссылке.

Поиск узкого места

В первую очередь, необходимо определить причину проблемы. Чтобы читателю был понятен дальнейший ход мысли, на следующей схеме показано, из каких основных служб состоит программный комплекс SCADA.

Основные службы SCADA
Основные службы SCADA

Краткое описание служб SCADA:

  • Служба Коммуникатор опрашивает контроллеры. Передаёт данные измерений службе Сервер и принимает команды телеуправления.

  • Служба Сервер обеспечивает запись и чтение архивов, расчёт по формулам, выполнение скриптов. Сервер взаимодействует с клиентами (службами Коммуникатор и Веб-приложением), которые подключаются по бинарному протоколу приложений поверх TCP.

  • Веб-приложение предоставляет интерфейс оператора через браузер.

Все приложения пишут подробную информацию в текстовые логи, поэтому есть возможность проследить прохождение информации по цепочке снизу вверх – от контроллера, в нашем случае виртуального, до веб-клиента. Проверим журнал одной из линий связи:

Журнал линии связи
Журнал линии связи

Видим, что опрос выполняется с требуемой скоростью, примерно 5 сеансов в секунду (в конфигурации установлена задержка 200 мс после каждого сеанса). По другим линиям связи лог аналогичен, то есть в норме.

Примечание. Линия связи с программной точки зрения – это отдельный поток, взаимодействующий с приборами по выбранному каналу связи, например, последовательному порту или TCP.

Далее Коммуникатор добавляет полученные текущие данные в очередь и передаёт на Сервер. За это отвечает так называемый «источник данных» Server Data Source.

Источник данных Server Data Source
Источник данных Server Data Source

Очередь текущих данных начинает заполняться и чуть более, чем за 20 секунд переполняется, последующие данные теряются. Увеличение размера очереди не решает проблему, просто очередь большей длины переполняется медленнее. Похоже, причина проблемы обнаружена. Однако, стоит проверить оставшиеся элементы цепочки.

Как сказано в начале статьи, проект настраивался на новом поколении ПО SCADA, и специальных диагностических инструментов на тот момент не существовало. Открыв веб-приложение и построив несколько трендов, бегло убедились, что интерфейс отзывчив, тренды формируются менее, чем за секунду. Использование CPU службами SCADA также находилось в пределах нескольких %.

Таким образом, сделан предварительный вывод о том, что узким местом системы является передача текущих данных от службы Коммуникатор службе Сервер. Необходимо исследовать проблему подробнее и найти её решение, не забывая о том, что скоро демонстрация проекта.

Временное решение

Хотелось бы показать проект потенциальному заказчику на новом перспективном поколении SCADA, не откатываясь на текущую версию. Но исследование и ликвидация бутылочного горлышка, скорее всего, потребует больше времени, чем оставалось до демонстрации. 5 минут расфокусированного взгляда на монитор привели к такой догадке – если один «источник данных» не успевает передать нужный объём информации, то почему бы не попробовать несколько «источников данных» параллельно. И вторая резервная мысль – если данные не получается передать, то нужно их сгенерировать непосредственно на Сервере. Постараемся реализовать 1-й более честный вариант.

К счастью, настройка нашего ПО SCADA достаточно гибкая, что позволило создать следующую конфигурацию, возможность которой при проектировании ПО даже не предполагалась:

Источники данных, передающие информацию параллельно
Источники данных, передающие информацию параллельно

Скриншот выше означает, что в настройках Коммуникатора были созданы 10 «источников данных», каждый из которых отвечает за передачу данных от устройств одной линии связи на Сервер. Привязка к линии выполнена с помощью фильтра по номерам устройств.

Запускаем проект и убеждаемся в том, что идея сработала! Данные успевают передаваться на Сервер и оператор видит изменения значений на веб-странице – достаточно для демонстрации.

Исследование проблемы

Демонстрация завершена, заказчик отправился думать и сравнивать наш продукт с конкурентами. Пора решить проблему системно, тем более что пользователи из сообщества также обнаружили данную «особенность» опытным путём.

Среда испытаний

Испытания проводились на компьютере и ПО со следующими характеристиками:

Процессор

12th Gen Intel(R) Core(TM) i9-12900K   3.19 GHz

Оперативная память

32,0 ГБ

ОС

Windows 11 Pro, версия 21H2

Версия испытуемой SCADA

6.0.2

Для проведения испытаний разработаны следующие инструменты:

  1. Модуль ModPerformanceMonitor, который позволяет измерить скорость приёма данных различных видов, поступающих на Сервер.

  2. Консольное приложение DataWriter, отправляющее Серверу различные виды данных с заданным размером и скоростью.

Испытание №1

Вместо службы Коммуникатор будем использовать утилиту DataWriter, которая подключается к службе Сервер по TCP и в цикле передаёт данные заданного размера. Данные передаются в виде среза.

Срез – это объект, состоящий из метки времени и массива значений каналов на этот момент времени. Длина среза – количество каналов.

Канал – это именованный элемент информации, который обрабатывается SCADA,
имеющий уникальный номер. Синонимы канала в других SCADA-системах: тег, переменная или точка.

Результаты испытаний представлены ниже.

Длина среза

Текущие данные, срезов в секунду

Исторические данные, срезов в секунду

10

9,2

5,4

100

9,2

1,6

1000

9,2

0,2

Из таблицы видно, что скорость передачи текущих данных не зависит от длины среза, а скорость передачи исторических данных существенно зависит от неё (оптимизация передачи исторических данных выходит за рамки статьи).

Текущие и исторические данные – это разные виды информации в рамках SCADA-системы, которые обрабатываются по-разному. Текущие данные, имея метку времени, при получении Сервером помещаются в оперативную память. Каждый канал имеет только одно текущее значение. Исторические данные при получении Сервером записываются в архив, в файловую систему или в базу данных. Исторические данные одного канала за период времени образуют тренд канала.

Испытание №2

Определим, каким образом количество параллельных клиентов влияют на скорость передачи текущих данных между клиентом и службой Сервер.

В таблице ниже содержатся результаты при фиксированной длине среза равной 100 каналам.

Количество клиентов

Текущие данные, срезов в секунду

Скорость основного цикла, итераций в секунду

1

9,2

9,2

2

18,4

9,2

3

27,6

9,2

Ниже результаты аналогичного испытания при длине среза 1000. Результаты совпадают.

Количество клиентов

Текущие данные, срезов в секунду

Скорость основного цикла, итераций в секунду

1

9,2

9,2

2

18,4

9,2

3

27,6

9,2

Скорость основного цикла измеряется с помощью модуля ModPerformanceMonitor, работа которого показана на скриншоте ниже. В основном цикле Сервер производит обработку данных, например, расчёт по формулам, поэтому скорость основного цикла (или длительность его итерации) – важный параметр, показывающий не находится ли система в «ступоре».

Работа модуля Performance Monitor
Работа модуля Performance Monitor

Таким образом, скорость передачи данных возрастает кратно количеству клиентов. По крайней мере, данное утверждение верно для нескольких клиентов. Это означает, что обработка текущих данных не является узким местом, а корректно наше начальное заключение, что узким местом является именно передача данных.

Скорость основного цикла не снижается при увеличении длины среза и количества клиентов – здесь норма.

Примечание. Тестирование системы на предельное количество одновременно подключенных клиентов является задачей другого исследования.

Оптимизация передачи данных

Повторюсь, что Сервер принимает TCP подключения от клиентов, открывая TCP-порт на прослушивание. Клиент подключается, инициирует запрос и получает ответ от Сервера. Поверх TCP используется бинарный протокол обмена данными, имеющий пакеты следующего вида:

Protocol Header

Protocol Data Unit (PDU)

Transaction ID

2 bytes

Length

4 bytes

Session ID

8 bytes

Function ID

2 bytes

Function Arguments

N bytes

Не буду расшифровывать поля пакета, чтобы не утомлять читателя.

Исходный код проекта насчитывает несколько сотен тысяч строк, поэтому поиск частей кода, которые требуется оптимизировать, должен быть основан на общем знании кода и информации, полученной в результате испытаний. Мы обнаружили, что количество обрабатываемых запросов от TCP-клиента, по сути, фиксировано. Клиентское приложение DataWriter отправляет запросы с той максимальной частотой, которая ограничивается временем ожидания ответа от Сервера. Следовательно, начать проверку кода было бы разумно с класса, который обрабатывает запросы клиентов.

Базовым классом, прослушивающим соединение и отвечающим на запросы клиентов, является ListenerBase (ссылка на GitHub, если ссылка нарушает правила публикации, просьба модератору её удалить). Оказалось, что после приёма данных от клиента вызывается задержка потока равная 100 мс. То есть обработка следующего клиентского запроса начнётся не ранее, чем через 100 мс после завершения обработки предыдущего. С этим связана фиксированная скорость приёма текущих данных 9,2 запроса в секунду, не зависящая от длины среза, и кратное увеличение скорости при увеличении количества клиентов.

Установим задержку в потоке подключенного клиента равной 10 мс. При такой задержке Сервер максимально сможет обработать 100 запросов в секунду от 1 клиента.

Ниже приведены результаты испытаний, аналогичных предыдущим, после уменьшения задержки в потоке клиента до 10 мс. Длина среза 100.

Количество клиентов

Текущие данные, срезов в секунду

Скорость основного цикла, итераций в секунду

1

64,4

9,2

2

128,7

9,2

3

193,2

9,2

Аналогичное испытание. Задержка 10 мс. Длина среза 1000.

Количество клиентов

Текущие данные, срезов в секунду

Скорость основного цикла, итераций в секунду

1

64,4

9,2

2

128,6

9,2

3

193,3

9,2

Суммарная скорость также кратна количеству клиентов и скорость основного цикла стабильна при небольшом количестве клиентов. Загрузка CPU службой ScadaServerWkr составляет менее 1%.

Промежуточный вывод: используя 1 клиентское подключение, удалось передать 64,4 среза секунду или 64400 точек в секунду, что близко к 4 млн точек в минуту. Такая скорость устраивает, но требуется достичь сопоставимых показателей совместно с Коммуникатором, а не утилитой DataWriter.

В чём недостаток протокола обмена данными в той версии, которая была на момент обнаружения проблемы? Дело в том, что при передаче текущих данных один пакет протокола приложений содержал только один срез. Если срез имеет длину 1000 или выше, тогда мы достигаем показателя 64К точек в секунду. А если, как в исходном демопроекте, длина среза менее 10? Тогда и скорость на несколько порядков меньше.

Решение достаточно очевидно – необходимо изменить протокол обмена данными между приложениями таким образом, чтобы один пакет протокола содержал переменное количество срезов. Источник данных Server Data Source, который упоминался в начале статьи, должен упаковывать срезы из своей очереди в пакеты, контролируя суммарную длину срезов в пакете.

Подтверждением данного подхода служит клиент популярной базы данных временных рядов InfluxDB, который имеет следующие параметры (ссылка):

  • BatchSize – количество точек данных для сбора в пакете,

  • FlushInterval – время до записи пакета.

Результаты оптимизации

В результате передача данных каналов была модернизирована следующим образом:

  1. Команды записи текущих и исторических данных в протоколе приложений были объединены в одну команду записи данных каналов. Эта новая команда позволяет передавать на Сервер сразу список срезов.

  2. Обновлён драйвер «источника данных». Из очереди извлекаются срезы текущих данных, имеющие суммарное количество каналов до 5000, затем они передаются одним пакетом. За одну итерацию цикла драйвера передаётся 10 пакетов. Передача исторических данных и событий не менялась.

Параметры суммарной длина срезов (5000) и количества пакетов за итерацию (10) были подобраны опытным путём, чтобы достичь целевого показателя – не менее 64 пакета в секунду. Цель – «хорошая» скорость передачи, а не максимально возможная. Под хорошей скоростью понимается скорость, достаточная для решения задач клиентов с некоторым запасом.

Описанные изменения в совокупности с оптимизацией программных блокировок Сервера (о блокировках в другой раз) позволили передавать из Коммуникатора на Сервер около 80000 каналов в секунду, используя 1 клиентское подключение. Например, для проекта, содержащего 1 млн каналов, для обновления всех секущих данных потребуется 12,5 секунд, если контроллеры предоставят такой поток измерений.

В разделе Среда испытаний упоминается версия ПО SCADA, с которой начиналось тестирование, поэтому стоит отметить, что изменения вошли в новую версию программного продукта 6.1.0.

Планы на будущее

Для комплексной оценки быстродействия ПО SCADA будет полезно провести следующие испытания:

  1. Сравнение быстродействия исторических архивов, использующих различные средства хранения данных – файловую систему, СУБД PostgreSQL и InfluxDB.

  2. Нагрузочное тестирование REST API и веб-приложения.

P.S. В комментариях было бы интересно узнать о Вашем опыте, связанном с производительностью SCADA-систем, систем мониторинга и IoT, и любых систем, к быстродействию которых Вы причастны. Конструктивная критика статьи принимается.

Комментарии (5)


  1. Yorique
    05.04.2023 19:05

    Раз название этой великолепной скады нового поколения нигде не указано - напишу: Rapid SCADA.


  1. Willy64
    05.04.2023 19:05

    Проблема в сегменте SCADA основном в наличии хороших библиотек, под все существующие русские скады приходится писать свои компонеты (сужу по своей сфере деятельности), затрачивая на это большое количество времени и нервов. Другая сложность в тенденции к отвязке от Windows, многие хорошие продукты намертво прибиты к ней.


    1. 2mik Автор
      05.04.2023 19:05

      Наиболее сложно, на мой взгляд, отвязать от Windows срезу разработки, имеющую относительно сложный UI. Серверную часть (runtime) многие отвязали.


  1. AndreyDmitriev
    05.04.2023 19:05

    Любопытно - а как (по какому протоколу) данные из ПЛК 1,2,... N поступают в "Коммуникатор" в данном конкретном случае? OPC UA? Какие протоколы поддерживаются в общем случае?


    1. 2mik Автор
      05.04.2023 19:05

      Для данных испытаний использовался драйвер-симулятор, который не опрашивает физический ПЛК, а генерирует значения. В общем случае поддерживаются Modbus, MQTT, OPC Classic, OPC UA, SNMP, нестандартные протоколы некоторых приборов. Люди из сообщества тоже разрабатывают драйверы, как верно заметили в предыдущем комментарии.