Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Асинхронное чтение с СОМ-порта  (Прочитано 26296 раз)
0 Пользователей и 5 Гостей смотрят эту тему.
Tuborg
Команда клуба

ee
Offline Offline

« : 21-02-2008 09:23 » 

создаю хэндл порта:

Код:
CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

читаю порт (пока один раз на запуске)

Код:
void CSerialPort::ReadEx(void* lpBuf, DWORD dwCount)
{
  ASSERT(IsOpen());

  OVERLAPPED* pOverlapped = new OVERLAPPED;
  ZeroMemory(pOverlapped, sizeof(OVERLAPPED));
  pOverlapped->hEvent = (HANDLE) this;
  if (!ReadFileEx(m_hComm, lpBuf, dwCount, pOverlapped, _OnCompletion))
  {
    delete pOverlapped;
    TRACE(_T("Failed in call to ReadFileEx\n"));
    AfxThrowSerialException();
  }
}

Хочу чтобы как только приходил какой-нибудь байтик в порт, тут же запускалась CALL-BACK  функция _OnCompletion:


Код:
void CSerialPort::_OnCompletion(DWORD dwErrorCode, DWORD dwCount, LPOVERLAPPED lpOverlapped)
{
  //Validate our parameters
  ASSERT(lpOverlapped);

  //Convert back to the C++ world
  CSerialPort* pSerialPort = (CSerialPort*) lpOverlapped->hEvent;
  ASSERT(pSerialPort->IsKindOf(RUNTIME_CLASS(CSerialPort)));

  //Call the C++ function
  pSerialPort->OnCompletion(dwErrorCode, dwCount, lpOverlapped);
}

ставлю внутрь _OnCompletion breakpoint но управление к этой функции так и не передается, что я не пытаюсь запихнуть в порт.
В Синхронном режиме всё работает нормально.

Где я не прав?
Записан
v2
Помогающий

ua
Offline Offline

« Ответ #1 : 21-02-2008 14:04 » 

//?? Тут вроде должен быть Event
Код:
 pOverlapped->hEvent = (HANDLE) this;

Из MSDN
Код:
VOID [b]CALLBACK[/b] FileIOCompletionRoutine(
  DWORD dwErrorCode,
  DWORD dwNumberOfBytesTransfered,
  LPOVERLAPPED lpOverlapped
);


PS.
IOCompletionRoutine не лучший вариант.
Записан
Tuborg
Команда клуба

ee
Offline Offline

« Ответ #2 : 21-02-2008 14:26 » 

почему
IOCompletionRoutine не лучший вариант? Что вместо этого можно ещё использовать?
Записан
v2
Помогающий

ua
Offline Offline

« Ответ #3 : 21-02-2008 14:47 » new

Например WaitForMultipleObjects в отдельном потоке, куда и указатель передать можно.

/**/
startTh pVoid

readEv,commEv,sendEv,endEv

WaitCommEvent
ReadFile

while 1
 WaitForMultipleObjects
  sendEv -> OnSendEn;
  readEv -> OnReadEn, ReadFile;
  commEv -> OnCommEv, WaitCommEvent;
  endEv  -> break;

CloseHandles
endTh
/**/



 
Записан
Tuborg
Команда клуба

ee
Offline Offline

« Ответ #4 : 21-02-2008 14:50 » 

Понятно... а ещё такой вопрос.... как передать данные во время чтения по ком-порту в ДОСе я понимаю... а в винде в режиме асинхронного чтения такое можно? или я зря парюсь?
Записан
v2
Помогающий

ua
Offline Offline

« Ответ #5 : 21-02-2008 14:55 » 

Да, с этим проблем нет.
Записан
Tuborg
Команда клуба

ee
Offline Offline

« Ответ #6 : 27-02-2008 16:20 » 

приватные переменные класса:
Код:
DWORD dwEvtMask;
OVERLAPPED m_overlapped;
 HANDLE m_hComm;

при открытии порта делаю следующее:
Код:
    bool Open(...){

        m_hComm = CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED);

m_overlapped.hEvent = CreateEvent(
NULL,   // default security attributes
FALSE,  // auto reset event
FALSE,  // not signaled
NULL    // no name
  );
   

// Intialize the rest of the OVERLAPPED structure to zero.
m_overlapped.Internal = 0;
m_overlapped.InternalHigh = 0;
m_overlapped.Offset = 0;
m_overlapped.OffsetHigh = 0;
....
}

в параллельном потоке на каждый из ком-портов (4 их у меня) делаю следующее:

Код:
if (WaitCommEvent(m_hComm, &dwEvtMask, &m_overlapped)) 
    {
printf("I/O ...\n");
        if (dwEvtMask & EV_RXCHAR)
        {
             // To do.

        }

        if (dwEvtMask & EV_TXEMPTY)
        {
            // To do.
        }
    }
    else
    {
        DWORD dwRet = GetLastError();
        if( ERROR_IO_PENDING == dwRet)
        {
            printf("I/O is pending...\n");

            // To do.
        }
        else
            printf("Wait failed with error %d.\n", GetLastError());
    }

при посылки байтиков в ком-порт строка printf("I/O ...\n"); не работает, ставил туда даже breakpoint. Всё время переходит по else.

Почему не работает? О чём я забыл?
Записан
Scorp__)
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 27-02-2008 17:31 » 

По-моему, SetCommMask неплохо бы еще сделать Улыбаюсь При инициализации.
« Последнее редактирование: 27-02-2008 17:34 от Scorp__) » Записан

- А Вы сами-то верите в привидения?
- Конечно, нет, - ответил лектор и медленно растаял в воздухе.
Tuborg
Команда клуба

ee
Offline Offline

« Ответ #8 : 27-02-2008 17:43 » 

Я тут подумал головой... и сделал так:
Сначала взвёл событие
WaitCommEvent(m_hComm, &dwEvtMask, &m_overlapped)

где dwEvtMask=EV_RXCHAR;

а потом поставил

WaitForSingleObject(m_hComm, INFINITE);

и оно сработало =)

как-то странно... вот если читать мсдн, то в примере для WaitCommEvent (For an example, see Monitoring Communications Events.)

всё написано так, как я писал в предыдущем посте... никакого WaitForSingleObject там нет... в чём подвох? или это всё капиталистические происки? Хотя если подумать головой то ожидание событие просто необходимо.... кто может пояснить пример из мсдн?
Записан
v2
Помогающий

ua
Offline Offline

« Ответ #9 : 27-02-2008 20:44 » 

А в M$ только ошибки ждут Улыбаюсь
Код:
    
...
else
    {
        DWORD dwRet = GetLastError();
        if( ERROR_IO_PENDING == dwRet)
        {
            printf("I/O is pending...\n");

            // To do.
        }
...

Если у тебя много портов, смотри в сторону CreateIoCompletionPort и задай timeouts чтобы не дергаться за каждым байтом.
Записан
v2
Помогающий

ua
Offline Offline

« Ответ #10 : 27-02-2008 21:11 » 

ERROR_IO_PENDING эта "ошибка" почти всегда после OVERLAPPED Read/Write/.. 
Записан
Tuborg
Команда клуба

ee
Offline Offline

« Ответ #11 : 28-02-2008 13:52 » 

Для истории (вдруг кто ещё будет это искать) есть приличная инфа по этому поводу - http://www.caxapa.ru/lib/comwinnt.html#_ftn1
Записан
lag
Участник

ru
Offline Offline

« Ответ #12 : 02-03-2008 12:52 » 

я не применяю здесь объекты ядра, а делаю так:

  COMMTIMEOUTS cto;
  cto.ReadIntervalTimeout         = 1;
  cto.ReadTotalTimeoutConstant    = g_dwCommTimeout;
  cto.ReadTotalTimeoutMultiplier  = 0;
  cto.WriteTotalTimeoutConstant   = 30;
  cto.WriteTotalTimeoutMultiplier = 0;
  SetCommTimeouts(g_hComm, &cto);

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

ReadIntervalTimeout - максимальное время, в миллисекундах, допустимое между двумя
последовательными символами считываемыми с коммуникационной линии.

Определив таким образом, делаю просто - WriteFile и сразу ReadFile. Конечно в моем
случае я знаю, что данные (ответ) должны быть получены не более чем через g_dwCommTimeout
миллисекунд.

Если же я просто слушаю порт, то в отдельном потоке:

  while (!ReadFile(...))
    Sleep(1);  // чтобы не нагружать процессор



Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 02-03-2008 13:23 » 

lag, while (!ReadFile(...)) - потеряешь первый (как минимум) байт. Тогда уж надо его запомнить - и добавить в начало буфера, куда будешь читать остальное
Записан

lag
Участник

ru
Offline Offline

« Ответ #14 : 03-03-2008 07:28 » 

Ну так я и не стал дописывать, что я делаю после приема данных.
У каждого ведь свои задачи после чтения. Реально я собираю данные и
разбираю по пакетам. Затем анализирую каждый пакет и выделяю нужное
в соответствии с протоколом. Я описываю, что не всегда надо задействовать
WaitFor...Object - компорт нам сам дает возможность принять все данные
за g_dwCommTimeout миллисекунд. Надо только грамотно установить таймауты.
По крайней мере меня устраивает, как это работает в моей задаче и с моим
протоколом.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 03-03-2008 07:38 » 

а я тоже так делаю, кстати ) Без событий - просто суюсь в порт и смотрю, идёт что нибудь или нет. События пока даже не приходилось использовать
Записан

SergI
Гость
« Ответ #16 : 09-03-2008 15:28 » 

Для истории (вдруг кто ещё будет это искать) есть приличная инфа по этому поводу - http://www.caxapa.ru/lib/comwinnt.html#_ftn1

 А по-моему это порочное руководство.
 Почитай тут :http://lord-n.narod.ru/walla.html
 Вот это:
    П.Агуров.
Последовательные интерфейсы ПК.
Практика программирования.
объём файла - 4'338'122 byte
http://lord-n.narod.ru/download/books/walla/programming/P.Agurov.Posledovatelnie.interfacy.PK.Practica.programmirovania.rar

+ CD к книге
http://lord-n.narod.ru/download/books/walla/programming/CD.for.book.Pavel.Agyrov.Interfaces.zip

 После прочтения станет понятно, почему алгоритм, приведенный на Сахаре, не очень хорош.
« Последнее редактирование: 09-03-2008 15:45 от SergI » Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines