При интеграции 1С с промышленным оборудованием через протокол OPC DA (Data Access) программисты часто сталкиваются с жесткими требованиями к типам данных. В отличие от 1С, где типизация переменных динамическая, COM-объекты (такие как OPC.Automation) ожидают строго определенные типы: Long, Variant, а также специфические массивы SafeArray. В этой статье мы подробно разберем, как правильно подготовить данные для вызова метода SyncRead и избежать ошибок несовпадения типов.
Для начала проанализируем сигнатуру метода SyncRead, который используется для синхронного чтения данных из OPC-сервера. Она выглядит следующим образом:
SyncRead(Source As Integer, NumItems As Long, ServerHandles() As Long, ByRef Values() As Variant, ByRef Errors() As Long, Optional ByRef Qualities As Variant, Optional ByRef TimeStamps As Variant)
Основные сложности в 1С вызывают следующие параметры:
Numeric (с плавающей точкой), поэтому при передаче в COM-метод важно убедиться, что оно передается без дробной части.Рассмотрим, как в среде 1С создать структуры, которые будут понятны COM-объекту. Единственным способом передать типизированный массив является использование объекта COMSafeArray. При его создании мы должны явно указать тип элементов, используя константы типов VARTYPE.
Выделим основные типы, которые нам понадобятся:
VT_I4 — соответствует 4-байтовому целому числу со знаком (именно это требуется для Long в терминологии VB/COM).VT_VARIANT — универсальный тип, который может содержать данные любого другого типа.VT_I2 — 2-байтовое целое (используется для типа Integer или Short).Давайте разберем процесс подготовки переменных по шагам. Предположим, что мы уже успешно создали соединение с сервером и добавили необходимые группы тегов.
Шаг 1: Подготовка идентификаторов (Server Handles)
Прежде чем читать данные, нам нужно получить массив серверных дескрипторов тегов. Эти дескрипторы выдает сервер при вызове метода AddItem. Проанализируем пример кода:
// Инициализируем количество элементов
КоличествоТегов = 1;
// Создаем массив для дескрипторов (тип Long -> VT_I4)
ServerHandles = Новый COMSafeArray("VT_I4", КоличествоТегов);
// Получаем дескриптор тега (обычно это свойство объекта OPCItem)
Дескриптор = Item.ServerHandle;
// Заполняем массив (индексация в COMSafeArray в 1С начинается с 0)
ServerHandles.SetValue(0, Дескриптор);
Шаг 2: Создание приемных массивов для данных и ошибок
Метод SyncRead требует передачи массивов, в которые он "вернет" результат. Даже если мы читаем один элемент, это должен быть массив. Важно инициализировать их правильными типами:
// Массив для значений (VT_VARIANT)
Values = Новый COMSafeArray("VT_VARIANT", КоличествоТегов);
// Массив для ошибок (VT_I4)
Errors = Новый COMSafeArray("VT_I4", КоличествоТегов);
// Необязательные параметры (Качество и Метки времени)
// Их также рекомендуется инициализировать как SafeArray для стабильности
Qualities = Новый COMSafeArray("VT_VARIANT", КоличествоТегов);
TimeStamps = Новый COMSafeArray("VT_VARIANT", КоличествоТегов);
Шаг 3: Вызов метода SyncRead
Теперь, когда все переменные типизированы, вызовем метод чтения. Выясним назначение первого параметра Source: значение 1 (OPC_DS_CACHE) означает чтение из кэша сервера, значение 2 (OPC_DS_DEVICE) — прямое чтение с физического устройства.
ИсточникДанных = 1; // Читаем из кэша
Попытка
OPCGroup.SyncRead(ИсточникДанных, КоличествоТегов, ServerHandles, Values, Errors, Qualities, TimeStamps);
Исключение
Сообщить("Ошибка при вызове SyncRead: " + ОписаниеОшибки());
КонецПопытки;
После успешного выполнения метода SyncRead, данные будут находиться внутри объектов COMSafeArray. Нам необходимо извлечь их для дальнейшего использования в 1С. Посмотрим, как это сделать правильно:
Для Индекс = 0 По КоличествоТегов - 1 Цикл
// Проверяем код ошибки для конкретного тега (0 - успех)
КодОшибки = Errors.GetValue(Индекс);
Если КодОшибки = 0 Тогда
ТекущееЗначение = Values.GetValue(Индекс);
ТекущееКачество = Qualities.GetValue(Индекс);
МеткаВремени = TimeStamps.GetValue(Индекс);
Сообщить("Значение тега: " + Строка(ТекущееЗначение) + " | Качество: " + Строка(ТекущееКачество));
Иначе
Сообщить("Ошибка чтения тега с индексом " + Индекс + ". Код: " + КодОшибки);
КонецЕсли;
КонецЦикла;
1. Проблема нижней границы массива (LBound)
Исторически библиотеки OPC Automation писались на Visual Basic 6, где массивы часто начинаются с индекса 1. Платформа 1С при создании COMSafeArray всегда создает массив с нижней границей 0. Большинство современных OPC-серверов корректно это обрабатывают, но если вы получаете ошибку "Неверный аргумент", стоит проверить документацию конкретного OPC-сервера. Возможно, он ожидает массив, созданный через специальную обертку с базой 1.
2. Использование метода Item.Read()
Если вам нужно прочитать всего один тег и вы хотите избежать сложностей с COMSafeArray, рассмотрим альтернативный вариант. У каждого объекта OPCItem есть собственный метод Read:
// Объявляем переменные для результата
Значение = Неопределено;
Качество = Неопределено;
Время = Неопределено;
// Вызов метода (параметры передаются по ссылке)
Item.Read(1, Значение, Качество, Время);
Сообщить("Результат: " + Значение);
Этот способ значительно проще в реализации, но он менее эффективен, если вам нужно считывать сотни параметров одновременно, так как каждый вызов Read создает отдельный сетевой запрос.
3. Типизация параметра NumItems
Хотя 1С автоматически пытается привести типы при передаче в COM, иногда возникают ошибки, если число передается в формате с плавающей точкой (например, 1.000). Чтобы гарантировать тип Long, убедитесь, что вы передаете целое число, полученное функцией Цел(), или используйте прямое указание значения.
Работа с типизированными переменными в 1С при взаимодействии с COM-объектами требует внимательности к деталям. Использование COMSafeArray с типами VT_I4 и VT_VARIANT позволяет полностью реализовать протокол обмена данными с любым OPC DA сервером без использования сторонних внешних компонент. Мы разобрали основные шаги от инициализации массивов до извлечения данных, что должно помочь вам успешно решить задачу интеграции.