2012-02-29 2 views
3

Я разработал небольшую программу, которая слушает последовательный порт. Моя программа получает данные. Проблема в том, что она не отображает ее в нужном формате (одна строка). Данные моя программа получает в двух строках, например:Анализ/форматирование данных с последовательного порта - C#

ID:34242 State:NY 

Почтовый индекс: 12345 StreetType: Ave

Его время, показываемые кусками и некоторые данные передаются на следующие строки, как, например:

ID:34242 
State:N 
Y Zip:12 
345 Street 
Type:Ave 

Я использовал SerialDataReceive обработчик событий, чтобы получить мои данные, и это выглядит следующим образом:

private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 

     SerialPort spL = (SerialPort) sender; 
     int bufSize = 20; 
     Byte[] dataBuffer = new Byte[bufSize]; 
     Console.WriteLine("Data Received at"+DateTime.Now); 
     Console.WriteLine(spL.Read(dataBuffer, 0, bufSize)); 
     string s = System.Text.ASCIIEncoding.ASCII.GetString(dataBuffer); 
     Console.WriteLine(s); 



    } 

Как вы можете видеть, я извлекаю байты в буфер, создаю массив байтов для хранения данных и использую кодировку ASCII для перевода байтов в строку. Я попытался использовать ReadLine(), но мои данные не показывают событие с этой функцией. Кто-нибудь знает какой-либо другой способ разбора и форматирования данных в одну строку?

+1

Есть ли разделитель в конце каждой записи? Распечатайте числовые байтовые значения и посмотрите особенно после отправки последней записи. Данные поступают как непрерывный поток, но, надеюсь, протокол был разработан с каким-то символом завершения (возврат каретки был бы типичным, но, вероятно, это не так, поскольку ReadLine не сработал для вас). – Justin

ответ

4

Проблема в том, что событие DataReceived возникает, как только вы получили данные по последовательному порту. Там не может быть полной записи; объект SerialPort не имеет понятия, что вы считаете «достаточным», чтобы быть значительным или работоспособным.

Обычным решением является поддержание другого «буфера» полученных данных, содержащих любые данные, которые вы признали неполными. Когда данные поступают через порт и срабатывает событие, он должен сначала взять то, что находится в буфере, и добавить его к тому, что вы уже получили. Затем вы должны начать с начала этого буфера данных и проверить полученные данные, ища известные образцы атомных «кусков» данных, имеющих для вас значение; например, скажем, первое, что вы получаете, это "ID: 12". Вы берете это, помещаете его в буфер, затем просматриваете буфер, ища шаблон, определенный регулярным выражением "ID: \d*? ". Поскольку конечное пространство отсутствует в вашем буфере, ваш шаблон не находит ничего значимого, и теперь вы знаете, что не получили полного сообщения.

Затем, при следующем поднятии события DataReceived, вы вытаскиваете "453 Sta" из последовательного буфера. Вы добавляете его к тому, что у вас уже есть, и получаете "ID:12453 Sta", и когда вы применяете регулярное выражение, вы получаете совпадение «ID: 12345». Вы передаете это в метод для дальнейшей обработки (отобразите консоль, возможно), и удалите ту же строку из передней части буфера, оставив «Sta». Сканирование снова, вы не найдете ничего интересного, поэтому вы оставляете то, что у вас есть, и цикл повторяется. Данные aws продолжают поступать. Очевидно, вы будете тестировать больше шаблонов, чем только шаблон ID; вы можете найти целую «строку», которую вы ожидаете получить, например, "ID: \d*? State: \w{2} ". Вы даже можете хранить данные в своем буфере, пока у вас нет обеих строк для записи: "ID:\d*? State:\w{2} Zip:\d{5} StreetType:\w*? ".

В любом случае вам необходимо определить, являются ли получаемые данные надежной «фиксированной длиной» (что означает, что каждая строка определенного типа всегда будет иметь одинаковое количество байтов или символов) или надежно «ограничена», (это означает, что будет некоторая комбинация символов или символов, которая всегда отделяет важные элементы данных). Если ни одно из них не применимо, может быть очень сложно разобрать данные в однополевые куски.

Вот пример основан на том, что у вас есть уже:

private static StringBuilder receiveBuffer = new StringBuilder(); 

private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 

    SerialPort spL = (SerialPort) sender; 
    int bufSize = 20; 
    Byte[] dataBuffer = new Byte[bufSize]; 
    Console.WriteLine("Data Received at"+DateTime.Now); 
    Console.WriteLine(spL.Read(dataBuffer, 0, bufSize)); 
    string s = System.Text.ASCIIEncoding.ASCII.GetString(dataBuffer); 
    //here's the difference; append what you have to the buffer, then check it front-to-back 
    //for known patterns indicating fields 
    receiveBuffer.Append(s); 

    var regex = new Regex(@"(ID:\d*? State:\w{2} Zip:\d{5} StreetType:\w*?)"); 
    Match match; 
    do{ 
     match = regex.Match(receiveBuffer.ToString()); 
     if(match.Success) 
     { 
      //"Process" the significant chunk of data 
      Console.WriteLine(match.Captures[0].Value); 
      //remove what we've processed from the StringBuilder. 
      receiveBuffer.Remove(match.Captures[0].Index, match.Captures[0].Length); 
     } 
    } while (match.Success); 
} 
+0

Спасибо за предложение. Я попытался добавить это в свой код. К сожалению, все, что я вижу, это количество байтов на кусок данных. – Rick

0

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

Вы можете попробовать вставить новую строку перед каждым ID: (например, заменить "ID:" на "\r\n\ID:"). Это будет по-прежнему сбой, если вы сначала получите StreetType:AveI, а затем "D:23566 St". Чтобы исправить это, вы могли бы просто поискать I после StreetType:, но это не так просто, как кажется - что, если вы видите 345 Stre, etTy, pe:RdI. Кроме того, что, если I действительный символ (tType:DRI, VE ID:23525)?

Я думаю, что следующий код должен правильно обрабатывать эти случаи. Обратите внимание, что я перешел из Console.WriteLine в Console.Write и вручную добавить новую строку, когда это необходимо:

private static var previousStringPerPort = new Dictionary<SerialPort,string>(); 
private static void Port_DataReceived(object sender, 
             SerialDataReceivedEventArgs e) 
{ 
    SerialPort spL = (SerialPort) sender; 
    int bufSize = 20; 
    Byte[] dataBuffer = new Byte[bufSize]; 
    Console.WriteLine("Data Received at"+DateTime.Now); 
    Console.WriteLine(spL.Read(dataBuffer, 0, bufSize)); 
    if (!previousStringPerPort.ContainsKey(spL)) 
     previousStringPerPort[spL] = ""; 
    string s = previousStringPerPort[spL] + 
       System.Text.ASCIIEncoding.ASCII.GetString(dataBuffer); 
    s = s.Replace("ID:",Environment.NewLine + "ID:"); 
    if (s.EndsWith("I")) 
    { 
     previousStringPerPort[spL] = "I"; 
     s = s.Remove(s.Length-1); 
    } 
    else if (s.EndsWith("ID")) 
    { 
     previousStringPerPort[spL] = "ID"; 
     s = s.Remove(s.Length - 2); 
    } 
    Console.Write(s); 
} 

Теперь остальная Единственная проблема заключается в том, что если последняя запись действительно конец в I или ID, он никогда не будет напечатан. Периодический тайм-аут для сброса предыдущей строки может исправить это, но он вводит (многие) больше собственных проблем.

+0

Спасибо, Джастин. Нет возврата каретки. Я попробую то, что вы рекомендовали выше. Это похоже на хороший способ определить начало и конец строки, что мне и нужно. – Rick

2

См наконечник # 1

http://blogs.msdn.com/b/bclteam/archive/2006/10/10/top-5-serialport-tips-_5b00_kim-hamilton_5d00_.aspx

При использовании SerialPort.Read (буфер, смещение, счетчик), где граф является число байтов, которые вы хотите прочитать, проверить возвращаемое значение, который сообщает о количестве фактически прочитанных байтов. Разработчики иногда принимают , что количество байтов/символов будет возвращено, когда Read закончит. Вот что такое Чтение на самом деле. Если на последовательном порту имеются байты, Чтение возвращает до подсчета байтов, но не будет блокировать оставшиеся байтов. Если на последовательном порту нет байтов, Read будет блокировать до тех пор, пока не будет доступен по крайней мере один байт на порту, до тех пор, пока не истекут миллисекунды ReadTimeout, и в это время будет выведено исключение времени ожидания 10.Чтобы исправить это в вашем коде, проверьте количество фактически прочитанных байтов и используйте это значение при обработке возвращаемых данных .

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

+0

Итак, основываясь на этом, вам нужно знать точное количество байтов в буфере для его отображения или обработки по желанию? Если да, то как бы реализовать это, когда каждый кусок данных имеет другой байт? – Rick

+0

В вашем примере кода метод Read возвращает количество прочитанных байтов, чтобы вы могли смотреть на них и действовать соответственно. Если вы ожидаете 20 и получите 10, вы можете продолжать читать и строить свои данные, пока не получите 20 байт. Если ваши данные не являются фиксированной длиной, вам придется продолжать смотреть, что вы получили до сих пор, чтобы решить, есть ли у вас полный фрагмент данных. – hatchet

+0

Обратите внимание, что метод Read принимает смещение. Это полезно только для этой проблемы. – hatchet

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