Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Работа с непрерывным потоком данных


Автор: RinOSpro 27.1.2009, 10:46
Здравствуйте! Есть COM порт, с него нужно принимать данные и обрабатывать
Сейчас работаю через API CreateFile, ReadFile, WriteFile.

С COM порта идет непрерывный поток данных, мне нужно его обрабатывать и еще успевать выводить на экран результаты.

Все работает, но иногда данные теряются. К примеру должно быть, и что пришло:


5149 6159 7139 7149C0B83109319931 A9 3129311931A9311931899

5149 6159 71A9 3129311931A9311931899

потерялись

Как быть что делать даже если самая базовая операция ReadFile, глючит...

Автор: Virtuals 27.1.2009, 11:09
RinOSpro, 
код показывай, так небывает.

Автор: Alexeis 27.1.2009, 11:49
  Может и бывает если внутренний буфер переполняется. Обычно чтение из COM порта делают в отдельном потоке. Есть и куча примеров и готовых компонентов.

Автор: RinOSpro 27.1.2009, 11:59
Подскажите нормальный примерчик, асинхронной записи/чтения. 
И может есть какая книжка толковая, а то в статьях информация кусками...

Автор: Alexeis 27.1.2009, 12:47
Я вот использовал у себя TiaRS232 (см. атач)

Там примерно такой алгоритм.
Создаем и назначаем обработчик 
Код

  RS232 := TiaRS232.Create;
  RS232.OnRSReceived := DoOnReceiveEvent;


настариваем 
Код

  RS232.Properties.PortNum    := StrToInt(ComboBox1.Text[4]);//выбираю компорт
  RS232.Properties.Parity     := NOPARITY;//четность
  RS232.Properties.BaudRate   := CBR_115200;//скорость
  RS232.Properties.StopBits   := 0;
  RS232.UseSynchronizeReceive := false;//асинхронный режим
  RS232.Open;//открыли
  RS232.StartListner;//слушаем


Завершаем
Код

  RS232.StopListner;
  RS232.Close;

сам обработчик пришедших данных
Код

procedure TForm1.DoOnReceiveEvent(Sender: TObject; const aData: TiaBuf;
  aCount: Cardinal);//размер буфера в aCount
var
  DataBuf  : PAnsiChar;
begin
  DataBuf  := @aData[0];//буфер с данными
...........



Автор: Felan 27.1.2009, 13:05
ААААААААА,  smile  smile 

Собственно как автор сего творения хочу заявить, что
Цитата(Alexeis @  27.1.2009,  14:47 Найти цитируемый пост)
RS232.UseSynchronizeReceive := false;//асинхронный режим

не есть асинхронный режим. Это всего лишь указание слушающему потоку, выполнять получение данных напрямую, или использую метод Synchronize, класса Thread.

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

Автор: Alexeis 27.1.2009, 13:13
Цитата(Felan @  27.1.2009,  12:05 Найти цитируемый пост)
не есть асинхронный режим.

  Т.е. режим всегда асинхронный? 

Автор: Felan 27.1.2009, 14:13
Ну в строгом смысле там вообще нет асинхронного режима. Он эмулируется постоянным чтением данных из вторичного потока. Если данные есть, то происходит событие.
Т.е. это событие вызывается из отдельного потока, то для работы в нем надо либо не использовать VCL, либо ставить этот флаг с тру, что бы он работал через Synchronize.

А так да... можно сказать, что всегда асинхронный.

Но можно и не запускать прослушку, можно самому читать (Receive). Тогда уже будет самый натуральный синхронный smile

Автор: Romikgy 27.1.2009, 14:39
есть асинхроный и без потоков, на событиях винды

Автор: Alexeis 27.1.2009, 14:42
  А на счет внутренней буферизации кто-то что-то знает? Какой объем внутреннего буфера у винда, чтобы как-то оценить сколько времени можно не читать данные по COM порту.

Автор: Romikgy 27.1.2009, 15:24
SetupComm для установки буферов ( smile ) 
низкоуровневый фифо до 16 байт в обе стороны

Автор: Alexeis 27.1.2009, 15:42
Цитата(Romikgy @  27.1.2009,  14:24 Найти цитируемый пост)
низкоуровневый фифо до 16 байт в обе стороны 

  Ну это совсем мало, у винды свой наверное куда по больше.

Автор: Felan 28.1.2009, 11:46
Цитата(Romikgy @  27.1.2009,  17:24 Найти цитируемый пост)
SetupComm для установки буферов ( smile ) 
низкоуровневый фифо до 16 байт в обе стороны 

Она устанавливает рекомендованные значения. Т.е. винда их может и не использовать. Если туда че-то не лезет, она будет автоматически его расширять.

Цитата(Alexeis @  27.1.2009,  17:42 Найти цитируемый пост)
Ну это совсем мало, у винды свой наверное куда по больше. 

Какой свой? Не думаю, что там есть какой-то свой.
Это все тот же. Вот до каких приделов он может расти... вот это вопрос smile Я не знаю.
Мне кажется, это ограничено только адресным пространством. Интересно, что будет потом? smile

ЗЫЖ На моей практике ни разу не переполнялся и ни разу данные не терялись (ну с учетом того, что передача была правильная, и в проводах ничего не потерялось).

Добавлено через 1 минуту и 14 секунд
Цитата(Romikgy @  27.1.2009,  16:39 Найти цитируемый пост)
есть асинхроный и без потоков, на событиях винды 

А можно его в мыло? Интересуют исходники, а то я как-то пытался сделать асинхронное чтение, но у меня оно глючно получилось.

Автор: Virtuals 28.1.2009, 12:40
Felan, 
Цитата

Мне кажется, это ограничено только адресным пространством. Интересно, что будет потом? 

хош скажу?
на всех виндах начиная от нт4 и до ХРsp3 если в порт идут данные непрерывным потоком, а ни одно приложение их не забирает, то !:
переполняется какойто там буфер, у драйвера порта сносит крышу, и все!!! порт неработает до перезагрузки виндов.
ЗЫ проверено лично и не раз.


Alexeis, 
Цитата

Ну это совсем мало,

для меня так наоборот совсем много, всегда отрубаю фифо, иначе фиг угадаеш когда посылка данных прекращается,... что ручками подергать ну там dtr.

пользую, компонент для портов (асинхроный с потоками, на событиях винды), и все ...

раз пошла такая пьянка вот...где спер незнаю, но вроде чет про автора есть...

так пользуем
Код

procedure TForm1.Comm321ReceiveData(Buffer: Pointer; BufferLength: Word);
var buf: array of Byte;
    s:ansistring;
    tmp:dword;
begin
s:='';
buf:=Buffer;
for tmp:=0 to BufferLength-1 do s:=s+inttohex(buf[tmp],2)+' ';
Memo1.Lines.Add(s);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Comm321.WriteCommData(pchar(edit1.Text),length(edit1.Text) );
end;

procedure TForm1.openClick(Sender: TObject);
begin
Comm321.StartComm;
end;


Автор: RinOSpro 28.1.2009, 14:01
Я думаю моя проблема от того что мало себе представляю как физически устроен COM порт, и как работает.

Открываю порт:

Код

function TThreadIO.Open: Boolean;
var
  dcb: TDCB;
  CommTimeOuts: TCommTimeouts;
begin
  FHandle := CreateFile(PChar(FConnectionString),
                        GENERIC_READ or GENERIC_WRITE,
                        0, nil, OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);

  if FHandle <> INVALID_HANDLE_VALUE then
  begin
    SetupComm(FHandle, 512, 512);
    GetCommState(FHandle, dcb);
    dcb.BaudRate := 230400;
    dcb.ByteSize := 8;
    dcb.Parity := NOPARITY;
    dcb.StopBits := ONESTOPBIT;
    SetCommState(FHandle, dcb);

    CommTimeOuts.ReadIntervalTimeout := 2;
    CommTimeOuts.ReadTotalTimeoutMultiplier := 1;
    CommTimeOuts.ReadTotalTimeoutConstant := 1;
    CommTimeOuts.WriteTotalTimeoutMultiplier := 1;
    CommTimeOuts.WriteTotalTimeoutConstant := 1;

    SetCommTimeouts(FHandle, CommTimeOuts);
    PurgeComm(FHandle, PURGE_RXCLEAR);
    PurgeComm(FHandle, PURGE_TXCLEAR);

    FillChar(FOverlapped, SizeOF(TOverlapped), 0);
    FOverlapped.hEvent := CreateEvent(0, False, False, '');
  end;

  Result := (FHandle <> INVALID_HANDLE_VALUE);
end;


Читаю из порта вот так:

Код

function TThreadIO.ReadData(ACountByte: Integer): Boolean;
var
  i: Integer;
begin
  Result := False;
  try
    ClearBuf;
    if FHandle <> INVALID_HANDLE_VALUE then
    begin
      FOverlapped.Internal := 0;
      FOverlapped.InternalHigh := 0;
      FCount := 0;
      ReadFile(FHandle, FBuf, ACountByte, FCount, @FOverlapped);

      repeat
        Result := GetOverlappedResult(FHandle, FOverlapped, FCount, True);
      until Result;

      FOverlapped.Offset := FOverlapped.Offset + FCount;
    end;
  except
    Result := False;
  end;
end;


Пишу примерно так же...

Вот сомнение возникло, а может ли запись в порт как то что то сбивать? Особенно если всегда идет сплошной поток данных.


Автор: Felan 28.1.2009, 18:23
Цитата(Virtuals @  28.1.2009,  14:40 Найти цитируемый пост)
хош скажу?
на всех виндах начиная от нт4 и до ХРsp3 если в порт идут данные непрерывным потоком, а ни одно приложение их не забирает, то !:
переполняется какойто там буфер, у драйвера порта сносит крышу, и все!!! порт неработает до перезагрузки виндов.
ЗЫ проверено лично и не раз.

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

Цитата(RinOSpro @  28.1.2009,  16:01 Найти цитируемый пост)
Пишу примерно так же...

Вот сомнение возникло, а может ли запись в порт как то что то сбивать? Особенно если всегда идет сплошной поток данных.

Ну вроде нормально все...
Сравним с моим исходником. Он в неизменном виде используется уже 3 года. Может там и не все красиво и "круто", но надежно smile

А что она может сбивать? Ни идет они и идет... че такого?

Автор: RinOSpro 3.2.2009, 13:22
Блин... дело было в железе... Ненавижу.... 

Автор: Robus 16.2.2009, 20:15
Я думаю, что если ты сделаешь вот так:
Код

     COMMTIMEOUTS.ReadIntervalTimeout         := $FFFFFFFF;
     COMMTIMEOUTS.ReadTotalTimeoutMultiplier  := 0;
     COMMTIMEOUTS.ReadTotalTimeoutConstant    := 0;
     COMMTIMEOUTS.WriteTotalTimeoutMultiplier := 0;
     COMMTIMEOUTS.WriteTotalTimeoutConstant   := 0;


То, твоя проблема исчезнет ... В твоём примере стоят "1", а это как минимум два такта UART'а ... Поэтому со временем ты вычитываешь медленее чем буфер заполняется, и в какой-то момент теряешь данные !!! Лучше всего поставить "0", это будет минимальная задержка.

Так же я у тебя заметил скорость 115200х2 !!! Круто берёшь !!! А позволяет ли твой UART такое ??? Просто проверь ... 2 мегагерца это круто для RS-232 !!!

И ещё ... Я заметил, что после получания пачки данных ты улетаешь на обработку это пачки !!! А тем временем новой пачке всего 512 байт отдано ... Винда, конечно, калечаня, поскольку застваляет извращаться, но я бы на твоём месте перебросил бы это всё ещё в один мега буффер, эдак на 65536 байт, и отпустил бы поток. а уже в ещё одном потоке обрабатывал бы последний буфер.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)