Откуда при поступлении данных по com-порту берётся двойное срабатывание WaitCommEvent??
Программа ожидает события EV_RXCHAR. Событие срабатывает, читаем принятые данные, переходим на ожидание события (и ту самое интересное!!!) и функция сразу же возвращает управление с данным событием. Смотрим количество поступивших данных, и оно равно 0!!!
Здравствуйте, _alecs, Вы писали:
_>Откуда при поступлении данных по com-порту берётся двойное срабатывание WaitCommEvent??
_>Программа ожидает события EV_RXCHAR. Событие срабатывает, читаем принятые данные, переходим на ожидание события (и ту самое интересное!!!) и функция сразу же возвращает управление с данным событием. Смотрим количество поступивших данных, и оно равно 0!!!
_>В чём причина???
Кинь код как ты ждешь event + код вокруг вызовов SetCommMask и SetCommTimeouts
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
у тебя manual reset event — их же надо ручками сбрасывать иначе они все время будут в signaled state
вероятно поэтому у тебя второй раз wait срабатывает сразу. Соответственно и данных нет, потому как они не поступили еще
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, _alecs, Вы писали:
CC>у тебя manual reset event — их же надо ручками сбрасывать иначе они все время будут в signaled state CC>вероятно поэтому у тебя второй раз wait срабатывает сразу.
Ну уж ненаю почему срабатывает у тебя RX_CHAR но в MSDN написано, что OVERLAPPED::hEvent должно быть "auto reset event", т.е. создавать его надо так:
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, _alecs, Вы писали:
CC>у тебя manual reset event — их же надо ручками сбрасывать иначе они все время будут в signaled state CC>вероятно поэтому у тебя второй раз wait срабатывает сразу. Соответственно и данных нет, потому как они не поступили еще
Конечно ты прав, просто покоцылось кое-что когда выдёргивал часть кода... Конечно всё сбрасывается! Если бы не сбрасывалось срабатываний было бы гораздо больше!
А здесь чётко два. Такое впечатление, что когда WaitCommEvent присаживается на событие, то по приходу первого байта (события EV_RXCHAR) она вылетает и устанавливает oWait.hEvent. При этом флаг события EV_RXCHAR сбрасывается. По приходу остальных байт, из-за того что управление не дошло вновь до WaitCommEvent (переключение потоков и т.д.) устанавливается флаг события EV_RXCHAR. Потом когда управление вновь передаётся потоку и тот считывает все на этот момент поступившие данные флаг поступления события EV_RXCHAR остаётся в установленном состоянии. И когда функция WaitCommEvent опять лезет за событием, она сразу получает событие от предыдущих данных. Почти на 99% уверен что так и происходит, просто хотел узнать мнение остальных разработчиков. Сталкивались ли они с этим...? И как сбросить (элегантным способом!!) все события перед вызовом WaitCommEvent. И вообще, хочу услышать рассуждения других специалистов.
Вот уточнил:
SetCommMask( hCom, EV_RXCHAR );
while( ! Terminated )
{
// Ожидаем событие от ком-порта.if ( ! WaitCommEvent( hCom, dwEvtMask, &oWait ) )
{
if ( GetLastError() != ERROR_IO_PENDING )
{
// Прекращаем выполнение программы.
// ...
}
ResetEvent( oWait.hEvent );
// Ожидаем окончания операции.
HANDLE hEvents[] = { oWait.hEvent, hBreakEvent };
DWORD dwWait = WaitForMultipleObjects(
sizeof( hEvents ) / sizeof( HANDLE ), // number of object handles
hEvents, // pointer to an array of object handles
FALSE, // returns when the state of any one of the objects is set to signaled
dwMilliseconds); // waits indefinitelyswitch( dwWait )
{
case WAIT_OBJECT_0:
// Считываем данные с ком-порта (считываются все переданные данные, потерянных нет).
// ...break;
case WAIT_OBJECT_0 + 1: continue;
default:
// Прекращаем выполнение программы.
// CancelIo( hCom ); ...
}
}
else
{
// Считываем данные с ком-порта (считываются все переданные данные, потерянных нет).
// ...
}
} // end-while
Здравствуйте, _alecs, Вы писали:
_>Млин, нитуда вставил Reset!!! Конечно же ResetEvent перед WaitCommEvent! Основной код на работе а здесь пришлось выдёргивать из старого.
Так будут теряться события. Почему бы не использовать auto-reset event?
Здравствуйте, quodum, Вы писали:
Q>Здравствуйте, _alecs, Вы писали:
Q>[]
Q>А ты уверен, что срабатывает именно это событие? У тебя в коде их вроде две штуки. Или три. Q>Приведи лучше код еще раз, только теперь целиком.
Ок! Действующий код приведу чуть попозже, но отличий там небудет. Он будет лишь иметь дополнительный класс TCom в котором и будут реализованны метод — инициализация порта, запись/чтение данных и ожидание события. Событие по записи oWrite.hEvent не используется. Используются только три события oWait.hEvent для ожидания события от порта, oRead.hEvent для чтения данных и hBreakEvent для прекращения операций и завершения потока. Возвращаемое событие EV_RXCHAR тоже проверяется. Как уже сказал точный код приведу попозже, но основной цикл будет выглядеть примерно так:
pCom->SetMask( EV_RXCHAR );
while( ! Terminated )
{
// Ожидаем событие от ком-порта.
if ( ! pCom->WaitEvent( &dwEvtMask ) ) continue;
Здравствуйте, quodum, Вы писали:
Q>Здравствуйте, _alecs, Вы писали:
Q>[]
Q>А ты уверен, что срабатывает именно это событие? У тебя в коде их вроде две штуки. Или три. Q>Приведи лучше код еще раз, только теперь целиком.
Ок! Действующий код приведу чуть попозже, но отличий там небудет. Он будет лишь иметь дополнительный класс TCom в котором и будут реализованны метод — инициализация порта, запись/чтение данных и ожидание события. Событие по записи oWrite.hEvent не используется. Используются только три события oWait.hEvent для ожидания события от порта, oRead.hEvent для чтения данных и hBreakEvent для прекращения операций и завершения потока. Возвращаемое событие EV_RXCHAR тоже проверяется. Как уже сказал точный код приведу попозже, но основной цикл будет выглядеть примерно так:
2.1. приходит первый байт
2.2. для порта выставляется событие EV_RXCHAR.
2.3. WaitCommEvent счищает EV_RXCHAR во внутреннем состоянии порта и возвращает управление
2.4. приходит второй байт
2.5. для порта выставляется EV_RXCHAR.
_>3. Срабатывает ТОЧКА ОСТАНОВА. _>4. Проверяем событие — dwEvtMask == EV_RXCHAR. _>5. Читаем данные — dwLength == 6.
_>6. Запускаем WaitEvent — сразуже получаем EV_RXCHAR (проверяем, действительно сработало событие oWait.hEvent).
...потому что EV_RXCHAR во внутреннем состоянии порта уже установлен при приходе второго байта
_>7. Читаем данные — dwLength == 0. _>8. Запускаем WaitEvent — уходит на ожидание EV_RXCHAR (при повторной посылки всё повторяется).
Ну, данное поведение кажется я уже описал в 4-ом сообщении. Но вопрос: — "Как элигантным образом сбросить событие перед вызовом WaitCommEvent?" остаётся открытым..
Здравствуйте, _alecs, Вы писали:
_>Ну, данное поведение кажется я уже описал в 4-ом сообщении. Но вопрос: — "Как элигантным образом сбросить событие перед вызовом WaitCommEvent?" остаётся открытым..
Не плоди сущностей без необходимости. Это событие, равно как и то, с помощью которого прерывается цикл, нафиг не сдалось. WaitCommEvent вызывается синхронно, когда нужно прервать — зовем SetCommMask(handle, 0), при этом ожидание в WaitCommEvent прерывается и она возвращает 0 в маске. Вот работающий код на Дельфи:
while FConnected do begin
dwEvtMask := 0;
WaitCommEvent(FHandle, dwEvtMask, nil);
if (dwEvtMask and EV_RXCHAR) <> EV_RXCHAR then continue;
repeat
ClearCommError(FHandle, dwErrorFlags, @ComStat);
n := ComStat.cbInQue;
if n > sizeof(Buffer) then n := sizeof(Buffer);
if n = 0 then break;
if not ReadFile(FHandle, Buffer, n, n, @osRead) then
begin
dwError := GetLastError;
if dwError = ERROR_IO_PENDING then begin
GetOverlappedResult(FHandle, osRead, n, TRUE);
end
else begin
DoError;
break;
end;
end;
// Тут обрабатываются принятые данные...until FALSE;
end; // while FConnected
// для прерывания:
FConnected := False;
SetCommMask(FHandle, 0);
Здравствуйте, Sergei I. Gorelkin, Вы писали, мы читали.
Вы наверное не поняли о чём здесь идёт речь, проблемы в том чтобы прервать выполнение WaitCommEven нет. Ворос наоборот другой, не в том что функция неможет завершится, ожидает события и как её выбить из состояния ожидания, а как предотвратить ложное срабатывание функции от предыдущих событий!
Здравствуйте, _alecs, Вы писали:
_>Вы наверное не поняли о чём здесь идёт речь, проблемы в том чтобы прервать выполнение WaitCommEven нет. Ворос наоборот другой, не в том что функция неможет завершится, ожидает события и как её выбить из состояния ожидания, а как предотвратить ложное срабатывание функции от предыдущих событий!
Я просто привел пример кода, который в свое время был отлажен и работает. То, что в нем на два евента меньше — это, скорее всего, не столь важно. Ключевые моменты — во-первых, dwEvtMask сбрасывается в ноль перед вызовом WaitCommEvent, во-вторых, получив EV_RXCHAR, уходит в цикл из ClearCommError/ReadFile до тех пор, пока ClearCommError не вернет нулевое количество байт во входном буфере. При таком подходе возможные ложные срабатывания WaitCommEvent просто игнорируются и ни на что не влияют.
Здравствуйте, Sergei I. Gorelkin, Вы писали, мы читали.
Сбрасывать dwEvtMask в ноль перед вызовом WaitCommEvent не имеет никакого смысла, потому что этот аргумент для функции является выходным, и функция запишет в эту переменную своё значение. Повторяю 'запишет', а не установит отдельные биты соответствующие поступившем событиям. Так что перестраховываться и сбрасывать эту переменную в ноль нету смысла. И ещё хочу указать Вам на различия в синхронной и асинхронной работе Вашего (т.к. Ваш ком-порт был открыт без флага FILE_FLAG_OVERLAPPED, иначе аргумент lpOverlapped в вызове функции WaitCommEvent обязан был иметь указатель на структуру OVERLAPPED) и моего приложения. Нет-нет, я не хочу рассказывать об принципах синхронной и асинхронной работы которые и так всем известны, а только об особенностях ком-порта.
И так, особенностью синхронной и асинхронной работы ком-порта является то, что при синхронной работе невозможна одновременная запись и чтение данных из и в ком-порт. Другими словами перед тем как записать данные в ком порт вам необходимо вышибить функцию WaitCommEvent с ожидания события. При этом Вам необходимо отследить, чтобы после прерывания функции WaitCommEvent поток ожидавший событие, не успел отработать и вновь сесть на ожидание события от ком-порта перед тем как вы попытаетесь записать данные в ком-порт. Отсюда следует что у Вас появятся ещё пара событий (или других объектов синхронизации), иначе ваше приложение иногда просто будет виснуть по непонятным для вас причинам.
В асинхронном же режиме такой проблемы нет. Один поток может писать данные в ком-порт, не обращая внимания на то, что второй поток в это время находится в ожидании/чтении данных из этого же ком-порта.
Игнорирование же ложных срабатывания WaitCommEvent не подходит для высокопроизводительных систем, которые ожидают начала поступления данных. После начала поступления данных, система переходит на время ожидания всей посылки. Если же начало посылки нет (устройство отсутствует), то система сразу переходит к следующему устройству, не дожидаясь времени окончания всей посылки. Что увеличивает производительность системы на порядок. Ложные же срабатывания заставляют систему дожидаться времени окончания посылки (тратить время), и затем уже фиксировать ошибку.
Вопрос такой: — "Как сделать так, чтобы при чтении данных ком-порта сбрасывалось событие EV_RXCHAR".
_>Вопрос такой: — "Как сделать так, чтобы при чтении данных ком-порта сбрасывалось событие EV_RXCHAR".
_>Кстати, Всем благодарен за ответы! И жду новых...
Какое-то время назад смотрел исходники драйвера СОМ порта в ДДК и нашел тот-же способ предложенный выше — после WaitCommEvent вызывать ClearCommError и смотреть в cbInQue число байтов во входном буфере СОМ порта. На производительность это не должно сильно влиять.
Похоже что EV_RXCHAR можно очистить тремя способами
1. вызвать WaitCommEvent
2. Переоткрыть порт
3. SetCommMask(handle, 0)