Форум: "WinAPI";
Текущий архив: 2006.04.16;
Скачать: [xml.tar.bz2];
ВнизАсинхронная работа с 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 вся ветка
Форум: "WinAPI";
Текущий архив: 2006.04.16;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.119 c