Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Асинхронная работа с Com портом. [D7, WinXP]


Страницы:   1 2

Kolan ©   (2006-01-27 20:31:00) 

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

Сделал тестовый проект по этой статье:
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


Kolan   (2006-01-30 12:53:00) [1]

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

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


Это делаю.

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

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

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

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

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

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


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


> Вопрос: Да "в эту переменную будет помещено число реально
> считанных", а где же они считываются ведь второй раз 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 11:25:00) [3]

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


Kolan   (2006-01-29 15:12:00) [4]

И наверное последний вопрос:
 Для чтения я написал акую функцию:
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-29 14:23:00) [5]

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

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

  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 не вызывается ?


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


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

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

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

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

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

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


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


> 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 ©

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


tesseract   (2006-01-28 23:27:00) [8]

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


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

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

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

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

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


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


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


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

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


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

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


ZlDoc   (2006-01-28 22:15:00) [11]

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 21:42:00) [12]


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


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


ZlDoc   (2006-01-28 21:35:00) [13]

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


tesseract   (2006-01-28 21:34:00) [14]


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


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

То >>>GannibalLector

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

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


Kolan   (2006-01-28 21:12:00) [15]

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


Kolan   (2006-01-28 19:27:00) [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 13:30:00) [17]

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

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


GanibalLector   (2006-01-28 13:09:00) [18]

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

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


Kolan   (2006-01-28 12:05:00) [19]

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

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


Kolan   (2006-01-28 11:32:00) [20]

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

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

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

Вот :)


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо