首页 > 解决方案 > C# Windows 窗体 - 解析从串口接收到的字符串

问题描述

我正在使用 C# 开发一个 Windows 窗体应用程序,在该应用程序中我从串行端口接收数据,现在我有以下代码(这只是我的问题的相关代码):

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)     
{
      ReceivedData = serialPort1.ReadExisting();
      this.Invoke(new EventHandler(interp_string));       
}

private void interp_string(object sender, EventArgs e)      
{
      textReceive.Text += ReceivedData + "\n";
}

但现在我需要将接收到的数据解析成小字符串。ReceivedData 变量是具有以下格式的多个字符串的组合:“value time \n”,其中 value 从 0 到 1024 并且时间以秒为单位(并且始终增加)并且有 4 个小数位。我需要将 ReceivedData 变量拆分为单独的值,并将其绘制在图表中是相应的时间。考虑到使用ReadExisting,可能会发生一个字符串只被部分读取,其余的只会在下次触发DataReceived事件时读取,但我不介意丢失一点数据,并不重要。

我已经尝试使用 ReadLine 而不是 ReadExisting 并且我设法拆分每个字符串并绘制数据,但是,鉴于应用程序正在接收大量数据,每 1 毫秒一个字符串,应用程序无法跟上,即使它已经过了 10 秒,应用程序仍在从第 2 秒开始打印数据,我按下按钮停止接收数据,应用程序长时间保持打印值,我认为这些值是存储在接收缓冲区中的值。更改为 ReadExisting 是我发现实时读取和打印所有内容的唯一方法。

标签: c#winformssplitserial-portstring-parsing

解决方案


DataReceived事件可能会在字符串中间触发,您可能还没有收到整个消息。您需要知道要查找的内容,通常是换行符 (LF) 或回车符 (CR)。我StringBuilder用来构建我的字符串,下面是一个例子,其中 LF 意味着我得到了整个消息。我将字符附加到我的字符串,直到我知道我有整个消息。我快速清除缓冲区,因为您的下一个字符串可能会在您评估时进入。对于这个简单的示例,我只是调用一个函数来评估我的字符串,但您可能希望在此处使用委托。

StringBuilder sb = new StringBuilder();
char LF = (char)10;

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPort1.ReadExisting();

    foreach (char c in Data)
    {
        if (c == LF)
        {
            sb.Append(c);

            CurrentLine = sb.ToString();
            sb.Clear();

            //do something with your response 'CurrentLine'
            Eval_String(CurrentLine);
        }
        else
        {
            sb.Append(c);
        }
    }
}

获得完整信息后,您可以根据需要对其进行评估。我不确定您的数据是什么样的,但在我的示例中,我的消息以逗号分隔,因此我可以使用逗号作为分隔符将字符串拆分为字符串数组,并从消息中获取每个值。

public void Eval_String(string s)
{
    string[] eachParam;
    eachParam = s.Split(',');

    if (eachParam.Length == 5)
    {
        //do something
        Console.WriteLine(eachParam[2]);
    }
}

编辑:这是一个关于如何使用队列更新 gui 的示例。

StringBuilder sb = new StringBuilder();
char LF = (char)10;
Queue<string> q = new Queue<string>();

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPort1.ReadExisting();

    foreach (char c in Data)
    {
        if (c == LF)
        {
            sb.Append(c);

            CurrentLine = sb.ToString();
            sb.Clear();

            q.Enqueue(currentLine);
        }
        else
        {
            sb.Append(c);
        }
    }
}

private void backgroundWorkerQ_DoWork(object sender, DoWorkEventArgs e)
{
    while (true)
    {
        if (backgroundWorkerQ.CancellationPending)
            break;

        if (q.Count > 0)
        {
            richTextBoxTerminal.AppendText(q.Dequeue());
        }

        Thread.Sleep(10);
    }
}

如果您从未使用过后台工作人员,则只需创建一个新的后台工作人员对象,设置supportsCancellation为 true,然后为其事件添加一个DoWork事件。然后,当您启动表单时,您可以告诉它.RunWorkerAsync()

if (!backgroundWorkerQ.IsBusy)
{
    backgroundWorkerQ.RunWorkerAsync();
}

请参阅:https ://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view= netframework-4.7.2 另请注意,backgroundworker 在它自己的线程中,您很可能需要 BeginInvoke它来更新你的文本框。


推荐阅读