窗口数据发送和接受的类 新建 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 ,表示车检器的端口通讯失败,如果是 - 表示车检器回应的数据不正确"); }
转载于:https://www.cnblogs.com/bennylam/archive/2009/10/27/1590404.html
相关资源:各显卡算力对照表!