Изображение квадрата Дюрера

ООО АВТОМАТИКА плюс

Rambler's Top100

Рейтинг@Mail.ru

Выбор библиотеки для работы с COM-портами в Delphi

Версия от 30.10.2011

Данная тема в Интернете раскрыта уже до такой  степени, что мне остается добавить от себя лишь несколько слов.
Существует огромное количество библиотек для работы с COM-портами, как платных, так и бесплатных. Разумеется, обзором платных библиотек заниматься мы здесь не будем, ограничимся только бесплатными. Среди них наиболее популярными являются: AsyncPro и ComPort Library.

AsyncPro

AsyncPro - это известная, в былые времена популярная библиотека для работы с различным подключаемым к компьютеру оборудованием, обладающая огромными возможностями. Библиотека весьма сложная и громоздкая. Поддерживает большое количество устройств, конвертеров, адаптеров, драйверов и т.п. Библиотека упрощает работу с обычными модемами, GSM-модемами (в том числе отправка SMS) и т.д.
В части работы с COM-портами (и не только) выявлены следующие недостатки библиотеки AsyncPro:

  • Вся синхронизация выполняется в основном потоке. Попытка реализовать прием данных в дополнительном потоке не увенчалась успехом. Плохо то, что COM-порт приходится открывать в основном потоке. Для встроенных COM-портов и PCI-to-Serial-плат в этом нет ничего страшного, однако при использовании популярных конвертеров Ethernet-to-Serial Moxa подвисания основного потока составляют секунды. Кроме того, основной поток весьма существенно подвисает при попытке работы с модемом в том случае, если он не подключен, либо некорректно выставлена скорость.
  • В связи с необходимостью поддержки большого числа COM-устройств и всевозможных драйверов работы с COM-портами неограниченного радиуса кривизны, в библиотеке APRO реализована обработка всех возможных событий и ситуаций (особенно радуют "выкрутасы" с "недокументированным" EV_RINGTE). Набор событий "EV_", на которые реагирует APRO, задан с помощью константы, поэтому программист поменять его не может. Для чтения одной стандартной порции из 14 байт (размер буфера FIFO) библиотека APRO выполнит сотни вызовов WaitCommEvent и GetModemStatus. Даже после окончания обмена данными библиотека продолжит каждые 50 мс осуществлять вызов функций GetModemStatus и ReadFile (опять же, с целью поддержки кривых драйверов). Таким образом, библиотека APRO существенно ухудшает производительность приложения. Особенно это становится заметным, если Вам требуется произвести обмен данными с несколькими устройствами одновременно. На практике, пользователю очень быстро надоедает работать с подобными приложениями.
  • Библиотека давно перестала развиваться. Представленная на сайте sf.net последняя версия 5.00 хоть и портирована под юникодные Delphi, однако доступны лишь несколько компонентов (к счастью, в их числе компонент для работы с COM-портами TApdComPort). Качество портирования вызывает большие сомнения, поскольку при компиляции постоянно лезут варнинги, преимущественно связанные с преобразованием строк. В правильно портированной библиотеке не должно быть никаких варнингов.
  • Имеются трудности в установке библиотеки APRO на юникодные Delphi (однако они легко решаются).

В качестве особенности библиотеки APRO следует упомянуть кэширование принимаемых из порта данных (вернее сказать - от драйвера). В процессе приема байтов участвуют как минимум 3 кэша: буфер FIFO COM-порта (для обычных UART - до 14 байт, как указано в окне настройки COM-порта в Windows; для других устройств, видимо, имеется что-то похожее), затем данные из буфера COM-порта поступают в кэш драйвера (обычно не более 4КБ, для драйверов память - это всегда большой дефицит), после этого программа считывает принятые данные из кэша драйвера в свой собственных кэш (с помощью функции ReadFile). У библиотеки APRO имеется еще один кэш, в который складируются данные, считанные с помощью череды вызовов ReadFile. Наличие собственного большого кэша снижает вероятность ошибки переполнения кэша драйвера (ведь если эти данные не считывать, то кэш драйвера может заполниться очень быстро - это 400 мс на скорости 115200). Даже если в обработчике OnTriggerAvail вызвать ShowMessage, библиотека APRO все равно будет параллельно считывать данные с COM-порта (пока не переполнится ее собственный кэш).

ComPort Library

ComPort Library - это проверенная годами (более 10 лет) библиотека для работы с COM-портами. Ее реализация во много раз проще и понятнее, чем APRO. Библиотека имеет следующие особенности:

  • По умолчанию используется синхронизация через основной поток, однако ее можно легко отключить (с помощью свойства SyncMethod, его следует вызывать до метода Open).
  • Важной особенностью данной библиотеки является то, что она не кэширует принимаемые данные, а вместо этого попросту вызывает обработчик события OnRxChar (либо override-метод DoRxChar), причем в контексте основного потока (по умолчанию), а уже из этого обработчика следует вызвать одну из функций приёма данных, например, Read или ReadStr и только после этого будут считаны данные, накопившиеся в кэше драйвера. Очевидно, что если основной поток в это время занят чем-то другим, то он может не успеть считать кэш драйвера, произойдет его переполнение, что может привести к потере данных и нарушению протокола обмена с устройством. Может быть другая ситуация: устройство (например, касса Штрих или Феликс) отправляет на компьютер данные и ожидает от него не более 100 мс байта подтверждения приёма ACK. Если основной поток подвит, то он не успеет ответить за 100 мс и произойдет нарушение обмена данными. В таких случаях следует отключать синхронизацию. При этом важно помнить, что обработчик OnRxChar будет вызван из дополнительного потока, поэтому в этом обработчике не должно быть кода, обращающегося к объектам основного потока без синхронизации. Следует тщательно следить за кодом отправки данных - нельзя вызывать метод Write одновременно из разных потоков.
  • Важным преимуществом библиотеки ComPort Library является возможность установить набор событий COM-порта, на которые библиотека будет реагировать, например, Port.Events := [evRxChar]. Благодаря этому, работа с COM-портом не приводит к ухудшению производительности (даже если осуществляется одновременный обмен с множеством устройств, подключенных к разным COM-портам).
  • Следующей особенностью библиотеки является то, что метод Write осуществляет синхронную запись в порт. Функция не завершится, пока в порт не будут переданы все байты. Для асинхронной записи служит метод WriteAsync, однако перед его вызовом необходимо инициализировать указатель PAsync с помощью процедуры InitAsync (см. исходники библиотеки).

Ссылки

Разумеется, при работе с COM-портами следует иметь общее представление об используемым механизмах. Предлагаю следующую подборку ссылок:

А для простых экспериментов достаточно закоротить Tx на Rx (замкнуть между собой выходы 2 и 3 в разъеме COM-порта DB9).

Логинов Дмитрий © 2005-2015