Предоставляет реализацию протокола OPC-UA в части клиента и сервера, в виде отдельной библиотеки.
Лицензия:
GPL2
GPL2
LGPL3
Введение
OPC (OLE for Process Control) — это семейство протоколов и технологий, предоставляющих единый интерфейс для управления объектами автоматизации и технологическими процессами. Создание и поддержку спецификаций OPC координирует международная некоммерческая организация OPC Foundation, созданная в 1994 году ведущими производителями средств промышленной автоматизации.
В виду того, что значительное влияние в организации OPC Foundation имеет корпорация Microsoft, протоколы OPC до последнего времени были одноплатформенными и закрытыми, по причине привязки к закрытым технологиям MS Windows. Однако, с недавних пор, организацией OPC Foundation были созданы такие многоплатформенные решения, как OPC XML-DA и OPC-UA. Наибольший интерес из них представляет OPC-UA, как унифицирующий все протоколы ранних версий в рамках открытых и многоплатформенных технологий.
Данный модуль реализует поддержку интерфейса и протокола OPC-UA как в виде клиентского сервиса, так и в виде сервера OPC-UA. Клиентский сервис OPC-UA реализуется одноимённым модулем подсистемы "Сбор данных", а сервер реализуется модулем подсистемы "Протоколы". Весь код реализации этим модулем специфики протокола OPC-UA был вынесен, по просьбе пользователей, в отдельную библиотеку, которая распространяется под лицензией LGPL3.
В текущей версии данных модулей и библиотеки реализуются бинарная часть протокола и базовые сервисы в небезопасном режиме и безопасных режимах политик "Base128Rsa15" и "Base256". В последствии планируется расширение модуля для работы через HTTP/SOAP и реализации остальных сервисов OPC-UA, по потребности.
Хотя протокол OPC-UA и является многоплатформенным, его спецификация и SDK не являются свободнодоступными, а предоставляются только членам организации OPC Foundation. По этой причине реализация данных модулей столкнулась со значительными препятствиями и проблемами.
Во первых, протокол OPC-UA сложен и реализация его вообще без спецификации крайне трудоёмка. По этой причине работы над данными модулями долгое время не начиналась, и только благодаря спонсорской помощи одной из организаций-члена OPC Foundation проект OpenSCADA получил документацию спецификации. При этом SDK и исходные тексты ANSIС-API протокола OPC-UA получены не были по причине несовместимости их лицензии с GPL и, как следствие, потенциальной угрозы нарушения лицензии при работе с исходными текстами, что могло привести к последующим юридическим проблемам при свободном распространении данных модулей.
Во вторых, даже наличие спецификации не позволяет решить ряд технических вопросов без примеров реализации и возможности проверки на рабочем прототипе клиента и сервера OPC-UA. Например, именно технические особенности реализации алгоритмов симметричного шифрования и получения ключей для них не позволили реализовать поддержку политик безопасности сразу.
Для отладки функционирования модулей использовалось демонстрационное ПО фирмы Unified Automation, в составе OPC-UA клиента — UAExpert и сервера — "OPC-UA Demo Server", из пакета SDK. В виду постоянного развития самого клиента "UAExpert", в плане интерпретации спецификации OPC-UA, новые его версии часто имеют проблемы при работе с сервером OPC-UA от OpenSCADA. В целом результаты совместимости работы с клиентами и серверами различных производителей можно получить в приложении.
1. Протокол OPC-UA
OPC-UA — это платформо-независимый стандарт, с помощью которого системы и устройства различного типа могут взаимодействовать путём отправки сообщений между клиентом и сервером через различные типы сетей. Протокол поддерживает безопасное взаимодействие путём валидации клиентов и серверов, а также противодействия атакам. OPC-UA определяет понятие Сервисы, которые сервера могут предоставлять, а также сервисы, которые сервер поддерживает для клиента. Информация передаётся в виде типов данных, определённых OPC-UA и производителем, кроме того сервера определяют объектную модель, для которой клиенты могут осуществлять динамический обзор.
OPC-UA предоставляет совмещение интегрированного адресного пространства и сервисной модели. Это позволяет серверу интегрировать данные, нарушения (Alarms), события (Events) и историю в этом адресном пространстве, а также предоставлять доступ к ним посредством интегрированных сервисов. Сервисы также предоставляют интегрированную модель безопасности.
OPC-UA позволяет серверам предоставлять для клиентов определения типов, для доступа к объектам из адресного пространства. OPC-UA допускает предоставление данных в различных форматах, включая бинарные структуры и XML-документы. Через адресное пространство клиенты могут запросить у сервера метаданные, которые описывают формат данных.
OPC-UA добавляет поддержку множественной связности между узлами вместо простого ограничения иерархичностью. Такая гибкость в комбинации с определением типов позволяет применять OPC-UA для решения задач в широкой проблемной области.
OPC-UA спроектирован для обеспечения надёжной выдачи данных. Основная особенность всех OPC серверов — способность выдавать данные и события.
OPC-UA спроектирован для поддержки широкого диапазона серверов, от простых ПЛК до промышленных серверов. Эти сервера характеризуются широким спектром размеров, производительности, платформ исполнения и функциональной ёмкости. Следовательно, OPC-UA определяет исчерпывающее множество возможностей, и сервер может имплементировать подмножества этих возможностей. Для обеспечения совместимости OPC-UA определяет подмножества, именуемые Профилями, которые сервера могут указывать для согласования. Клиенты могут в последствии выполнять обзор профилей сервера и пробрасывать взаимодействие с сервером, основанном на профилях.
OPC-UA спецификация спроектирована как ядро в слое, изолированном от подлежащих компьютерных технологий и сетевых транспортов. Это позволяет OPC-UA при необходимости расширяться на будущие технологии без отторжения основы дизайна. На данный момент спецификацией определены два способа кодирования данных: XML/text и UA Binary. В дополнение, определено два типа транспортного слоя: TCP и HTTP/SOAP.
OPC-UA спроектирован как решение для миграции с OPC клиентов и серверов, которые основаны на Microsoft COM технологиях. OPC COM сервера (DA, HDA и A&E) могут быть легко отражены в OPС-UA. Производители могут самостоятельно осуществлять такую миграцию или же рекомендовать пользователям использовать обёртки и конверторы между этими протоколами. OPC-UA унифицирует предыдущие модели в едином адресном пространстве с единым множеством сервисов.
2. Модуль реализации сервера
Модуль сервера содержит код реализации серверной части OPC-UA — серверных сервисов, в части специфичной для OpenSCADA, и используя библиотеку для OPC-UA специфичной части. Для построения OPC-UA сервера достаточно создать входящий транспорт, обычно это TCP-транспорт модуля Sockets, и выбрать в нём модуль данного протокола, а также сконфигурировать хотя бы один конечный узел модуля протокола, о чём ниже.
2.1. Обслуживание запросов по протоколу OPC-UA
Входящие запросы к модулю-протоколу обрабатываются модулем в соответствии со сконфигурированными конечными узлами OPC-UA (EndPoints) (рис.1).
Рис.1. Конечные узлы протокола.
Конечный узел протокола OPC-UA это фактически объект сервера OPC-UA. Конечные узлы в OPC-UA могут быть как локальными, так и удалёнными. Локальный конечный узел предназначен для предоставления ресурсов станции OpenSCADA по протоколу OPC-UA, в тоже время удалённые конечные узлы служат для выполнения как сервиса обзора доступных OPC-UA узлов, так и для шлюзования запросов к удалённым станциям. В данной версии модуля поддерживается только конфигурация локальных конечных узлов.
Общая конфигурация конечного узла осуществляется на главной вкладке страницы конечного узла (рис.2) параметрами:
Состояние узла, а именно: статус, "Включен" и имя БД, содержащей конфигурацию.
Идентификатор, имя и описание узла.
Состояние, в которое переводить узел при загрузке: "Включен".
Тип кодирования протокола. На данный момент это только "Бинарный".
URL конечной точки.
Сертификат сервера в формате PEM.
Приватный ключ в формате PEM.
Политики безопасности сервера.
Рис.2. Главная вкладка страницы конечного узла.
3. Модуль сбора данных
Модуль сбора данных предоставляет возможность опроса и записи атрибутов значения(13) узлов типа "Переменная".
3.1. Объект контроллера данных
Для добавления источника данных OPC-UA создаётся и конфигурируется объект контроллера в системе OpenSCADA. Пример вкладки конфигурации объекта контроллера данного типа изображен на рисунке 3.
Рис.3. Вкладка конфигурации объекта контроллера.
С помощью этой вкладки можно установить:
Состояние контроллера, а именно: Статус, "Включен", Запущен" и имя БД, содержащей конфигурацию.
Идентификатор, имя и описание контроллера.
Состояние, в которое переводить контроллер при загрузке: "Включен" и "Запущен".
Имя таблицы для хранения конфигурации параметров контроллера.
Политика планирования и приоритет задачи сбора данных.
Период синхронизации конфигурации атрибутов параметров с удалённой станцией, а также время повтора попыток восстановления подключения.
URL конечного узла удалённой станции — сервера OPC-UA. Вначале этот адрес можно указать в виде "opc.tcp://{IP|name}:{port}", после чего, в случае включения объекта контроллера и наличия указанного OPC-UA узла, появится возможность выбрать уточнённый адрес.
Часто встречается ситуация, когда уточнённый адрес является символьным, который в этой сети не резолвится, из-за некорректной настройки сервера. В таких случаях нужно оставить исходный IP-адрес или имя которое резолвится в IP правильно.
Политика безопасности и режим безопасности сообщения.
Сертификат клиента и приватный ключ в формате PEM.
Пользователь и пароль для аутентификации на сервере. Пустое значение включает анонимный доступ.
Ограничение числа атрибутов в параметре.
С целью облегчения идентификации узлов на удалённой станции, а также выбора их для вставки в объекте параметра контроллера, в самом объекте контроллера предусмотрена вкладка навигации по узлам удалённой станции "Обзор узлов сервера", где можно пройти по дереву объектов и ознакомится с их атрибутами (рис.4).
Рис.4. Вкладка "Обзор узлов сервера" страницы объекта контроллера.
3.2. Параметры
Модуль сбора данных предоставляет только один тип параметров — "Стандарт". Дополнительным конфигурационным полем параметра данного модуля (рис.5) является перечень узлов OPC-UA и поле однострочной навигации по узлам OPC-UA для вставки выбранных узлов типа "Переменная" в указанный перечень. Атрибут в этом перечне записывается следующим образом: {ns}:{id}.
Где:
ns — область имён, числом; нулевое значение может быть опущено; id — идентификатор узла, числом, строкой, строкой байт или GUID.
Примеры:
84 — корневой узел; 3:"BasicDevices2" — узел базовых устройств в области имён 3 и в виде строки; 4:"61626364" — узел в области имён 4 и в виде строки байт; 4:{40d95ab0-50d6-46d3-bffd-f55639b853d4} — узел в области имён 4 и в виде GUID.
Рис.5. Вкладка конфигурации объекта параметра.
Узлы типа "Переменная" со значением в виде структуры прочитать целиком обычно нельзя поэтому необходимо её элементы вставлять в перечень узлов чтения отдельно.
В соответствии с указанным списком узлов выполняется опрос и создание атрибутов параметра (рис.6).
Рис.6. Вкладка атрибутов параметра.
4. Библиотека libOPC_UA
Основываясь на наработках данного модуля протокольный код OPC-UA был вынесен в отдельную библиотеку и опубликован под лицензией LGPLv3. Данные действия выполнены с целью предоставить возможность простого добавления поддержки протокола OPC-UA сторонними проектами. Библиотека представлена двумя файлами libOPC_UA.h, libOPC_UA.cpp; поддерживается и содержится в составе данного модуля, т.е. последнюю версию Вы можете загрузить здесь: http://oscada.org/svn/trunk/OpenSCADA/src/moduls/daq/OPC_UA/libOPC_UA.
Библиотека, как и данный модуль, написан на языке программирования C++. Статическая диаграмма классов, отражающая архитектуру библиотеки, приведена на рисунке 7. Согласно диаграмме классов библиотека выполнена в области имён "OPC", а архитектурно её можно разделить на клиентскую "Client" и серверную "Server" части, которые унаследованы от общего класса протокола "UA". Кроме непосредственно классов протокола "OPC-UA" библиотека включает в себя набор функций и классов для обработки или хранения данных протокола, отдельно из которых нужно отметить класс узла языка XML "XML_N", используемый для унификации обращений к API библиотеки.
Рис.7. Статическая диаграмма классов библиотеки libOPC_UA.
Использование библиотеки, в целом, заключается в наследовании от класса "Client" и/или "Server", согласно с функциями конечной программы, и последующей реализации виртуальных функций свойств клиента/сервера, в контексте протокола OPC-UA, а также транспортной части коммуникации, т.е. подключение/открытие к TCP-сокету и передачу/чтение неструктурированного потока данных. Последующие запросы и обработка запросов данных (для сервера) осуществляются через вызов функции reqService(), запрос к сервису, и/или обработкой виртуальной функции reqData() запроса к данным, т.е. по сути интеграция в модель данных приложения.
4.1. Служебные объекты, функции и класс UA
Данные
Типы реализаций (enum — SerializerType):
ST_Binary = 0 — бинарный.
Тип запроса открытия канала безопасности (enum — SC_ReqTP):
SC_ISSUE = 0 — исходный;
SC_RENEW = 1 — обновляющий.
Режим безопасности сообщения (enum — MessageSecurityMode):
MS_None = 1 — без безопасности;
MS_Sign = 2 — подпись;
MS_SignAndEncrypt = 3 — подпись и шифрование;
Тип аутентификации (enum — AuthTp):
A_Anon = 0 — анонимно;
A_UserNm = 1 — пользователь+пароль;
A_Cert = 2 — сертификат.
Классы узлов (enum — NodeClasses):
NC_Object = 1 — объект;
NC_Variable = 2 — переменная;
NC_Method = 4 — метод;
NC_ObjectType = 8 — тип объекта;
NC_VariableType = 16 — тип переменной;
NC_ReferenceType = 32 — тип ссылки;
NC_DataType = 64 — тип данных;
NC_View = 128 — вид.
Направление обзора (enum — BrowseDirection):
BD_FORWARD = 0 — вперёд;
BD_INVERSE = 1 — назад;
BD_BOTH = 2 — вперёд и назад.
Возвратная метка времени (enum — TimestampsToReturn):
TS_SOURCE = 0 — источника;
TS_SERVER = 1 — сервера;
TS_BOTH = 2 — источника и сервера;
TS_NEITHER = 3 — отсутствует.
Доступ (enum — Access):
ACS_Read = 0x01 — чтение;
ACS_Write = 0x02 — запись;
ACS_HistRead = 0x04 — чтение истории;
ACS_HistWrite = 0x08 — запись истории;
ACS_SemChange = 0x10 — ?.
Элементы маски описания обзорного запроса (enum — RefDscrResMask):
RdRm_RefType = 0x01 — тип ссылки;
RdRm_IsForward = 0x02 — направление;
RdRm_NodeClass = 0x04 — класс узла;
RdRm_BrowseName = 0x08 — имя обзора;
RdRm_DisplayName = 0x10 — имя отображения;
RdRm_TypeDef = 0x20 — тип определения.
Идентификаторы атрибутов узла (enum — AttrIds):
Aid_Error = 0 — ошибка;
AId_NodeId = 1 — идентификатор узла;
AId_NodeClass = 2 — класс узла;
AId_BrowseName = 3 — имя обзора;
AId_DisplayName = 4 — имя отображения;
AId_Descr = 5 — описание;
AId_WriteMask = 6 — маска записи;
AId_UserWriteMask = 7 — маска записи пользователя;
AId_IsAbstract = 8 — абстрактность;
AId_Symmetric = 9 — симметричность;
AId_InverseName = 10 — инверсное имя;
AId_ContainsNoLoops = 11 — несодержание петлей;
AId_EventNotifier = 12 — уведомление событий;
AId_Value = 13 — значение;
AId_DataType = 14 — тип данных;
AId_ValueRank = 15 — ранг значения;
AId_ArrayDimensions = 16 — размерность массива;
AId_AccessLevel = 17 — уровень доступа;
AId_UserAccessLevel = 18 — уровень доступа пользователя;
В библиотеку включен ряд внешних функций объекта TSYS ядра OpenSCADA, для упрощения и унификации ряда внутренних операций:
int64_t curTime( ); — Текущее время в микросекундах с начала эпохи Unix (01.01.1970).
string int2str( int val ); — Преобразование целого знакового в строку, в десятичном представлении.
string uint2str( unsigned val ); — Преобразования целого беззнакового в строку, в десятичном представлении.
string ll2str( int64_t val ); — Преобразования длинного целого (64бит) в строку, в десятичном представлении.
string real2str( double val, int prec = 15, char tp = 'g' ); — Преобразования вещественного с точностью prec знаков и типом tp в строку.
string strParse( const string &path, int level, const string &sep, int *off = NULL, bool mergeSepSymb = false ); — Разбор строки path на составляющие, отделённые разделителем sep, объединяя односимвольные mergeSepSymb, начиная со смещения off и контролируя смещение конца элемента в нём же.
string strMess( const char *fmt, ... ); — Формирование стоки по шаблону fmt и аргументам. Реализован на основе "printf".
Ошибка OPC (OPCError)
Объект ошибки "OPCError" является урезанной копией объекта "TError" ядра OpenSCADA.
int childIns( unsigned id, XML_N *nd ); — Вставка вложенного тега nd в позицию id. Отрицательное значение id отсчитывает с конца.
XML_N* childIns( unsigned id, const string &name = "" ); — Вставка вложенного тега с именем name в позицию id. Отрицательное значение id отсчитывает с конца.
void childDel( const unsigned id ); — Удаление вложенного тега id. Отрицательное значение id отсчитывает с конца.
void childDel( XML_N *nd ); — Удаление вложенного тега по его адресу nd.
XML_N* childGet( const string &name, const int numb = 0, bool noex = false ) const; — Получение вложенного numb порядкового тега по имени тега name. noex указывает на запрет генерации исключения в случае отсутствия тега.
XML_N* childGet( const string &attr, const string &name, bool noex = false ) const; — Получение вложенного numb порядкового тега по значению name атрибута attr. noex указывает на запрет генерации исключения в случае отсутствия тега.
XML_N* getElementBy( const string &attr, const string &val ); — Поиск вложенного узла по значению val атрибута attr.
XML_N* parent( ); — Родительский тег данного тега.
NodeId( uint32_t in, uint16_t ins = 0 ); — Численный инициирующий конструктор, для числа in в области имён ins.
NodeId( const string &istr, uint16_t ins = 0, Type tp = String ); — Строковый инициирующий конструктор, для строки istr в области имён ins, с типом tp.
virtual uint32_t rcvBufSz( ); — размер буфера приёмника, больше 8192.
virtual uint32_t sndBufSz( ); — размер буфера передатчика, больше 8192.
virtual uint32_t msgMaxSz( ); — максимальный размер сообщения, 0 для отсутствия ограничения.
virtual uint32_t chunkMaxCnt( ); — максимальное количество кусков, 0 для отсутствия ограничения.
static string iErr( const string &buf, int &off ); — чтение ошибки из потока buf по смещению off.
static const char *iVal( const string &buf, int &off, char vSz ); — чтение значения размером vSz, как участок данных, из потока buf по смещению off.
static int64_t iN( const string &rb, int &off, char vSz ); — чтение знакового целого размером vSz (1, 2, 4, 8) из потока rb по смещению off.
static uint64_t iNu( const string &rb, int &off, char vSz ); — чтение беззнакового целого размером vSz (1, 2, 4, 8) из потока rb по смещению off.
static double iR( const string &rb, int &off, char vSz = 4 ); — чтение вещественного размером vSz (4, 8) из потока rb по смещению off.
static string iS( const string &buf, int &off ); — чтение строки из потока buf по смещению off.
static string iSl( const string &buf, int &off, string *locale = NULL ); — чтение локализованной locale строки из потока buf по смещению off.
static string iSqlf( const string &buf, int &off, uint16_t *nsIdx = NULL ); — чтение строки с квалификатором nsIdx из потока buf по смещению off.
static int64_t iTm( const string &buf, int &off ); — чтение времени, с преобразованием в эпоху UNIX, из потока buf по смещению off.
static NodeId iNodeId( const string &buf, int &off ); — чтение идентификатора узла из потока buf по смещению off.
static string iVariant( const string &buf, int &off, uint8_t *tp = NULL ); — чтение типа вариант из потока buf по смещению off. Возвращает вариант в строковом виде для типа tp.
static void iDataValue( const string &buf, int &off, XML_N &nVal ); — чтение комплексного значения (структура DataValue) в nVal из потока buf по смещению off.
static void oN( string &buf, int64_t val, char sz, int off = -1 ); — запись знакового целого val размером sz (1, 2, 4, 8) в поток buf по смещению off.
static void oNu( string &buf, uint64_t val, char sz, int off = -1 ); — запись беззнакового целого val размером sz (1, 2, 4, 8) в поток buf по смещению off.
static void oR( string &buf, double val, char sz = 4 ); — запись вещественного val размером sz (4, 8) в поток buf по смещению off.
static void oS( string &buf, const string &val, int off = -1 ); — запись строки val в поток buf по смещению off.
static void oSl( string &buf, const string &val, const string &locale = "" ); — запись локализованной locale строки val в поток buf по смещению off.
static void oSqlf( string &buf, const string &val, uint16_t nsIdx = 0 ); — запись строки val с квалификатором nsIdx в поток buf по смещению off.
static void oTm( string &buf, int64_t val ); — запись времени val, в эпохе UNIX, в поток buf по смещению off.
static void oNodeId( string &buf, const NodeId &val ); — запись идентификатора узла val в поток buf по смещению off.
static void oRef( string &buf, uint32_t resMask, const NodeId &nodeId, const NodeId &refTypeId, bool isForward, const string &name, uint32_t nodeClass, const NodeId &typeDef ); — запись в поток buf описателя обзора (структура ReferenceDescription) для маски результата resMask, узла nodeId, типа ссылки refTypeId, направления isForward, имени name, класса узла nodeClass, типа определения typeDef.
void oDataValue( string &buf, uint8_t eMsk, const string &vl, uint8_t vEMsk = 0, int64_t srcTmStmp = 0 ); — запись комплексного значения (структура DataValue) в поток buf для маски кодирования eMsk, значения vl, маски значения vEMsk, времени источника srcTmStmp.
static string randBytes( int num ); — генерация потока случайных данных в количестве num.
static string certPEM2DER( const string &certPem ); — преобразование сертификата из формата PEM certPem в формат DER.
static string certDER2PEM( const string &certDer ); — преобразование сертификата из формата DER certDer в формат PEM.
virtual void clientRcvBufSzSet( const string &inPrtId, uint32_t vl ) = 0; — установка полученного от клиента размера буфера приёмника в значение vl, для подключения inPrtId.
virtual void clientSndBufSzSet( const string &inPrtId, uint32_t vl ) = 0; — установка полученного от клиента размера буфера передатчика в значение vl, для подключения inPrtId.
virtual void clientMsgMaxSzSet( const string &inPrtId, uint32_t vl ) = 0; — установка полученного от клиента максимального размера сообщения в значение vl, для подключения inPrtId.
virtual void clientChunkMaxCntSet( const string &inPrtId, uint32_t vl ) = 0; — установка полученного от клиента максимального количества кусков в значение vl, для подключения inPrtId.
int chnlSet( int cid, const string &iEp, int32_t lifeTm = 0, const string& iClCert = "", const string &iSecPolicy = "None", char iSecMessMode = 1, const string &iclAddr = "", uint32_t iseqN = 1 ); — установка канала безопасности с идентификатором cid (ненулевое значение для обновления) для конечной точки iEp, времени жизни lifeTm, клиентского сертификата iClCert, политики безопасности iSecPolicy, режима безопасности сообщения iSecMessMode, адреса клиента iclAddr, номера последовательности пакета iseqN.
void chnlClose( int cid ); — закрытие канала безопасности cid.
SecCnl chnlGet( int cid ); SecCnl &chnlGet_( int cid ); — получение копии и доступа к объекту канала безопасности cid.
void chnlSecSet( int cid, const string &servKey, const string &clKey ); — установка для канала безопасности cid серверного servKey и клиентского clKey ключа.
static string mkError( uint32_t errId, const string &err = "" ); — формирование ошибки с идентификатором errId и сообщением err.
virtual EP *epEnAt( const string &ep ) = 0; — обработчик запроса объекта конечной точки.
Включенный объект канала безопасности (SecCnl)
Публичные методы:
SecCnl( const string &iEp, uint32_t iTokenId, int32_t iLifeTm, const string &iClCert, const string &iSecPolicy, char iSecMessMode, const string &iclAddr, uint32_t isecN ); — конструктор объекта канала безопасности для: конечной точки iEp, талона безопасности iTokenId, времени жизни iLifeTm, клиентского сертификата iClCert, политики безопасности iSecPolicy, режима безопасности сообщения iSecMessMode, адреса клиента iclAddr, номера последовательности создания канала безопасности isecN.
Публичные атрибуты:
string endPoint; — конечная точка;
string secPolicy; — политика безопасности;
char secMessMode; — режим безопасности сообщения;
int64_t tCreate; — время создание;
int32_t tLife; — время жизни;
uint32_t TokenId, TokenIdPrev; — текущий и предыдущий идентификаторы талона;
string clCert, clAddr; — сертификат и адрес клиента;
string servKey, clKey; — ключ сервера и клиента;
uint32_t servSeqN, clSeqN, startClSeqN; — текущий серверный, клиентский и стартовый номер последовательности.
Включенный объект сеанса (Sess)
Публичные методы:
Sess( const string &iName, double iTInact ); — конструктор объекта для имени iName и таймаута неактивности iTInact.
Публичные атрибуты:
string name, inPrtId, idPolicyId, user; — имя, идентификатор входящего протокола, идентификатор политики безопасности и пользователь;
Включенный объект точки продолжения обзора (ContPoint)
Публичные методы:
ContPoint( const string &i_brNode, const string &i_lstNode, uint32_t i_brDir, uint32_t i_refPerN, const string &i_refTypeId, uint32_t i_nClassMask, uint32_t i_resMask ) — конструктор объекта для узла ветви продолжения обзора i_brNode, узла списка i_lstNode, направления обзора i_brDir, числа ссылок на узел i_refPerN, идентификатора ссылки i_refTypeId, маски класса узла i_nClassMask и маски результата i_resMask.
bool empty( ) const; — точка продолжения пуста.
Публичные атрибуты:
uint32_t brDir, refPerN, nClassMask, resMask; — направление обзора, число ссылок на узел, маска класса узла, маска результата;
string brNode, lstNode, refTypeId; — ветвь узлов, список узла и идентификатор типа ссылки.
Включенный объект подписки (Subscr)
Публичные методы:
Subscr copy( bool noWorkData = true ); — копия объекта подписки, без рабочих данных noWorkData.
SubScrSt setState( SubScrSt st = SS_CUR ); — установка состояния в st.
Публичные атрибуты:
SubScrSt st; — статус подписки;
int sess; — сеанс подписки;
bool en; — статус "Включен";
double publInterv; — интервал публикации (мс);
uint32_t seqN; — номер последовательности для ответов, заворачивается через 1, не инкрементируется на KeepAlive сообщениях;
uint32_t cntrLifeTime, wLT; — счётчик, по исчерпанию которого в течении отсутствия уведомления от клиента необходимо удалять данный объект;
uint32_t cntrKeepAlive, wKA; — счётчик, по исчерпанию которого нужно отправлять пустой ответ публикации и устанавливать StatusChangeNotification в Bad_Timeout;
uint32_t maxNotPerPubl; — максимальное количество уведомлений на один ответ публикации;
uint8_t pr; — приоритет;
vector<MonitItem> mItems; — перечень элементов мониторинга;
deque<string> retrQueue; — очередь перепосылки; используется запросом перепосылки (Republish); очищается на глубину согласно KeepAlive или прямым запросом в наборе подтверждения.
Включенный объект элемента мониторинга (MonitItem)
Публичные атрибуты:
MonitoringMode md; — режим мониторинга;
NodeId nd; — целевой узел;
uint32_t aid; — идентификатор атрибута узла;
TimestampsToReturn tmToRet; — метка времени для возврата;
double smplItv; — интервал измерений;
uint32_t qSz; — размер очереди;
bool dO; — отбрасывать старые;
uint32_t cH; — указатель клиента;
XML_N fltr; — фильтр;
int vTp; — тип значений;
int64_t dtTm; — время последнего значения;
deque<Val> vQueue; — очередь значений.
Включенный объект элемента значения (Val)
Публичные методы:
Val( const string &ivl, int64_t itm ) — конструктор объекта значения ivl на время tm.
Публичные атрибуты:
string vl; — значение;
int64_t tm; — время значения.
Включенный объект конечной точки (EP)
Публичные методы:
EP( Server *serv ); — конструктор объекта, привязанного к серверу serv.
virtual double subscrProcPer( ) = 0; — общий минимальный период цикла публикации и обработки его данных;
virtual uint32_t limSubScr( ); — ограничение на количество подписок;
virtual uint32_t limMonitItms( ); — ограничение количества элементов мониторинга;
virtual uint32_t limRetrQueueTm( ); — ограничение времени на глубину очереди повторной передачи;
bool enableStat( ); — состояние "Включено";
virtual bool publishInPool( ) = 0; — публикация в режиме пула транспорта, иначе из внешней задачи;
virtual void setEnable( bool vl ); — установить во "Включено";
virtual void setPublish( const string &inPrtId ); — запустить задачу публикации или вызов subScrCycle() во входном запросе;
void subScrCycle( unsigned cntr, string *answ = NULL, const string &inPrtId = "" ); — функция вызова цикла обработки подписок на шаге cntr для ответа в answ в режиме пула; inPrtId устанавливается для обработки подписок указанного входного транспорта (подключения) в режиме пула или пустое для обработки всех подписок в специальной задаче;
int secSize( ); — количество политик безопасности;
string secPolicy( int isec ); — получение описания политики безопасности isec;
MessageSecurityMode secMessageMode( int isec ); — режим безопасности сообщения политики безопасности isec;
int sessCreate( const string &iName, double iTInact ); — создание сеанса с именем iName и таймаутом неактивности iTInact, возвращает идентификатор сеанса;
void sessServNonceSet( int sid, const string &servNonce ); — установка последовательности безопасности сеанса sid сервера в servNonce;
virtual uint32_t sessActivate( int sid, uint32_t secCnl, bool check = false, const string &inPrtId = "", const XML_N &identTkn = XML_N() ); — активация сеанса sid для связывания с каналом безопасности secCnl, c проверкой check на возможность-необходимость переназначения, после разрыва предыдущего канала безопасности, возвращает ошибку (0 - ошибки нет);
void sessClose( int sid ); — закрытие сеанса sid;
Sess sessGet( int sid ); — получение экземпляра объекта сеанса для sid;
Sess::ContPoint sessCpGet( int sid, const string &cpId ); — получение точки продолжения браузинга cpId сеанса sid;
void sessCpSet( int sid, const string &cpId, const Sess::ContPoint &cp = Sess::ContPoint() ); — установка точки продолжения браузинга cp для сеанса sid и идентификатора cpId;
uint32_t subscrSet( uint32_t ssId, SubScrSt st, bool en = false, int sess = -1, double publInterv = 0, uint32_t cntrLifeTime = 0, uint32_t cntrKeepAlive = 0, uint32_t maxNotePerPubl = OpcUa_NPosID, int pr = -1 ); — установка-создание подписки ssId для: состояния st, включения en, сеанса sess, интервала публикации publInterv, счётчика времени жизни cntrLifeTime, счётчика сохранения "живым" cntrKeepAlive, максимального количества уведомлений в публикации maxNotePerPubl, приоритета pr;
Subscr subscrGet( uint32_t ssId, bool noWorkData = true ); — получение экземпляра подписки ssId, без рабочих данных noWorkData;
virtual uint32_t reqData( int reqTp, XML_N &req ); — обработчик запроса данных, запрос к дереву узлов сервера.
Защищённые методы:
XML_N *nodeReg( const NodeId &parent, const NodeId &ndId, const string &name, int ndClass, const NodeId &refTypeId, const NodeId &typeDef = 0 ); — регистрация узла ndId в дереве узлов сервера для: родителя parent, класса узла ndClass, идентификатора типа ссылки refTypeId и типа определения typeDef;
Sess *sessGet_( int sid ); — получение ссылки на объект сеанса sid, доступ не защищён захватом ресурса.
Защищённые атрибуты:
char mEn; — состояние "Включен";
uint64_t cntReq; — счётчик запросов;
vector<SecuritySetting> mSec; — перечень политик безопасности конечного узла;
vector<Sess> mSess; — перечень открытых сеансов;
vector<Subscr> mSubScr; — перечень подписок;
XML_N objTree; — дерево узлов сервера;
map<string, XML_N*> ndMap; — карта ссылок на узлы дерева;
pthread_mutex_t> mtxData; — мютекс для блокирования многопоточного доступа;
Server *serv; — ссылка на сервер, контейнер объекта конечной точки.
5. Приватные ключи и сертификаты
Для работы клиентской и протокольной части OPC-UA необходимо создание и помещение приватного ключа и сертификата в конфигурацию объекта клиента и сервера. В общем случае достаточно создания обычного самоподписанного сертификата и приватного ключа без пароля, однако для исключения предупредительных сообщений необходимо добавить ряд служебных полей в сертификат. Это можно сделать взяв файл конфигурации создания сертификата и выполнить следующую процедуру:
6. Замечания
В процессе реализации модулей поддержки OPC-UA был обнаружен ряд несоответствий официального SDK со спецификацией OPC-UA:
OPC-UA Part 6 на странице 27 содержит изображение процесса рукопожатия для установления безопасного канала. Пакет создания сессии, исходя из этого процесса, подписывается клиентским симметричным ключём, а кодируется серверным. На самом деле и подпись и шифрование осуществляется серверным ключём.
OPC-UA Part 4 на странице 141 содержит описание структуры данных подписи, где первыми идут данные подписи, а затем строка алгоритма. На самом деле реализован обратный порядок.
7. Приложение: Таблица совместимости с реализациями OPC-UA других производителей