2012-05-11 2 views
1

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

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

+2

Это известная проблема. Если бы он был в большей среде. Триггер событий данных действительно быстрее, чем данные, которые следуют. Не уверен, что есть что-то еще, кроме небольшого ожидания перед чтением. – pritaeas

+0

Это вполне нормально, и вы должны просто сохранить символы в буфере и сбросить таймер. Затем используйте таймер для запуска фактической активности - таймер может быть довольно плотным. Вы хотите закончить «конечный автомат» и иметь триггеры для событий. – mj2008

+0

Предположим, вы используете Djaan Crnila и другие TComport (несколько компонентов называются «TComport»). Эта проблема может быть связана с триггером события данных, но также возможна скорость передачи в бодах вашего последовательного устройства в соответствии со скоростью, отличной от скорости вашего контрольного элемента. Если ваше устройство отправляет пакеты один раз каждые несколько мс, вы должны легко улавливать и анализировать весь пакет данных, если пакет имеет разумную длину. Отправляет ли устройство конец символа управления пакетами? - облегчает его.Если вы предоставите дополнительную информацию об устройстве, полученном пакете и т. Д., Кто-то может предоставить помощь. – SteveJG

ответ

3

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

3

Типичный обработчик события 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; 
+0

строка содержит символы, такие как # 0, поэтому использование метода strstr defenetly из the.question – opc0de

+1

Это не важно для строк pascal (до вывода функций ОС) – MBo

+0

@ opc0de, в паскале символ # 0 похож на любой другой символ. Если вы предпочитаете работать с байтами, замените AnsiChars/AnsiStrings на байты/TBytes. Это предпочтительный путь с D2009 и unicode. –

2

Примечание. Большинство компонентов компрометирования не имеют понятия, когда следует сообщать об этом владельцу. Обычно поток, ответственный за сбор байтов от порта, сообщает ОС, что один или несколько байтов готовы к обработке. Затем эта информация просто появляется на вашем уровне. Поэтому, когда вы ожидаете передачи сообщения, вы получаете то, что дает вам ОС.

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

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

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

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; 
1

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

+0

Я получаю нарушение прав доступа, когда я использую TComDataPacket в Delphi 7 не все время, но когда пакет больше – opc0de

+0

Хорошо, я видел Уоррена, чтобы выступить против него в другом вопросе. Однако мы использовали его с успехом в Delphi XE2. –

+0

Кажется, он определяет фиксированный буфер размером 1024 байта. Попытайтесь увеличить его в конструкторе и посмотреть, помогает ли это с большими пакетами. –

-2

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

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

...

Смежные вопросы