Приветствую Вас Гость | RSS

Меню сайта

Реклама

Категории раздела
CD-ROM [11]
DLL и PlugIns [75]
Help файлы [20]
INI файлы [16]
RS232 [2]
Активные директории [1]
Директории [0]
Диски [0]
Корзина [5]
Порты [26]
Ресурсы [0]
Файлы [0]
Форматы файлов [15]
Ярлыки [0]

Наш опрос
Оцените мой сайт
Всего ответов: 30

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Форма входа

Главная » Статьи » Файловая система » Порты

Асинхронный режим чтения из Com-порта
Вступление Порядок запуска и работы "службы" (назовем все описываемое ниже так) Com-портов состоит из нескольких достаточно хорошо описанных шагов ( см. статьи по теме ):
  1. Инициализация Com-порта посредством вызова функции CreateFile.
  2. Установка параметров Com-порта посредством последовательного вызова функций GetCommState и SetCommState, а также SetupComm.
  3. Установка параметров тайм-аутов для чтения и записи - GetCommTimeouts и SetCommTimeouts.
  4. Собственно записи в Com-порт - WriteFile и чтения из него - ReadFile.
  5. Закрытие порта по окончанию работ CloseHandle.
Очень большой сложности описанные выше шаги не представляют, однако реализация чтения данных из порта в асинхронном (неблокирующем) режиме заставляет почесать затылок. Об этом и поговорим.

Чтение из Com-порта.

Судя по контексту справки, касающейся функции CreateFile, для "отлова" момента поступления данных в Com-порт следует использовать функцию WaitCommEvent. Предварительно установив маску SetCommMask на то событие, которое хотелось бы отследить. Нужное событие наступает - вызываем функцию ReadFile для чтения поступающих данных.

Казалось бы все в порядке, но... Вызов функции WaitCommEvent насмерть тормозит приложение, пока какие-либо данные не поступят в Com-порт.

Можно конечно, просто взять и запустить в непрерывном цикле ReadFile, однако приложение хотя и будет как-то шевелиться, но это шевеление скорее всего будет напоминать предсмертные судороги.

Как выход из ситуации многие предлагают использовать потоки (thread), забывая при этом описать как это делать :)

Итак потоки.

В модуле Classes для потоков определен специальный класс TThread. Для создания потоков специалисты рекомендуют использовать именно его, а не создавать потоки используя BeginThread и EndThread, т.к. библиотека VCL не является защищенной для потоков в такой реализации. Следуя советам экспертов, для организации контроля поступающих данных в Com-порт и будем использовать готовый класс TThread.

В раздел interface определим тип переменных этого класса, переопределив только один метод класса - Execute, ну и дополнительно объявим свой метод, который и займется опросом Com-порта.

Далее в разделе глобальных переменных определим поток-переменную полученного выше типа
CommThread:TCommThread; //наш поток, в котором будет работать процедура опроса порта
Затем в разделе implementation начинаем ваять.
ВНИМАНИЕ!!!
К этому времени порт уже должен быть инициализирован функцией CreateFile.
  1. 1. Инициализируем поток, используя метод Create.
    type
    //определим тип TComThread - наследника класса TThread
    TCommThread = class(TThread)
    private
    //процедура, занимающаяся опросом порта
    procedure QueryPort;
    protected
    //переопределим метод запуска потока
    procedure Execute; override;
    end;

    procedure StartComThread;
    //инициализация нашего потока
    begin {StartComThread}
    //пытаемся инициализировать поток
    CommThread := TCommThread.Create(False);
    //проверяем получилось или нет
    if CommThread = nil then
    begin {Nil}
    //ошибка, все выключаем и выходим
    SysErrorMessage(GetLastError);
    fmMain.btnStop.Click;
    Exit;
    end; {Nil}
    end; {StartComThread}
    Куски кода взяты из файла проекта, поэтому нажимание на кнопку btnStop главной формы fmMain - это "примочки" примера, не обращайте внимания.

  2. Запускаем процедуру опроса порта в нашем потоке.
    procedure TCommThread.Execute;
    begin {Execute}
    repeat
    QueryPort; //процедура опроса порта будет производиться пока поток не будет прекращен
    until Terminated;
    end; {Execute}
  3. Реализуем асинхронные опрос порта и чтение из него данных
    procedure TCommThread.QueryPort;
    var
    MyBuff: array[0..1023] of Char; //буфер для чтения данных
    ByteReaded: Integer; //количество считанных байт
    Str: string; //вспомогательная строка
    Status: DWord; //статус устройства (модема)
    begin {QueryPort}
    //получим статус COM-порта устройства (модема)
    if not GetCommModemStatus(hPort, Status) then
    begin {ошибка при получении статуса модема}
    //ошибка, все выключаем и выходим
    SysErrorMessage(GetLastError);
    fmMain.btnStop.Click;
    Exit;
    end; {ошибка при получении статуса модема}
    //Обработаем статус устройства (модема) и будем включать(выключать) лампочки
    //готовность устройства (модема) получать данные
    fmMain.imgCTSOn.Visible := ((Status and MS_CTS_ON) = MS_CTS_ON);
    //готовность устройства (модема) к сеансу связи
    fmMain.imgDSROn.Visible := ((Status and MS_DSR_ON) = MS_DSR_ON);
    //принимаются данные с линии сигнала
    fmMain.imgRLSDOn.Visible := ((Status and MS_RLSD_ON) = MS_RLSD_ON);
    //входящий звонок
    fmMain.imgRingOn.Visible := ((Status and MS_RING_ON) = MS_RING_ON);

    //читаем буфер из Com-порта
    FillChar(MyBuff, SizeOf(MyBuff), #0);
    if not ReadFile(hPort, MyBuff, SizeOf(MyBuff), ByteReaded, nil) then
    begin {ошибка при чтении данных}
    //ошибка, все закрываем и уходим
    SysErrorMessage(GetLastError);
    fmMain.btnStop.Click;
    Exit;
    end; {ошибка при чтении данных}
    //данные пришли
    if ByteReaded > 0 then
    begin {ByteReaded>0}
    //посчитаем общее количество прочитанных байтов
    ReciveBytes := ReciveBytes + ByteReaded;
    //преобразуем массив в строку
    Str := string(MyBuff);
    //отправим строку на просмотр
    fmMain.Memo1.Text := fmMain.Memo1.Text + Str;
    //покажем количество считанных байтов
    fmMain.lbRecv.Caption := 'recv: ' + IntToStr(ReciveBytes) + ' bytes...';
    end; {ByteReaded>0}
    end; {QueryPort}

На этом по поводу использования потоков для считывания данных из Com-порта, пожалуй, все.

Прилагающийся пример

Следуя правилам хорошего тона, прикладываю ко всему написанному работающий пример.
В примере используется самое доступное устройство для пользователей интернет - модем (на Com-порту). В качестве "примочек" я использовал лампочки, которые включаются (или выключаются) при изменении статуса модема. Можно было прикрутить лампочки-детекторы входящих-выходящих сигналов, но вместо них используются счетчики байтов.
Реализация кода включения-выключения не самая лучшая: можно было бы использовать TImageList для хранения изображений лампочек. Но почему-то ??? (кто знает почему - напишите) использование ImageList.GetBitmap при наличии запущенного потока "подвешивает" приложение насмерть. Причем это происходит под Windows'98, если тоже самое делать под Windows'95, то все в порядке.

Для проверки работоспособности примера попробуйте понабирать AT-команды
  • ATZ - инициализировать модем
  • ATH - положить трубку
  • ATH1 - поднять трубку
  • ATS0=1 - включить автоподнятие трубки на первый сигнал
  • ATS0=0 - выключить автоподнятие трубки
  • ATDP_номер_телефона_интернет_провайдера - мне нравится больше всего :)
  • ATDP - набор в импульсном режиме, ATDT - набор в тоновом режиме


Да, еще. Проект написан под Delphi3, при использовании Delphi более свежих версий возможны ошибки "несовпадения типов".
В этим случае поменяйте типы "ошибочных" переменных с Integer на Cardinal.
Категория: Порты | Добавил: Angel (07.07.2008)
Просмотров: 679 | Рейтинг: 2.0/1
  Delphi Lab   Главная   Регистрация   Вход  
Интересная Цитата

Поиск

Магазин


Copyright MyCorp © 2025 Хостинг от uCoz