Модулі "OPC-UA", підсистем "Збір даних" та "Транспортні протоколи"
Параметр
Модуль 1
Модуль 2
Бібліотека
ID:
OPC_UA
OPC_UA
libOPC_UA
Ім'я:
Клієнт OPC-UA
Сервер OPC-UA
Бібліотека реалізації OPC-UA у OpenSCADA
Тип:
Збір Даних
Протокол
Бібліотека
Джерело:
daq_OPC_UA.so
daq_OPC_UA.so
libOPC_UA.{h,cpp}
Версія:
1.6
1.8
1.2
Автор:
Роман Савоченко
Опис:
Надає реалізацію OPC-UA клієнтського сервісу.
Надає реалізацію OPC-UA сервісу серверу.
Надає реалізацію протоколу 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).
Модуль збору даних надає лише один тип параметрів — "Стандарт". Додатковим конфігураційним полем параметру даного модуля (рис.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 (RU) ядра 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".
Об'єкт автоматичного розблоковування POSIX мютексу для OPC (OPCAlloc)
Цей об'єкт керування мютексом є копією об'єкту "MtxAlloc" для ядра OpenSCADA.
Публічні методи:
OPCAlloc( pthread_mutex_t &iM, bool ilock = false ); — Ініціалізація об'єкту автоматичного розблокування мютексу iM, створеного раніш. Із блокуванням при створені за lock.
int lock( ); — Захоплення ресурсу. Повернення нуля при вдалому виконанні.
int unlock( ); — Звільнення ресурсу. Повернення нуля при вдалому виконанні.
int tryLock( ); — Спроба захоплення ресурсу, без очікування звільнення. Повернення нуля при вдалому виконанні.
Помилка OPC (OPCError)
Об'єкт помилки "OPCError" є урізаною копією об'єкту "TError" ядра OpenSCADA.
XML_N &operator=( const XML_N &prm ); — Копіювання гілки XML-дерева із prm.
string name( ) const; — Ім'я тегу.
XML_N* setName( const string &s ); — Встановлення ім'я тегу у s.
string text( bool childs = false, bool recursive = false ) const; — Текст тегу. childs — для отримання тексту із спеціалізованих вузлів тексту.
XML_N* setText( const string &s, bool childs = false ); — Встановлення тексту тегу у s. childs — для встановлення тексту у спеціалізованих вузол тексту.
void attrList( vector<string> & list ) const; — Перелік атрибутів list у тегу.
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 int, bool noex = false ) const; — отримання вкладеного тегу за порядковим номером.
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.
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.
static string certThumbprint( const string &certPem ); — отримання підпису із сертифікату PEM certPem.
static string asymmetricEncrypt( const string &mess, const string &certPem, const string &secPolicy ); — асиметричне кодування потоку повідомлення mess сертифікатом certPem (відкритим ключем) для політики secPolicy.
static string asymmetricDecrypt( const string &mess, const string &pvKeyPem, const string &secPolicy ); — асиметричне декодування потоку повідомлення mess ключем pvKeyPem для політики secPolicy.
static string asymmetricSign( const string &mess, const string &pvPem ); — отримання асиметричного підпису закритим ключем сертифікату pvPem для повідомлення mess.
static int asymmetricKeyLength( const string &keyCertPem ); — отримання довжини ключа сертифікату keyCertPem.
static string deriveKey( const string &secret, const string &seed, int keyLen ); — вилучення ключа розміром keyLen із секрету secret та seed.
static string symmetricEncrypt( const string &mess, const string &keySet, const string &secPolicy ); — симетричне шифрування потоку повідомлення mess ключем keySet для політики secPolicy.
static string symmetricDecrypt( const string &mess, const string &keySet, const string &secPolicy ); — асиметричне дешифрування потоку повідомлення mess ключем keySet для політики secPolicy.
static string symmetricSign( const string &mess, const string &keySet, const string &secPolicy ); — отримання симетричного підпису ключем keySet для повідомлення mess та політики secPolicy.
Включений об'єкт параметрів безпеки (SecuritySetting)
Отримані дані:
string policy — політика безпеки;
MessageSecurityMode messageMode — режим повідомлення;
Публічні методи:
SecuritySetting( const string &iplc, int8_t imMode ) — конструктор об'єкту із політикою безпеки iplc та режимом повідомлення imMode.
SecuritySetting( ) — конструктор об'єкту із політикою безпеки "None" та режимом повідомлення MS_None.
4.2. Основний об'єкт клієнта (Client->UA)
Застосування: Прямо успадковується користувацьким об'єктом — клієнт OPC-UA.
Публічні методи:
virtual string applicationUri( ) = 0; — URI додатку.
virtual string productUri( ) = 0; — URI продукту.
virtual string applicationName( ) = 0; — ім'я додатку.
virtual string sessionName( ) = 0; — ім'я сеансу.
virtual string endPoint( ) = 0; — кінцева точка.
virtual string secPolicy( ) = 0; — політика безпеки.
virtual int secMessMode( ) = 0; — режим безпеки повідомлення.
virtual string cert( ) = 0; — сертифікат.
virtual string pvKey( ) = 0; — приватний ключ.
virtual string authData( ) = 0; — дані аутентифікації:
"<Empty>" — анонімний;
"{User}\n{Password}" — за користувачем та паролем.
virtual int messIO( const char *oBuf, int oLen, char *iBuf = NULL, int iLen = 0 ) = 0; — обмін повідомленням, передача запиту та очікування відповіді.
virtual bool connect( int8_t est = -1 ); — отримання статусу підключення, встановлення підключення для est = 1, відключення для est = 0.
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.
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, з перевіркою 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;
Subscr::MonitItem mItGet( uint32_t ssId, uint32_t mItId ); — отримання екземпляру елементу моніторингу mItId для сеансу ssId;
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 інших виробників
Result mask processing fix into the service "Browse" for nodes of OpenSCADA data model. ...
Kepware
Pass
Pass
Pass
Pass
Pass
Specific value types OpcUa_IntAuto and OpcUa_UIntAuto was added for adaptive integer type selection, mostly for provide integer not fixed as int64. Time stamp was removed from "Write" package but the client tell 0x80730000(OpcUa_BadWriteNotSupported)
UAExpert 1.4
Pass
Pass
Pass
Pass
Pass
Packages sequence number split from it's request and set self managing.
Servers
IgnitionOPC_UA
Pass
Pass
Pass
Not tested
NA
B&R Embedded OPC-UA Server
Pass
Pass
Pass
Not tested
NA
The authenticate process fixed by the server provides self specific identifiers to its. The string of bytes wrong interpretation fixed.