首页 > 解决方案 > 从串口读取数据正在丢失包

问题描述

您好,我创建了一个从记分牌读取串行数据的程序,然后根据字符串,程序将数据分成表格上的不同框,然后分成不同的 txt 文件。这样做的目的是使用这些 txt 文件进行直播在篮球比赛中。这是我第一次使用串行数据,我不是一个很有经验的程序员。

我的问题,正如这篇文章的标题所暗示的那样,我时不时会无缘无故丢失一些包裹。这是随机发生的,例如在 10 秒内我可以 1 个包裹,而下一个没有或 4 个。

private void ReadData() 
        {
            Thread MyThread = null;
           
            {
                try
                {
                     ThreadStart ThreadMethod = new ThreadStart(ReadFromPort);
                     MyThread = new Thread(ThreadMethod);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Failed to create thread! " + e.Message);
                    return;
                }
                try
                {
                    MyThread.Start();

                }
                catch (Exception e)
                {
                    Console.WriteLine("The thread failed to start! " + e.Message);
                }
            }
        }

        //Recieves data and write them on textbox (optionally on a txt)
        private void ReadFromPort()
        {
            
            while (Receiver == true)
            {
                try
                {
                    
                    int count = ComPort.BytesToRead;
                    System.Windows.Forms.Application.DoEvents();
                    
                   
                    byte[] data = new byte[count];
                    ComPort.Read(data, 0, data.Length);           
                    currentMessage = Combine(currentMessage, data);
                    ReceivedData = (BitConverter.ToString(data)); 
                                   
                    if (count > 0)
                    {
                        if (chBoxUpdate.Checked)
                        {
                            DataType = count;
                            tempData = ReceivedData;
                            this.Invoke(new EventHandler(DisplayText));
                            if (chboxTxt.Checked)
                            {
                                this.Invoke(new EventHandler(ExportData));
                            }
                        }
                        else if (chBoxPrevious.Checked)
                        {
                            DataType = count;
                            tempData = ReceivedData;
                            this.Invoke(new EventHandler(ClearData));
                            this.Invoke(new EventHandler(DisplayText));
                            if (chboxTxt.Checked)
                            {
                                this.Invoke(new EventHandler(ExportData));
                            }
                        }
                        
                    }
                }
                catch (Exception e)
                {

                }
            }
        }

        //Displays Text
        private void DisplayText(object sender, EventArgs e)
        {
            string temp;
            Console.WriteLine(tempData+ " (" + tempData.Length.ToString() + ")");
           
            try
            {
                if (tempData.Length == 38)// && ReceivedData.Substring(12, 5) == "03-02")
                {
                    if (tempData.Substring(12, 5) == "03-02")
                    {
                        DataText.AppendText(tempData.Substring(24, 8));
                        DataText.AppendText("\n");
                        Blink.Text = "Reading...";
                        timer1.Start();
                        timer1.Enabled = true;
                    }
                }
                else
                if (tempData.Length == 35)   
                {
                    if (tempData.Substring(12, 5) == "45-02")
                    {
                        AttackTime.AppendText(tempData.Substring(24, 5));
                        Blink.Text = "Reading...";
                        AttackTime.AppendText("\n");
                        timer1.Start();
                        timer1.Enabled = true;
                    }
                }
                else
                if (tempData.Length == 29)  
                {
                    if (tempData.Substring(12, 5) == "03-36")  
                    {
                        HomeScore.AppendText(tempData.Substring(21, 2));
                        Blink.Text = "Reading...";
                        HomeScore.AppendText("\n");
                        timer1.Start();
                        timer1.Enabled = true;
                    }
                    else
                    if (tempData.Substring(12, 5) == "03-46")  
                    {
                        AwayScore.AppendText(tempData.Substring(21, 2));
                        Blink.Text = "Reading...";
                        AwayScore.AppendText("\n");
                        timer1.Start();
                        timer1.Enabled = true;
                    }
                }
            }
            catch (ArgumentOutOfRangeException)
            {

            }
        }


请记住,这里的 tempData 和 ReceivedData 是相同的,并且由于程序现在出现,没有任何特别的理由来设置 tempData=ReceivedData。这是我在开始时使用的不同的旧代码的一部分,我从未更改过,但它没有不影响程序。

起初我使用一个线程来启用 ReadFromPort 然后如果程序发现有可用数据在线显示它们并使用 DisplayText 并使用 ExportData 将数据导出到 txt。我认为问题出在某个地方但是因为我不是很有经验,所以我不知道在哪里。

关于如何改进我的代码有什么建议吗?如果您提供更多详细信息或信息,我可以提供。

标签: c#serial-port

解决方案


您正在使用Invoke,这会阻塞调用者线程,直到事件被处理,并且由于您正在更新 UI,这可能需要一些时间。这可能会导致一些缓冲区溢出和数据被丢弃。所以你应该在阅读线程上做尽可能少的工作。

有几种选择

  1. 将数据放在concurrentQueue中,并让 UI 线程有一个计时器,该计时器会定期触发从队列中读取并更新 UI 的方法。
  2. 将数据放在concurrentQueue上,包装在阻塞集合中,让另一个后台线程使用GetConsumingEnumerable遍历阻塞集合,并在完成后将结果发布到主线程。如果要进行大量处理,这将是合适的。
  3. 使用BeginInvoke将接收到的数据发布到主线程,这不会等待调用完成,但可能会比以前的替代方案具有稍高的开销。我建议不要从后台线程访问任何 UI 属性,因为默认情况下,UI 类的任何属性都不是线程安全的。如果你只是阅读,即使你可能侥幸逃脱,我也不会冒险。

推荐阅读