Синхронизация последовательного порта в Delphi

У меня все еще есть проблемы с компонентом TComPort, но на этот раз логика не лежит на самом компоненте. У меня есть устройство, которое отправляет некоторые строки ascii через последовательный порт, мне нужно вставить эти строки, проблема в том, что компьютер реагирует очень быстро, поэтому в случае, если char захватывает только часть строки, остальная часть строки возвращается позже ... поэтому анализ, когда он получен, делает невозможным.

Я думал о написании таймера, который проверял, не было ли последовательной активности 10 секунд или более, а затем вставлял строку, которую я сохраняю, в буфер. Но этот метод непрофессионален, если нет праздного события, которое я могу выслушать ... В ожидании лучшего решения моей проблемы. Благодарю.

Ответы на вопрос(5)

Для определенного количества символов мы можем использовать задержку за несколько миллисекунд до ReadStr, чтобы убедиться, что данные полностью отправлены. Пример для 4 количества символов:

procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
  Str: String;
  tegangan : real;
begin
  sleep(100); //delay for 100ms
  ComPort1.ReadStr(Str, 4);

...

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

Note. Most com-port components do not have a clue when to report back to the owner. Normally the thread that is responsible to gather the bytes from the port is informed by the OS that one or more bytes are ready to be processed. This information is then simply popped up to your level. So when you expect the message to be transferred, you get what the OS is giving you.

Вы должны буферизовать все входящие символы в глобальном буфере. Когда вы получите последний символ в строке сообщения, обработайте сообщение.

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

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

var
  finalBuf: AnsiString;

{- Checking message }
Function ParseAndCheckMessage(const parseS: AnsiString) : Integer;
begin
  Result := 0; // Assume ok
  {- Make tests to confirm a valid message }
  ...
end;


procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var
  i,err: Integer;
  strBuf: AnsiString;
begin
  ComPort.ReadStr(strBuf, Count);
  for i := 1 to Length(strBuf) do
    case strBuf[i] of
      '
var
  finalBuf: AnsiString;

{- Checking message }
Function ParseAndCheckMessage(const parseS: AnsiString) : Integer;
begin
  Result := 0; // Assume ok
  {- Make tests to confirm a valid message }
  ...
end;


procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var
  i,err: Integer;
  strBuf: AnsiString;
begin
  ComPort.ReadStr(strBuf, Count);
  for i := 1 to Length(strBuf) do
    case strBuf[i] of
      '$' : 
        finalBuf := '$';  // Start of package
      #10 :
        begin
          if (finalBuf <> '') and (finalBuf[1] = '$') then  // Simple validate check 
            begin
              SetLength( finalBuf, Length(finalBuf) - 1); // Strips CR
              err := ParseAndCheckMessage(finalBuf);
              if (err = 0) then 
                {- Handle validated string }
              else
                {- Handle error } 
            end;
          finalBuf := '';
        end; 
    else
      finalBuf := finalBuf + strBuf[i];  
    end;
end;
apos; : finalBuf := '
var
  finalBuf: AnsiString;

{- Checking message }
Function ParseAndCheckMessage(const parseS: AnsiString) : Integer;
begin
  Result := 0; // Assume ok
  {- Make tests to confirm a valid message }
  ...
end;


procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var
  i,err: Integer;
  strBuf: AnsiString;
begin
  ComPort.ReadStr(strBuf, Count);
  for i := 1 to Length(strBuf) do
    case strBuf[i] of
      '$' : 
        finalBuf := '$';  // Start of package
      #10 :
        begin
          if (finalBuf <> '') and (finalBuf[1] = '$') then  // Simple validate check 
            begin
              SetLength( finalBuf, Length(finalBuf) - 1); // Strips CR
              err := ParseAndCheckMessage(finalBuf);
              if (err = 0) then 
                {- Handle validated string }
              else
                {- Handle error } 
            end;
          finalBuf := '';
        end; 
    else
      finalBuf := finalBuf + strBuf[i];  
    end;
end;
apos;; // Start of package #10 : begin if (finalBuf <> '') and (finalBuf[1] = '
var
  finalBuf: AnsiString;

{- Checking message }
Function ParseAndCheckMessage(const parseS: AnsiString) : Integer;
begin
  Result := 0; // Assume ok
  {- Make tests to confirm a valid message }
  ...
end;


procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var
  i,err: Integer;
  strBuf: AnsiString;
begin
  ComPort.ReadStr(strBuf, Count);
  for i := 1 to Length(strBuf) do
    case strBuf[i] of
      '$' : 
        finalBuf := '$';  // Start of package
      #10 :
        begin
          if (finalBuf <> '') and (finalBuf[1] = '$') then  // Simple validate check 
            begin
              SetLength( finalBuf, Length(finalBuf) - 1); // Strips CR
              err := ParseAndCheckMessage(finalBuf);
              if (err = 0) then 
                {- Handle validated string }
              else
                {- Handle error } 
            end;
          finalBuf := '';
        end; 
    else
      finalBuf := finalBuf + strBuf[i];  
    end;
end;
apos;) then // Simple validate check begin SetLength( finalBuf, Length(finalBuf) - 1); // Strips CR err := ParseAndCheckMessage(finalBuf); if (err = 0) then {- Handle validated string } else {- Handle error } end; finalBuf := ''; end; else finalBuf := finalBuf + strBuf[i]; end; end;

Типичный обработчик для события OnRXChar:

procedure XXX.RXChar(Sender: TObject; Count: Integer);
begin
  ComPort.ReadStr(s, Count);
  Accumulator := Accumulator + s;
  if not AccumContainsPacketStart then
    Accumulator := ''
  else if AccumContainsPacketEndAfterStart then begin
    ExtractFullStringFromAccum;
    ParseIt;
  end;
end;
Решение Вопроса

После использования ряда компонентов последовательного порта я получил лучшие результаты до сих пор, используяCreateFile('\\?\COM1',GENERIC_READ or GENERIC_WRITE,0,nil,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0), передавая этот дескриптор экземпляру THandleStream и запуская выделенный поток для чтения из него. Я знаю, что потоки занимают немного больше времени, чем написание обработчика событий, но это все же лучший способ справиться с любыми проблемами синхронизации, возникающими при использовании последовательных портов.

Ваш ответ на вопрос