А не пощупать ли нам OPC UA на практике?

— Хм? Отчего ж достопочтенным джентльменам нельзя пощупать. Давайте, господа гуссары, полапаем как следует!

Для этого дела воспользуемся Питоном, ибо он прост в применении. Будем юзать Python-3.

Для начала нужно установить на комп кое-какие пакеты.

Поставьте пакет pip, если он у вас еще не установлен.

$ sudo apt install python3-pip

Это инсталлятор для питоновских пакетов. Конечно, многие питоновские программы можно устанавливать традиционно — средствами операционной системы из системного репозотория. Но, я думаю, что питоновские проги всё же лучше устанавливать специализированным инструментом. Он лучше «понимает» свои задачи. К тому же репозиторий питоновских программ и репозиторий пакетов дистрибутива — это разные репозитории.

Короче, следующим шагом устанавливаем пару питоновских пакетов с помощью питоновского инсталлятора:

$ pip3 install freeopcua
$ pip3 install opcua-client

Пакет freeopcua — это наш самый главный пакет, в котором содержатся все необходимые классы для создания сервера и клиента.

Пакет opcua-client — это вьювер UPC UA-серверов. С помощью этой проги можно «подсмотреть» содержимое сервера и можно подправить то, что доступно для изменения со стороны клиента. В общем, этот есть инструмент.

Далее, создаем пару текстовых файлов. Это будут наши программы одна — сервер, другая клиент.

Сервер будет имитировать измерение напряжения в розетке и предоставлять доступ к измеренному значению клиентам. Пусть скорость измерения будет 3 раза в секунду. Напряжение будет имитироваться генератором случайных чисел. В процессе измерения сервер будет выводить полученные значения в консоль.

Клиентская программа будет обращаться к серверу раз в секунду и полученное от сервера значение выводить в свою консоль.

Сначала создадим сервер. Я назвал файл программы как volotage-serv.py .

#!/usr/bin/env python3

'''
voltage_serv.py

Сервер напряжения.
Иммитирует измерение напряжения и предоставляет значение для клиентов.
'''


URL = "opc.tcp://0.0.0.0:4840"


import sys
import random
import time

from opcua import Server


if __name__ == "__main__":
  server = Server()
  server.set_endpoint(URL)

  objects   = server.get_objects_node()
  ns        = server.register_namespace("Мои понятия")
  voltmeter = objects.add_object(ns, "Вольтметр")    
  voltage   = voltmeter.add_variable(ns, "Напряжение", 0.0)

  server.start()
    
  V = 220.0
  while True:            
    V = random.uniform(190.0, 240.0)
    print("{:8.1f} В".format(V))
    voltage.set_value(V)
    
    time.sleep(0.33)

  server.stop()

Что тут есть что?

URL — это описание сетевого ресурса. Описание состоит из трёх частей. Первая часть назначает протокол для обмена с другими сетевыми устройствами — «opc.tcp».

Вторая часть — «0.0.0.0» — IP-шный адрес. В этом поле указывается адрес клиента, с которого разрешается приём пакетов. Если вы не предполагает работу в сети (с несколькими сетевыми устройствами), то можете здесь указать IP-адрес своего компа (например, 192.168.0. 32). Можете вместо цифр вписать слово «localhost», операционка сама его конвертирует в IP-адрес. Только нужно помнить, что к вашему серверу никто, кроме вас, не сможет подключиться. Для того, чтобы сервер мог обслуживать запросы от других компов, нужно «обнулить» адрес.

Последнее поле «4840» — это порт, на который должны поступать запросы от OPC UA-клиентов.

Таким образом, команда server.set_endpoint(URL) создает точку для подключения клиентов.

Далее нам нужно зарегистрировать наше адресное пространство имён (ns) в структуре сервера. Затем мы должны в этом адресном пространстве создать объект — измерительный прибор voltmeter, а в нём — измеряемую величину voltage.

В общем, у нас должно получится следующее дерево:

После чего, можно запускать сервер в работу.

Сервер запускается в своём рабочем потоке, а это — в котором находится код нашей программы, — мы можем использовать для изменения значения переменной voltage.

В программе сервера присутствует ещё одна переменная — V. Это локальная переменная, она не особо нужна. Но здесь используется для наглядности. В цикле мы генерируем случайное целое число в диапазоне от 1900 до 2400, уменьшаем его в 10 раз (для получения случайного числа с плавающей точкой) и присваиваем результат этой локальной переменной.

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

Выход из программы сервера осуществляется по Ctrl-C. так же можно «прихлопнуть» процесс командой kill. Понятно, что программы так обычно не пишутся. Но здесь правильность написания я принёс в жертву прозрачности работы. Мне важно было показать сам принцип работы программы.

Сейчас, устанавливаем у файла атрибут исполняемого

$ chmod +x voltage-serv.py

и запускаем

$ ./voltage-serv.py

Через две-три секунды (в зависимости от вычислительной мощности вашего компа) в консоли появится следующая картинка:

Это значит, что сервак поднялся и заработал.

Теперь запускаем OPCUA-вьювер:

$ opcua-client

Появится вот такое окно:

В верхнем левом поле введите свой URL

и нажмите кнопку «Connect» (она справа).

Теперь последовательно раскройте веточки «Objects», «Вольтметр». Выделите объект «Напряжение» — в окне справа появятся его атрибуты

Понаблюдайте за значением Value. Оно статичное, но чтобы его подновить, нужно нажимать на кнопку «Refresh».

Но сейчас главное не это. Сейчас нам нужно обратить внимание на значение графе NodeId у объекта «Напряжение». На моём компе там записано «ns=2;i=2». У вас скорее всего тоже самое. Но не факт!

Эта строка — ни что иное, как идентификатор узла, который отвечает за напряжение. Этот узел расположен в пространстве имен 2 (ns=2) и имеет порядковый номер 2 (i=2). Эти параметры нам понадобятся в программе клиента.

У программы клиента название тоже не затейливое — voltage-cli.py .

#!/usr/bin/env python3

'''
voltage_cli.py

Напряжёметр -- клиент удалённого вольтметра.
'''


URL = "opc.tcp://localhost:4840"


import time
from opcua import Client


if __name__ == "__main__":
  client = Client(URL)
  client.connect()
  
  voltNode = client.get_node("ns=2;i=2")
  
  while True:
    voltage = voltNode.get_value()
    
    print("{0:.1f} В".format(voltage))
    
    time.sleep(1)

URL в программе клиента — этот несколько другой URL. Он немного отличается от URL-а сервера. Здесь URL служит для указания сетевого ресурса, к которому клиент должен подключиться.

voltNode — это узел, которая отвечает за напряжение. До этого узла можно добираться длинным путём, опрашивая ветки дерева на предмет наличия у них нужных нам подветок. А можно напрямую проявить «читерство» — узнать у вьювера точный идентификатор узла. Что мы, собственно, и сделали. Я пока не знаю, меняются ли от запуска к запуску идентификаторы узлов. Но вроде как это работает.

А далее всё просто. В цикле программы мы периодически запрашиваем у сервера значение напряжения и присваиваем его локальной переменной voltage. Затем эту переменную выводим в консоль и засыпаем на одну секунду, итак по кругу, пока юзер не нажмёт на Ctrl-C.

Ну, вот, вроде бы и всё, что хотел сказать. Играйтесь!

Вообще, интересно поиграться не только с одним компом, на котором крутятся и клиент, и сервер. Интересно разнести клиента и сервера на разные компы. Интересно запустить несколько клиентов. Особенно интересно запустить сервер не на настольном компе или нотике (на платформе Intel/Amd), а на Rasberry Pi (на платформе ARM). Короче, создать зоопарк и посмотреть, как это всё работает.

15 responses to “А не пощупать ли нам OPC UA на практике?

  1. Добрый день. На какой ОС запускали freeopcua? У меня на Ubuntu 14.04 LTS Desktop работать не захотел.

    • Игрался на обычном компе, на котором был Debian-10 (amd64), и запускал на Paspberry Pi (вроде 3-я версия). На RPi был установлен Raspbian, версию не помню.

      И там, и там запускалось и даже работало.

  2. Спасибо! Буду пробовать.

  3. К сожалению и на Debian 10.6.0 i386 не работает. При запуске клиента выдается сообщение:

    root@debian# opcua-client
    Traceback (most recent call last):
    File «/usr/local/bin/opcua-client», line 6, in
    from uaclient.mainwindow import main
    File «/usr/local/lib/python3.7/dist-packages/uaclient/mainwindow.py», line 10, in
    from PyQt5.QtCore import pyqtSignal, QTimer, Qt, QObject, QSettings, QItemSelection, QMimeData, QCoreApplication
    ModuleNotFoundError: No module named ‘PyQt5’

    • В сообщении говорится, что в системе отсутствует модуль PyQt5.

      Это не проблема OPC UA. Это вообще другой модуль. Этот модуль нужен для построения графических (оконных) программ и отображения на экране различных виджетов. Вам нужно его установить в систему.

      Модуль OPC UA никак не связан с модулем PyQt. В принципе, OPC UA работает и в консоли. Программист сам определяет тип программы, какая у него она будет — консольная или оконная. Вы, видимо, пишите оконную программу.

  4. Добрый день, уважаемый автор! Возможно Вы в курсе, поэтому спрошу… Есть ли возможность в данном случае заставить сервер OPC UA реагировать на изменение своей переменной? Т.е., изменилось значение переменной на сервере и он делает определенный действия. Заранее благодарю!

    • Эм-м… я не совсем понял вопрос. Поэтому, отвечу, как понял.

      Сервер OPCUA — это программа. Естественно, если значение переменной в программе изменяется, то программа знает, что у неё там происходит. Собственно, за частую так и бывает — это сама программа меняет значение своей переменной.

      Та реализация сервера OPC UA, с которой я имел дело имеет как бы две части — одна часть сервера (программы) библиотечная, а другая — код, который пишет программист.

      Специфика построения OPCUA-программ, как я понял, состоит в том, чтобы обычные переменные (привычные для программиста, которыми он оперирует в своей программе) поставить в жёсткую зависимость с переменными UPCUA. И, если Ваша программа изменяет значение какой-то своей переменной, то Вам нужно дописать код, чтобы и OPCUA-переменная тоже получала это же значение.

      Наверно как-то так. Ну, либо уточняйте.

    • Эм-м… я не совсем понял вопрос. Поэтому, отвечу, как понял.

      Сервер OPCUA — это программа. Естественно, если значение переменной в программе изменяется, то программа знает, что у неё там происходит. Собственно, за частую так и бывает — это сама программа меняет значение своей переменной.

      Та реализация сервера OPC UA, с которой я имел дело, имеет как бы две части — одна часть сервера (программы) библиотечная, а другая — код, который пишет программист.

      Специфика построения OPCUA-программ, как я понял, состоит в том, чтобы обычные переменные (привычные для программиста, которыми он оперирует в своей программе) поставить в жёсткую зависимость с переменными UPCUA. И, если Ваша программа изменяет значение какой-то своей переменной, то Вам нужно дописать код, чтобы и OPCUA-переменная тоже получала это же значение.

      Наверно как-то так. Ну, либо уточняйте свой вопрос.

  5. Добрый день! Я так понимаю, это клиент и просто имитация ОРС сервера. Вы не пробовали написать «настоящий» ОРС сервер на питоне? Или не знаете, где можно его найти

    • Добрый!
      Да. Здесь описаны простейшие примеры использования технологии OPCUA.

      Не очень понимаю, что Вы имеете ввиду под определением «настоящий сервер».

      Примерно два года назад, когда я знакомился с этой технологией, я написал пару тестовых проектов. Один так назывался «Хлебзавод», а второй — я уже и не помню, что там было и как называлось. Но тоже что-то типа автоматизации какого-то предприятия.

      Там была развёрнута многоуровневая система из клиентов и серверов. Исходные данные брались с датчиков (ток, напряжение, температура, счетчик импульсов — энкодер), а обработанная информация поступала на десктоп-комп, на котором крутилась программа и рисовала графики и картинки. В принципе всё работало. Поделиться проектами не могу, это не моя собственность, а предприятия, оплатившего эти разработки.

      Там только две сложности. Первая — это ужасные тормоза технологии OPCUA, плюс ещё сам Питон способствует этому. И второе — для реализации на микроконтроллерах (проекты писались не на Питоне, а на С++) типа STM32 нужно закладывать не просто «жирные» камни, а очень «жирные». Нужно как минимум оперативы от 150-160 кБайт. Лучше где-то 200-300 килобайт. И тактовая частота 180 МГц — это очень мало.

      В общем, технология — та ещё! Я считаю, что всё это разводилово потребителя на бабки (если вообще не производственная диверсия!) на слова «модная технология» и «Индустрия 4.0».

      Я уже более двух лет не занимаюсь этой технологией, так что я уже не в курсе где что есть.

      • Иван

        «Исходные данные брались с датчиков (ток, напряжение, температура, счетчик импульсов — энкодер)»
        Здравствуйте, Вы бы не могли чуть подробнее расписать как доставали данные? (ток, температура и т.п.) Спасибо за ответ

      • К сожалению, нет. Извините.

      • Странный конечно подход самостоятельно писать ОРС сервера на python и делать самодельные ПЛК на микроконтроллерах. Мало того, что это чудовищно не эффективно по трудозатратам, так и обслуживать это все будет тот еще геморрой, так как все это видимо уникальные разработки. Обычно берутся ПЛК, готовые ОРС сервера, SCADA и все собирается в единую систему автоматизации. Всякие системы отчетности и прочие навороты уже могут писаться отдельно.

      • Если вы видите противоречия, то означает, что вы чего не учитываете. (с) А.Капранов (вроде бы это его фраза.)

        Даже я — исполнитель «хотелок» руководителей компании, не знаю всех исходных данных. Мне была поставлена задача — поиграться с этой технологией и определить, сможет ли компания использовать её в своих разработках.

        Я честно поигрался и как «технарь» выдал руководству своё заключение. С тех пор к OPC UA я больше не подходил.

        Что касается фразы Капранова, то она призывает смотреть на мир шире. Рассматривайте не только техническую сторону аспекта (OPC UA) . Например, это может быть это неплохая тема для «попилов». А может быть это одно из проявления неравномерности распределения денежных средств в государстве — в каких-то областях денег вообще нет, и не понятно, как выпускать нужную для государства продукцию, а где-то денег хоть жопой ешь.

  6. Добрый день! Поддерживает ли протокол OPC UA запрос и получение информации о статусе ПЛК? RUN MODE или PROGRAM MODE?

Оставьте комментарий