Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2009.04.19;
Скачать: [xml.tar.bz2];

Вниз

Параллельный порт. Асинхронная запись/чтение   Найти похожие ветки 

 
nicestep   (2008-05-20 20:12) [0]

Необходимо провести асинхронную запись / асинхронное чтение параллельного порта COM.
Подключено устройство, работающее по принципу "запрос - ответ". Но дело в том, что ответ от него приходит с порядочной задержкой ввиду специфики работы самого устройства (до ~3 секунд).

Раньше все было реализовано в синхронном варианте (если WriteFile, то ReadFile и все), и для того, чтобы программа не "замирала", чтение с порта было вынесено в поток, а задержки регулировались через таймаут чтения порта (те самые 3 секунды). Но в итоге возникла проблема - иногда получалось так, что устройство начинало отвечать на, к примеру, 2,5 секунде, а на 3 секунде срабатывал таймаут ReadFile и входной пакет "обрубался". При дальнейшей обработке пакет естественно принимался неверным, после чего программа снова пыталась сделать запрос к устройству, которое на данный момент все еще отправляло "ошметки" предыдущего пакета... В результате кавардак. Увеличение таймаута - не выход, слишком много времени будет уходить на запрос к неотвечающему порту, а это ИМХО не дело.

Для этого решил перейти на асинхронный режим. Программа передает байты на порт. Ждет окончания передачи (EV_TXCLEAR) 3 секунды. Если отправка данных завершена успешно, запускается ожидание входных данных (EV_RXCHAR), тоже на 3 секунды. Если это событие происходит, то начинается чтение с порта. Вроде красиво... К сожалению в инете в основном приведены примеры только потоков, которые постоянно читаю с порта, а мне это не нужно.

Запись и чтение вынесены в отдельный поток, для того чтобы все эти ожидания не тормозили систему. Процедура потока приведена ниже:

// Поток записи/чтения порта
procedure TPortThread.Execute;
const
 TIMEOUT = 3000;
var
 BuffStruct: ^TBuffStruct;
 EventMask: Cardinal;
 Count: Cardinal;
 Error: Boolean;
begin
 Error := False;
 EventMask := 0;

 if not WriteFile(FPortHandle, FOutBuff, FOutBuffCount, Count, @FOverlapped)
 then
   if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
     if GetLastError = ERROR_IO_PENDING then
       // Запись данных в процессе. Ожидание завершения
       if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0
       then
         Error := True;

 if Error and (EventMask <> EV_TXEMPTY) then begin
   PostMessage(FWindowHandle, FMessageWriteEnd, 0, 0);
   Exit;
 end else begin
   GetMem(BuffStruct, SizeOf(TBuffStruct));
   BuffStruct^.Buff := FOutBuff;
   BuffStruct^.Count := FOutBuffCount;
   PostMessage(FWindowHandle, FMessageWriteEnd, 1, Integer(BuffStruct));
 end;

 Error := False;
 EventMask := 0;
 // Ожидание начала приема байт
 if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
   if GetLastError = ERROR_IO_PENDING then begin
     if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0 then
       Error := True;
   end;

 if not Error or (EventMask = EV_RXCHAR) then begin
   GetMem(BuffStruct, SizeOf(TBuffStruct));
   with BuffStruct^ do begin
     ReadFile(FPortHandle, Buff, Length(Buff), Count, @Overlapped);
     PostMessage(FWindowHandle, FMessageReadEnd, 1, Integer(BuffStruct))
   end;
 end else begin
   PostMessage(WindowHandle, MessageReadEnd, 0, 0);
   Exit;
 end;

end;


Открытие порта и создание события hEvent:

PortHandle := CreateFile(PChar(FName), GENERIC_READ or GENERIC_WRITE, 0, nil,
   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);
...
FOverlapped.hEvent := CreateEvent(nil, True, False, nil);


В итоге по логу из PortMon.exe получаем, ЧТО программа все правильно пишет в порт, ожидает завершения записи, включает ожидание и... даже если ответные байты приходят, не запускает чтение

IRP_MJ_CREATE   Serial0 SUCCESS Options: Open  
IOCTL_SERIAL_GET_BAUD_RATE Serial0 SUCCESS  
4IOCTL_SERIAL_GET_LINE_CONTROL Serial0 SUCCESS  
5IOCTL_SERIAL_GET_CHARS  Serial0 SUCCESS  
6IOCTL_SERIAL_GET_HANDFLOW Serial0 SUCCESS  
IOCTL_SERIAL_GET_BAUD_RATE Serial0 SUCCESS  
IOCTL_SERIAL_GET_LINE_CONTROL Serial0 SUCCESS  
IOCTL_SERIAL_GET_CHARS  Serial0 SUCCESS  
IOCTL_SERIAL_GET_HANDFLOW Serial0 SUCCESS  
IOCTL_SERIAL_SET_BAUD_RATE Serial0 SUCCESS Rate: 1200
IOCTL_SERIAL_SET_RTS  Serial0 SUCCESS  
IOCTL_SERIAL_SET_DTR  Serial0 SUCCESS  
IOCTL_SERIAL_SET_LINE_CONTROL Serial0 SUCCESS StopBits: 1 Parity: NONE WordLength: 8
IOCTL_SERIAL_SET_CHAR  Serial0 SUCCESS EOF:0 ERR:0 BRK:0 EVT:0 XON:11 XOFF:13
IOCTL_SERIAL_SET_HANDFLOW Serial0 SUCCESS Shake:1 Replace:40 XonLimit:2048 XoffLimit:512
IOCTL_SERIAL_SET_TIMEOUTS Serial0 SUCCESS RI:100 RM:10 RC:3000 WM:10 WC:10
IOCTL_SERIAL_SET_WAIT_MASK Serial0 SUCCESS Mask: RXCHAR TXEMPTY  
IOCTL_SERIAL_PURGE  Serial0 SUCCESS Purge: TXCLEAR RXCLEAR
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED  
IRP_MJ_WRITE   Serial0 SUCCESS Length 3: 01 02 03  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS  
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED


Если в код вставить сброс события hEvent:

 // Ожидание начала приема байт
 if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
   if GetLastError = ERROR_IO_PENDING then begin
     ResetEvent(FOverlapped.hEvent);
     if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0 then
       Error := True;
   end;


Чтение запускаться будет, но при этом считывать будет всегда 0 байт.

Вообщем, я совсем запутался со всеми этими задержками, ожиданиями, сообщениями от том, что было сообщение о том, что было... Что я делаю неправильно. Вроде и SDK штудирую, не помогает. Что я делаю неправильно? Или может кто-нибудь делал асинхронную работу именно в режиме запрос-ответ, есть наметки?


 
tesseract ©   (2008-05-20 20:18) [1]


> Необходимо провести асинхронную запись / асинхронное чтение
> параллельного порта COM.


Com или Rs232 - синхронный  последовательный интерфейс. Параллельный LPT или IEEE-1284


> WaitCommEvent(FPortHandle, EventMask, @FOverlapped)


GetOverlappedResult !  Или толку в твоей "Асинхронности" ноль. Все операции с асинхронным чтением/записью делаються в отдельном потоке/потоках, иначе толку ноль.
Ну почитай же наконец документацию.

ЗЫ: Вообще конечно проблема с кадрами..........


 
nicestep   (2008-05-20 20:22) [2]

> Com или Rs232 - синхронный  последовательный интерфейс.
> Параллельный LPT или IEEE-1284

RS232

> GetOverlappedResult !  Или толку в твоей "Асинхронности"
> ноль. Все операции с асинхронным чтением/записью делаються
> в отдельном потоке/потоках, иначе толку ноль.

Так ведь оно и сделано в отдельном потоке вроде


 
tesseract ©   (2008-05-20 20:43) [3]


> Так ведь оно и сделано в отдельном потоке вроде


Рекомендую книгу Павла Агурова.   Не код, а каша. ERROR_IO_PENDING совершенно другая функция возвращает. WaitCommEvent тут вообще не к месту.


 
Сергей М. ©   (2008-05-20 20:45) [4]


> Так ведь оно и сделано в отдельном потоке вроде


так а нафига тогда асинхронный режим ?
У тебя что - отдельный поток еще чем-то важным занят, нежели транспортом ?


 
nicestep   (2008-05-20 20:58) [5]

WaitCommEvent
...
If the overlapped operation cannot be completed immediately, the function returns FALSE and the GetLastError function returns ERROR_IO_PENDING, indicating that the operation is executing in the background. When this happens, the system sets the hEvent member of the OVERLAPPED structure to the not-signaled state before WaitCommEvent returns, and then it sets it to the signaled state when one of the specified events or an error occurs. The calling process can use one of the wait functions to determine the event object"s state and then use the GetOverlappedResult function to determine the results of the WaitCommEvent operation. GetOverlappedResult reports the success or failure of the operation, and the variable pointed to by the lpEvtMask parameter is set to indicate the event that occurred.


Что я поидее и делаю... Функция вовращает Ложь, GetLastError = ERROR_IO_PENDING, т.е. операция выполняется в фоне. Запускаю одну из "wait functions"  - WaitForSingleObject - и ожидаю событие. Т.к. на регистрацию указано их 2:

SetCommMask(PortHandle, EV_RXCHAR or EV_TXEMPTY)

то это либо "конец записи на порт", либо "на порт приходят данные".
Да, там сказано, что надо еще использовать и GetOverlappedResult, я почитал SDK, но ничего не понял, для чего она вообще нужна. Примеров с ней тоже нет. Вроде бы эти должны уже справляться с поставленной целью. Может подскажешь, как же мне ее применить правильно тогда?


 
nicestep   (2008-05-20 21:04) [6]

> так а нафига тогда асинхронный режим ?
> У тебя что - отдельный поток еще чем-то важным занят, нежели
> транспортом ?

Да ну нафиг не нужен. Все раньше и без него работало прекрасно. Просто надо, чтобы пакеты входные по таймауту не обрубались, при этом не перебарщиваяя с самим значением таймаута. А выход я нашел только один - этот. Да и пора бы уже научиться, ибо с портами работаю уже больше года.


 
nicestep   (2008-05-20 22:55) [7]

Эх. Разобрался. Почему то здесь мне не указали на этот промах. Я просто не дожидался конца чтения с порта

> WaitCommEvent тут вообще не к месту.
К месту. Но только при ожидании входящих данных на порт. При записи он был не нужен

> GetOverlappedResult !
Тоже нужна, но только при чтении и только для того, чтобы определить количество присланных байт

Вообщем, вот переписаная рабочая процедура потока

procedure TPortThread.Execute;
const
 TIMEOUT = 3000;
var
 BuffStruct: ^TBuffStruct;
 EventMask: Cardinal;
 Count: Cardinal;
 Error: Boolean;
begin
 PurgeComm(PortHandle, PURGE_RXCLEAR or PURGE_TXCLEAR);
 Error := False;
 if not WriteFile(FPortHandle, FOutBuff, FOutBuffCount, Count, @FOverlapped)
 then
   if GetLastError = ERROR_IO_PENDING then
     // Запись данных в процессе. Ожидание завершения
     if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0
     then
       Error := True;
 if Error then begin
   PostMessage(FWindowHandle, FMessageWriteEnd, 0, 0);
   Exit;
 end else begin
   GetMem(BuffStruct, SizeOf(TBuffStruct));
   BuffStruct^.Buff := FOutBuff;
   BuffStruct^.Count := FOutBuffCount;
   PostMessage(FWindowHandle, FMessageWriteEnd, 1, Integer(BuffStruct));
 end;
 Error := False;
 EventMask := 0;
 SetCommMask(FPortHandle, EV_RXCHAR);
 // Ожидание начала приема байт
 if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
   if GetLastError = ERROR_IO_PENDING then begin
     if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0 then
       Error := True;
   end;
 if not Error or (EventMask = EV_RXCHAR) then begin
   // Прием байт начался. Чтение
   GetMem(BuffStruct, SizeOf(TBuffStruct));
   ReadFile(FPortHandle, BuffStruct^.Buff, Length(BuffStruct^.Buff), Count,
     @Overlapped);
   if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) = WAIT_OBJECT_0 then
     GetOverlappedResult(FPortHandle, FOverlapped, BuffStruct^.Count, False);
   PostMessage(FWindowHandle, FMessageReadEnd, 1, Integer(BuffStruct))
 end else begin
   PostMessage(WindowHandle, MessageReadEnd, 0, 0);
   Exit;
 end;
end;


Спасибо Олегу Титову за статью "Работа с коммуникационными портами (COM и LPT) в программах для Win32". И всем вам!



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2009.04.19;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.5 MB
Время: 0.042 c
2-1235724440
D@nger
2009-02-27 11:47
2009.04.19
Почему отсекается первый символ?


2-1236075271
markers
2009-03-03 13:14
2009.04.19
Клик по иконке чужого приложения в трее


3-1213711617
Георгий
2008-06-17 18:06
2009.04.19
dbgrid - выделить запись которая находится под курсором мыши


2-1236095021
mahab
2009-03-03 18:43
2009.04.19
Доступ к файлу


1-1210569343
User1
2008-05-12 09:15
2009.04.19
Не соображу, что за ошибка...





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский