以多线程和缓存的方式处理串口的发送和接收动作

转载:https://www.cnblogs.com/showlie/articles/2367154.html

一、现象
不管如何设置ReceivedBytesThreshold的值,DataReceived接收到的数据都是比较混乱,不是一个完整的应答数据。
二、原因
1、上位机下发的命令比较密集,以200ms周期发送实时状态轮询命令。
2、在状态实时轮询命令中间有操作命令插入。
2、不同的命令,接收的应答格式也不同。
三、分析
不同的命令有不同的应答数据,但是不同的应答数据中都具有唯一的结束符,可以根据结束符来作为多个应答数据的分割标志。因此可以把应答数据进行缓存,然后另起一个线程对缓存的应答数据进行分析处理。
因此系统具有:
1、命令队列用来插入操作命令,空闲时处理状态实时轮询命令。
2、命令发送线程,以200ms周期性的发送队列中的命令。
3、应答集合,用来缓存DataReceived接收数据。
4、应答处理线程,对应答集合中的数据进行集中处理。

四、代码片段
1.1、定义

/// <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.开始、停止线程

 /// <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.发送线程

/// <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(200);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

4.接收和处理

/// <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(this, new 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(this, new ShelvesDataReceivedEventArgs(this.realtime));
                                        }
                                        break;
                                    }
                            }
                        }
                    }
                }
                Monitor.Exit(this.responses);
                Thread.Sleep(200);
            }

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

总结:这种方法挺好,我在项目中采用他的 接收数据辅助线程 private void SerialPortDataReceived(object sender, EventArgs e) 和处理数据线程分开。
接收线程只管接收,处理线程进行数据处理。两线程之间通过list 列表作为队列缓冲区,接收线程往list集合尾部添加数据,数据处理线程从list集合头部取出数据。采用锁来实现两线程的访问list同步。
好处:
1.这样处理速度更快。
2.接收数据辅助线程来一个字节触发一次,现在这种分开后,处理线程就不会频繁的处理断截的数据(因为list缓冲区缓存了,满足一帧数据才处理)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值