На днях пришлось крепко повозиться с настройкой вызова удалённого сервера по протоколу OPC DA 2.05a, и эта информация бы очень пригодилась, знай я её заранее.
В общем случае OPC — это набор открытых протоколов, регламентирующих взаимодействие между собой различных объектов автоматизации, таких как SCADA-системы, к примеру. OPC DA (Data Access) — это один из таких протоколов, он обеспечивает обмен данными с устройствами или программными компонентами. В моем случае по этому протоколу нужно было периодически забирать данные со SCADA-системы. И самое важное — OPC DA работает на базе технологии COM, так что взаимодействие с OPC сервером по сути сводится к взаимодействию с COM сервером.
Называются OPC .NET API 2.00 Redistributable — их нельзя скачать просто так, нужно быть «мембером» (= зарегиться и занести деньжат). Там же и OPC Core Components, от которых эта библиотека зависит. На рутрекере можно найти и то, и другое. А вообще не совсем понятно, почему для получения библиотеки от Opc Foundation — компании, которая продвигает «открытый стандарт» — нужно что-то платить.
Что могу сказать по поводу этой библиотеки. Документации к ней нигде нет, и API построен не самым лучшим образом (например есть несколько интерфейсов и классов с одним и тем же именем, но в разных пространствах имен, жутко неудобно, постоянно нужно лезть в Object Browser и смотреть, какой именно класс нужен), однако функциональности полно — можно что угодно сделать, что только можно сделать с OPC серверами. Кстати, я для удобства прогнал сборки рефлектором и работал уже с исходниками — все проблемы декомпиляции по счастливой случайности возникли в других протоколах (OPC AE, OPC HDA) и я их просто выкинул за ненадобностью. Могу прислать солюшен, если кому интересно, пишите.
Платные компоненты (причем весьма дорогие). Скачал я Evaluation версию — инсталлятор, который потребовал пароль (!), ну а пароль пришел по почте. Самое полезное в этом наборе компонент — тестовые клиенты для OPC — winforms приложения, позволяющие попробовать приконнектиться и посмотреть, что есть внутри OPC сервера. Сами библиотеки я не смотрел, они обфусцированы и в них заложено ограничение по времени — полчаса, потом программу надо перезапускать. Но с тестовым клиентом возился долго, так как система у меня была 64битная, а сборка тестового клиента, как оказалось, собрана под Target Platform = AnyCPU, и в 32битной винде запускалась как 32битное приложение (и все работало как надо), а в 64битной — как 64битное. Что приводило к ошибке в коде COM-интеропа вида «CLSID is not registered». А я думал, что у меня что-то неправильно настроено и 2 дня убил на копания в secpol.msc, dcomcnfg и compmgmt.msc. По счастливой случайности догадался запустить клиент с другой тачки и все стало ясно. С помощью ILDASM и Hex-редактора определил смещение флага TargetPlatform (от начала CLR Header), добавил туда второй бит 32BITREQUIRED и все заработало. Вывод — если у вас не работает COM Interop, первым делом проверьте соответствие платформы. К слову, клиент тоже был обфусцирован (с помощью SmartAssembly), и его CLR Header был расположен в конце.
Библиотека от энтузиаста на codeproject.com. Ничего не могу сказать, но именно её код использовал мой предшественник, который реализовал локальное взаимодействие с OPC сервером. Судя по тому, что написано в статье, она как раз для локального взаимодействия и предназначена. Плюсы — доступные исходники, наличие тестового клиента, отсутствие зависимостей.
В принципе, ничего сложного в этом нет, если вы имели опыт взаимодействия с COM/DCOM приложениями. А тем, кто как и я, не особо разбирается в этих технологиях, могу порекомендовать писать код, поглядывая на декомпилированные исходники библиотеки от OPC Foundation. По сути, для взаимодействия с OPC сервером достаточно всего лишь сделать интеропы на необходимые интерфейсы, получить их и дергать методы.
— Тестовый клиент не подключается с ошибкой RPC сервер недоступен — проверьте доступность портов, порта номер 135 как минимум (основной порт DCOM).
— Access Denied — придется повозиться с настройкой как сервера, так и клиента. См. ссылки внизу
— CLSID is not registered — проверьте, установлен ли у вас Core Components, возможно их не хватает. Либо проверьте Target Platform сборки, осуществляющей интероп. Может быть, там AnyCPU а должно быть x86.
— CoCreateInstanceEx возвращает валидный COM объект, но при касте его к COM интерфейсам вываливается Access Denied (0x80070001). С этой проблемой я возился полдня. Эта штука происходит, когда для доступа к серверу необходимо указать юзера и пароль. Вы вызываете CoCreateInstanceEx, заполнив перед этим SERVER_INFO, и вам приходит ссылка на объект. Однако следующие вызовы QueryInterface не сохраняют параметров доступа, которые вы указали при получении объекта, и это приводит к Access Denied. Решение — вызвать магическую функцию CoInitializeSecurity, которая установит дефолтные параметры безопасности для COM-вызовов. Код:
При вызове этой функции может случиться ошибка RPC_E_TOO_LATE. Эта ошибка возникает обычно из-за хост-процесса Visual Studio, который неявно вызывает CoInitializeSecurity при старте. Для решения проблемы достаточно отключить использование хост-процесса в настройках проекта.
По этой проблеме есть несколько ссылок с полезной информацией:
Как чел с stackoverflow сам себе помог разобраться
Готовые интеропы функции с ремарками
Предложения майрософту от разработчиков по этой проблеме
OPC Training Institute — сайт с множеством отлично оформленных статей, которые помогают в случае проблем. Например, как настроить DCOM, какие возможные причины ошибки RPC server is not available итд. Требует регистрации, регистрация бесплатна.
Туториалы по настройке DCOM — еще 1 хорошо оформленный туториал для настройки.
1. Что такое OPC DA и в частности OPC DA 2.05a
В общем случае OPC — это набор открытых протоколов, регламентирующих взаимодействие между собой различных объектов автоматизации, таких как SCADA-системы, к примеру. OPC DA (Data Access) — это один из таких протоколов, он обеспечивает обмен данными с устройствами или программными компонентами. В моем случае по этому протоколу нужно было периодически забирать данные со SCADA-системы. И самое важное — OPC DA работает на базе технологии COM, так что взаимодействие с OPC сервером по сути сводится к взаимодействию с COM сервером.
2. Какие есть библиотеки
Бинарники от Opc Foundation
Называются OPC .NET API 2.00 Redistributable — их нельзя скачать просто так, нужно быть «мембером» (= зарегиться и занести деньжат). Там же и OPC Core Components, от которых эта библиотека зависит. На рутрекере можно найти и то, и другое. А вообще не совсем понятно, почему для получения библиотеки от Opc Foundation — компании, которая продвигает «открытый стандарт» — нужно что-то платить.
Что могу сказать по поводу этой библиотеки. Документации к ней нигде нет, и API построен не самым лучшим образом (например есть несколько интерфейсов и классов с одним и тем же именем, но в разных пространствах имен, жутко неудобно, постоянно нужно лезть в Object Browser и смотреть, какой именно класс нужен), однако функциональности полно — можно что угодно сделать, что только можно сделать с OPC серверами. Кстати, я для удобства прогнал сборки рефлектором и работал уже с исходниками — все проблемы декомпиляции по счастливой случайности возникли в других протоколах (OPC AE, OPC HDA) и я их просто выкинул за ненадобностью. Могу прислать солюшен, если кому интересно, пишите.
Компоненты от Advasol
Платные компоненты (причем весьма дорогие). Скачал я Evaluation версию — инсталлятор, который потребовал пароль (!), ну а пароль пришел по почте. Самое полезное в этом наборе компонент — тестовые клиенты для OPC — winforms приложения, позволяющие попробовать приконнектиться и посмотреть, что есть внутри OPC сервера. Сами библиотеки я не смотрел, они обфусцированы и в них заложено ограничение по времени — полчаса, потом программу надо перезапускать. Но с тестовым клиентом возился долго, так как система у меня была 64битная, а сборка тестового клиента, как оказалось, собрана под Target Platform = AnyCPU, и в 32битной винде запускалась как 32битное приложение (и все работало как надо), а в 64битной — как 64битное. Что приводило к ошибке в коде COM-интеропа вида «CLSID is not registered». А я думал, что у меня что-то неправильно настроено и 2 дня убил на копания в secpol.msc, dcomcnfg и compmgmt.msc. По счастливой случайности догадался запустить клиент с другой тачки и все стало ясно. С помощью ILDASM и Hex-редактора определил смещение флага TargetPlatform (от начала CLR Header), добавил туда второй бит 32BITREQUIRED и все заработало. Вывод — если у вас не работает COM Interop, первым делом проверьте соответствие платформы. К слову, клиент тоже был обфусцирован (с помощью SmartAssembly), и его CLR Header был расположен в конце.
Библиотека OPCDOTNET
Библиотека от энтузиаста на codeproject.com. Ничего не могу сказать, но именно её код использовал мой предшественник, который реализовал локальное взаимодействие с OPC сервером. Судя по тому, что написано в статье, она как раз для локального взаимодействия и предназначена. Плюсы — доступные исходники, наличие тестового клиента, отсутствие зависимостей.
3. Можно ли написать код без использования библиотек
В принципе, ничего сложного в этом нет, если вы имели опыт взаимодействия с COM/DCOM приложениями. А тем, кто как и я, не особо разбирается в этих технологиях, могу порекомендовать писать код, поглядывая на декомпилированные исходники библиотеки от OPC Foundation. По сути, для взаимодействия с OPC сервером достаточно всего лишь сделать интеропы на необходимые интерфейсы, получить их и дергать методы.
4. Проблемы
— Тестовый клиент не подключается с ошибкой RPC сервер недоступен — проверьте доступность портов, порта номер 135 как минимум (основной порт DCOM).
— Access Denied — придется повозиться с настройкой как сервера, так и клиента. См. ссылки внизу
— CLSID is not registered — проверьте, установлен ли у вас Core Components, возможно их не хватает. Либо проверьте Target Platform сборки, осуществляющей интероп. Может быть, там AnyCPU а должно быть x86.
— CoCreateInstanceEx возвращает валидный COM объект, но при касте его к COM интерфейсам вываливается Access Denied (0x80070001). С этой проблемой я возился полдня. Эта штука происходит, когда для доступа к серверу необходимо указать юзера и пароль. Вы вызываете CoCreateInstanceEx, заполнив перед этим SERVER_INFO, и вам приходит ссылка на объект. Однако следующие вызовы QueryInterface не сохраняют параметров доступа, которые вы указали при получении объекта, и это приводит к Access Denied. Решение — вызвать магическую функцию CoInitializeSecurity, которая установит дефолтные параметры безопасности для COM-вызовов. Код:
[DllImport("ole32.dll")]
private static extern int CoInitializeSecurity(IntPtr pSecDesc, int cAuthSvc,
SOLE_AUTHENTICATION_SERVICE[] asAuthSvc, IntPtr pReserved1,
uint dwAuthnLevel, uint dwImpLevel, IntPtr pAuthList,
uint dwCapabilities, IntPtr pReserved3);
public static void InitializeSecurity() {
int errorCode = CoInitializeSecurity(IntPtr.Zero, -1, null, IntPtr.Zero, 1, 2,
IntPtr.Zero, 0, IntPtr.Zero);
if (errorCode != 0) {
throw new ExternalException("CoInitializeSecurity: " +
GetSystemMessage(errorCode), errorCode);
}
}
При вызове этой функции может случиться ошибка RPC_E_TOO_LATE. Эта ошибка возникает обычно из-за хост-процесса Visual Studio, который неявно вызывает CoInitializeSecurity при старте. Для решения проблемы достаточно отключить использование хост-процесса в настройках проекта.
По этой проблеме есть несколько ссылок с полезной информацией:
Как чел с stackoverflow сам себе помог разобраться
Готовые интеропы функции с ремарками
Предложения майрософту от разработчиков по этой проблеме
5. Ссылки по теме
OPC Training Institute — сайт с множеством отлично оформленных статей, которые помогают в случае проблем. Например, как настроить DCOM, какие возможные причины ошибки RPC server is not available итд. Требует регистрации, регистрация бесплатна.
Туториалы по настройке DCOM — еще 1 хорошо оформленный туториал для настройки.