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

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

Асинхронный ввод/выод com-порта [D7, WinXP]


bobah ©   (2005-07-19 14:01:00) 

На сайте Королевство Делфи мне попался код. Вроде с ним разобрался, однако один блок не дает мне покоя (см. пояснение в тексте // начало непонятного блока; // конец непонятного блока)

//инициализируем TOverlapped структуру
 FillChar(Ovr,SizeOf(TOverlapped),0);
 Ovr.hEvent := CreateEvent(nil,TRUE,FALSE,#0);
//стартуем бесконечный цикл
 while not Parms.Terminated do with Parms do begin
   if Terminated then break;
   Mask := 0;
//запрашиваем ожилаемую маску ивэнтов
//-----------------------------------
// Начало непонятного блока
//------------------------------------
   if not WaitCommEvent(cId,Mask,@Ovr) then begin
     if GetLastError() = ERROR_IO_PENDING
      then GetOverlappedResult(cId,Ovr,Trans,False);
   end;
// -------------------------------
// Конец непонятного блока
// ------------------------------
    //очищаем ошибки
   ClearCommError(cId,Errs,@Stat);
   if Terminated then break;
   if Mask = 0  then continue;
   if (Mask and EV_RXCHAR) = EV_RXCHAR then begin
   //Если принят хотя бы один байт
    if Stat.cbInQue < 512
     then Kols := Stat.cbInQue
      else Kols := 512;
     if Terminated then break;
   //пытаемся считать принятые байты
     ReadFile(cId,Buff,Kols,Kols,@Ovr);
.........................................

А именно не понятно зачем здесь GetOverlappedResult? И вообще, если несложно, объясните зачем она нужна эта функция?


Digitman   (2005-07-20 12:23:00) [1]

1. Ивент нужно создать допервого обращения к нему.

2. Справка на эту тему отвечает так :

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

т.е. если ты используешь WaitCommEvent, то о сбросе/установке ивента заботиться не нужно

3. CancelIO нужен для немедленной отмены ранее стартованной асинхронной операции ввода/вывода, когда та вернула ERROR_IO_PENDING

4. Ф-ции GetOverlappedResult, WaitCommEvent, Read/Write в этом случае вернут отказ с кодом ошибки ERROR_TIMEOUT = 1460


bobah   (2005-07-20 11:08:00) [2]

У меня появились следующие вопросы (уж простите если они вам покажутся глупыми):
1. Правильно ли вызывать функцию CreateEvent( то есть создовать собыие) в функции чтения, с учетом того что она не блокируется, т.е. вызывается n-ое количество раз в потоке, не накладно ли это для системы?
2. В каких случаях следует вызывать ResetEvent и SetEvent, например у меня функции чтения и записи в одном отдельном потоке, они не конкурируют... есть надобность в вызове этих функций для моего случая?
3. Какой смысл в вызове функции отмены операции чтения (CancelIO), когда ее следует использовать?
4. Как понять, что сработал таймаут, определенный в структуре COMMTIMEOUTS (можно на примере, предложенным tesseract, за который ему спасибо)?


tesseract   (2005-07-20 10:02:00) [3]

to ZlDoc >> Ну а у нас что
  WaitCommEvent(hCom,tMask,@READOL);
  // смотрим что у нас
   fSignaled:= WaitForSingleObject(ReadOl.hEvent,WaitInt);


ZlDoc   (2005-07-19 17:28:00) [4]

WaitComEvent Возвращается сразу. Для ожидания завершения операции нужно  WaitForSingleObject. Освобождается event ,вроде, CloseHandle(надо проверить).


tesseract   (2005-07-19 16:18:00) [5]

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


function trThread.InitPort:boolean;
var
   CommTimeOuts:TCOMMTIMEOUTS;
begin
// Не верим, что получится
 Result:=false;
// реагируем на символ
 fMask:=EV_RXCHAR and  not EV_RING; // в NT/2k/XP reqired!!!!!
 fPortname:=PChar('COM'+inttostr(fScaleSet.Port));
// создаём дескриптор
//crit.Acquire;
 try
  hCom:=CreateFile(fPortName,GENERIC_READ OR GENERIC_WRITE,0,NIL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED{FILE_ATTRIBUTE_NORMAL},0);
// Если неверен выходим
    if GetLastError=ERROR_ALREADY_EXISTS then
     begin
      CloseHandle(hCOM);
      status:=GetLastError;
      AddLog('Порт занят'+ fPortName)
     end;
     FillChar(ReadOl,SizeOF(ReadOL),0);

    ReadOl.hevent:=CreateEvent(nil,true,true,nil);
    Status:=GetLastError;

   if (hCom=0) or (hCom=INVALID_HANDLE_VALUE) then
    begin
     Status:=GetLastError;
     AddLog('Ошибка порта '+ fportName);
     // crit.Release;
     exit;
    end
    else
        AddLog('Открыт порт '+ fportName);
// Устанавливаем буфер
   if not(SetupComm(hCom,22,10)) then
    begin
     status:=GetLastError;
    AddLog('SetupComm Error '+ fPortName);
     exit;
    end;
// Чистим буфер
   if not(PurgeComm(hCom,PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR )) then exit;
// Устанавливаем задержки/время ожидания
     CommTimeOuts.ReadIntervalTimeout:= 50;
     CommTimeOuts.ReadTotalTimeoutMultiplier := 10;
     CommTimeOuts.ReadTotalTimeoutConstant := 00;
     CommTimeOuts.WriteTotalTimeoutMultiplier := 10;
     CommTimeOuts.WriteTotalTimeoutConstant := 00;
// DTR и RTS нам на фиг не нужен
    EscapeCommFunction(hCom,SETDTR);
    EscapeCommFunction(hCom,CLRRTS);
  //Если не получилось выходим
    SetCommTimeouts(hCom,CommTimeOuts);
 // Устанавливаем параметры порта через DCB
  if not(GetCommState(hCom,mDCB)) then
   begin
    status:=GetLastError;
    AddLog('GetCommState Error '+ fPortName);
    exit;
   end;

   MDCB.BaudRate:=fScalePar.hDCB.BaudRate;
   MDCB.ByteSize:=fScalePAr.hDCB.ByteSize;
   MDCB.Parity:=FscalePar.hDCB.Parity;
   MDCB.StopBits:=FScalePar.hDCB.StopBits;

//Заполняем нужные параметры
   // Устанавливаем параметры порта и перхвата
   if not(SetCommState(hCom,mDCB)) then
    begin
     Status:=GetLastError;
     Addlog('SetCommState Error '+ fPortName);
     exit;
    end;
// устанавливаем маску перехвата
    if not(SetCommMask(hCom,fMask)) then
     begin
       Status:=GetLastError;
       Addlog('SetComMask Error' +fportName);
       exit;
     end;
// Если дошли до сюда то всё ок
    Result:=true;
 finally
 end;
end;

// Чтение

function trThread.AsyncRead:boolean;
var tMask:DWord;
 begin
result:=false;
if terminated  then  exit;
// убираем событие - идёт запись

fWait.ReSetEvent;
{if State<>'Чтение' then
begin
  State:='Чтение';
  ChangeState;
 end;
 }
if hCom=0 then exit;
// Если надо синхронизироваться, то делаем это

// Ждём сообщения о чтении
WaitCommEvent(hCom,tMask,@READOL);

    // смотрим что у нас
    fSignaled:= WaitForSingleObject(ReadOl.hEvent,WaitInt);
    // Если событие произошло то
     if (fSignaled = WAIT_OBJECT_0) then
      begin
         // Читаем
        if GetOverlappedResult(hCom, REadOL, BytesTrans,false) then
         begin
           // Сравниваем произошедшее событие с прибытием символа
           if (tMask and EV_RXCHAR)<>0 then
            begin
             AvBytes:=GetAvBytes; // Сколько байтов пришло
             if AvBytes>=fScalePar.Size then
              begin
               //try
                // Если пришло нужное кол-во то
                 //buf:='';
                 if ReadFile(hCom,buf^,AvBytes,ReadBytes,@ReadOL) then
                 begin
                   // Чистим буфер
                   PurgeComm(hCom,PURGE_RXCLEAR);
                   result:=true;
                   // Если прочитали нужное кол-во
                   if (ReadBytes>=fScalePAr.Size) then
                   begin
                     if not terminated
                      then
                      begin
                      fWeight:=ScaleRead;
//                       ChangeWeight;
                      State:='Данные разобраны';
                     end;
                    end;
                 end// if REadFile
                 else
                  begin
                   // Ошибка
                   fWait.setEvent;
                   Status:=GetLastError;
                   Addlog('Чтение невозможно');
                  end; // if REadFile

               //  end;
                 end; // if AvBytes
            end; // if Fmask
         end;// if GetOVerlappedResult
      end; // if Signaled
  // end;
    end; // waitcommevent
CancelIO(hCom); // это отмена операций чтения
fWait.SetEvent;
end;

function trThread.ClosePort:boolean;
begin
// Закрываем отложенное чтение
if REadOL.hEvent<> 0 then
 CloseHandle(REadOL.hEvent);
// Сбро настроек порта
if hCom<>0 then
 begin
  try
  SetCommMask(hCom,0);
  CloseHandle(hCom);
  Status:=GetLastError;
  except
     AddLog(' Ошибка порта COM' + inttostr(fScaleSet.port));
  end;
 end;
  AddLog('Порт  COM' + inttostr(fScaleSet.port)+ ' закрыт');
 result:=true;
end;

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


bobah   (2005-07-19 14:32:00) [6]

Насчет код несовсем грамотный нельзя ли поподробнее?
Хотел спросить еще, насколько я знаяю при вызове WaitCommEvent, ReadFile, WriteFile в асинхронном режиме они запускаются, но прграмма не зависает, так вот как можно этот запущенный waitcommevent остановить, т.е. освободить hevent(случайно не с помощью ли GetOverlappedResult или WaitForSingleObject)?


tesseract   (2005-07-19 14:13:00) [7]

Данный блок ждёт события Com-порта по маске. GetOverlappedResult  возвращает результат операции отложенного чтения. ERROR_IO_PENDING операция незавершена.

ЗЫ Код кстати не совсем грамотный.


Наверх

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