При подключении торгового оборудования, такого как электронные весы или фискальные регистраторы, через интерфейс RS-232 часто возникает задача прямой низкоуровневой отправки управляющих последовательностей. Для этого нередко используют стандартную библиотеку mscomm.ocx (ActiveX-компонента MsCommLib.MsComm). Проанализируем типичную проблему: как правильно передать байтовую последовательность (HEX/двоичные данные) в свойство драйвера Output, корректно настроить порт и обработать полученный ответ.
Перед передачей команд порт необходимо корректно открыть и инициализировать. Настройку параметров связи (скорость, четность, бит данных, стоповые биты) выполняют через строку свойств Settings, а режим обмена задают с помощью параметров буфера и подтверждения связи.
Разберем базовую процедуру подключения:
Драйвер = Новый COMОбъект("MsCommLib.MsComm.1");
Драйвер.CommPort = Число(НомерПорта); // Например, 1 для COM1
Драйвер.Settings = СокрЛП(Скорость) + ",N,8,1"; // Например, "9600,N,8,1"
Драйвер.InputLen = 0; // Считывать весь буфер за один вызов
Драйвер.InputMode = 0; // 0 - comInputModeText, 1 - comInputModeBinary
Драйвер.NullDiscard = 0;
Драйвер.RThreshold = 0;
Драйвер.Handshaking = 0; // 0 - без подтверждения, 1 - XOnXOff, 2 - RTS/CTS
Драйвер.EOFEnable = Истина;
Если Драйвер.PortOpen = 1 Тогда
Драйвер.PortOpen = 0;
КонецЕсли;
Драйвер.PortOpen = 1;
Параметр Handshaking задает протокол подтверждения связи. Допустимые значения:
Самый надежный способ передать любую последовательность байтов (включая нулевые байты 0x00) — использовать типизированный массив COM. Если передавать команду обычной строкой, нулевой байт воспримется как символ окончания строки (null-terminator), и пакет будет обрезан. Компонента mscomm.ocx принимает бинарные массивы с типом элемента VT_UI1 (беззнаковый байт).
Разберем по шагам создание и отправку такого пакета:
// Создаем одномерный массив байтов COM нужной длины
РазмерПакета = 8;
ДвоичныйМассив = Новый COMSafeArray("vt_ui1", РазмерПакета);
// Заполняем массив десятичными кодами байтов
ДвоичныйМассив.Установить(0, 2); // 0x02
ДвоичныйМассив.Установить(1, 5); // 0x05
ДвоичныйМассив.Установить(2, 19); // 0x13
ДвоичныйМассив.Установить(3, 30); // 0x1E
ДвоичныйМассив.Установить(4, 0); // 0x00
ДвоичныйМассив.Установить(5, 0); // 0x00
ДвоичныйМассив.Установить(6, 0); // 0x00
ДвоичныйМассив.Установить(7, 8); // 0x08
// Отправляем пакет в устройство
Объект.Драйвер.Output = ДвоичныйМассив;
Такой подход полностью исключает искажение байтов выше 127 и обрезание пакета на нулевых значениях.
Если протокол весов ориентирован на текстовые команды или не содержит нулевых байтов, можно передавать данные в виде строки. Для преобразования строки HEX-кодов вида "02 05 13 1E 08" в последовательность символов используют функции перевода систем счисления и преобразования кодов в символы.
Рассмотрим реализацию вспомогательных функций:
Функция ToDec(Знач тЗначение, тОснование)
тРезультат = 0;
тДлина = СтрДлина(тЗначение);
Для тСимвол = 1 По тДлина Цикл
тМножитель = 1;
Для тСчет = 1 По тДлина - тСимвол Цикл
тМножитель = тМножитель * тОснование;
КонецЦикла;
тРезультат = тРезультат + (Найти("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", ВРег(Сред(тЗначение, тСимвол, 1))) - 1) * тМножитель;
КонецЦикла;
Возврат Окр(тРезультат);
КонецФункции
Функция СимволКода(КодБайта)
// Для стандартных кодов можно использовать штатную функцию Символ()
// Либо выполнять генерацию через VBScript Chr() при необходимости строгой ANSI-кодировки
Возврат Символ(КодБайта);
КонецФункции
Функция ВернутьСтрИзHex(СтрHex)
Стр = СтрЗаменить(СтрHex, " ", Символы.ПС);
Ткс = Новый ТекстовыйДокумент;
Ткс.УстановитьТекст(Стр);
Команда = "";
Для н = 1 По Ткс.КоличествоСтрок() Цикл
Слово = СокрЛП(Ткс.ПолучитьСтроку(н));
Если ЗначениеЗаполнено(Слово) Тогда
Команда = Команда + СимволКода(ToDec(Слово, 16));
КонецЕсли;
КонецЦикла;
Возврат Команда;
КонецФункции
Отправка команды в порт в таком случае выглядит следующим образом:
Объект.Драйвер.Output = ВернутьСтрИзHex("02 05 13 1E 08");
После отправки команды порт переходит в режим ожидания ответа. Не следует использовать бесконечные блокирующие циклы без ограничения времени, так как это приводит к зависанию интерфейса платформы. Рекомендуется использовать цикл с ограничением по таймауту и проверкой количества поступивших байт через InBufferCount.
Посмотрим на пример безопасного опроса буфера:
Функция ПолучитьОтвет(Драйвер, ТаймаутСекунд = 3)
ВремяНачала = ТекущаяДата();
Ответ = "";
Пока (ТекущаяДата() - ВремяНачала) < ТаймаутСекунд Цикл
ОбработкаПрерыванияПользователя();
Если Драйвер.InBufferCount > 0 Тогда
Попытка
Ответ = Ответ + Драйвер.Input;
Исключение
Сообщить("Ошибка чтения из буфера: " + ОписаниеОшибки());
Прервать;
КонецПопытки;
// Если получен признак конца кадра (например, символ перевода строки), завершаем чтение
Если Найти(Ответ, Символы.ВК) > 0 Или Найти(Ответ, Символы.ПС) > 0 Тогда
Прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Ответ;
КонецФункции
При использовании mscomm.ocx важно учитывать следующие архитектурные особенности:
MsCommLib.MsComm в среде Windows требуется наличие специального лицензионного ключа времени выполнения в реестре. На рабочих машинах без среды разработки библиотека может вызывать ошибку инициализации лицензии.OnComm часто приводит к аварийному завершению платформы из-за межпотоковых конфликтов.Если есть возможность выбора, для промышленной эксплуатации рекомендуется использовать компоненты на базе Native API от разработчиков торгового оборудования или типовую Библиотеку подключаемого оборудования (БПО), в которой протоколы взаимодействия с весами уже реализованы и отлажены на уровне сертифицированных драйверов.