Форум: "WinAPI";
Текущий архив: 2006.12.03;
Скачать: [xml.tar.bz2];
ВнизРазличное поведение различных СОМ портов при асинхронном доступе Найти похожие ветки
← →
Balkon (2006-07-17 14:25) [0]Приветствую опять!
Пишу управляющую программу для девайса, работающего в режиме запрос-ответ. Работу с портом организую во вторичном потоке в режиме асинхронного доступа.
Столкнулся со следующей проблемой. На физически различных портах событие EV_RXCHAR генерится по появлению различного количества байт. Например жду ответа длинною 12 байт. На моей машине на родном порту прилетают все 12 байт. На соседней машине событие генерится по прилету 8,0,4 байт на каждый ответ. Если же пользую на своей машине УСБ-СОМ адаптер, то событие генерится 12 раз по байту. Соответственно в последнем случае ответы обрабатываются дольше всего и при настройках частоты опроса, подобранных для первого случая начинает "забиваться" командная очередь.
Конечно можно увеличить интервал опроса девайсов. Но хотелось бы знать причину и может быть поправить код так, чтобы работало одинаково на всех портах.
В чем может быть причина?
Может быть некорректно написаны нижеприведенные процедуры?
Спасибо за внимание.
procedure TAKRThread.Execute;
Var CurrentState : TComStat;
AvaibleBytes, ErrCode, RealRead : Cardinal;
ReadOL : TOverLapped;
Signaled, Mask : DWORD;
BytesTrans : DWORD;
Success: Boolean;
TmpPackage: TReadPackage;
Begin
if WriteAKRComand then
Try
FillChar(ReadOL, SizeOf(ReadOL), 0);
ReadOL.hEvent:= CreateEvent(nil, True, True, nil);
while (not Terminated) do
begin
Success := WaitCommEvent(FPortHandle, Mask, @ReadOL) or
(GetLastError = ERROR_IO_PENDING);
Signaled:= WaitForSingleObject(ReadOL.hEvent, 1000);
if Success and (Signaled = WAIT_OBJECT_0) and
GetOverlappedResult(FPortHandle, ReadOL, BytesTrans, False) then
begin
If (Mask and EV_RXCHAR) <> 0 then
begin
ClearCommError(FPortHandle, ErrCode, @CurrentState);
AvaibleBytes:= CurrentState.cbInQue;
PostMessage(FOwner,WM_MESFROMTHREAD,AvaibleBytes,0);
if (AvaibleBytes > 0) then
begin
Success := ReadFile(FPortHandle, TmpPackage, AvaibleBytes, RealRead, @ReadOL)
or (GetLastError = ERROR_IO_PENDING);
if Success and GetOverlappedResult(FPortHandle, ReadOL, BytesTrans, False) then
CheckBytes(TmpPackage,BytesTrans);
end;
End;
end
else
RepeatAKRComand;
End;
Finally
CloseHandle(ReadOL.hEvent);
SetCommMask(FPortHandle, 0);
End
else
raise Exception.Create("...");
End;
function TAKRThread.OpenPort(ComNumber: Integer): boolean;
Var
ComName : String;
Begin
Result := false;
ComName:= Format("\\.\COM%-d", [ComNumber]);
FPortHandle:= CreateFile(PChar(ComName),GENERIC_READ or GENERIC_WRITE,
0,nil,OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,0);
if not(
SetCommMask(FPortHandle, EV_RXCHAR) and
SetupComm(FPortHandle,48,48) and
PurgeComm(FPortHandle,PURGE_TXABORT or PURGE_RXABORT or
PURGE_TXCLEAR or PURGE_RXCLEAR) and
ApplyComSettings) then
begin
CloseHandle(FPortHandle);
Exit;
end;
Result := true;
end;
function TAKRThread.ApplyComSettings: boolean;
Var
dcb: TDCB;
CommTimeOut: TCommTimeOuts;
Begin
if FPortHandle <> INVALID_HANDLE_VALUE then
begin
FillChar(dcb, SizeOf(dcb), 0);
dcb.DCBLength:= SizeOf(dcb);
dcb.BaudRate := CBR_9600;
dcb.Flags := dcb_Binary;
dcb.ByteSize := 8;
dcb.Parity := 0;
dcb.StopBits := 0;
FillChar(CommTimeOut,SizeOf(CommTimeOut),0);
CommTimeOut.ReadTotalTimeoutConstant := 200;
Result := SetCommState(FPortHandle, dcb) and SetCommTimeOuts(FPortHandle,CommTimeOut);
end
else
Result := false;
End;
← →
tesseract © (2006-07-17 14:29) [1]
> В чем может быть причина?
Вроде сам ответил - в различных портах. Совершенно нормальное явление.
← →
Balkon (2006-07-17 14:47) [2]>Совершенно нормальное явление.
Интуитивно понятно, что нормальное.
Однако мне не ясно почему заданиеCommTimeOut.ReadTotalTimeoutConstant := 200;
не форсирует программу при чтении ждать интервал 200 милисек за которые гарантированно должны прилететь все байты.
Или это должно работать только в синхронном режиме доступа?
← →
tesseract © (2006-07-17 15:43) [3]ну так это для чтения, а не для EV_RXCHAR.
А судя по коду, ты считываешь кол-во байт пришедших в порт по событию. Т.Е всё верно :-)
← →
GanibalLector © (2006-07-19 00:11) [4]2 tesseract,Balkon
Я попридираюсь ;)
>ReadOL.hEvent:= CreateEvent(nil, True, True, nil);
На мой взгляд, очень кривой подход. В идеале, создать евент нужно в главном потоке...до операции чтения. А при чтении,например,выводить форму с кнопкой "Остановить"(при нажатии сбрасывать евент). Вот тогда будет сладко ;) Тогда и на WaitFor можно ставить Infinite - пусть ждет.
З.Ы. Естественно, это мое ИМХО.
← →
tesseract © (2006-07-19 11:02) [5]
> На мой взгляд, очень кривой подход. В идеале, создать евент
> нужно в главном потоке...до операции чтения.
Это само собой :-)
← →
Balkon (2006-07-19 14:48) [6]Спасибо всем.
Схема то приложения вообще следующая.
Есть основной интерфейсный поток, в котором заполняется командная очередь. Есть вторичный (промежуточный) поток, который получает команду, выгребает ее из очереди, создает третий поток работы с портом и ждет его завершения. Т.е. на каждую команду для девайса создается поток (третий),
который на входе получает команду и номер порта. С помощью промежуточного потока достигается некая буферизация команд - пока не будет обработана полностью одна команда следующая в порт не посылается.
Третий поток отправляет команду и если в течении определенного времени Т не получает правильного (или вообще никакого) ответа, повторяет команду N раз. Поток либо после получения правильного ответа, либо после N неудачных попыток дождаться такового добавляет ответ в очередь и завершает исполнение.
Поэтому и WaitForSingleObject ждет только конечное время Т.
Идея заключается в том, чтобы всю работу с портом выделить в один вторичный поток. Создаваемый и убиваемый под каждую команду.
Ж) Сумбурненькое объяснение получилось, но надеюсь понятно.
Не слишком ли схема приложения и реализация кривые? Мне кажется, что все довольно изящно.
← →
Balkon (2006-07-19 14:55) [7]Есть следующий вопрос по моему же коду.
Я так понимаю, что GetOverlappedResult после WaitCommEvent
есть смысл вызывать только для того чтобы проверить маску произошедших событий. Необходимо ли мне проверять содержимое Mask если я жду только
прилета символа. Может быть код:
...Try
FillChar(ReadOL, SizeOf(ReadOL), 0);
ReadOL.hEvent:= CreateEvent(nil, True, True, nil);
while (not Terminated) do
begin
Success := WaitCommEvent(FPortHandle, Mask, @ReadOL) or
(GetLastError = ERROR_IO_PENDING);
Signaled:= WaitForSingleObject(ReadOL.hEvent, 1000);
if Success and (Signaled = WAIT_OBJECT_0) and
GetOverlappedResult(FPortHandle, ReadOL, BytesTrans, False) then
begin
If (Mask and EV_RXCHAR) <> 0 then
begin
ClearCommError(FPortHandle, ErrCode, @CurrentState);
AvaibleBytes:= CurrentState.cbInQue;...
упростить до:
...Try
FillChar(ReadOL, SizeOf(ReadOL), 0);
ReadOL.hEvent:= CreateEvent(nil, True, True, nil);
while (not Terminated) do
begin
Success := WaitCommEvent(FPortHandle, Mask, @ReadOL) or
(GetLastError = ERROR_IO_PENDING);
Signaled:= WaitForSingleObject(ReadOL.hEvent, 1000);
if Success and (Signaled = WAIT_OBJECT_0) then
begin
ClearCommError(FPortHandle, ErrCode, @CurrentState);
AvaibleBytes:= CurrentState.cbInQue;...
?
← →
Balkon (2006-07-19 15:25) [8]Еще момент, будте добры, просвятите:
Ни в одном из примеров работы с последовательным портом (drkb23,delphikingdom) не встречал чтобы люди проверяли результат функции GetOverlappedResult, вызываемой после ReadFile. В доке написано про, что в случае параметра bWait=False "if FALSE and the operation is still pending, the function returns FALSE and the GetLastError function returns ERROR_IO_INCOMPLETE".
Нет ли смысла после ReadFile проверять еще и результат GetOverlappedResult и в случае ошибки сравнивать ее код с ERROR_IO_INCOMPLETE?
Или правивльнее просто после ReadFile вызывать еще и WaitFOrSingleObject?...ClearCommError(FPortHandle, ErrCode, @CurrentState);
AvaibleBytes:= CurrentState.cbInQue;
if (AvaibleBytes > 0) then
begin
ReadFile(FPortHandle, TmpPackage, AvaibleBytes, RealRead, @ReadOL)
if WaitForSingleObject(ReadOL.hEvent,SOMEVALUE) = WAIT_OBJECT_0 then
then
begin
GetOverlappedResult(FPortHandle, ReadOL, BytesTrans, False) then
CheckBytes(TmpPackage,BytesTrans);
end;
end;
...
← →
GanibalLector © (2006-07-20 01:27) [9]2 Balkon [6]
Да, нормально.
2 Balkon (19.07.06 15:25) [8]
В книге П.Агурова есть такой сабж, но...правда не на ReadFile, а на WriteFile.
← →
tesseract © (2006-07-20 11:20) [10]
> Нет ли смысла после ReadFile проверять еще и результат GetOverlappedResult
> и в случае ошибки сравнивать ее код с ERROR_IO_INCOMPLETE?
>
Я проверяю.
Просто не всегда это нужно, если нужно ждать определённое кол-во времени, и GetOverlapped не вернул значение, то проверять в общем бесполезно, будет ERROR_IO_INCOMPLETE
Страницы: 1 вся ветка
Форум: "WinAPI";
Текущий архив: 2006.12.03;
Скачать: [xml.tar.bz2];
Память: 0.49 MB
Время: 0.071 c