Обработка текста с серийной строки в C # - PullRequest
6 голосов
/ 16 августа 2011

У меня есть встроенное устройство под управлением Linux, с которым я могу общаться через RS232. Я пытаюсь программно управлять им из существующего приложения .NET, поэтому я дурачился с классом System.IO.Ports.SerialPort. Я сейчас читаю данные с порта, используя обработчик событий SerialPortDataRecieved и проверяю количество байтов для чтения, затем передаю byte[] такого размера, ТО затем помещаю byte[] в Queue<byte[]> для дальнейшей обработки, вот так:

public class SerialTalk{
SerialPort s;    
Queue<byte[]> RecQueue=new Queue<byte[]>;
//...bunch of irrelevant stuff here (ctors etc)
   private void DataReceivedHandler(Object sender, SerialDataReceivedEventArgs e)
   {
        byte[] recv;
        lock (s_lock)
        {
            int bytes = s.BytesToRead;
            recv = new byte[bytes];
            s.Read(recv, 0, bytes);
        }

        Console.Write(recv);
        lock (RecQueue)
        {
            RecQueue.Enqueue(recv);
        }
   }
}

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

То, что я ищу, - это простой, производительный, многопоточный способ перехода от фрагмента строки byte[] к string, который организован аккуратно в соответствии со строками. Одна из моих идей заключалась в том, чтобы создать другой поток, бесконечно зацикливающийся, который бы вытеснил byte[] с из очереди, превратил их в string с, добавил их к StringBuilder, а затем разделил string, возвращенный из StringBuilder.ToString() вдоль newlines, взять строку LAST и использовать ее для создания нового StringBuilder в следующий раз, затем поместить все остальные строки в Queue<string> - таким образом, фрагменты будут собраны вместе ... ... но это не сработает в самом важном случае - когда я жду, когда найду приглашение оболочки, так как эта строка никогда не попадет во вторую очередь.

Идеи

Ответы [ 2 ]

4 голосов
/ 16 августа 2011

Вы можете сделать небольшой конечный автомат.Для каждого полученного байта, если он не является новой строкой, добавьте его к данным 'current string'.

Если это новая строка, запустите 'new-data';event.

Если вы обнаружите, что текущая строка равна ожидаемой подсказке оболочки, запустите событие new-data.

Выполните эту обработку для каждого байта, полученного вочередь.Это, конечно, не останавливает чтение всех доступных байтов с порта.

0 голосов
/ 16 августа 2011

Я бы просто буферизовал символы в DataReceivedHandler до тех пор, пока вы не обнаружите новую строку, а затем поставил в очередь символы перед новой строкой (\ r \ n) в виде строки и сохранял оставшиеся символы в буфере до следующей новой строки. Обычно для ситуаций \ r \ n я всегда чищу \ r и интерпретирую только \ n.

Вот быстрый мод к вашему источнику для иллюстрации.

public class SerialTalk{
SerialPort s;    
Queue<string> RecQueue=new Queue<string>();
//...bunch of irrelevant stuff here (ctors etc)
string innerBuffer = ""; //a buffer we keep in between DataReceiveHandlers for the portion of text not newlined yet
private void DataReceivedHandler(Object sender, object e)
{
    byte[] recv;

    lock (s_lock)
    {
        int bytes = s.BytesToRead;
        recv = new byte[bytes];
        s.Read(recv, 0, bytes);
    }
    innerBuffer += System.Text.Encoding.GetEncoding("utf-8").GetString(recv).Replace("\r\n","\n"); //could be enhanced using stringbuilder.
    string[] lines = innerBuffer.Split('\n'); 
    innerBuffer = lines[lines.Length-1];  //always keep the portion after the last newline in the buffer
    Console.Write(recv);
    lock (RecQueue)
    {
        for(int x=0;x<innerBuffer.Length-2;x++) //-2 here instead of -1 as you don't want to send the remainder of the inner buffer until the next newline
            RecQueue.Enqueue(lines[x]);
    }
}
}

* следует отметить, что преобразование в utf-8 из вашего байтового массива может вызвать дополнительные проблемы. Если вы все еще хотите отправить его как байт [] в очередь, вы все равно можете хранить буфер локально, как это делает строка innerBuffer, но метки выделения \ r \ n не будут такими же тривиальными для поиска, как Split, хотя все же ничего страшного.

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