rss· 投稿· 设为首页· 加入收藏· 繁體版
当前位置: 火魔网 » 程序开发 » C#

C#编写的通用串口通信类,支持异步通信

using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using Microsoft.Win32;
namespace CLCom32
{
    internal class COM32
    {

        #region---------------------内部参数---------------------
        /// <summary>
        /// 波特率
        /// </summary>
        private string baudRate;
        /// <summary>
        /// 数据位
        /// </summary>
        private string dataBits;
        /// <summary>
        /// 停止位
        /// </summary>
        private string stopBits;
        /// <summary>
        /// 校验位
        /// </summary>
        private string checkBits;
        /// <summary>
        /// 端口号
        /// </summary>
        private string portNum;
        /// <summary>
        /// 停止缓冲接收
        /// </summary>
        private bool stopReceive = false;
        /// <summary>
        /// 串口线程互斥
        /// </summary>
        private object ThreadObject = new object();
        /// <summary>
        /// 串口控制对象
        /// </summary>
        private SerialPort spCom;
        /// <summary>
        /// 返回数据缓冲帧队列
        /// </summary>
        private Queue<byte[]> returnBuff;
        /// <summary>
        /// 缓冲帧队列互斥
        /// </summary>
        private object QueueObject = new object();
        /// <summary>
        /// 自定义拆帧接口对象
        /// </summary>
        private IFrameAnalysis fAnalysis = null;

        #endregion

        /// <summary>
        /// 读取超时时间
        /// </summary>
        private int readTimeOut = 500;

        /// <summary>
        /// 是否异步读取
        /// </summary>
        private bool isAsynchronousRead = false;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="Settings">通信参数1200,e,8,1</param>
        /// <param name="ComNum">串口号</param>
        public COM32(string Settings, int ComNum)
        {
            string[] Tmp = Settings.Split(',');
            if (Tmp.Length != 4)
            {
                baudRate = "1200";
                checkBits = "n";
                dataBits = "8";
                stopBits = "1";
            }
            else
            {
                baudRate = Tmp[0];
                checkBits = Tmp[1];
                dataBits = Tmp[2];
                stopBits = Tmp[3];
            }
            portNum = string.Format("COM{0}", ComNum);

            returnBuff = new Queue<byte[]>();

            spCom = new SerialPort();
        }
        ~COM32()
        {
            if (spCom.IsOpen)
            {
                spCom.Close();
            }
            spCom.Dispose();

            returnBuff = null;
        }

        /// <summary>
        /// 是否异步读取返回数据
        /// </summary>
        public bool IsAsynchronousRead
        {
            get { return isAsynchronousRead; }
            set { isAsynchronousRead = value; }
        }
        /// <summary>
        /// 读取和设置下位机返回数据的超时时间(默认500ms)
        /// </summary>
        public int ReadTimeOut
        {
            get { return readTimeOut; }
            set { readTimeOut = value; }
        }

        /// <summary>
        /// 返回缓冲区帧数量
        /// </summary>
        public int frameCount
        {
            get { return returnBuff.Count; }
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            lock (ThreadObject)
            {
                try
                {
                    if (spCom.IsOpen) return true;

                    spCom.BaudRate = int.Parse(baudRate);
                    spCom.StopBits = (StopBits)int.Parse(stopBits);
                    spCom.DataBits = int.Parse(dataBits);
                    spCom.Parity = checkBits.ToLower() == "n" ? Parity.None : checkBits.ToLower() == "e" ? Parity.Even : Parity.Mark;
                    spCom.PortName = portNum;
                    spCom.DtrEnable = true;
                    spCom.Encoding = Encoding.GetEncoding("iso-8859-1");            //设置编码类型为8859-1,这样可以用字符形式读取0-255的ASCII码
                    spCom.Open();
                    return true;
                }
                catch (Exception e)
                {
                    if (spCom.IsOpen)
                    {
                        spCom.Close();
                    }
                    throw e;
                }
            }
        }
        /// <summary>
        /// 关闭串口
        /// </summary>
        public void Close()
        {
            stopReceive = true;      //停止缓冲打开
            returnBuff.Clear();         //清理帧BUFF
            lock (ThreadObject)
            {
                if (spCom.IsOpen) spCom.Close();
            }
        }

        /// <summary>
        /// 等待接收数据
        /// </summary>
        private void WaiteReceiveData()
        {
            if (!spCom.IsOpen) return;
            System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(ThreadReceive), null);             //开启线程循环取帧
        }
        /// <summary>
        /// 开启一个线程接收数据
        /// </summary>
        /// <param name="Obj"></param>
        private void ThreadReceive(Object Obj)
        {
            List<byte> tmplist = new List<byte>();
            lock (ThreadObject)
            {
                while (!stopReceive)          //如果没有主动停止就一直读
                {
                    if (spCom.BytesToRead > 0)
                    {
                        spCom.ReadTimeout = readTimeOut;       //设置阅读超时时间,该超时时间只能作为READLINE的超时

                        lock (QueueObject)
                        {
                            if (fAnalysis == null)
                            {
                                try
                                {
                                    returnBuff.Enqueue(spCom.Encoding.GetBytes(string.Format("{0}{1}", spCom.ReadLine(), spCom.NewLine)));            //读取缓冲区中断针符之前的数据
                                }
                                catch
                                {
                                    //byte[] tmp = new byte[spCom.BytesToRead];
                                    //spCom.Read(tmp, 0, tmp.Length);
                                    //returnBuff.Enqueue(tmp);
                                    returnBuff.Enqueue(spCom.Encoding.GetBytes(spCom.ReadExisting()));     //读取当前缓冲区可用的所有字符
                                }
                            }
                            else
                            {
                                tmplist.AddRange(spCom.Encoding.GetBytes(spCom.ReadExisting()));
                                int vstart, vLen;
                                if (fAnalysis.FrameAnalysis(tmplist.ToArray(), out vstart, out vLen))
                                {
                                    try
                                    {
                                        returnBuff.Enqueue(tmplist.GetRange(vstart, vLen).ToArray());
                                    }
                                    catch
                                    {
                                        throw new ExecutionEngineException("拆帧接口长度参数返回越界");
                                    }
                                    tmplist.RemoveRange(0, vstart + vLen);
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 发送数据\接收数据
        /// </summary>
        /// <param name="vData">发送的数据和读取返回的数据)</param>
        /// <returns></returns>
        public bool SendData(ref byte[] vData, bool IsReturn)
        {
            stopReceive = true;           //停止线程
            if (!spCom.IsOpen)            //检查串口是否打开
            {
                bool isOpen = this.Open();
                if (!isOpen) return false;
            }
            lock (ThreadObject)
            {
                spCom.DiscardInBuffer();              //清理缓冲区BUFF
                spCom.DiscardOutBuffer();
            }
            stopReceive = false;                          //关闭线程循环读取标志

            returnBuff.Clear();                             //清理帧BUFF

            spCom.Write(vData, 0, vData.Length);       //发送数据

            System.Threading.Thread.Sleep(100);         //发送后停100MS  

            if (!IsReturn) { this.Close(); return true; }          //如果不需要返回数据,就直接关闭串口,返回OK

            this.WaiteReceiveData();                            //如果要返回数据,就开启线程读取数据

            if (!isAsynchronousRead)      //如果不是异步读取数据最后会关闭串口,同时会更新vdata,如果是异步读取数据,则不会更新vdata
            {
                vData = new byte[0];

                DateTime tmptime1 = DateTime.Now;

                while (TimeSub(DateTime.Now, tmptime1) < readTimeOut * 2)                 //等待超时时间的2倍时间
                {
                    if (returnBuff.Count > 0)       //如果帧缓冲区有数据
                    {
                        vData = returnBuff.Dequeue();     //获取一帧数据
                        break;
                    }
                }
                this.Close();               //获取完毕后关闭串口
                return vData.Length > 0 ? true : false;
            }

            return true;               //如果是异步则不需要关闭串口,直接返回OK
        }
        /// <summary>
        /// 发送数据\接收数据
        /// </summary>
        /// <param name="vData">发送的数据\发送完毕后会填充返回的数据(如果isAsynchronousRead为真,则vData不返回数据)</param>
        /// <param name="stopto">缓冲区中读到的停止字符(当读到该字符时停止读取并返回)</param>
        /// <returns></returns>
        public bool SendData(ref byte[] vData, byte stopto)
        {
            spCom.NewLine = Convert.ToChar(stopto).ToString();
            return SendData(ref vData, true);
        }
        /// <summary>
        /// 发送数据\接收数据
        /// </summary>
        /// <param name="vData">发送的数据\发送完毕后会填充返回的数据(如果isAsynchronousRead为真,则vData不返回数据)</param>
        /// <param name="stopto">缓冲区中读到的停止字符(当读到该字符时停止读取并返回)</param>
        /// <returns></returns>
        public bool SendData(ref byte[] vData, byte[] stoptos)
        {
            string newLine = "";
            foreach (byte b in stoptos)
            {
                newLine = string.Format("{0}{1}", newLine, Convert.ToChar(b));
            }
            spCom.NewLine = newLine;
            return SendData(ref vData, true);
        }

        /// <summary>
        ///  发送数据(有响应数据返回)
        /// </summary>
        /// <param name="vData">发送的数据帧(如果isAsynchronousRead为真,则vData不返回数据)</param>
        /// <returns></returns>
        public bool SendData(ref byte[] vData)
        {
            return SendData(ref vData, true);
        }
        /// <summary>
        /// 发送数据(有响应数据返回),并按照自定义方式拆帧
        /// </summary>
        /// <param name="vData">发送的数据帧(如果isAsynchronousRead为真,则vData不返回数据)</param>
        /// <param name="Analysis">自定义拆帧方式类</param>
        /// <returns></returns>
        public bool SendData(ref byte[] vData, IFrameAnalysis Analysis)
        {
            if (Analysis != null) fAnalysis = Analysis;
            return SendData(ref vData, true);
        }

        /// <summary>
        /// 读缓冲区的数据帧
        /// </summary>
        /// <returns></returns>
        public byte[] Read()
        {
            lock (QueueObject)
            {
                if (returnBuff.Count > 0)
                {
                    return returnBuff.Dequeue();     //出栈一帧数据
                }
                else
                {
                    return new byte[0];           //如果BUFF为空,则返回0长度字节数组
                }
            }
        }

        /// <summary>
        /// 时间差计算
        /// </summary>
        /// <param name="Time1">参与计算的时间1</param>
        /// <param name="Time2">参与计算的时间2</param>
        /// <returns></returns>
        private long TimeSub(DateTime Time1, DateTime Time2)
        {
            TimeSpan tsSub = Time1.Subtract(Time2);
            return tsSub.Hours * 60 * 60 * 1000 + tsSub.Minutes * 60 * 1000 + tsSub.Seconds * 1000 + tsSub.Milliseconds;
        }

        /// <summary>
        /// 获取可用串口列表
        /// </summary>
        /// <returns></returns>
        public static System.Collections.IEnumerable ComList()
        {
            RegistryKey rKey = Registry.LocalMachine.OpenSubKey("Hardware\\DeviceMap\\SerialComm");
            if (rKey != null)
            {
                string[] sSubKeys = rKey.GetValueNames();
                foreach (string sName in sSubKeys)
                {
                    yield return rKey.GetValue(sName);
                }
            }
        }
    }
    /// <summary>
    /// 帧自定义解析长度接口
    /// </summary>
    public interface IFrameAnalysis
    {
        /// <summary>
        ///  解析帧长度
        /// </summary>
        /// <param name="vData">数据字节数组</param>
        /// <param name="start">帧开始位置</param>
        /// <param name="end">帧长度</param>
        /// <returns>里面存在一帧返回真,不够一帧返回FLASE</returns>
        bool FrameAnalysis(byte[] vData,out int start,out int len);
    }

顶一下
(4)
踩一下
(0)