Ускорение процесса создания modbus.py


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


В настоящий момент мы немного поменяли концепцию построения системы опроса устройств с использованием языка python.

Большинство модулей SCADA систем строится по принципу связки «исполняемый файл — файл настройки».

Исполняемый файл — это как правило скомпилированный бинарный файл, который непосредственно занимается опросом подчиненных устройств по определенному протоколу, его еще могут называть «драйвером протокола».

Параметры опроса (порт, адреса, скорость и т.д.) для драйвера записываются в файл настройки, который бывает разных типов. Это может быть таблица или таблицы в какой-либо СУБД, например, часто используется SQLite или Firebird, может также использоваться файл xml.
Работая с различными устройствами, мы пришли к выводу, что было бы очень удобно иметь в наличие уже скомпилированный файл драйвера со всеми настройками (переменными) внутри, хорошо было бы еще иметь возможность корректировать драйвер уже на сервере при организации опроса устройств.

Для подобной цели рассматривался вариант создания приложения, которое на основе конфигурационного файла xml будет создавать драйвер на python.

Основное условие – программа, формирующая код драйвера, должна обязательно работать и в Windows и в Linux.

Первоначально решили сделать приложение на PyQt, но откровенно говоря, для нас оказалось довольно сложно писать программу в такой связке, не имея в наличие полноценной визуальной IDE для построения интерфейса пользователя.

Ранее приходилось часто делать программы на C Builder и Delfi, поэтому обратили свой взор в сторону Lazarus. Попробовали скомпилировать программы на разных ОС – результатом остались довольны.

Решили попытаться написать приложение на Lazarus IDE 1.8.2., назвали его ScadaPy Creator.
Все настройки конфигурации сохраняются в одном единственном файле xml.

image

После компиляции на выходе мы имеем драйвер modbus, драйвер счетчика Меркурий-230, сервер http Json и файл html в качестве клиента.

Как это работает


Запускаем драйвер modbus или Меркурий-230 для опроса устройств.

Получив ответы согласно описанным переменным, драйвер передает данные по UDP протоколу на порт 64000. Запускаем Json HTTP сервер, который в свою очередь получает данные от драйвера и формирует пакет в формате json для http запросов.

Запускаем в браузере html файл клиента и видим уже обработанные данные в трех форматах — json, текстовом и в виде SVG картинки.

Далее можно редактировать и html и SVG под свои нужды.

image

Мне, откровенно говоря, достаточно представления данных в виде простой таблицы, а для кого-то лучше информация воспринимается в виде мнемосхемы.

Подобный вариант использования python скриптов был неоднократно опробован на удаленных подстанциях, где установлены объектовые серверы на базе ОС Linux и доступ туда имелся только по SSH.

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

Вот типичный вариант, что называется, из последнего.

Задача:

Имеется устройство, работающее по протоколу modbus — датчик температуры и влажности Lumel P18.

image

Нужно получать значения температуры и влажность каждые пять минут и отображать на экране монитора в любом виде.

Устройство находится далеко, на расстоянии около 90 км от офиса.

Для начала создаем проект.

image

У Р18 температура берется по двум адресам 7002 и 7003, 32 бит, число с плавающей запятой, а влажность берется по двум адресам 7004 и 7005, 32 бит, число с плавающей запятой.

image

Кроме температуры и влажности, имеется возможность отображать и более развернутую информацию на объекте: относительная и абсолютная влажность, точка росы и т.д.

image

После генерации, будут созданы два файла modbus.py и jserver.py.

Отправляем эти файлы на сервер объекта, а на компьютере клиента запускаем jclient.html.

image

Можно конечно файл html переместить и на web сервер.

Вот в принципе и все.

Проект с примерами можно скачать здесь.

P.S.
Осталась одна проблема, решение которой мы пока не нашли.

Запущенный драйвер опроса отбирает 100% ресурсов процессора, если запустить два драйвера, то соответственно каждый отбирает по 50%. На работе объектового сервера это никак не отражается, но почему так?

Приветствуется любая критика, замечания и советы, но конструктивная и по существу.

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


  1. Siemargl
    01.05.2018 01:12

    дилетантство чистой воды. с изобретением велосипедов.
    «мы сделали поделку — но она работает плохо, почему так?»

    Чтобы не было травли, спрошу пруф утверждению

    Большинство модулей SCADA систем строится по принципу связки «исполняемый файл — файл настройки».

    В каких SCADA'х так сделано?


    1. jackmas Автор
      01.05.2018 09:12

      Очень хороший комментарий, особенно там где о велосипеде.
      Вы лично можете «травить», это нормально.
      Только почему вы сразу решили, что «поделка» работает плохо?
      Приведите пример системы где настройки нигде не хранятся.


      1. Siemargl
        02.05.2018 21:12

        Настройки хранятся в единой базе конфигурации. WinCC, Intouch, iFix, RSView, Kepserver.

        Вы сами описываете, что работает плохо — цикл 0,5с, загрузка ЦПУ 100%


        1. jackmas Автор
          02.05.2018 22:46

          Но вы же понимаете — суть не в названии.
          Пусть это будет единая база конфигурации, конфигурационный файл(ы) или таблица конфигурации, физически параметры структуры объекта где-то должны описываться и храниться.
          К тому же проблему с циклом решили, спасибо за подсказку Yak52.


  1. Yak52
    01.05.2018 10:48

    100% ресурсов как правило отбирает простой цикл. Какой период опроса датчика у вас?


    1. jackmas Автор
      01.05.2018 11:05

      От 0.5 до 1 сек

      Здесь код

      Каждый профиль запускается в отдельном thread, в нем цикл установлен и задержка
      time.sleep(float(timeOut[i]))

      Меня еще смущает в конце кода вот это
      ########################### treads block
      modb_4 = threading.Thread(target=Proc_4,args=(1,))
      modb_4.daemon = True
      modb_4.start()
      while True:
      pass


      Может быть проблема в цикле while?


  1. Yak52
    01.05.2018 11:23
    +1

    Проблема именно в нем. Создайте скрипт только с таким циклом и получите 100% загрузку процессора. Фактически этот цикл у вас необходим для того что бы создать подобие «демона» или «сервиса». Если вы основную обработку производите в отдельных порожденных потоках, то вам просто необходимо ожидать когда эти потоки завершатся. Посмотрите в сторону метода threading.Thread.join


    1. jackmas Автор
      01.05.2018 12:43

      Спасибо за совет, посмотрел в сторону join — все получилось.
      На виндовс отнимал 25% теперь берет только 0.3-0.4%.