Search code examples
c#.netstringserial-port

How to correctly parse received serial data into lines?


I'm creating a program which communicates with a serial device which is constantly sending data. I'm reading data from device every 100ms (using a timer). I use port.ReadExisting() to receive all currently available data from the device then I try split it into lines, because I need to check some of the received data and the best way is to check lines. The problem occurs when device sends data which doesn't end with "\r\n" or '\n'.

In a perfect situation port.ReadExisting() returns: "sampletext\r\nsomesampletext\nsampletext\r\n

But a problem occurs when there's no CR or LF character at the end:

First time port.ReadExisting() returns this: "text\nsamp"

Second time port.ReadExisting() returns this: letext\r\ntext\r\n"

End result should look like this:

text
sampletext
text

But what I get looks like this:

text
samp
letext
text

My code:

This is the timer which runs every 100ms:

private void CommandTimer_Tick(object sender, EventArgs e)
{
    BackgroundWorker seriaDataWorker = new BackgroundWorker();
    seriaDataWorker.DoWork += (obj, p) => PrintSerialData();
    seriaDataWorker.RunWorkerAsync();
}

BackgroundWorker which gets called by the timer:

private void PrintSerialData()
    {
        try
        {
            if (RandomReboot)
            {
                RebootWatch.Start();
            }
            if (COMport.IsOpen)
            {
                if (COMport.BytesToRead != 0)
                {
                    SerialPrint(COMport.ReadExisting());
                }
            }
        }
        catch (System.IO.IOException SerialException)
        {
            return;
        }
        
    }

Function which parses received data into lines:

private void SerialPrint(string data)
    {
        using (var buffer = new StringReader(data))
        {
            string line = "";


            
            while((line = buffer.ReadLine()) != null)
            {


                if (CheckForAnsw)
                {
                    ReceivedCommandData = line;
                    if (ReceivedCommandData.Contains(AnswExpected))
                    {
                        ReceivedAnsw = true;
                        ReceivedLine = ReceivedCommandData;
                        ReceivedCommandData = "";
                    }
                }

                this.Invoke(new MethodInvoker(delegate
                {
                    AppendText(TextBox_System_Log, Color.Black, line + "\r\n");
                }
                ));
            }
        }
    }

I know that the problem is that buffer.ReadLine() treats remainder of the string which doesn't end with a CR or LF character as a seperate line but I don't know how to fix it.

I tried using port.ReadLine() in the past but it is way slower and causes problems for me when serial ports get disconnected etc.


Solution

  • I don't think there's an easy way to handle this with the StringReader. Instead, you can split the string yourself:

    private static string _buffer = string.Empty;
    
    private static void SerialPrint(string data)
    {
        // Append the new data to the leftover of the previous operation
        data = _buffer + data;
    
        int index = data.IndexOf('\n');
        int start = 0;
    
        while (index != -1)
        {
            var command = data.Substring(start, index - start);
            ProcessCommand(command.TrimEnd('\r'));
            start = index + 1;
            index = data.IndexOf('\n', start);
        }
    
        // Store the leftover in the buffer
        if (!data.EndsWith("\n"))
        {
            _buffer = data.Substring(start);
        }
        else
        {
            _buffer = string.Empty;
        }
    }
    
    private static void ProcessCommand(string command)
    {
        Console.WriteLine(command);
    }