Разбор / форматирование данных с последовательного порта - C # - PullRequest
3 голосов
/ 29 февраля 2012

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

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 (), но мои данные не отображаются с помощью этой функции.Кто-нибудь знает какой-либо другой способ разбора и форматирования данных в одну строку?

Ответы [ 3 ]

4 голосов
/ 01 марта 2012

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

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

Затем при следующем вызове события DataReceived вы извлекаете "453 Sta" из последовательного буфера. Вы добавляете его к тому, что у вас уже есть, и получаете "ID:12453 Sta", а когда вы применяете регулярное выражение, вы получаете совпадение «ID: 12345». Вы передаете это в метод для дальнейшей обработки (возможно, вывод на консоль) и удаляете ту же строку в начале буфера, оставляя «Sta». При повторном сканировании вы не найдете ничего более интересного, поэтому вы оставляете то, что у вас есть, и цикл повторяется, пока данные продолжают поступать. Очевидно, вы будете тестировать больше шаблонов, чем просто шаблон идентификатора; Вы можете искать всю «строку», которую ожидаете получить, например, "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);
}
2 голосов
/ 29 февраля 2012

См. Совет № 1

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

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

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

0 голосов
/ 29 февраля 2012

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

Вы можете попытаться вставить новую строку перед каждым 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, она никогда не будет напечатана. Периодический тайм-аут для сброса предыдущей строки может исправить это, но он создает (много) больше собственных проблем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...