Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.04.16;
Скачать: CL | DM;

Вниз

Асинхронная работа с 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 вся ветка

Текущий архив: 2006.04.16;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.031 c
6-1136206191
DeadMeat
2006-01-02 15:49
2006.04.16
Хаос при передаче по UDP (indy 10)


1-1140536806
SurgeonY
2006-02-21 18:46
2006.04.16
Linking VC++ static lib in Delphi (or BCB) project


2-1143885703
Dyakon_Frost
2006-04-01 14:01
2006.04.16
Проблемы с StringGrid


4-1137259089
Guest386x
2006-01-14 20:18
2006.04.16
Блокирование клавиатуры


15-1143572066
QuickFinder
2006-03-28 22:54
2006.04.16
Delphi5 for Windows x64