Содержание
Здесь описывается сервер
opcuasrv, являющийся сервером OPCUA
общего назначения для пакета crwdaq.
Сервер обеспечивает обмен данными между пакетом crwdaq
и клиентами OPCUA, к которому они подключаются.
Сервер состоит из:
Сначала ознакомьтесь с терминологией
OPCUA.
Затем узнайте как работает
сервер.
См. также заметки по
OPCUA.
См. также монитор opcuamon.
opc.tcp://localhost:4840.http://opcfoundation.org. Обычно
используются имена, похожиеns=2;i=2003 по шаблону
/^ns=(\d+);(i=\d+)$/i./^ns=(\d+);(i=\d+|s=\S+|g=\S+)$/i.ns=2;i=2003 или
ns=2;s=Tag321.ns2i2003 по шаблону
/^ns(\d+)(i\d+)$/i,/^ns(\d+)(i\d+|s\S+|g\S+)$/i.ns2i2003 или
ns2sTag321.Псевдонимы используются в программе сервера OpcuaSrv для понятности и читабельности, при выполнении программы псевдонимы автоматически заменяются на соответствующие индексы узлов. Псевдонимы должны быть простыми именами по правилам Pascal и не должны быть идентификаторами узлов, т.к. весь смысл псевдонимов состоит в замене nodeId на понятное (для программистов) читабельное имя.
Сервер OPCUA работает с формализованной моделью данных.
С теоретической точки зрения модель данных - это формализованное описание данных, которые публикует сервер, в виде сложно структурированной древовидной иерархии объектов, включающей описание типов, переменных, свойств, методов и других элементов.
С практической точки зрения модель данных - это файл
формата .xml, который содержит описание
публикуемых сервером данных.
Для создания и редактирования моделей данных есть редактор, который можно вызывать через меню “Старт/Панель Управления OPCUA”, либо командой:
unix grun pyvenv opcua-modeler # способ 1
unix opcua-cpl opcua-modeler-gui # способ 2При создании/редактировании узлов-переменных (в которых, собственно, и находятся данные), следует обращать особое внимание на следующие атрибуты (в окне Attributes editor):
По умолчанию переменные создаются с правами чтения (для клиентов).
Если есть необходимость сделать переменные с правами записи (для
клиентов), то нужно либо задать флаг
AccessLevel/CurrentWrite в модели данных, либо
использовать команду @OpcWritable ….
Программа opcuasrv.py
используется в готовом виде, “как есть”.
Всё конфигурирование происходит на уровне оболочки opcuasrv.pas.
Оболочка полностью отвечает за интеграцию сервера в пакет crwdaq.
Для использования сервера нужно правильно сконфигурировать оболочку
opcuasrv.pas.
При конфигурировании описываются подключения узлов
OPCUA к кривым и тегами пакета crwdaq,
а также способы их опроса и обновления.
.xml файла с описанием данных, их имен,
типов и т.д.unix pyvenv opcua-modeler..xml с моделью данных помещается
в папку конфигурации прикладной DAQ системы..xml файла и публикует
эти данные.@OpcuaNodeRead.@OpcuaNodeSend.Работая с программами монитора (клиента) opcuamon и сервера opcuasrv всегда надо помнить, что в клиент-серверных программах всегда есть несколько копий данных:
В нормальной ситуации сервер и клиент стараются синхронизовать (обновлять) данные как можно скорее, чтобы все копии данных соответствовали оригиналу (исходным данным сервера). Однако, поскольку сервер и клиент работают в разных процессах, а приемник и передатчик - в разных потоках, всегда существуют задержки с обновлениями, а потому эти четыре копии данных могут отличаться друг от друга. Больше того, в некоторых ситуациях копии гарантированно отличаются. Например, когда клиент инициирует команду записи, заранее предполагается, что текущая копия данных клиента отличается от (желаемых) данных сервера. Целью записи является именно сстремление изменить данные сервера, чтобы затем сервер, прислав обновление клиенту, изменил копию данных клиента на желаемое значение. Больше того, в некоторых ситуациях копии гарантированно отличаются. Например, когда клиент инициирует команду записи, заранее предполагается, что текущая копия данных клиента отличается от (желаемых) данных сервера. Целью записи является именно сстремление изменить данные сервера, чтобы затем сервер, прислав обновление клиенту, изменил копию данных клиента на желаемое значение.
В нашем случае имеется четыре возможные копии данных:
Для понимания работы программ opcuasrv и opcuamon необходимо понимать, что всякое изменение данных происходит путем последовательного изменения (синхронизации) указанных копий данных.
Например, если клиент вызывал команду
@OpcNodeSend WaveStarted 1, происходит
примерно следующее:
@OpcDataSend node 1В случае подключения к другому (внешнему) серверу OPCUA вместо серверной программы opcuasrv.py и её оболочки opcuasrv.pas будет выступать внешний сервер (например, SCADA). Но логика остается примерно такой же. Понимание этой логики (наличие нескольких копий данных и синхронизация между ними) позволит избежать ошибок.
Оболочка сервера opcuasrv.pas содержит программу DaqPascal, которая отвечает за приемопередачу данных OPCUA от серверной программы opcuasrv.py на стороне пакета crwdaq. Задачей оболочки opcuasrv.pas является интеграция сервера в пакет crwdaq, включая функции запуска остановки, конфигурирования и обмена данными с серверной программой opcuasrv.py.
Конфигурирование opcuasrv.pas включает в себя:
Имеется пример demo_opcuamon демонстрационной конфигурации для иллюстрации работы сервера opcuasrv. В конфигурации имеется справка.
Для использования сервера opcuasrv надо создать
DAQ устройство - например,
&OpcuaSrv.
Самый простой способ - использовать default конфигурацию
opcuasrv.cfg для
стандартного сервера &OpcuaSrv.
Для этого достаточно добавить в конфигурацию код:
[ConfigFileList] ; Default config for &OpcuaSrv
ConfigFile = ~~\resource\daqsite\default\opcuasrv.cfg
[]Стандартная конфигурация хорошо подходит для большинства прикладных задач.
Можно также сконфигурировать устройство вручную.
Пример конфигурации устройства
&OpcuaSrv:
[DeviceList]
&OpcuaSrv = device software program
[&OpcuaSrv]
Comment = OPCUA_SERVING_PROGRAM
InquiryPeriod = 1
DevicePolling = 10, tpNormal
ProgramSource = ~~\resource\daqsite\opcuaserver\opcuasrv.pas
DebugFlags = 3
OpenConsole = 2
StdInFifo = 128
StdOutFifo = 128
AnalogFifo = 1000
DigitalFifo = 1000
tagStdPyAppStat = Device/&OpcuaSrv/StdPyApp.Stat
tagOpcuaSrvStat = Device/&OpcuaSrv/OpcuaSrv.Stat
StartupScript = [&OpcuaSrv.StartupScript]
FinallyScript = [&OpcuaSrv.FinallyScript]
StartServingSection = [&OpcuaSrv.StartServing]
StartServingPattern = /^(@Opc\w+.*|@Py\w+\s.*)$/i
StartServingTrigger = Welcome to opcuasrv program.
DelayServingTrigger = 5000
FilterTooltipPattern = /^(info:|information:|warn:|warning:|error:|@warning|@error)\s/i
FilterConsolePattern = /^(info:|information:|warn:|warning:|error:)\s/i
tipInfoDelay = 15000
tipWarnDelay = 30000
tipErroDelay = 60000
[]
[TagList]
Device/&OpcuaSrv/StdPyApp.Stat = string 0
Device/&OpcuaSrv/OpcuaSrv.Stat = string 0
[]Кроме обычных параметров устройства задаются:
Тег tagStdPyAppStat определен так, чтобы
гарантированно избежать конфликта имен.
Этот тег содержит строку статистики в формате cookie
списка:
rss=RSS;vmd=VMS;gc=GE,OB,C0,C1,C2,GG,GW;poll=PC,PR,PW;ping=PT,PW;
где:
- RSS - resident set size - размер резидентной
памяти программы opcuasrv.py в байтах,
- VMS - virtual memory size - размер
виртуальной памяти программы opcuasrv.py в
байтах,
- GE - флаг (0/1) разрешения сборщика
мусора gc.isenabled(),
- OB - счетчик всех (динамических) объектов
len(gc.get_objects()),
- C0 - счетчик сборщика мусора для
0-го поколения объектов
gc.getcount()[0],
- C1 - счетчик сборщика мусора для
1-го поколения объектов
gc.getcount()[1],
- C2 - счетчик сборщика мусора для
2-го поколения объектов
gc.getcount()[2],
- GG - счетчик сборщика мусора для неопознанных
объектов len(gc.garbage),
- GW - метка времени - когда обновлялась информация о
памяти и сборщике мусора,
- PC - poll count - счетчик циклов опроса
программы opcuasrv.py,
- PR - poll rate - частота циклов опроса
программы opcuasrv.py,
- PW - poll when - метка времени когда
обновлялись счетчики опроса,
- PT - ping time - время запроса-ответа в
миллисекундах,
- PW - ping when - метка времени когда
обновляялся ping.
Тег tagOpcuaSrvStat определен так, чтобы
гарантированно избежать конфликта имен.
Этот тег содержит строку статистики в формате cookie
списка:
State=ST;ReadRate=RR;SendRate=SR;TimeStamp=TS;
где:
- ST - статус процесса:
DEAD,STARTING,PREPARING,RUNNING,STOPPED,RESTARTING.
- RR - частота чтения данных, операций в секунду,
- SR - частота записи данных, операций в секунду,
- TS - метка времени, ms.
Тег tagStdPyAppStat позволяет клиентской программе наблюдать за работой программы opcuasrv.py, чтобы была возможность изучать статистику её работы. При необходимости статистика извлекается из строки.
В этих секциях описываются действия при старте и завершении работы
DAQ-системы.
Секция завершения FinallyScript обычно пуста, т.к.
после работы делать нечего.
Секция StartupScript должна содержать команды запуска
программы opcuasrv.py.
Стандартный вид секции инициализации приведен ниже:
[&OpcuaSrv.StartupScript] ; PyApp startup:
@PyApp Set Launch unix pyvenv ; Command to launch script
@PyApp Set Script ~~/resource/daqsite/opcuaserver/opcuasrv.py ; Target script pathname
@PyApp Set Params ; Script parameters
@PyApp Set PingCall @PollCount ; Command on ping task
@PyApp Add PingCall @Memory ; Command on ping task
@PyApp Set AutoStart 1 ; Enable AutoStart
@PyApp Set PipeSizeKb 128 ; Pipe buffer size, KB
@PyApp Set PreferToSend 0 ; 0:devPost, 1:devSend
@PyApp Set TimeOutToSend 100 ; Timeout to send data, ms
@PyApp Set TimeOutToStop 1000 ; Timeout to stop task, ms
@PyApp Set GuardTimerPeriod 5000 ; Timer period to AutoStart
@PyApp Set PingTimerPeriod 1000 ; Timer period to PingCall
@PyApp Set CalcPing 1 ; Flag - calc Ping time
[]
[&OpcUaMon.FinallyScript]
[]В обычной ситуации нет необходимости менять эти секции.
Псевдонимы узлов используются для того, чтобы иметь возможность оперировать понятными (читабельными) именами узлов данных вместо трудно понимаемых индексных идентификаторов. Псевдонимы позволяют сосредоточить все проблемы подключения данных в одном месте, в описании псевдонимов, а дальше работать в терминах предметной области, используя понятные для людей имена узлов.
Псевдонимы узлов задаются в секции устройства (например [&OpcuaSrv]) в формате строк:
Alias name = nodeгде
Например:
[&OpcuaSrv]
;***************************
;**** Alias to Node mapping:
;***************************
Alias WaveSin = ns2i2003
Alias WaveCos = ns2i2004
Alias WaveAmplitude = ns2i2005
Alias WaveFrequency = ns2i2006
Alias WaveNoise = ns2i2007
Alias WaveStarted = ns2i2008
[]При выполнении программы сервера каждый псевдоним заменяется на короткий индексный идентификатор узла.
Настоятельно рекомендуется задавать псевдонимы для всех используемых узлов и работать с псевдонимами, а не с индексными идентификаторами. Это облегчит написание кода и его сопровождение.
Список связей задается в секции описания устройства, например,
[&OpcuaSrv]. Он задает связи узлов
OPCUA с кривыми, тегами, сообщениями и
атрибутами.
Это связи указывают, куда и как “укладывать” полученные от сервера
данные.
Пример секции связей:
[&OpcuaSrv]
;*****************************************
;**** Link nodes with tags/curves/messages
;*****************************************
Link node WaveSin with AnalogOutput 0 tag DEMO_OPCUAMON.WAVE.SIN monitor 5000 message @DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data
Link node WaveCos with AnalogOutput 1 tag DEMO_OPCUAMON.WAVE.COS monitor 5000 message @DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data
Link node WaveAmplitude with tag DEMO_OPCUAMON.WAVE.AMPLITUDE refresh 5000 message @DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data
Link node WaveFrequency with tag DEMO_OPCUAMON.WAVE.FREQUENCY refresh 5000 message @DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data
Link node WaveNoise with tag DEMO_OPCUAMON.WAVE.NOISE refresh 5000 message @DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data
Link node WaveStarted with tag DEMO_OPCUAMON.WAVE.STARTED refresh 5000 message @DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data
Link node WaveSin with AnalogInput 0 tag DEMO_OPCUAMON.TEST trigger 5000 :y message @DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data
[]Общий формат:
[&OpcuaSrv]
Link node n with (Analog|Digital)(Input|Output) i tag t (monitor|polling|trigger|refresh) p y message m
[]
# n - псевдоним или индексный идентификатор узла OPCUA
# i - номер подключения (Аналог|Цифрового)(Ввода|Вывода)
# t - имя подключеннного к узлу тега
# p - период проверки/обновления данных, мс
# y - необязательный тип триггера (:x|:y|:t), по умолчанию :y
# m - сообщение c подстановкой $node (идентификатор узла),
# $alias (псевдоним узла), $data (значение данных)Параметры
(Analog|Digital)(Input|Output) i,
tag t,
message m - необязательны.
Их можно не указывать, если они не нужны.
Наличие ключевых слов
Link node … with … - строго обязательно,
их порядок имеет значение.
Параметры
Link node n with … (monitor|polling|trigger|refresh) p …
строго обязательны.
Параметр node n задает псевдоним или
(короткий) индексный идентификатор узла.
Рекомендуется всегда использовать псевдонимы - так будет легче понимать
код.
Параметры
(monitor|polling|trigger|refresh) p y
задают способ опроса:
monitor p - читать данные по подписке, но при
отсутствии обновлений читать данные по запросу с периодом
p миллисекунд.
Для правильной работы должна быть оформлена подписка на данный
узел.
Это основной режим работы сервера для наблюдения данных.
Для этого способа опроса узла запись запрещена.
При попытке записи узла регистрируется ошибка.
polling p - читать данные по явному клиентскому
запросу с периодом p миллисекунд.
Этот метод подходит для чтения редко изменяемых данных без оформления
подписки.
Для этого способа опроса узла запись запрещена.
При попытке записи узла регистрируется ошибка.
trigger p y - запись данных по
триггеру (при изменении входных данных), но при отсутствии
изменений
делать запись по таймеру с периодом не реже p
миллисекунд. При этом учитывается источник
триггера,
который указывает, что именно (абсцисса или ордината кривой или значение
тега) считается триггером.
При этом чтение значений узла также происходит (аналогично режиму
monitor).
Необязательный параметр y задает источник триггера на
запись:
refresh p y - запись данных по таймеру с
периодом p миллисекунд без анализа изменений.
При этом чтение значений узла также происходит (аналогично режиму
monitor).
Этот метод подходит для записи редко изменяемых данных.
Заметим, что при указании периода опроса p=0
чтение/запись по таймеру отключается.
Это может быть полезно, чтобы читать/записывать данные только по
(внешним) событиям.
При работе по внешним событиям чтение/запись инициируется командами:
@OpcNodeRead n - чтение с сервера
значения узла n,@OpcNodeSend n v - запись значения
v в узел n,@OpcNodePoke n s - запись значения из
источника s в узел n.Для понимания работы сервера следует помнить, что клиент
OPCUA - имеет лишь копию
данных, которыми владеет сервер. Поэтому клиент не может изменить данные
сразу, вместо этого он
посылает серверу команду (запрос) на изменение данных на сервере. Затем,
если сервер одобрил
изменения, обновленные данные приходят клиенту по подписке (либо
считываются клиентом явно),
чтобы клиент обновил свою копию данных.
Режим monitor p с периодом таймера
p является основным методом чтения (наблюдения)
данных.
Для правильной работы на узел должна быть оформлена подписка на
обновления @OpcBooking ….
Таймер p гарантирует, что чтение данных будет
выполняться не реже p миллисекунд,
даже если сервер долго не обновляет данные. При нулевом значении
p таймер отключается.
Нулевой таймер можно использовать для работы по событиям (посылкой явных
команд чтения).
При работе клиент сохраняет прочитанные с сервера данные в подключенные
кривые
(Analog|Digital)Output … и теги Tag
….
Пример:
# Чтение узла WaveSin по подписке, но не реже 5сек
Link node WaveSin with AnalogOutput 0 monitor 5000Режим polling p с периодом таймера
p является вспомогательным методом чтения редко
изменяемых данных.
Таймер p обеспечивает чтение данных с периодом
p миллисекунд, даже если нет подписки на
обновления.
При нулевом значении p таймер отключается,
автоматических обновлений при этом не происхоит.
Нулевой таймер можно использовать для работы по событиям (посылкой явных
команд чтения).
При работе клиент сохраняет прочитанные с сервера данные в подключенные
кривые
(Analog|Digital)Output … и теги Tag
….
Пример:
# Чтение узла WaveSin без подписки, раз в 5сек
Link node WaveSin with AnalogOutput 0 polling 5000Режим trigger p y с периодом таймера
p является основным методом записи (и наблюдения)
данных.
Для правильной работы на узел должна быть оформлена подписка на
обновления @OpcBooking ….
Таймер p гарантирует, что запись и чтение данных будет
идти не реже p миллисекунд,
даже если сервер долго не обновляет данные. При нулевом значении
p таймер отключается.
Нулевой таймер можно использовать для работы по событиям (посылкой явных
команд записи/чтения).
Параметр y со значением по умолчанию
:y задает источник триггера на запись.
Источник :y - триггер записи при изменении координаты
y подключенной кривой
(Analog|Digital)Input.
Источник :x - триггер записи при изменении координаты
x подключенной кривой
(Analog|Digital)Input.
Источник :t - триггер записи при изменении значения
t подключенного тега.
При этом в подключенную кривую (Analog|Digital)Output …
или тег Tag … идет сохранение
значений узла, прочитанных с сервера, аналогично режиму monitor
p.
Пример:
# Запись/чтение узла WaveSin по триггеру :y, но не реже 5сек
Link node WaveSin with AnalogInput 0 tag Demo.WaveSin trigger 5000 :yРежим refresh p y работает аналогично режиму
trigger, только триггер на запись по
изменению значений не используется, а запись идет по таймеру
p (или по внешним событиям).
Источник триггера играет роль только при подключении тега - он
указывает, что при записи данные
следует брать из тега, если указано значение источника триггера
:t.
При этом в подключенную кривую (Analog|Digital)Output …
или тег Tag … идет сохранение
значений узла, прочитанных с сервера, аналогично режиму monitor
p.
Пример:
# Запись/чтение узла WaveSin по таймеру 5сек
Link node WaveSin with AnalogInput 0 tag Demo.WaveSin refresh 5000 :yНаконец, при указании нулевого таймера p
чтение/запись ведется по событиям,
т.е. по сообщениям @OpcNodeRead …,
@OpcNodeSend …,
@OpcNodePoke ….
В этой секции описываются команды, вызываемые при старте сервера. Исполняются только команды, подходящие под шаблон StartServingPattern. Эти команды посылаются в консоль оболочки opcuasrv.pas для выполнения.
Монитор стартует только после получения от серверной программы opcuasrv.py ключевой (триггерной) строки StartServingTrigger, а также после задержки на заданную величину DelayServingTrigger миллисекунд. Это нужно для правильного запуска серверной программы, т.к. ей требуется некоторое время для проведения внутренней инициализации.
В секции старта сервера задаются параметры подключения к серверу OPCUA, а также оформляется подписка на обновление нужных для работы узлов. Затем делается старт цикла опроса сервера для начала обмена данными. Также, если нужно, делается запись начальных значений некоторых узлов.
Пример типичной секции старта сервера:
[&OpcuaSrv.StartServing] ; Start serving actions:
@OpcServName OpcuaSrv ; Assign server name
@OpcXmlModel ../daqpas/wave-demo-server.xml ; Data Model XML File
@OpcNamespace http//freeopcua/defaults/modeler ; Assign server namespace
@OpcEndpoint opc.tcp://0.0.0.0:4840/daqgroup/demo ; Set server endpoint URL
@OpcBooking /how=(monitor|trigger|refresh)/i ; Subscribe (monitor|trigger|refresh) nodes
@OpcSubInter 1 ; Subscription update interval, ms
@UseShortKey 1 ; Flag to use short node id
@OpcServing ; Start serving
@OpcNodePoke WaveAmplitude :t ; Assign node value from linked tag
@OpcNodePoke WaveFrequency :t ; Assign node value from linked tag
@OpcNodePoke WaveNoise :t ; Assign node value from linked tag
@OpcNodeSend WaveStarted 1 ; Write node value
[]OpcuaSrv.opcuasrv.xml.http//freeopcua/defaults/modeler.opc.tcp://0.0.0.0:4840/daqgroup/demo./.*/ задает все узлы,
/Wave.*/i - псевдонимы, начинающиеся на
Wave,/how=monitor/i - все узлы с атрибутом
опроса в режиме monitor.Ниже описаны команды консоли оболочки opcuasrv.pas.
Следует специально отметить, что консоли оболочки opcuasrv.pas и сервера opcuasrv.py - это две разные консоли для двух разных программных потоков, из не надо путать друг с другом.
Команда @OpcServName name задает
условное имя name для сервера
OPCUA.
Например:
@OpcServName OpcuaSrvИмя сервера OPCUA следует задавать в самом начале,
до старта сервера командой
@OpcServing.
Команда @OpcNamespace n задает имя
адресного пространства n для модели данных сервера
OPCUA.
Например:
@OpcNamespace http//freeopcua/defaults/modelerВажно, чтобы имя адресного пространства n совпадало с именем модели данных, загруженной из XML файла командой @OpcXmlModel. Иначе сервер не сможет отыскать данные для подключения.
Имя адресного пространства сервера OPCUA следует
задавать в самом начале, до старта сервера командой
@OpcServing.
Команда @OpcEndpoint url задает адрес
URL для точки подключения сервера
OPCUA.
Например:
@OpcEndpoint opc.tcp://localhost:4840/daqgroup/demoЗдесь 4840 - стандартный TCP порт OPCUA, зарегистрированный в IANA как OPC UA Connection Protocol. См. service-names-port-numbers.txt.
Адрес точки подключения сервера OPCUA следует
задавать в самом начале, до старта сервера командой
@OpcServing.
Команда @OpcBooking pattern задает
шаблон pattern (регулярное выражение) для подписки на
обновления данных от сервера OPCUA.
Например:
@OpcBooking /.*/i # подписка на все объявленные узлы
@OpcBooking /Wave.*/i # подписка на все узлы с псевдонимами Wave*
@OpcBooking /how=(monitor|trigger|refresh)/i # подписка на узлы с типом опроса (monitor|trigger|refresh)При подписке на данные сервер OPCUA будет присылать уведомлении об изменении данных при их обновлении на стороне сервера.
При правильном конфигурировании обновления будут автоматически записываться в подключенные кривые или теги, а также будут посылаться сообщения, если они были указаны в конфигурации подключения узла.
Подписку на обновления данных сервера OPCUA следует
делать до старта сервера командой
@OpcServing.
Команда @OpcServing запускает цикл
опроса серверной программы opcuasrv.py. Перед запуском
необходимо задать (как минимум) адрес сервера @OpcAddress и
шаблоны для подписки узлов @OpcBooking.
Например:
@OpcServing # запуск сервераПосле запуска сервера должен начаться обмен данными с сервером OPCUA.
Команда @OpcNodeRead node посылает
серверной программе opcuasrv.py запрос на чтение узла
node. В качестве имени узла задается псевдоним или
индексный идентификатор узла.
Например:
@OpcNodeRead WaveSin # чтение узла с псевдонимом WaveSinКоманда чтения используется, если надо прочитать конкретный узел досрочно. Это может быть узел, на который не была оформлена подписка, либо подписка была оформлена, но данные почему-то долго не обновлялись.
При успешном выполнении данные будут (через некоторое время) присланы сервером.
Команда чтения работает только после старта цикла опроса сервера.
Команда @OpcNodeSend node value
посылает серверной программе opcuasrv.py запрос на
запись в узел node значения value на
стороне сервера OPCUA. В качестве имени узла
node задается псевдоним или индексный идентификатор
узла. Новое значение value передается в виде
строки.
Например:
@OpcNodeSend WaveAmplitude 3.14 # запись в узел с псевдонимом WaveAmplitude значения 3.14Команда записи используется, чтобы задать узлу (переменной на стороне сервера) новое значение, явно заданное в аргументе команды.
При успешном выполнении данные будут (через некоторое время) обновлены на стороне сервера. Соответственно придет уведомление об изменении данных (если оформлена подписка). Надо, однако, быть готовым к тому, что сервер может отклонить изменение, если оно не разрешено, поэтому обновление данных может и не произойти.
Команда записи работает только после старта цикла опроса сервера.
Команда @OpcNodePoke node source
посылает серверной программе opcuasrv.py запрос на
запись в узел node значения, взятого из источника
source.
В качестве имени узла node задается псевдоним или индексный идентификатор узла.
Значениями источника source могут быть:
Например:
@OpcNodePoke WaveAmplitude :t # запись в узел с псевдонимом WaveAmplitude значения из присоединенног тегаКоманда записи используется, чтобы задать узлу (переменной на стороне сервера) новое значение, взяятое из присоединенного тега или кривой (вместо указания явного значения).
Команда записи работает только после старта цикла опроса сервера.
Команда @OpcInform topic печатает в
консоли серверной оболочки opcuasrv.pas информацию на
тему topic:
@OpcInform - выдает справку (список
тем),@OpcInform * - выдает информацию по
всем темам,@OpcInform Alias - выдает список
псевдонимов,a = n, где@OpcInform Links - выдает список
связей узлов с хранилищем данных и атрибутами,a - how=…;ttr=…;ali=…;nai=…;ndi=…;nao=…;ndo=…;tag=…;msg=…;,
где$alias, узла
$node и значения данных
$data.@OpcInform Books - список подписок на
обновление данных,a = booked, где
a - псевдоним узла.@OpcInform how - выдает тип триггера
для каждого узла.Пример вывода команды @OpcInform:
@opcinform
&OPCUAMON ! No topics specified.
&OPCUAMON : Available topics are:
&OPCUAMON : Links - table of node links.
&OPCUAMON : Alias - alias to node table.
&OPCUAMON : Books - nodes booking table.
&OPCUAMON : How - node trig how table.
&OPCUAMON : * - all listed topics.
@opcinform *
&OPCUAMON : Alias to Node table:
wavenoise = ns2i2007
wavesin = ns2i2003
wavecos = ns2i2004
wavefrequency = ns2i2006
wavestarted = ns2i2008
waveamplitude = ns2i2005
&OPCUAMON : Table of Node links:
wavenoise - how=refresh 5000;tag=264;ali=wavenoise;nod=ns2i2007;msg=@DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data;
wavesin - how=monitor 5000;nao=0;tag=265;ali=wavesin;nod=ns2i2003;msg=@DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data;
wavecos - how=monitor 5000;nao=1;tag=266;ali=wavecos;nod=ns2i2004;msg=@DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data;
wavefrequency - how=refresh 5000;tag=262;ali=wavefrequency;nod=ns2i2006;msg=@DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data;
wavestarted - how=refresh 5000;tag=261;ali=wavestarted;nod=ns2i2008;msg=@DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data;
waveamplitude - how=refresh 5000;tag=263;ali=waveamplitude;nod=ns2i2005;msg=@DevPost &DEMO_OPCUAMON.MAIN.CTRL OpcData $alias $data;
&OPCUAMON : Nodes Booking table:
wavenoise = Booked
wavesin = Booked
wavecos = Booked
wavefrequency = Booked
wavestarted = Booked
waveamplitude = Booked
&OPCUAMON : Node Trig How table:
wavenoise = refresh
wavesin = monitor
wavecos = monitor
wavefrequency = refresh
wavestarted = refresh
waveamplitude = refreshЗдесь описывается серверная
программа opcuasrv.py -
сервер для публикации данных OPCUA.
Он написан на языке Python с использованием модулей
asyncua и pycrwkit.
Сервер opcuasrv.py используется в готовом виде,
“как есть”.
Всё конфигурирование происходит на уровне оболочки
opcuasrv.pas.
Программа opcuasrv.py вызывается командами:
unix pyvenv opcuasrv.py # простой вызов без параметров
unix pyvenv opcuasrv.py -u opc.tcp://localhost:4840 # вызов с указанием адреса сервера
unix pyvenv opcuasrv.py --url opc.tcp://localhost:4840 # вызов с указанием адреса сервераКоманда unix pyvenv … вызывает
программу в (правильном) виртуальном окружении.
Необязательная опция -u или
--url задает адрес сервера для
подключения.
Сервер подключения может быть задан позже командой @OpcServAddr.
Ниже описаны команды консоли серверной программы opcuasrv.py.
Следует специально отметить, что консоли оболочки opcuasrv.pas и сервера opcuasrv.py - это две разные консоли для двух разных программных потоков, из не надо путать друг с другом.
Управление программой opcuasrv.py и обмен данными идет с помощью консольных команд, посылаемых клиентом в поток stdin и ответных сообщений от программы в потоке stdout. Формат команд и сообщений - текстовый, в кодировке utf8, с обычным разделением на строки с помощью разделителя строк EOL, равного LF под Unix или CRLF под Windows.
Команды задаются в формате DaqScript:
@,@ следует непустой
идентификатор команды,Пример команд:
@Help # команда без аргументов
@Exit 1 # команда с аргументом 1Печатает справку по командам.
@help
@Help - print this help
@Exit n - exit with code n
@PingEcho s - print echo (for testing)
@PollCount - print polling loop counter
@PollPeriod n - set polling loop period n ms
@TermOnError n - terminate on error flag (0/1)
@Memory - get process memory rss,vms,gc
@OpcServName n - get/set server name (n), like OpcuaSrv
@OpcEndpoint u - get/set server endpoint URL (u), like opc.tcp://0.0.0.0:4840/daqgroup/crwdaq/server
@OpcServAddr u - get/set server endpoint URL (u), like opc.tcp://0.0.0.0:4840/daqgroup/crwdaq/server
@OpcNamespace n - get/set server namespace (n), like http//freeopcua/defaults/modeler
@OpcNspArray - get list of namespaces available on server
@OpcNspIndex - get index of server namespace in list of namespaces
@OpcXmlModel x - get/set server XML model (x), like /mnt/data/home/alex/projects/sandbox/opcua/opcua_test/opcuasrv.xml
@OpcWritable n - make client`s writable node (n), like ns=2;i=13 or ns2i13
@OpcDataBook n - subscribe data change of node (n), like ns=2;i=13 or ns2i13
@UseShortKey f - flag (0/1) to use short key (ns2i13 instead of ns=2;i=13)
@OpcSubInter p - subscription interval (period) ms, default is 100
@OpcStartSrv - start OPCUA server to publish nodes data
@OpcDataVary n v - notification on node (n) changed to value (v)
@OpcGotEvent e - notification on got event (e)
@OpcDataSend n v - write node (n) value (v)
@OpcDataRead n - read node (n) valueКоманда @Exit n завершает программу с
кодом выхода n. Код выхода необязателен, по умолчанию
0.
Ответное сообщение: @Exit n -
уведомляет о выполнении команды.
Команда @PingEcho s выводит в консоль
эхо - т.е. саму себя. Применяется для проверки связи
(ping) и измерения времени отклика.
Ответное сообщение: @PingEcho s -
уведомляет о выполнении команды.
Команда @PollCount используется для
чтения счетчика циклов. Счетчик циклов служит для оценки частоты цикла
опроса команд и сообщений.
Ответное сообщение: @PollCount n -
возвращает значение счетчика циклов n.
Команда @PollPeriod n используется для
чтения/записи периода опроса n цикла опроса команд и
сообщений. При отсутствии аргумента происходит чтение, при наличии -
запись и чтение периода опроса.
Ответное сообщение: @PollPeriod n -
возвращает значение периода опроса n в миллисекундах
(мс).
Начальное значение периода опроса - 4 мс.
Команда @TermOnError n читает/задает
флаг n = (0/1) для обработки ошибок. Если флаг
установлен, при ошибках (исключениях) программа прекращает
выполнение.
Ответное сообщение: @TermOnError n -
возвращает значение флага.
Начальное значение флага - 0.
Команда @Memory используется для чтения
счетчиков памяти.
Ответное сообщение:
@Memory rss=R; vms=V; gc=G - возвращает
значение счетчика резидентной памяти R и виртуальной
памяти V в байтах, а также счетчиков “сборщика
мусора” (garbage collection) G.
Счетчики сборщика мусора имеет формат gc=ge,go,g0,g1,g2,gg:
Команда @OpcServName n задает имя
сервера n.
По умолчанию имя сервера OpcuaSrv.
Имя сервера должно задаваться ДО старта сервера командой
@OpcStartSrv.
Команда @OpcEndpoint u задает адрес
точки подключения сервера u.
Параметр u имеет формат URL
(universal resource location), например,
opc.tcp://localhost:4840/daqgroup/crwdaq/server.
В качестве имени сервера допустимо использовать IP
адрес 0.0.0.0, что означает “все сетевые
адаптеры на данном компьютере”. Также можно указывать имя компьютера
(hostname), ссылку localhost или
IP адрес конкретного сетевого адаптера.
Для связи можно использовать стандартный порт OPCUA
номер 4840. Этот порт официально зарегистрирован в
организации IANA под записью
opcua-tcp 4840 tcp "OPC UA Connection Protocol" [OPC_Foundation] [Randy_Armstrong] 2018-01-04.
Либо можно использовать альтернативный порт, например,
16550.
Альтернативным способом задания адреса точки подключения является
опция командной строки -u url или
--url url при вызове прграммы. В этом
случае команду можно не использовать.
По умолчанию используется адрес
opc.tcp://0.0.0.0:4840/daqgroup/crwdaq/server.
Адрес точки подключения сервера должен задаваться ДО старта сервера командой
@OpcStartSrv.
Команда @OpcServAddr u является
синонимом команды @OpcEndpoint u.
Команда @OpcNamespace n задает имя
адресного пространства (namespace)
n.
У сервера может быть несколько адресных пространств, в которых расположены поименованные объекты. Команда задает имя адресного пространства модели данных, которое будет публиковать сервер.
Предполагается, что модель данных загружается из
XML файла командой
@OpcXmlModel x. Имя адресного пространства
x должно совпадать с именем адресного пространства,
которое сохранено в XML файле модели данных, иначе
сервер не сможет отыскать нужные данные.
По умолчанию используется адресное пространство
http//freeopcua/defaults/modeler.
Это имя адресного пространства по умолчанию для программы
opcua-modeler,
которая используется для создания и редактирования модели данных.
Имя адресного пространства сервера должно задаваться ДО старта сервера командой
@OpcStartSrv.
Команда @OpcNspArray выводят список
адресных пространств (namespace) сервера. Она
носит информативный характер.
Команда @OpcNspIndex выводят индекс
заданного адресного пространства (namespace) в
массиве адресных пространств сервера. Она носит информативный
характер.
Возвращаемый индекс используется для построения индексных
идентификаторов ns=…, используемых для
доступа к данным.
Команда @OpcXmlModel x задает имя
x для XML файла модели
данных.
Имя файла должно быть задано абсолютным путем файла. Оно может включать переменные среды окружения, например, $CRW_DAQ_CONFIG_HOME_DIR/opcuasrv.xml.
Задание файла модели данных является обязательным для работы сервера.
Альтернативным способом задания файла является опция командной строки
-x file.xml или
--xml file.xml при вызове прграммы. В этом
случае команду можно не использовать.
Имя файла модели данных сервера должно задаваться ДО старта сервера командой
@OpcStartSrv.
Команда @OpcWritable n задает для узла
n разрешение на запись данных
клиентами. Если узел не имеет разрешения, попытка
записи значения узла клиентами будет отвергнута. При этом запись данных
сервером разрешена всегда.
Другим способом задать для узла разрешение на запись данных клиентами является установка флага CurrentWrite в свойстве AccessLevel и UserAccessLevel данного узла. Это можно сделать при редактировании модели данных в редакторе opcua-modeler.
Команда @OpcDataBook n служит для
подписки узла (node) с идентификатором n на
уведомления об изменении значения.
Идентификатор n задается в полной форме вида (например)
ns=2;i=13 или короткой форме
ns2i13.
Здесь:
Ответное сообщение: @OpcDataBook n -
уведомляет о выполнении команды.
Подписка должна выполняться (строго) до старта сервера командой
@OpcStartSrv.
В результате подписка сервер OPCUA будет посылать
уведомления @OpcDataVary n v про каждом
изменении данных узла n на значение
v.
Команда @UseShortKey f читает/задает
флаг f = (0/1) для формата печати идентификаторов
узлов.
Если флаг установлен, при печати используется короткий формат
(ns2i13 вместо
ns=2;i=13).
Ответное сообщение: @UseShortKey f -
возвращает значение флага.
Начальное значение флага - 0.
Команда @OpcSubInter p читает/задает
интервал (период) опроса p для проверки обновления
подписки узлов (subscription interval).
Этот интервал определяяет частоту (периодичность) обновления данных на
стороне клиента.
Данные клиента будут обновляться с учетом двух факторов:
Другими словами, клиент будет получать обновления данных по факту изменения данных на стороне сервера, но не чаще интервала обновления.
Например, при значении
@OpcSubInter 1000 (интервал одна секунда)
обновления будут приходить с периодичностью раз в секунду - но только
для тех узлов, которые изменились за эту секунду.
Ответное сообщение: @OpcSubInter p -
возвращает значение интервала обновления подписки.
Начальное значение интервала подписки - 100.
Для получения максимальной частоты обновления используйте интервал
обновления @OpcSubInter 1.
В этом случае обновление данных на стороне клиента будет максимально
приближено к обновлению данных на сервере.
Значение интервала обновления должно быть задано командой @OpcSubInter ДО старта сервера командой @OpcStartSrv. Попытка изменения интервала обновления после старта сервера будет (молча) проигнорирована.
Команда @OpcStartSrv стартует сервер,
т.е. начинает обмен данными, на которые была оформлена подписка.
После старта сервера оформлять новые подписки уже нельзя вплоть до
перезапуска программы сервера.
Ответное сообщение: @OpcStartSrv s -
возвращает статус сервера s = (0|1).
Сообшение @OpcDataVary n v посылается
при получении от сервера обновления узла с идентификатором
n с новым значением v.
Это сообщение является основным способом передачи данных от сервера при
их изменении.
Клиент должен обновлять свои (локальные) копии данных при получении этого сообщения.
Сообшение @OpcGotEvent e посылается при
получении от сервера события e.
Клиент должен выполнять обратотку событий при получении этого сообщения.
Сообшение @Error class e - m посылается
при возникновении ошибки (исключения) класса e с
сообщением m.
При этом программа может продолжить выполнение, если исключение было
перехвачено и обработано.
Но возможны и фатальные ошибки, при которых программа прекращает
работу.
Клиент должен выполнять обработку ошибок при получении этого сообщения.
Команда @OpcDataSend n v посылается для
записи в узел с идентификатором n значения
v.
Сервер должен выполнить запись данных узла при получении этой
команды.
Команда работает только после старта сервера командой @OpcStartSrv.
Ответное сообщение: @OpcDataSend n v -
уведомляет о выполнении команды.
Команда @OpcDataRead n инициирует
непоследственное (без подписки) чтение значения узла с идентификатором
n.
Используется для чтения редко изменяемых данных без подписки, либо для
досрочного чтения данных.
Команда работает только после старта сервера командой @OpcStartSrv.
Ответное сообщение: @OpcDataRead n v -
возвращает идентификатор узла n и его значение
v.
Здесь приведен пример сеанса работы opcuasrv.py с указанием направления передачи:
< - ввод команд в поток
stdin программы opcuasrv.py,> - вывод сообщений программы
opcuasrv.py в поток stdout,> Start opcuasrv.
> Type '@Exit' to terminate.
> Type '@Help' to show help.
> Welcome to opcuasrv program.
< @OpcEndpoint opc.tcp://0.0.0.0:4840/daqgroup/opcuasrv
< @OpcNamespace http//freeopcua/defaults/modeler
< @OpcServName OpcuaSrv
< @OpcXmlModel opcuasrv
< @UseShortKey 1
<
< @OpcDataBook ns2i2003
< @OpcDataBook ns2i2004
< @OpcDataBook ns2i2005
< @OpcDataBook ns2i2006
< @OpcDataBook ns2i2007
< @OpcDataBook ns2i2008
<
< @OpcWritable ns2i2005
< @OpcWritable ns2i2006
< @OpcWritable ns2i2007
< @OpcWritable ns2i2008
<
< @OpcStartSrv
> @OpcEndpoint opc.tcp://0.0.0.0:4840/daqgroup/opcuasrv
> @OpcNamespace http//freeopcua/defaults/modeler
> @OpcServName OpcuaSrv
> @OpcXmlModel /mnt/data/home/alex/projects/daqgroup/suite/crwdaq/resource/daqsite/opcuaserver/opcua_test/opcuasrv.xml
> @UseShortKey 1
> @OpcDataBook ns=2;i=2003
> @OpcDataBook ns=2;i=2004
> @OpcDataBook ns=2;i=2005
> @OpcDataBook ns=2;i=2006
> @OpcDataBook ns=2;i=2007
> @OpcDataBook ns=2;i=2008
> @OpcWritable ns=2;i=2005
> @OpcWritable ns=2;i=2006
> @OpcWritable ns=2;i=2007
> @OpcWritable ns=2;i=2008
> @OpcStartSrv 1
> @OpcNspArray http://opcfoundation.org/UA/, urn:freeopcua:python:server, http//freeopcua/defaults/modeler
> @OpcNspIndex 2
> WARNING:asyncua.server.server:Endpoints other than open requested but private key and certificate are not set.
> @OpcNodeId ns2i2003 ns=2;i=2003 WaveSin 0:Root/0:Objects/2:DaqGroupPublic/2:WaveDemo/2:WaveSin
> @OpcNodeId ns2i2004 ns=2;i=2004 WaveCos 0:Root/0:Objects/2:DaqGroupPublic/2:WaveDemo/2:WaveCos
> @OpcNodeId ns2i2005 ns=2;i=2005 WaveAmplitude 0:Root/0:Objects/2:DaqGroupPublic/2:WaveDemo/2:WaveAmplitude
> @OpcNodeId ns2i2006 ns=2;i=2006 WaveFrequency 0:Root/0:Objects/2:DaqGroupPublic/2:WaveDemo/2:WaveFrequency
> @OpcNodeId ns2i2007 ns=2;i=2007 WaveNoise 0:Root/0:Objects/2:DaqGroupPublic/2:WaveDemo/2:WaveNoise
> @OpcNodeId ns2i2008 ns=2;i=2008 WaveOn 0:Root/0:Objects/2:DaqGroupPublic/2:WaveDemo/2:WaveOn
> Try to subscribe 6 items
> INFO: Subscription done for 6 node(s).
> @OpcDataVary ns2i2003 None
> @OpcDataVary ns2i2004 None
> @OpcDataVary ns2i2005 None
> @OpcDataVary ns2i2006 None
> @OpcDataVary ns2i2007 None
> @OpcDataVary ns2i2008 None
> @OpcWritable ns2i2005
> @OpcWritable ns2i2006
> @OpcWritable ns2i2007
> @OpcWritable ns2i2008
> @OpcDataRead ns2i2003
< @OpcDataSend ns2i2003 3.14
< @OpcDataRead ns2i2003
< @OpcDataSend ns2i2003 3.0
> @OpcDataSend ns2i2003 3.0
> @OpcDataRead ns2i2003 3.0
> @OpcDataSend ns2i2003 3.14
> @OpcDataRead ns2i2003 3.14
> @OpcDataVary ns2i2003 3.0
> @OpcDataVary ns2i2003 3.14
< @Exit
> Program terminated. Press Enter to exit.
> @Exit 0Желаем успешного использования opcuasrv.
CRW-DAQ Copyright (c) 2001-2025 Alexey Kuryakin daqgroup@mail.ru