Работа с последовательным (COM, RS-232) портом из 1С:Предприятие 7.7 и 8.0 Ключевые слова: последовательный, порт, COM, RS232, четность, кабель, сканер
Последовательный порт на практике служит для подключения к компьютеру считывателей штрих-кодов, электронных весов, а также другого внешнего оборудования. Интерфейс RS-232 был разработан в 1969 году рядом крупных промышленных корпораций и опубликован Ассоциацией электронной промышленности США (Electronic Industries Association — EIA). (http://ru.wikipedia.org/wiki/RS-232).
Несмотря на то, что стандарт не самый лучший по скоростным ("Стандарт RS-232 1969 года не использовал технологию витой пары, которая могла бы функционировать в сотни раз быстрее." http://www.emanual.ru/download/8778.html), электрическим (не позволяет питать более мощные устройства, чем мышь) и программным (я встречал более интуитивно "приятные" протоколы, такие как I2C) характеристикам, он повсеместно распространен, и его поддержку можно встретить у производителей самого различного внешнего оборудования и программного обеспечения.
При экспериментах я устанавливал следующие (стандартные) настройки порта: скорость 9600 бит/с, 1 стоповый бит, нет контроля четности, 8 бит/байт). Для своих опытов я использовал кабель для соединения портов COM1 и COM2. Вы можете спаять такой кабель http://subscribe.ru/archive/tech.electronics/200104/21033223.html), или приобрести его на рынке примерно за 70 рублей (на упаковке кабеля есть надпись - Made in China - спасибо китайцам).
Следует отметить, что COM и LPT-порты чувствительны к "горячему" подключению - соединяя включенные устройства, вы можете спалить порты (или что-то еще). Поэтому перед подключением устройства я рекомендую отключать их от сети питания, и только потом их выполнять их электрическое соединение.
Эмуляция COM-порта при наличии USB-соединения
Порой программное обеспечение оказывается даже более консервативным, чем оборудование: для таких случаев имеется возможность представить порт USB "под именем" свободного COM-порта, например, при помощи драйвера (FTDI, сайт http://www.ftdichip.com), и работать с ним как с новым COM-портом.
Посылка тестовых данных в COM-порт
Имея тестовый кабель, вы уже можете экспериментировать с внешними компонентами для 1С:Предприятие.
Посылку тестовых данных в порт я производил из пакетного (.BAT) файла MS-DOS. Содержимое файла test.bat:
mode com2 baud=9600 parity=n data=8 stop=1
type test.txt >com2
В файле test.txt введите несколько символов (например, 1234567890) и завершите - обязательно! - их символом перевода строки (нажатием Enter).
Программа, подключенная к другому концу кабеля, может быть тестовой программой TestComPort.exe (скачайте комплект, приведенный в конце статьи; в программе надо выбрать порт - в данном случае COM1, нажать Открыть, и уже можно посылать в нее символы при помощи BAT-файла, в большом окне вы увидите принятые текстовые строки), конфигурацией для 1С:Предприятие, приложенной в этом же архиве, или стандартной конфигурацией наподобие "1С:Торговля и Склад", где активизирована работа со сканером, подключенным в последовательный порт (см. описание настроек этой конфигурации и внешнюю компоненту scanopos.dll).
Получение внешнего события в 1С:Предприятие
///////////////////////////////////////////////////////////////////////
Процедура ОбработкаВнешнегоСобытия(Источник,Событие,Данные)//Предопределенная процедура 1С
//Глобальный обработчик внешнего события
Сообщить("Внешнее событие: Источник="+Источник+" Событие="+Событие+"
Данные="+Данные);
КонецПроцедуры
Данная процедура может располагаться в глобальном модуле или модуле формы 1С:Предприятие 7.7 или 8.0. Процедура принимает три входящих параметра: Источник, Событие и Данные. Это три текстовые строки, которые могут принимать произвольные значения - в качестве Источника можно установить имя внешней компоненты, в качестве события - строку-идентификатор события, например, "BarCodeValue", а в качестве данных - данные от устройства, например, считанный штрихкод.
Инициализация внешней компоненты
Код процедуры ПриНачалеРаботыСистемы() в тестовой конфигурации выглядит следующим образом:
///////////////////////////////////////////////////////////////////////
Процедура ПриНачалеРаботыСистемы() //Предопределенная процедура 1С
ок=1;
Если глЗагрузитьВнешнююКомпоненту("vk_rs232")=0 Тогда
Сообщить("Внешняя компонента не была загружена!","!");
Иначе
rs232=СоздатьОбъект("AddIn.vk_rs232");
КонецЕсли;
ИмяПорта=""+Константа.ComPort;
Сообщить("Константа.ComPort: "+ИмяПорта);
Сообщить("При считывании кода, который завершается символами #13#10, здесь должны
появляться считанные строки.");
//Начинаем слушать порт
rs232.ОткрытьПорт(ИмяПорта);
КонецПроцедуры
Первый запуск конфигурации необходимо производить под правами администратора или привилегированного пользователя Windows. Это необходимо для любых внешних компонент или OLE-объектов, чтобы компонента (DLL) могла "прописать" себя в системный реестр Windows.
Пример работы с COM-портом
Рассмотренные ниже примеры работы с последовательным (COM-) портом будут использовать средства Windows API (системные функции CreateFile и т.д.). Для компиляции примеров потребуется Delphi версии 6 (впрочем, вы легко сможете переделать примеры под любой язык программирования, который поддерживает вызовы Windows API).
В конце статьи приведена ссылка, по которой вы можете скачать работающие примеры кода внешней компоненты (vk_rs232.dll) и упрощенного тестового примера (TestComPort.exe, который содержит 120 строк тестового кода). Рассмотренная в качестве примера внешняя компонента для 1С:Предприятие
умеет читать текстовые строки из последовательного порта, и представлять их как событие считывания штрихкода "BarCodeValue". Поэтому компоненту можно без изменений использовать, например, в конфигурации "Торговля и Склад" в качестве замены для штатной компоненты для считывания штрихкодов. Также компонента умеет записывать "встречные" данные в последовательный порт, если это необходимо для работы с устройствами (для сканеров штрих-кодов это не нужно).
Пример расчитан на передачу в устройство или из устройства текстовых строк, разделенных символами 13,10, что характерно, например, для сканеров штрихкодов. Я использую фиксированные настройки (скорость - 9600, один стоп-бит, без бита четности, 8 бит в одном байте), а также фиксированные настройки таймаута (чтение "отваливается" через относительно короткий промежуток времени, чтобы избежать зависания). Вы можете переделать эти умолчания под ваше оборудование и требования к программному обеспечению.
Настройка сканера
Вы можете настроить сканер на те или иные завершающие коды (#13#10 и другие), а также изменить его настройки при помощи установочных штрихкодов, которые обычно поставляются в комплекте устройства.
Общая идеология работы с RS-232 в системе Windows
В MS-DOS (и ранних версиях Windows, включая 95 и 98), работа с последовательными портами часто производилась напрямую через порты ввода-вывода командами Ассемблера IN и OUT. Этот способ не поддерживают современные операционные системы, начиная с Windows NT. В них остается (надо сказать, довольно древняя) возможность работы с этими портами как с файлами.
Например, достаточно открыть файл с именем "COM1", чтобы можно было писать в него данные для внешнего устройства, и читать из него данные от этого устройства (входной и выходной поток данных не пересекаются).
hCom:=CreateFile("COM2", ... );
ok:=WriteFile( //uses Windows
hCom, //Файл
Buff[1], //Буфер откуда пишем
nBytes, //Число байтов для считывания
wr_cnt, //Число записанных байтов
nil
);
ok:=ReadFile( //uses Windows
hCom, //Файл
Buff, //Буфер куда считываем
100, //Число байтов для считывания
rd_cnt, //Число считанных байтов
nil
);
CloseHandle(hCom);
Настройки параметров и таймаутов COM-порта производятся при помощи функций SetCommState и SetCommTimeOuts соответственно. Ссылка на работающий пример кода, который показывает эту идею более развернуто, приведена в конце статьи.
Конечно, есть возможность работать с COM-портом и "более простыми средствами" - вплоть до открытия файла "COM2" встроенной функцией 1С и записи строк в этот файл и чтения встречных данных от устройства из этого же файла.
Внешняя компонента, по сравнению с этим "простым" режимом работы, добавляет возможность генерировать события считывания данных (например, штрихкодов со сканера) в асинхронном режиме, когда 1С не "замирает" до очередного считывания штрихкода, и не "крутится" в бесконечном цикле чтения, а получает внешние события, и реагирует на них в предопределенной процедуре ОбработкаВнешнегоСобытия(). Это удобно для пользователя, и не заставляет его нажимать на клавиатуре лишние клавиши перед считыванием штрихкода.
Обеспечение многопоточности
Как и в типовом примере от 1С (из "технологии создания внешних компонент"), я использую объект TTimer:
Timer := TTimer.Create(NIL);
Timer.Interval:=500; //пол-секунды
Timer.OnTimer := OnMyTimer;
Timer.Enabled := True;
Процедура OnMyTimer вызывается каждые 500 мс, производит чтение из "файла" и генерирует, при достижении символов конца строки, события чтения данных.
////////////////////////////////////////////////////
procedure AddInObject.OnMyTimer(Sender: TObject);
var str: String;
begin
Timer.Enabled := False;
try
str:=cp.ReadString;
str:=trim(str);
if str<>'' then begin
iEvent.ExternalEvent(c_AddinName, 'BarCodeValue', str);
end;
except
on E:Exception do begin
ShowErrorLog('Ошибка чтения из COM-порта: '+E.Message);
end;
end;
Timer.Enabled := True;
end;
В качестве данных я избегаю возвращать пустые строки (вы можете поставить и другие проверки).
Другой способ обеспечить многопоточную работу - применить объект TThread или более низкоуровневые средства Windows API, рассмотрение которых выходит за рамки этой статьи (можно сказать, что все прекрасно работает и без них).
Следует отметить, что при многопоточной работе возникает наибольшее число трудновоспроизводимых ошибок и "глюков". Если они имеют место быть, тщательно изучите и продумайте ваш код, чтобы избежать параллельного обращения к устройству из разных потоков программы. Простейший случай защиты от параллельного выполнения я применил, останавливая таймер и вновь запуская его при каждом срабатывании таймера.
////////////////////////////////////////////////////
procedure AddInObject.OnMyTimer(Sender: TObject);
var str: String;
begin
Timer.Enabled := False;
//...
Timer.Enabled := True;
end;
Более "идеологически правильный" способ отрабатывать подобные "пересечения" - использовать мьютексы (mutex), которые заставляют другие потоки ждать. Рассмотрение мьютексов, семафоров и других средств обеспечения "безглючности" в многопоточном режиме работы выходит за рамки этой статьи.
MSDN
В качестве официальной справки по различным функциям Windows используйте MSDN, который можно приобрести на DVD или на 3-х CD. В качестве "источника и составной части" он входит в комплект средств разработки от Microsoft, или приобретается отдельно.
Обработка ошибок
При работе с внешними компонентами рекомендуется тщательно отрабатывать исключительные ситуации - иначе 1С:Предприятие будет "обрушиваться" - порой, вместе с введенными пользователем данными - по мере наступления каких-либо ошибок. Я обрабатываю исключительные ситуации так:
try
//...
//Фрагмент кода, где возможна ошибка
//...
except
on E:Exception do begin
ShowErrorLog('Ошибка чтения из COM-порта: '+E.Message);
end;
end;
Метод ShowErrorLog выдает ошибку "красненьким" в окне сообщений 1С.
procedure AddInObject.ShowErrorLog(fMessage:WideString);
var
ErrInfo: PExcepInfo;
begin
If Trim(fMessage) = '' then Exit;
New(ErrInfo);
ErrInfo^.bstrSource := c_AddinName;
ErrInfo^.bstrDescription := fMessage;
ErrInfo^.wCode:=1006;
ErrInfo^.sCode:=E_FAIL; //генерация исключения в 1С
iError.AddError(nil, ErrInfo);
end;
При работе с функциями Windows API я использую стиль генерации исключений:
if not GetCommState(hCom,dcb) //uses Windows
then RaiseLastOSError;
Более древний стиль - использовать возвраты из функций или циклов с кодами ошибок. Это негодное и устаревшее средство, которое использовать настоятельно не рекомендуется - всегда применяйте исключения и их обработку (подробности - Дж.Рихтер "Windows для профессионалов"). Иначе ваши внешние компоненты будут "выполнять недопустимую операцию" и обрушивать программу 1С:Предприятие (вместе с введенными пользователем данными) вместо того, чтобы вывести на экран (или в файл) сообщение о возникшей ошибке.
Заключение
Мы рассмотрели работу с последовательным портом RS-232 из 1С:Предприятие. Различие между версиями 1С:Предприятие 7.7 и 8.0 в данном случае несущественно - внешние компоненты совместимы и могут использоваться в обеих версиях системы. Вы можете посылать данные в порт или считывать их из порта, чтобы управлять устройством или принимать данные от устройства по протоколу RS-232. При помощи тестового кабеля, который соединяет два COM-порта, вы можете имитировать работу с оборудованием, даже не имея его в наличии.
Скачать тестовую конфигурацию и исходный код внешней компоненты
Скачать тестовую конфигурацию и исходный код внешней компоненты вы можете по этой ссылке:
http://x-romix.narod.ru/vk_rs232.rar
(скачивать левой кнопкой мыши, 280К) |