При автоматизации рабочих мест на платформе 1С:Предприятие 7.7 часто возникает задача динамического считывания данных с внешнего оборудования: электронных весов, сканеров или контроллеров температуры через последовательный интерфейс (RS-232 / COM-порт). Когда опрос выполняется по схеме «отправить команду — подождать ответ — прочитать данные», задержки на ожидание сигнала приводят к регулярному зависанию диалоговых форм. Если процедура опроса запускается каждые 1–2 секунды, оператор физически не может непрерывно вводить текст в табличные части или поля документов. Разберем архитектурные причины этой проблемы и подробно рассмотрим практические способы ее решения.
Проанализируем, почему типовой синхронный опрос оборудования нарушает работу пользователя:
глСистема.Уснуть(), циклов Пока ... Цикл с замером времени или функции Sleep полностью останавливает обработку очереди сообщений Windows. В эти миллисекунды окно перестает реагировать на действия пользователя.ВнешнееСобытие(), либо в штатную процедуру ОбработкаОжидания(), при срабатывании обработчика платформа выполняет валидацию и перерисовку текущего активного элемента диалога. Стандартное текстовое поле Windows при получении таких системных команд выделяет весь набранный текст целиком, и следующее нажатие клавиши приводит к стиранию ранее введенных символов.Самая распространенная ошибка — использование жесткой паузы внутри одной итерации между отправкой команды в порт и чтением буфера. Разберем, как избавиться от зависаний штатными средствами платформы с помощью разделения процессов на фазы. Мы отправляем запрос в порт на одном шаге, а проверяем наличие байтов в буфере на следующем, не блокируя поток оператора.
Посмотрим на практическую реализацию логики конечного автомата в глобальном модуле:
Перем глВесы;
Перем глВес;
Перем глЖдемПоказаний;
Процедура глОпросВесов()
Если глВесы.PortOpen = 0 Тогда
Возврат;
КонецЕсли;
// Шаг 1: если ответ уже ожидается, проверяем наполнение входного буфера
Если глЖдемПоказаний = 1 Тогда
Если глВесы.InBufferCount >= 13 Тогда
Буфер = СокрЛП(глВесы.Input);
// Извлекаем полезные данные веса из пакета
пВес = Сред(Буфер, 2, 8);
глВес = Число(пВес);
глЖдемПоказаний = 0;
КонецЕсли;
КонецЕсли;
// Шаг 2: если порт свободен, отправляем следующую команду запроса
Если глЖдемПоказаний = 0 Тогда
глВесы.Settings = "19200,N,8,1";
// Отправка команды P и символа возврата каретки (CR)
глВесы.Output = Симв(080) + Симв(013);
глЖдемПоказаний = 1;
КонецЕсли;
КонецПроцедуры
Процедура НачатьМониторингВесов()
глЖдемПоказаний = 0;
// Штатная процедура запускает выполнение процедуры каждые N секунд
ОбработкаОжидания("глОпросВесов", 1);
КонецПроцедуры
Разберем важный нюанс вывода данных: чтобы динамический вес отображался на форме без принудительного вызова Форма.Обновить() (который сбивает курсор оператора), на форме документа размещается текстовое поле, в формуле которого указывается вызов глобальной функции:
Функция глПолучитьТекущийВес()
Возврат глВес;
КонецФункции
При таком подходе значение в текстовом поле будет актуализироваться при естественных интерфейсных событиях без зависания основного интерфейса ввода.
Штатная ОбработкаОжидания() в 1С 7.7 имеет дискретность не менее 1 секунды и может быть назначена только одна на всю информационную базу. Если требуется опрашивать весы чаще (например, каждые 300–500 мс), используем класс Таймер внешней компоненты FormEx. При этом логика проверки состояния также должна оставаться неблокирующей.
Рассмотрим пример кода в модуле формы документа:
Перем оТаймер;
Перем фБлокировкаТаймера;
Перем нСчетчикТаймаута;
Перем СостояниеОпроса; // "Старт", "Ожидание"
Процедура ОбработкаТаймера()
// Защита от повторного входа, если предыдущий шаг еще выполняется
Если фБлокировкаТаймера = 1 Тогда
Возврат;
КонецЕсли;
фБлокировкаТаймера = 1;
Если СостояниеОпроса = "Старт" Тогда
// Очищаем буфер и посылаем запрос веса
Весы.Output = Симв(080) + Симв(013);
СостояниеОпроса = "Ожидание";
нСчетчикТаймаута = 10; // Лимит тактов ожидания
ИначеЕсли СостояниеОпроса = "Ожидание" Тогда
Если Весы.InBufferCount >= 13 Тогда
Буфер = СокрЛП(Весы.Input);
ТекущийВесНаФорме = Число(Сред(Буфер, 2, 8));
СостояниеОпроса = "Старт";
Иначе
нСчетчикТаймаута = нСчетчикТаймаута - 1;
Если нСчетчикТаймаута <= 0 Тогда
// Таймаут ответа: сбрасываем состояние и пробуем снова
СостояниеОпроса = "Старт";
КонецЕсли;
КонецЕсли;
КонецЕсли;
фБлокировкаТаймера = 0;
КонецПроцедуры
Процедура ПриОткрытии()
СостояниеОпроса = "Старт";
фБлокировкаТаймера = 0;
нСчетчикТаймаута = 0;
// Инициализация таймера FormEx
оТаймер = СоздатьОбъект("Таймер");
оТаймер.ЗадатьОбработчикВМодулеФормы(Контекст, "ОбработкаТаймера");
оТаймер.Запустить(500); // Опрос каждые 500 миллисекунд
КонецПроцедуры
Процедура ПриЗакрытии()
Если ПустоеЗначение(оТаймер) = 0 Тогда
оТаймер.Остановить();
КонецЕсли;
КонецПроцедуры
Для гарантированного устранения влияния задержек ввода-вывода низкоуровневая работа с COM-портом выносится в отдельный рабочий поток (worker thread) внешней компоненты на C++, Delphi или .NET.
Разберем правильный архитектурный паттерн взаимодействия компоненты и 1С 7.7:
CR — код 0x0D) с заданным таймаутом, чтение строки и запись результата в защищенную критической секцией переменную памяти.ВнешнееСобытие() на каждый такт опроса, чтобы не вызывать сброс курсора ввода на форме 1С.ВК.ПолучитьВес()) только тогда, когда это действительно необходимо: по нажатию кнопки «Зафиксировать вес» или при переходе на новую строку.Посмотрим на пример вызова подобной компоненты в коде конфигурации:
// Инициализация при старте рабочего места
// Параметры: Порт, Настройки (Скорость, БитДанных, Четность, СтопБит), Таймаут, Интервал, Команда, Терминатор
Если ЗагрузитьВнешнююКомпоненту("vkcomm.dll") = 1 Тогда
ВК = СоздатьОбъект("AddIn.commScales");
// Настройка: 19200 бод, 8 бит данных, без четности, 1 стоп-бит, ожидание символа CR
ВК.Старт("COM1", "19200,8,N,0", 5000, 500, Симв(080) + Симв(013), Симв(013));
КонецЕсли;
// Получение данных по кнопке на форме без задержек и подвисаний
Процедура ПоКнопкеПолучитьВес()
СтрокаОтвета = ВК.ПолучитьТекущийВес();
// Обработка полученной строки до нужной разрядности
ТабличнаяЧасть.Вес = Число(СокрЛП(СтрокаОтвета));
КонецПроцедуры
Если доработка конфигурации ограничена или на рабочем месте используется комплекс разнородных датчиков (весы, RFID-сканеры, датчики температуры), проанализируем альтернативные способы интеграции:
WS_EX_NOACTIVATE, располагается поверх окна 1С и обновляет показания в реальном времени, физически не перехватывая фокус клавиатуры у редактируемых документов.