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

Вниз

Асинхронная работа с Com портом.   Найти похожие ветки 

 
Kolan ©   (2006-01-27 20:31) [0]

Здравствуйте,
 Помогите понять эту тему.

Сделал тестовый проект по этой статье:
http://articles.org.ru/cfaq/index.php?qid=1185

Сделали мне заглушку в Com порт. Те что послал то и принял.
Она 100% работает, проверил на своих проектах...

Чтение выполняется в отдельном потоке. Вот его код полнистью:


unit KAsyncComPortThread;

interface

uses
 Windows, Classes, KAsyncComPortRoutines, Variants;

type
 TReadThread = class(TThread)
 private
   { Private declarations }
   FBuffer: array[0..$FF] of Byte;
   FPortHandle: THandle;
   FOverRead: TOverlapped;
   FReadCount: DWORD;
   FReadEvent: TReadEvent;
   procedure DoRead;
 protected
   procedure Execute; override;
 public
   constructor Create(PortHandle: THandle; ReadEvent: TReadEvent);
   destructor Destroy; override;  
 end;

implementation

{ Important: Methods and properties of objects in visual components can only be
 used in a method called using Synchronize, for example,

     Synchronize(UpdateCaption);

 and UpdateCaption could look like,

   procedure TReadThread.UpdateCaption;
   begin
     Form1.Caption := "Updated in a thread";
   end; }

{ TReadThread }

constructor TReadThread.Create(PortHandle: THandle; ReadEvent: TReadEvent);
var
 {//}Err: Boolean;
begin
 FReadEvent := nil;
 FPortHandle := PortHandle;
 FReadEvent := ReadEvent;
 ZeroMemory(@FOverRead, SizeOf(@FOverRead));
 FOverRead.hEvent := CreateEvent(nil, True, False, nil);
 if FOverRead.hEvent = Null then
   {//}Err := True;
 inherited Create(False);
end;

destructor TReadThread.Destroy;
begin
 CloseHandle(FOverRead.hEvent);
 inherited;
end;

procedure TReadThread.DoRead;
var
 Answer: TByteArray;
 I: Integer;
begin
 if Assigned(FReadEvent) then
 begin
   SetLength(Answer, FReadCount);
   for I := Low(Answer) to High(Answer) do
     Answer[I] := FBuffer[I];
     FReadEvent(Self, Answer);
     SetLength(Answer, 0);
 end;
end;

procedure TReadThread.Execute;
var
 ComStat: TComStat;
 dwMask, dwError: DWORD;
 {//}Err: Boolean;
 I: LongBool;
begin
 { Place thread code here }
 FreeOnTerminate := True;
 while not Terminated do
 begin
   //I := WaitCommEvent(FPortHandle, dwMask, @FOverRead);
   if not WaitCommEvent(FPortHandle, dwMask, @FOverRead) then
   begin
     if GetLastError = ERROR_IO_PENDING then
       WaitForSingleObject(FOverRead.hEvent, INFINITE)
     else
       {//}Err := True;
   end;
   if not Terminated then
     if not ClearCommError(FPortHandle, dwError, @ComStat) then
       {//}Err := True;

   FReadCount := ComStat.cbInQue;
   if FreadCount > 0 then
   begin
     if not ReadFile(FPortHandle, FBuffer, FReadCount, FReadCount, @FOverRead) then
       {//}Err := True;

     Synchronize(DoRead);
   end;
 end;

end;

end.


Вроде я разобрался в том как это все должно работать.

Проблема заключается в том, что какое бы кол-во байт я не пошлю приходит всегда 4(а должно быть как-бы эхо).
Причем 2 последних всегда одинаковые.

Ума не приложу с чем это может быть связано...

Проверил действительно в этой строке:
FReadCount := ComStat.cbInQue;
cbInQue = 4


 
tesseract ©   (2006-01-27 22:27) [1]

Винда использует буфер.
подожди побольше - придёт побольше байтов.


 
Kolan ©   (2006-01-27 23:05) [2]

Непойму как это сделать?
Просто засыпть? - это решение мне ненравится...


 
tesseract ©   (2006-01-27 23:08) [3]


>Просто засыпть? - это решение мне ненравится...

Зря. Если используешь поток - нормально, на загрузке проца не скажется.
ClearCommError можно вызывать десяток раз, через некоторые промежутки времени Ж-)


 
Kolan ©   (2006-01-27 23:24) [4]

Шас попробую с задержкой. Ненравится мне этот вариант тк у меня будет RS-485.
Не уверен что получится

Если вас незатруднит обясните мне глубокий смысл ф-ции ClearCommError.

Из справки я понял что: Функция извлекает инф. о ошибках связи и сообщ текущее состояние устр-ва связи (это Com порт как я понял). Функция вызывается при возникновении ошибки, и отчишает флаг ошибки устр-ва чтобы включить дополнительные операции ввода/вывода.

Вот это я не очень понял...


 
tesseract ©   (2006-01-27 23:38) [5]

Не парься. Всё ок. Правда с RS-484 я работал только с конвертерами. Для особо одарённых случаев приходилось отключать  WaitCommMask и работать именно таким способом.


 
Kolan ©   (2006-01-27 23:46) [6]

 
  Sleep(100);
if not ClearCommError(FPortHandle, dwError, @ComStat) then
      {//}Err := True;

  FReadCount := ComStat.cbInQue;
  if FreadCount > 0 then


Так? - Непомогло


 
tesseract ©   (2006-01-27 23:54) [7]

А так?

for count:=1 to 5 do
begin
  ClearCommError(hCom, ErrCode, @hCurState);
  if hCurState.cbInQue>=Size then
   begin
   result:=true;
   exit;
   end;
   sleep(50);
end;


 
Kolan ©   (2006-01-28 00:17) [8]

Чуть чуть не понял.
1. Что такое Size, а точнее где его взять. Желательно чтобы приемник незнал сколько надо читать. Сколько пришло- столько пришло...

2. Блин что делает эта ClearCommError никак непойму?

3. Дело в том что скорее всего мне придется кождую секунду принимать массив в 512 остчетов(что-то вроде графика). Каждый остчет- 2 числа, каждое число наверно по 4 байта..... Вообшем довольно много...


 
tesseract ©   (2006-01-28 10:58) [9]

Сказал бы сразу что надо. Выслать мой класс для прямого общения с драйвером порта (Win2k+) ?


 
Kolan ©   (2006-01-28 11:11) [10]

Вышли конечно. А в Win98 непойдет?

А что насчет моего проекта...

PS
 Когдаже осушествится моя мечта и я напишу класс для работы с портом, котроый будет все правильно читать, не подвисать при чтении, самое главное работать с таймаутом, вообшем такой чтобы я забыл о этой проблемме..... уже год парюсь все неразберусь.


 
tesseract ©   (2006-01-28 11:25) [11]

Kolan не парься. К этому классу я шёл года 3.
В конечном итоге и поддержка масок и ThreadSafe и дружбу с классом для Tcp/IP соединений  реализовать таки удалось :-)

Вышли конечно. А в Win98 непойдет?

Пойдёт но только через  winapi. И номер порта не может быть больше 9.


 
Kolan ©   (2006-01-28 11:32) [12]

Дык давай я уже жду.. Давай :)
PS
Почта моя в анкете..

У меня есть класс, работающий в синхронном режиме. Единственное что меня неустраивало - это невозможность принять решение что связи с прибором нет. Те там нет возможности задать таймаут иесли он истек, и ничего нет, то значит сбой связи....

Думал реализую это через
 WaitForSingleObject(FOverRead.hEvent, INFINITE)

Вот :)


 
Kolan ©   (2006-01-28 12:05) [13]

Дай небольшой пример:
Как послоть открыть итд я разберусь...

Нужно:
 Прочитать допустим 5 байт. Причем если в течении 50 мс ничего в порт не пришло то сообщение(или что угодно) о ощибке прочтения...


 
GanibalLector ©   (2006-01-28 13:09) [14]

2 tesseract
Мне тот класс тоже интересен(в силу того,что он дружит с TCP/IP). Мыло : Talla2k[DOG]ukr.net

Заранее спасибо!


 
Kolan ©   (2006-01-28 13:30) [15]

Хотелось бы с Digitman"ом поговорить. Жаль его нет :(

GanibalLector ©   (28.01.06 13:09) [14]
Послал


 
Kolan ©   (2006-01-28 19:27) [16]

Ничего не понимаю...
Изменил код следующим образом


procedure TReadThread.Execute;
var
 ComStat: TComStat;
 dwMask, dwError: DWORD;
 {//}Err: Boolean;
 I: LongBool;
 J: Integer;
 BytesToReadCount: Cardinal;
 ReadBytes: Cardinal;
 Buffer: array of Byte;
begin
 { Place thread code here }
 FreeOnTerminate := True;
 while not Terminated do
 begin
   ReadBytes := 0;
   BytesToReadCount := 0;
   
   if not WaitCommEvent(FPortHandle, dwMask, @FOverRead) then
   begin
     if GetLastError = ERROR_IO_PENDING then
       if WaitForSingleObject(FOverRead.hEvent, INFINITE) = WAIT_OBJECT_0 then
(1)          GetOverlappedResult(FPortHandle, FOverRead, BytesToReadCount, True)
     else
       {//}Err := True;
   end;
   if not Terminated then

   if BytesToReadCount > 0 then
   begin
     
(2)      SetLength(Buffer,0);
   
(3)      if not ReadFile(FPortHandle, Buffer, BytesToReadCount, ReadBytes, @FOverRead) then
       {//}Err := True;

 
     if ReadBytes > 0 then
     begin
(4)        SetLength(FBuffer, Length(Buffer));
       for J := Low(FBuffer) to High(FBuffer) do
         FBuffer[J] := Buffer[J];
       Synchronize(DoRead);
       ClearCommError(FPortHandle, dwError, @ComStat);
     end;
   end;
 end;

end;


Ставлю точку на (1). Посылаю данные...
Когда данные приходят она срабатывает. Иду по F8. После вызова GetOverlappedResult
BytesToReadCount равно 4(всегда).
В точке (2) длинна Buffer становится равным 0.

В точке (3) длиннаBuffer становится равен длинне посылки(напоминаю, я получаю эхо).
Причем она <> 4 как сказала ф-ция GetOverlappedResult.
В точке (4) длинна FBuffer становится равна длинне  Buffer.

А в Buffer добавляется еще куча мусора(очень много)... Я в шоке почему?

Идем далее заходим опять в (1). Причем ничего не посылая...
Опять GetOverlappedResult говорит что BytesToReadCount = 4.
Идем до точки (3). После вызова ReadFile ReadBytes = 0 и сответственно длинна Buffer = 0.

После этого что не посылай всегда псле ReadFile ничего не читается...

Объясните что происходит и как исправить.... Я второй день мучаюсь...


 
Kolan ©   (2006-01-28 21:12) [17]

Вот еще что если в выше описанном примере
Buffer: array of Byte;
заменить на
Buffer: array[0..20] of Byte;
то считаются как раз те самые 4 байта(я думаю GetOverlappedResult именно их считает) , но они совсем не те что должны быть... И откуда они берутся я непойму...


 
tesseract ©   (2006-01-28 21:34) [18]


>А в Buffer добавляется еще куча мусора(очень много)... Я в шоке почему?


Buffer должен быть статическим массивом!!!!!!!!!

То >>>GannibalLector

Это тот же самый, что я тебе высылал. доработан тольео режим ожидания.

По поводу работы : Есть новый протокол Масса-К МК-А22 (наконец-то рабочий), Раскурочил TCP/IP neteye и Cas ethernet. Если интресно - пиши.


 
ZlDoc ©   (2006-01-28 21:35) [19]

ИМХО, GetOverlappedResult "считает" байты которые были записаны/прочитаны предидущими вызовами ReadFile и WriteFile. В данном примере ничего вразумительного она возвращать не будет (и не понятно почему после вызова ReadFile нет ожидания ее завершения, того же GetOverlappedResult).
Советую почитать статейку
http://bcbsql.narod.ru/pub/rabcomm.htm


 
tesseract ©   (2006-01-28 21:42) [20]


>ИМХО, GetOverlappedResult "считает" байты которые были записаны/прочитаны >предидущими вызовами ReadFile и WriteFile. В данном примере ничего >вразумительного она возвращать не будет (и не понятно почему после вызова >ReadFile нет ожидания ее завершения, того же GetOverlappedResult).
>Советую почитать статейку
>http://bcbsql.narod.ru/pub/rabcomm.htm


Ты явно не читал книг Агурова. В интернете  нет нормальных статей по работе с RS232/RS485 - слишком  прибыльное это дело,чтобы профессиональные секретв раскрывать.


 
ZlDoc ©   (2006-01-28 22:15) [21]

tesseract, ты прав Агурова я не читал. Но по WinApi инфы в сети много, и не скрывает ее ни кито.

Kolan, Прочитай статью по внимательней.

if not ClearCommError(FComPort.FPort, dwError, @ComStat) then
...
FRead := ComStat.cbInQue;

...
ReadFile(FComPort.FPort, FBuf, FRead, FRead, @FOverRead)


И всетаки надо добавить вызов GetOverlappedResult, после ReadFile. Чтобы дождаться окончания чтания данных.

Buffer не обязательно статический, но размер должен соотвецтвовать читаемой информации (SetLength(Buffer,BytesToReadCount) ).


 
tesseract ©   (2006-01-28 22:21) [22]


>Buffer не обязательно статический, но размер должен соотвецтвовать читаемой >информации (SetLength(Buffer,BytesToReadCount) ).


Только честно ПРОВЕРЯЛ ????????
Если проверял, то получишь мусор.
Никакой SetLength не поможет.

>tesseract, ты прав Агурова я не читал. Но по WinApi инфы в сети много, и не >скрывает ее ни кито


Её и MSDN не скрывает. И в delphi она есть. Только мы с GannibalLector как есть так и будем :-)

Имей в виду - есть дифференс в работе разных устройств, под разные версии Windows.


 
Kolan ©   (2006-01-28 23:15) [23]

Во первых большое человеческое спасибо ZlDoc за статью. Вопросы типа что это за функция, что она делает итд снимаются... :)

Далее
tesseract ©   (28.01.06 22:21) [22]
Завтра статьей возпользуюсь. Потом отвечу. Мож и вопросы уйдут...

PS
Спать пора. C 10:00 сижу...

Всех благодарю за помошь... :)


 
tesseract ©   (2006-01-28 23:27) [24]

Модуль дошёл ????


 
Набережных С. ©   (2006-01-29 07:56) [25]


> Kolan ©   (28.01.06 19:27) [16]


>    if BytesToReadCount > 0 then
>    begin
>      
> (2)      SetLength(Buffer,0);

Как ты думаешь, какой размер у буфера будет после такого кода?
SetLength(Buffer, BytesToReadCount);

> (3)      if not ReadFile(FPortHandle, Buffer, BytesToReadCount,
>  ReadBytes, @FOverRead) then

У тебя
Buffer: array of Byte;
Поэтому здесь ты передаешь в функцию указателя на свой буфер, т.е. адрес переменной Buffer. И функция пишет в саму эту переменную, а не в буфер, на который она указывает. В результате после вызова Buffer указывает в никуда, и то, что ты не получил AV - чистая случайность.

if not ReadFile(FPortHandle, Buffer[0], BytesToReadCount,
>  ReadBytes, @FOverRead) then

> tesseract ©

Абсолютно никакого значения не имеет, статический буфер или динамический, нужно просто правильно передавать параметры.


 
Набережных С. ©   (2006-01-29 08:19) [26]


> ZlDoc ©   (28.01.06 22:15) [21]

Вероятность того, что ReadFile в данной ситуации будет выполняться асинхронно, достаточно низка. Есть даже мнение, что она вообще равна нулю:) Практически я такого не встречал, хотя я и не так уж много работал с СОМ-портом. Однако я тоже считаю, что проверка на PENDING лишней не будет и всегда ее добавляю.

> Набережных С. ©   (29.01.06 07:56) [25]

Неудачно сформулировал:(

> Поэтому здесь ты передаешь в функцию указателя на свой буфер

Имелось в виду, что передается указатель на переменную Buffer, а надо передавать адрес первой ячейки памяти, на которую указывает переменная Buffer, потому как при объявлении "Buffer: array of Byte" переменная Buffer содержит именно указатель на первый элемент массива, если массив не нулевой длины.


 
Kolan ©   (2006-01-29 14:23) [27]

Благодарю еще раз за статью.
Все переписал. Работает. Не понял одну вешь...

Это часть статьи

  memset(&ovr,0,sizeof(ovr));
  ovr.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
  ReadFile(port,buf,buf_size,&bc,&ovr);
 
  /* Выполняем некую полезную работу */
 
  if(WaitForSingleObject(ovr.hEvent,10000)==WAIT_OBJECT_0) {
     GetOverlappedResult(port,&ovr,&bc,FALSE);
  } else {
 
     /* Обработка ошибки */
 
  }
 
  CloseHandle(port);
  CloseHandle(ovr.hEvent);

В этом примере переменная bc, предназначенная для получения количества считанных байт, после вызова ReadFile будет равна 0, так как никакой передачи информации еще не было. После вызова GetOverlappedResult в эту переменную будет помещено число реально считанных байт.


Вопрос: Да "в эту переменную будет помещено число реально считанных", а где же они считываются ведь второй раз ReadFile не вызывается ?


 
Kolan ©   (2006-01-29 15:12) [28]

И наверное последний вопрос:
 Для чтения я написал акую функцию:
function TAnisochronousComm.Read(var Buffer: array of Byte;
 NumberOfBytesToRead: Cardinal; TimeOut: Cardinal;
 var IsTimeOutExpire: Boolean): Cardinal;
begin
 Result := 0;
 IsTimeOutExpire := False;
 if FPortHandle <> INVALID_HANDLE_VALUE then
 begin
   ReadFile(FPortHandle, Buffer, NumberOfBytesToRead, Cardinal(Result),
     @FOverlappedRead);
   if WaitForSingleObject(FOverlappedRead.hEvent, TimeOut) = WAIT_OBJECT_0 then
     GetOverlappedResult(FPortHandle, FOverlappedRead, Cardinal(Result), False)
   else
     IsTimeOutExpire := True;
 end;
end;


Она прекрасно работает, но надо знать точное кол-во считываемых байт.
Как написоть процедуру, считывающюю все что есть в порте?

Пробывал узнавать длинну очереди и потом читать:

function TAnisochronousComm.ReadAll(var Buffer: array of Byte;
 TimeOut: Cardinal; var IsTimeOutExpire: Boolean): Cardinal;
var
 Errors: Cardinal;
begin
 if FPortHandle <> INVALID_HANDLE_VALUE then
 begin
   ClearCommError(FPortHandle, Errors, @FCommStat);

   Result := Read(Buffer, FCommStat.cbInQue, TimeOut, IsTimeOutExpire);
 end;
end;


Но по непомятны мне причинам в очереди FCommStat.cbInQue всегда одно и тоже число....


 
Kolan ©   (2006-01-30 11:25) [29]

Вопросы остлись...


 
tesseract ©   (2006-01-30 12:46) [30]


> Вопрос: Да "в эту переменную будет помещено число реально
> считанных", а где же они считываются ведь второй раз ReadFile
> не вызывается ?


Функция в случае успеха поместит данные в буфер указанный при первом вызове ReadFile.

> Но по непомятны мне причинам в очереди FCommStat.cbInQue
> всегда одно и тоже число....

Непонятно - у меня работает.

function TcomPort.pApiGetInCount: dword;
var  hCurState:TCOMSTAT;
    ErrCode:cardinal;
begin
  ClearCommError(hCom, ErrCode, @hCurState);
  result:=hCurState.cbInQue;
end;


Чтение идёт так :

    succ :=ReadFile(hCom,buffer,size,result,@REadOL);
      if not succ then
            begin
             tmp:=GetLastError;
              if tmp=ERROR_IO_PENDING then
              if WaitForSingleObject(ReadOl.hEvent,WaitInt)=WAIT_OBJECT_0 then
               GetOverlappedResult(hCom, ReadOL, result,true);
              end; // if not succ

После инициализации порта делай

 PurgeComm(hcom,PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR);

И поставь размер буферов в SetupCom.


 
Kolan ©   (2006-01-30 12:53) [31]

После инициализации порта делай

PurgeComm(hcom,PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR);


Это делаю.

И поставь размер буферов в SetupCom
Скорее всего дело именно в этом.

Вечером поробую. Шас нет перемычки на порт...

Функция в случае успеха поместит данные в буфер указанный при первом вызове ReadFile.

Ага разобрался... :)

Всех благодарю за помошь.... :)

Мечта из [10] осушествилась :)



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

Форум: "WinAPI";
Текущий архив: 2006.04.16;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.56 MB
Время: 0.035 c
15-1143572066
QuickFinder
2006-03-28 22:54
2006.04.16
Delphi5 for Windows x64


15-1143198619
Loginov Dmitry
2006-03-24 14:10
2006.04.16
Классы...


15-1143053426
Зм1й
2006-03-22 21:50
2006.04.16
Xp Home


2-1144097727
__alex
2006-04-04 00:55
2006.04.16
Чудеса с глобальными (public) переменными - 2


2-1144020067
Leshas
2006-04-03 03:21
2006.04.16
Выделенная ячейка в StringGrid





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский