zoukankan      html  css  js  c++  java
  • c#写的串口通讯

    窗口数据发送和接受的类 新建 PortControl
    程序代码
    using System;
    using System.IO.Ports;
    using System.Windows.Forms;
    namespace SPC
    {
        /// <summary>
        /// 串口控制  
        /// </summary>
        public class PortControl
        {
            /// <summary>
            /// 定义一个串口类
            /// </summary>
            private   SerialPort  MyPort;
            /// <summary>
            /// 初始化类
            /// </summary>
            public PortControl()
            {
                MyPort = new SerialPort();
                setup();
            }
            /// <summary>
            /// 直接使用给某个串口
            /// </summary>
            /// <param name="port">COM1,COM2。。。。。。</param>
            public  PortControl(string port)
            {
                _portname = port;
                MyPort = new SerialPort(_portname);
                setup();
            }
            private void setup()
            {
                MyPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
            }
            public bool  Open()
            {
                try
                {
                    if (MyPort.IsOpen != true) { MyPort.Open(); };
                    return true ;
                }
                catch
                {
                    return false;
                }
            }
            public void Open(string port)
            {
                MyPort.PortName = _portname;
                MyPort.Open();
            }
            public void Close()
            {
                MyPort.Close();
            }
            private string  _portname;
            /// <summary>
            /// 端口名称
            /// </summary>
            /// <example>COM1 COM2</example>
            public string  PortName
            {
                get { return _portname; }
                set { _portname = value; }
            }
            public Exception LastException;
            /// <summary>
            /// 最后收到的信息
            /// </summary>
            public string LastReceived
            {
                get { return hex_return; }
            }
            public bool Received = false;//是否收到了信息。 发送完数据,如果需要车检器返回数据,该属性设置为false;
                                  //当收到消息,并解析完成后。这个设置为true;
            string hex_return = "";//收到数据后把十六进制存到这个字符串中。
            byte[] bin_return = new byte[] { };
            double _timeout = 0.8;
            public double TimeOut
            {
                get { return _timeout; }
                set { _timeout = value; }
            }
            /// <summary>
            /// 向端口中发送命令。
            /// </summary>
            /// <param name="cmdstring">"0A 46 0B 31 30 30 32 35"</param>
            /// <example>  Send("0A 46 0B 31 30 30 32 35")</example>
            /// <remarks>超时设置为5秒,一般来说,端口速度不会延时超过1秒。</remarks>
            /// <summary>
            /// 向端口中发送命令。
            /// </summary>
            /// <param name="cmdstring">"0A 46 0B 31 30 30 32 35"</param>
            /// <param name="timeout">指定超时,按秒计算。端口速度一般不会迟延超过1秒。</param>
            /// <example>  Send("0A 46 0B 31 30 30 32 35")</example>
            public string Send(string cmdstring)
            {
                byte[] buff = Funs.HexStringToBinary(cmdstring.Trim());//转换命令字符串为字节数组
                hex_return = "";//十六进制返回设置为空。
                bin_return = new byte[] { };//重新初始化返回字节数组。
                Received = false;//设置标识为没有接受到信息
                MyPort.Write(buff, 0, buff.Length);//写入串口命令。
                double tmpx = DateTime.Now.TimeOfDay.TotalSeconds;//记录下当前总计秒数
                    do
                    {
                        Application.DoEvents();//释放CPU
                    } while ((Received != true) && (DateTime.Now.TimeOfDay.TotalSeconds - tmpx < _timeout));
                    if (Received == false) { return null; }
                //如果接受到了数据或者已超时就不再循环。
                string rt;//初始化返回内容。
                int sum = 0;//初始化命令总数。
                for (int i = 3; i < bin_return.Length - 2; i++)
                {
                    sum += bin_return[i];
                }
                int sum1 = bin_return[bin_return.Length - 2] + bin_return[bin_return.Length - 1]*256;
                if (
                    sum != sum1
                    &&
                    bin_return[bin_return.Length - 3] == 3
                    )
                { rt = null; }
                else
                {
                    rt = Funs.BinaryToHexString(bin_return);
                }
                return rt;
            }
            /// <summary>
            /// 如果接受到了数据。
            /// </summary>
            /// <param name="sender">发送者</param>
            /// <param name="e">时间参数</param>
            private void DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                //MyPort.DiscardInBuffer(); //丢弃事件发生前的数据。
                int n = MyPort.BytesToRead;//读取当前有多少数据可读。
                byte[] binary = new byte[n];//新建内容。
                MyPort.Read(binary, 0, n);//读取
                Array.Resize(ref bin_return, bin_return.Length + n);//改写数组大小
                if (bin_return.Length < 1) { return; }
                binary.CopyTo(bin_return, bin_return.Length - binary.Length );//复制数据。
                int infleng = 0;
                //16 16 02 10 01 02 00 07 00 03 1D 00  
                if (bin_return.Length > 7)//基本信息头应该是7个字节。
                {
                    if (bin_return[0] == bin_return[1] && bin_return[0]== 22 && bin_return[2] == 2)
                    //如果第零字节和第一字节相等,并且第二自己为0,那么表示信息的开头 。
                    {
                        //计算第五字节和第六字节。这两个字节表示长度。
                       infleng = bin_return[5] + bin_return[6] * 256;
                    }
                }
                else if (bin_return.Length == 3)
                {
                    Received = true;
                    return;
                }
                Received = ((10 + infleng) == bin_return.Length);//命令基本格式包含10个字节。
                //加上信息长度如果等于bin接收到的长度的话。说明接受完了。接受完了就设置receive为真。
                Console.WriteLine(String.Format("读取字节数:{0}:内容:{1}----{2}", n, hex_return,e.EventType));
            }
        }
    }
    其他一些相关的函数
    程序代码
    using System;
    using Microsoft.VisualBasic;
    namespace SPC
    {
        public class Funs
        {
           /// <summary>
           /// 十六进制字符串到日期时间的转换 。
           /// </summary>
           /// <param name="hexstring"></param>
           /// <returns></returns>
            public static DateTime HexString2DateTime(string hexstring)
            {                    
                int nian, yue, ri, xiaoshi, fenzhong, miao;
                //08 21 14 19 05 04    //时分秒天月年
                string tmp = hexstring.Trim();
                xiaoshi = Hex2Int(Strings.Left(tmp, 3).Trim()); tmp = tmp.Remove(0, 3);
                fenzhong = Hex2Int(Strings.Left(tmp, 3).Trim()); tmp = tmp.Remove(0, 3);
                miao = Hex2Int(Strings.Left(tmp, 3).Trim()); tmp = tmp.Remove(0, 3);
                ri = Hex2Int(Strings.Left(tmp, 3).Trim()); tmp = tmp.Remove(0, 3);
                ri = (ri != 0) ? ri : 1;
                yue = Hex2Int(Strings.Left(tmp, 3).Trim()); tmp = tmp.Remove(0, 3);
                yue = (yue != 0) ? yue : 1;
                nian = Hex2Int(Strings.Left(tmp, 3).Trim());
                nian = (nian != 0) ? 2000 + nian : 2000;
                return (new DateTime(nian, yue, ri, xiaoshi, fenzhong, miao));
            }
            /// <summary>
            /// 把时间转换为车检器需要的字符串 。
            /// </summary>
            /// <param name="dt">时间</param>
            /// <returns>格式为时分秒日月年</returns>
            public static string DateTime2HexString(DateTime dt)
            {
                string info = ToHex(dt.Hour) + " ";
                info += ToHex(dt.Minute) + " ";
                info += ToHex(dt.Second) + " ";
                info += ToHex(dt.Day) + " ";
                info += ToHex(dt.Month) + " ";
                info += ToHex(dt.Year-2000) + " ";
                return info.Trim();
            }
            /// <summary>
            /// 设置事件
            /// </summary>
            /// <param name="dt">时间</param>
            /// <returns>转换为秒分时日月年</returns>
            public static string DateTime2HexString2(DateTime dt)
            {
                string info = ToHex(dt.Second) + " ";
                info += ToHex(dt.Minute) + " ";
                info += ToHex(dt.Hour) + " ";
                info += ToHex(dt.Day) + " ";
                info += ToHex(dt.Month) + " ";
                info += ToHex(dt.Year - 2000) + " ";
                return info.Trim();
            }
            /// <summary>
            /// 转换为符合本程序的十六进制格式
            /// </summary>
            /// <param name="var">1 2 3 等。</param>
            /// <returns>返回十六进制字符串,如果是1-9的话,前面带零</returns>
            /// <example>例如: 5  ="05"  12 ="0C" 无论何时,都是两位数。  </example>
            public static string ToHex(int var)
            {
                int cs = var;
                string tmp = "";
                if (cs == 0) { tmp = "00"; }
                while (cs > 0)
                {
                    int ys;
                    cs = Math.DivRem(cs, 256, out   ys);
                    tmp += string.Format(" {0}", Strings.Right("00" + Convert.ToString(ys, 16), 2).ToUpper());
                }
                return tmp.Trim();
            }
            /// <summary>
            /// 转换为符合本程序的十六进制格式
            /// </summary>
            /// <param name="var">1 2 3 等。</param>
            /// <param name="tianbu">填补</param>
            /// <param name="lng">长度</param>
            /// <returns>返回指定长度字符串,缺少的用 tianbu 来补齐</returns>
            public static string ToHex(int var,string tianbu,int lng)
            {
                string tmp = ToHex(var);
                tmp = Strings.Left(tmp +" "+ tianbu,lng );
                return tmp;
            }
            /// <summary>
            /// 统计和
            /// </summary>
            /// <param name="v">被统计的变量</param>
            /// <returns></returns>
            public static int Sum(byte[] v)
            {
                int s = 0;
                foreach (int var in v)
                {
                    s += var;
                }
                return s;
            }
            /// <summary>
            /// 统计和
            /// </summary>
            /// <param name="v">被统计的变量</param>
            /// <returns></returns>
            /// <param name="istart">开始位置</param>
            public static int Sum(byte[] v, int istart)
            {
                int s = 0;
                for (int i = istart; i < v.Length; i++)
                {
                    s += v[i];
                }
                return s;
            }
            /// <summary>
            /// 统计和
            /// </summary>
            /// <param name="v">被统计的变量</param>
            /// <returns></returns>
            /// <param name="istart">开始位置</param>
            /// <param name="iend">结束位置</param>
            public static int Sum(byte[] v, int istart, int iend)
            {
                int s = 0;
                for (int i = istart; i < iend; i++)
                {
                    s += v[i];
                }
                return s;
            }
            /// <summary>
            /// 转换16进制字符串为int
            /// </summary>
            /// <param name="hexstring">字符串,低字节在前,高字节在后</param>
            /// <returns></returns>
            public static  int Hex2Int(string hexstring)
            {
                return Hex2Int(hexstring ,false );
            }
            /// <summary>
            /// 转换16进制字符串为int
            /// </summary>
            /// <param name="hexstring">字符串</param>
            /// <param name="Reverse">是否反转为高字节在前,低字节在后</param>
            /// <returns></returns>
            /// <remarks> 高字节在前,低字节在后</remarks>
            public static int Hex2Int(string hexstring,bool Reverse)
            {
                //02 00
                byte[] b = HexStringToBinary(hexstring);
                if (Reverse) { Array.Reverse(b); };
                int r = b[0];
                for (int i = 1; i < b.Length - 1; i++)
                {
                    if (b[i] != 0)
                    {
                        r += Convert.ToInt32(Math.Pow(16, b[i]));
                    }
                }
                return r;
            }
            /// <summary>
            /// 16进制字符串转换为二进制数组
            /// </summary>
            /// <param name="hexstring">字符串每个字节之间都应该有空格,大多数的串口通讯资料上面的16进制都是字节之间都是用空格来分割的。</param>
            /// <returns>返回一个二进制字符串</returns>
            public static byte[] HexStringToBinary(string hexstring)
            {
                string[] tmpary = hexstring.Trim().Split(' ');
                byte[] buff = new byte[tmpary.Length];
                for (int i = 0; i < buff.Length; i++)
                {
                    buff[i] = Convert.ToByte(tmpary[i], 16);
                }
                return buff;
            }
            /// <summary>
            /// 把二进制转换为十六进制字符串。
            /// </summary>
            /// <param name="buff">要被转换的二进制数组</param>
            /// <returns>返回十六进制字符串,以空格隔开每个字节 。</returns>
            public static string BinaryToHexString(byte[] buff)
            {
                string tmpstring = "";
                foreach (byte var in buff)
                {
                    tmpstring += " " + Strings.Right("00" + Convert.ToString(var, 16), 2);
                }
                return tmpstring.Trim().ToUpper();
            }
        }
    }
    命令的提取和剔除。
    程序代码
    using Microsoft.VisualBasic;
    namespace SPC
    {
        public class CommandString
        {
            public const string Sync2STX = "16 16 02";
            /// <summary>
            /// 从返回的字符串中取得信息部分。
            /// </summary>
            /// <param name="returnstring">返回的字符串。</param>
            /// <param name="loc">取得地址部分的字符串。</param>
            /// <returns>返回字符串的信息部分</returns>
            public static string GetCmdFromReturnString(string returnstring,out string loc)
            {
                /*
                SYNC|SYNC | STX | 地址  | 长度  |信息  |ETX |2字节校验和。
                16  | 16  |  02 | 10 01 | 02 00 |07 00  |03 |1D 00
                解释:
                开始的16 16 02 是信息的开头;
                10 01 即为该车检的地址,
                02 00是命令的长度低字节在前,高字节在后,代表查询的命令值是两个字节;
                07 00 即为查询的命令,具体解析在后;
                03为停止位;
                最后两位是校验位,为STX之后(不包括STX)到ETX(包括)所有字节的和。
                */
                string tmp = returnstring.Remove(0, Sync2STX.Length).Trim();//去掉头信息
                loc = Strings.Left(tmp, 5);//取得位置
                 tmp = tmp.Remove(0, 5).Trim();//去掉 位置
                int lng=Funs.Hex2Int(Strings.Left(tmp,5));//取得信息长度。
                tmp = tmp.Remove(0, 5).Trim();//去掉信息长度
                string r = Strings.Left(tmp, lng * 3).Trim();//取得信息。
                //校验和这里不验证了。在 PortControl 中接受的时候就验证过了。
                return r;
            }
            /// <summary>
            /// 从返回的字符串中取得信息部分。
            /// </summary>
            /// <param name="returnstring">返回的字符串</param>
            /// <returns>返回字符串的信息部分</returns>
            public static string GetCmdFromReturnString(string returnstring)
            {
                string loc;
                string r =GetCmdFromReturnString(returnstring , out  loc);
                return r ;
            }
            /// <summary>
            /// 计算完整的命令字符串
            /// </summary>
            /// <param name="pm0_dizhi">The pm0_dizhi.</param>
            /// <param name="pm2_mingling">The pm2_mingling.</param>
            /// <returns>
            /// 返回完整的十六进制字符串,包括信息头,地址,命令,校正码等内容。可以直接发送给车检器。
            /// </returns>
            public static string GetFullCommandString(string pm0_dizhi,string pm2_mingling )
            {
                /*
                SYNC|SYNC | STX | 地址  | 长度  |信息  |ETX |2字节校验和。
                16  | 16  |  02 | 10 01 | 02 00 |07 00  |03 |1D 00
                解释:
                开始的16 16 02 是信息的开头;
                10 01 即为该车检的地址,
                02 00是命令的长度低字节在前,高字节在后,代表查询的命令值是两个字节;
                07 00 即为查询的命令,具体解析在后;
                03为停止位;
                最后两位是校验位,为STX之后(不包括STX)到ETX(包括)所有字节的和。
                */
                //SYNC、SYNC、STX、地址、长度、信息、ETX、2字节校验和。
                string tmpstring = "16 16 02 {0} {1} {2} 03";
                //0 地址,1 长度,2 信息 3 校验和。
                string pm1_changdu = Funs.ToHex(pm2_mingling.Split(' ').Length,"00",5);
                tmpstring=string.Format(tmpstring,pm0_dizhi,pm1_changdu,pm2_mingling);
                int pm3_jiaoyanhe = Funs.Sum(Funs.HexStringToBinary(tmpstring), 3);
                tmpstring +=" "+ Funs.ToHex ( pm3_jiaoyanhe,"00",5);
                return tmpstring ;
            }
        }
    }
    如何调用:
    程序代码
            #region 获取版本号
            private const string _ver = "6C";
            private const string _ver_r = "6D";
            /// <summary>
            /// 得到车检器版本号
            /// </summary>
            /// <param name="loc">地址: 10 01</param>
            /// <returns>返回车检器版本号</returns>
            public static string GetVer(string loc)
            {
                string tmp = DoCmd(loc, _ver, _ver_r);
                if (_doresult == r_OK)
                {
                    double dx = Funs.Hex2Int(tmp);
                    double d = dx/10;
                    return d.ToString();
                }
                else
                {
                    return tmp;
                }
            }
            #endregion
            #region 公用常量
            /// <summary>
            /// 表示设置成功
            /// </summary>
            public const string r_ACK = "ACK";
            /// <summary>
            /// 表示查询失败
            /// </summary>
            public const string r_FAILED = "FAILED";
            /// <summary>
            /// 设置失败
            /// </summary>
            public const string r_NACK = "NACK";
            /// <summary>
            /// 出现错误。可能是接收的数据不符合要求等。
            /// </summary>
            public const string r_ERROR = "ERROR";
            /// <summary>
            /// 超时。
            /// </summary>
            public const string r_TIMEOUT = "TIMEOUT";
            /// <summary>
            /// 未知。不知道是什么原因,也就是说返回的内容目前程序还无法识别。
            /// </summary>
            public const string r_UNKNOW = "UNKNOW";
            /// <summary>
            /// 端口可能被占用
            /// </summary>
            public const string r_NULL = "NULL";
            /// <summary>
            /// 执行成功。
            /// </summary>
            public const string r_OK = "OK";
            /// <summary>
            /// 你给出的参数无法解析
            /// </summary>
            public const string r_PARAM_ERROR = "PARAM_ERROR";
            #endregion
            #region 执行命令
            private static string _doresult;
            /// <summary>
            /// 命令执行结果
            /// </summary>
            public static string DoResult
            {
                set { _doresult = value; }
                get { return _doresult; }
            }
            /// <summary>
            /// 执行一条命令
            /// </summary>
            /// <param name="loc">地址</param>
            /// <param name="cmd">命令</param>
            /// <param name="r">车检器的应答代码。</param>
            /// <returns>返回来的数据是不带r 的。</returns>
            /// <example>  string tmp = DoCmd(loc, _ver, _ver_r);</example>
            public static string DoCmd(string loc, string cmd)
            {
                return DoCmd(loc, cmd, "", true);
            }
            /// <summary>
            /// 执行一条命令
            /// </summary>
            /// <param name="loc">地址</param>
            /// <param name="cmd">命令</param>
            /// <param name="r">车检器的应答代码。</param>
            /// <returns>返回来的数据是不带r 的。</returns>
            /// <example>  string tmp = DoCmd(loc, _ver, _ver_r);</example>
            public static string DoCmd(string loc, string cmd, string r)
            {
                return DoCmd(loc, cmd, r, false);
            }
            /// <summary>
            /// 执行一条命令
            /// </summary>
            /// <param name="loc">地址</param>
            /// <param name="cmd">命令</param>
            /// <param name="r">车检器的应答代码。</param>
            /// <param name="noreturn">是否需要返回值。如果不需要则为真,如果需要则否。</param>
            /// <returns>返回来的数据是不带r 的。</returns>
            /// <example>  string tmp = DoCmd(loc, _ver, _ver_r);</example>
            public static string DoCmd(string loc, string cmd, string r, bool noreturn)
            {
                string inputstring = CommandString.GetFullCommandString(loc, cmd);
                Console.WriteLine("正在执行车检器指令:" + inputstring);
                PortControl pc = new PortControl("COM1");
                if (pc.Open())
                {
                    string returnstring = pc.Send(inputstring);
                    Console.WriteLine("车检器答复:" +returnstring );
                    //如果不需要返回,直接退出函数。            
                    if (noreturn)
                    {
                        pc.Close();
                        if (pc.Received)
                        {
                            string ixxx = (returnstring == "16 16 06"
                                               ? r_ACK
                                               : (returnstring == "16 16 07"
                                                      ? r_FAILED
                                                      : (returnstring == "16 16 15" ? r_NACK : r_UNKNOW)));
                            _doresult = ixxx;
                            return ixxx;
                        }
                        else
                        {
                            _doresult = r_ERROR;
                            return r_ERROR;
                        }
                    }
                    //如果接受超时。
                    if (pc.Received != true)
                    {
                        pc.Close();
                        _doresult = r_TIMEOUT;
                        return r_TIMEOUT;
                    }
                    string inf = CommandString.GetCmdFromReturnString(returnstring);
                    if (Strings.Left(inf, 2) == r)
                    {
                        pc.Close();
                        _doresult = r_OK;
                        return inf.Remove(0, r.Length).Trim();
                    }
                    else
                    {
                        pc.Close();
                        _doresult = r_UNKNOW;
                        return r_UNKNOW;
                    } //返回的内容不正确。
                }
                else
                {
                    _doresult = r_NULL;
                    return r_NULL;
                } //串口没有打开。
            }
            #endregion
        }
    单元测试
    程序代码
           [Test]
            public void GetVer()
            {
                string tmp = DoCommand.GetVer(_地址);
                Assert.AreEqual("3.3", tmp, "如果是 null ,表示车检器的端口通讯失败,如果是 -  表示车检器回应的数据不正确");
            }

  • 相关阅读:
    JDK源码之LinkedList
    MySQL常规知识点总结
    根据不同分辨率载入相应CSS样式表
    关于1.0.0版Backbone.js调用validate
    弹性盒模型计算
    使用CocoaPods
    Objective-C中的Block[转]
    将UTF8编码转化为中文 - NSString方法
    添加第三方类库造成的Undefined symbols for architecture i386:编译错误
    App适配iPhone 6/ Plus和iOS 8:10条小秘诀
  • 原文地址:https://www.cnblogs.com/bennylam/p/1590404.html
Copyright © 2011-2022 走看看