SerialPort.DataReceived之“管收不管埋”

通过把SerialPort进行封装,以多线程和缓存的方式处理串口的发送和接收动作。

一、现象

    不管如何设置ReceivedBytesThreshold的值,DataReceived接收到的数据都是比较混乱,不是一个完整的应答数据。

二、原因

1、上位机下发的命令比较密集,以200ms周期发送实时状态轮询命令。

2、在状态实时轮询命令中间有操作命令插入。

2、不同的命令,接收的应答格式也不同。

三、分析

    不同的命令有不同的应答数据,但是不同的应答数据中都具有唯一的结束符,可以根据结束符来作为多个应答数据的分割标志。因此可以把应答数据进行缓存,然后另起一个线程对缓存的应答数据进行分析处理。

因此系统具有:

1、命令队列用来插入操作命令,空闲时处理状态实时轮询命令。

2、命令发送线程,以200ms周期性的发送队列中的命令。

3、应答集合,用来缓存DataReceived接收数据。

4、应答处理线程,对应答集合中的数据进行集中处理。

四、代码片段

1、定义

View Code
复制代码
        ///   <summary>
        
///  请求命令队列
        
///   </summary>
         private Queue<Request> requests;
         ///   <summary>
        
///  应答数据集合
        
///   </summary>
         private List< byte> responses;
         ///   <summary>
        
///  发送线程同步信号
        
///   </summary>
         private ManualResetEvent sendWaiter;
         ///   <summary>
        
///  应答数据处理线程同步信号
        
///   </summary>
         private ManualResetEvent receiveWaiter;

            this.requests =  new Queue<Request>();
             this.responses =  new List< byte>();
             this.sendWaiter =  new ManualResetEvent( false);
             this.receiveWaiter =  new ManualResetEvent( false);
            //命令发送线程
            ThreadPool.QueueUserWorkItem( new WaitCallback(Send));
            //应答处理线程
            ThreadPool.QueueUserWorkItem( new WaitCallback(Received));
复制代码

2、开始、停止线程

View Code
复制代码
         ///   <summary>
        
///  启动服务
        
///   </summary>
         public  void Start()
        {
             try
            {
                 if (! this.serialPort1.IsOpen)
                {
                     this.serialPort1.Open();
                }
                 this.requests.Clear();
                 // 插入初始化命令
                 this.Push( new Request());
                 this.sendWaiter.Set();
                 this.receiveWaiter.Set();
            }
             catch (Exception ex)
            {
                 throw ex;
            }
        }

         ///   <summary>
        
///  停止服务
        
///   </summary>
         public  void Stop()
        {
             this.sendWaiter.Reset();
             this.receiveWaiter.Reset();
             if ( this.serialPort1.IsOpen)
            {
                 this.serialPort1.Close();
            }
             this.requests.Clear();
             this.responses.Clear();
        }
复制代码

3、发送线程

View Code
复制代码
         ///   <summary>
        
///  插入操作命令
        
///   </summary>
        
///   <param name="request"></param>
         public  void Push(Request request)
        {
            Monitor.Enter( this.requests);
             this.requests.Enqueue(request);
            Monitor.Exit( this.requests);
        }

         ///   <summary>
        
///  发送
        
///   </summary>
        
///   <param name="obj"></param>
         private  void Send( object obj)
        {
             while ( true)
            {
                 try
                {
                     this.sendWaiter.WaitOne();
                    Monitor.Enter( this.requests);
                    Request request =  null;
                     if ( this.requests.Count >  0)
                    {
                        request =  this.requests.Dequeue();
                    }
                     else  if ( this.Polling)
                    {
                         this.send++;
                        request =  new Request( this.config.Zone);
                    }
                     if (request !=  null)
                    {
                         byte[] buffer = request.ToBytes();
                         this.serialPort1.DiscardInBuffer();
                         this.serialPort1.Write(buffer,  0, buffer.Length);
                    }
                    Monitor.Exit( this.requests);
                    Thread.Sleep(2 00);
                }
                 catch (Exception ex)
                {
                     throw ex;
                }
            }
        }
复制代码

4、接收和处理

View Code
复制代码
         ///   <summary>
        
///  串口接收
        
///   </summary>
        
///   <param name="sender"></param>
        
///   <param name="e"></param>
         private  void SerialPortDataReceived( object sender, EventArgs e)
        {
             try
            {
                 if ( this.serialPort1.BytesToRead >  0)
                {
                     byte[] buffer =  new  byte[ this.serialPort1.BytesToRead];
                     int readCount =  this.serialPort1.Read(buffer,  0, buffer.Length);
                    Monitor.Enter( this.responses);
                     this.responses.AddRange(buffer);
                    Monitor.Exit( this.responses);
                }
            }
             catch (Exception ex)
            {
                 throw ex;
            }
        }

         ///   <summary>
        
///  缓存处理
        
///   </summary>
        
///   <param name="obj"></param>
         private  void Received( object obj)
        {
             while ( true)
            {
                 this.receiveWaiter.WaitOne();
                Monitor.Enter( this.responses);
                 if ( this.responses.Count >  0)
                {
                     int endIndex =  this.responses.IndexOf(Request.SYMBOL_END);
                     if (endIndex >=  0)
                    {
                         byte[] buffer =  this.responses.GetRange( 0, endIndex +  1).ToArray();
                         this.responses.RemoveRange( 0, endIndex +  1);
                         if (buffer.Length >  3)
                        {
                             int cmd = buffer[ 1];
                             switch (cmd)
                            {
                                 case Request.CMD_QUERY_STATE_SYSTEM:
                                 case Request.CMD_QUERY_UNKNOWN_ZONE:
                                    {
                                         this.config.Update(buffer,  0, buffer.Length);
                                         this.Polling =  true;
                                         if ( this.ShelvesInitialized !=  null)
                                        {
                                             this.ShelvesInitialized( thisnew ShelvesInitializedEventArgs( this.config));
                                        }
                                         break;
                                    }
                                 case Request.CMD_QUERY_STATE_LINE:
                                    {
                                         this.received++;
                                         this.realtime = Realtime.Parse(buffer,  0, buffer.Length);
                                         if ( this.ShelvesDataReceived !=  null &&  this.realtime !=  null)
                                        {
                                             this.ShelvesDataReceived( thisnew ShelvesDataReceivedEventArgs( this.realtime));
                                        }
                                         break;
                                    }
                            }
                        }
                    }
                }
                Monitor.Exit( this.responses);
                Thread.Sleep( 200);
            }
复制代码

5、以事件的形式在主界面实时显示处理后的应答数据

View Code
复制代码
     ///   <summary>
    
///  数据接收事件参数
    
///   </summary>
     public  class ShelvesDataReceivedEventArgs : EventArgs
    {
         private Realtime realtime;

         public ShelvesDataReceivedEventArgs(Realtime realtime)
        {
             this.realtime = realtime;
        }

         public Realtime Realtime
        {
             get
            {
                 return  this.realtime;
            }
        }
    }

     ///   <summary>
    
///  密集架数据接收事件处理
    
///   </summary>
    
///   <param name="sender"></param>
    
///   <param name="e"></param>
     public  delegate  void ShelvesDataReceivedEventHandler( object sender, ShelvesDataReceivedEventArgs e);
 
        //以委托的方式显示在主界面
        private delegate void UpdateDelegate(Realtime realtime);
        //事件处理函数

        private void ShelvesDataReceived(object sender, ShelvesDataReceivedEventArgs e)
        {
            this.Invoke(new UpdateDelegate(this.Update), new object[] { e.Realtime });
        }

        private void Update(Realtime realtime)
        {
            this.lbl_send.Text = this.shelves.send.ToString();
            this.lbl_received.Text = this.shelves.received.ToString();
            this.txt_lowLine.Text = realtime.ActionLineOfLowZone.ToString();
            this.txt_lowZoneState.Text = Request.GetStateMessage(realtime.LowZoneState);
            this.txt_highLine.Text = realtime.ActionLineOfHighZone.ToString();
            this.txt_highZoneState.Text = Request.GetStateMessage(realtime.HighZoneState);
            this.txt_temperature.Text = realtime.Temperature.ToString();
            this.txt_humidity.Text = realtime.Humidity.ToString();
        }

 

五、结论

一般情况下,当下位机高速发送应答数据时,串口接收到的数据不会是一个完整应答数据,而是多个应答数据的混合集,因此当你以单一应答数据来解析收到的数据时往往会发现应答数据格式不正确,在界面上的表现就是“没有收到数据”。

另外把收到的原始字节数组解析为程序能读懂的数据也是一项费时费力的事情,因此会出现“高速收,低速埋”的矛盾。但是,如果只让串口执行“收”,而辅助线程执行“埋”,那么就有效的解决了这个矛盾,即使下位机发的速度再高,系统也能抗得住。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值