Модулі "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.2
1.6.6
1.0.1
Автор:
Роман Савоченко
Опис:
Надає реалізацію 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".
Помилка 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.
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 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.
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 seqN, startSeqN; — текущий и стартовый номер последовательности пакета создания канала.
Включенный объект сеанса (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; — указатель клиента;
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 void setEnable( bool vl ); — установить во "Включено";
void subScrCycle( unsigned cntr ); — функция вызова цикла обработки подписок;
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 інших виробників