創建SerialPortFun類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Ports; using System.Threading; using LogSpace; namespace SerialPortSpace { public class SerialPortFun { /// <summary> /// 串口及串口緩存數據 /// </summary> public static List<ComPort> LstComPort = new List<ComPort>(); /// <summary> /// 是否在進行串口結束操作 /// </summary> public static bool PortIsCloseing = false; /// <summary> /// 初始化串口,並返回當前串口的結構 /// </summary> /// <param name="Port"></param> /// <param name="PortNo"></param> /// <param name="BaudRate"></param> /// <param name="ReciveData"></param> public static ComPort InitSerialPort(SerialPort Port, string PortNo, int BaudRate, BagStruct CurBagStruct, string StartStr, string EndStr, string PortNameDesc, string PortRemark) { Port.PortName = PortNo; Port.BaudRate = BaudRate; Port.DataBits = 8; Port.Parity = Parity.None; Port.StopBits = StopBits.One; Port.NewLine = "\r\n"; Port.WriteTimeout = 5000; Port.ReadTimeout = 5000; Port.Handshake = Handshake.None; Port.ReceivedBytesThreshold = 1; Port.RtsEnable = true; Port.DataReceived += Port_DataReceived;//定義接收數據事件 ComPort PortItem = new ComPort(); PortItem.Port = Port; PortItem.PortRealName = PortNameDesc; PortItem.PortDesc = PortRemark; PortItem.PortIsRcving = false; PortItem.LastRebackTime = DateTime.Now; PortItem.RcvBag = CurBagStruct; PortItem.StartStr = StartStr; PortItem.EndStr = EndStr; PortItem.SendOrder = new List<string>(); PortItem.RcvData = string.Empty; PortItem.RcvLst = new List<PortReciveDataItem>(); if (LstComPort.Count(c => c.Port == Port) > 0) { LstComPort.RemoveAll(c => c.Port == Port); } LstComPort.Add(PortItem); return PortItem; } /// <summary> /// 定義數據包中每項的開始位置及所占字節數 /// </summary> /// <param name="StartPosition"></param> /// <param name="Length"></param> /// <returns></returns> public static BagItemPosition SetBagItemPosition(int StartPosition, int Length) { BagItemPosition Item = new BagItemPosition(); Item.StartPosition = StartPosition; Item.Length = Length; return Item; } /// <summary> /// 定義返回數據包結構 /// </summary> /// <param name="ByteOrder">字節順序約定 0--高字節在前,低字節在后,1--高字節在后,低字節在前</param> /// <param name="HeadItem">包頭所占字節數</param> /// <param name="HeadItem">命令所占字節數</param> /// <param name="OthItem">其他位總共占字節數</param> /// <param name="DataLenItem">數據長度位所占字節數</param> /// <param name="VerItem">校驗位所占字節數</param> /// <param name="EndItem">包尾所占字節數</param> /// <returns></returns> public static BagStruct SetBagStruct(int ByteOrder, BagItemPosition HeadItem, BagItemPosition CmdItem, BagItemPosition OthItem, BagItemPosition DataLenItem, int RealDataItem, int VerItem, int EndItem) { BagStruct CurRcvBagStruct = new BagStruct(); CurRcvBagStruct.ByteOrder = ByteOrder; CurRcvBagStruct.Head = HeadItem; CurRcvBagStruct.Cmd = CmdItem; CurRcvBagStruct.Other = OthItem; CurRcvBagStruct.DataLen = DataLenItem; CurRcvBagStruct.RealDataPos = RealDataItem; CurRcvBagStruct.VerifyLen = VerItem; CurRcvBagStruct.EndLen = EndItem; return CurRcvBagStruct; } /// <summary> /// 打開串口 /// </summary> /// <param name="PortName"></param> public static void OpenSerialPort(SerialPort Port) { if (!Port.IsOpen) { Port.Open(); } } /// <summary> /// 發送串口數據 /// </summary> /// <param name="Port"></param> /// <param name="Data"></param> public static bool SetPortData(SerialPort Port, string Data) { bool CurSendFlag = false; if (Port.IsOpen) { byte[] buf = strToHexByte(Data); Port.Write(buf, 0, buf.Length); CurSendFlag = true; } return CurSendFlag; } #region 接收串口應答包 /// <summary> /// 接收串口應答包 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (PortIsCloseing) { return; } SerialPort port = sender as SerialPort; DateTime CurTime = DateTime.Now; string RcvData = string.Empty; if (LstComPort != null) { if (LstComPort.Count(c => c.Port.PortName == port.PortName) > 0) { ComPort comPort = LstComPort.Find(c => c.Port.PortName == port.PortName); try { Log.WriteLogToLocal(CurTime + "接收前" + comPort.PortRealName + "數據包臨時數據:\r\n" + comPort.RcvData + "\r\n------------------------\r\n", "Log\\IBO_LOG", comPort.PortDesc + "_Receive"); RcvData = comPort.RcvData; comPort.PortIsRcving = true;//正在接收串口數據 StringBuilder CurRcvData = new StringBuilder(); if (port.IsOpen && port.BytesToRead != 0) { while (port.BytesToRead != 0) { byte[] recvBytes = new byte[port.BytesToRead]; int bytes = port.Read(recvBytes, 0, recvBytes.Length); if (recvBytes.Length != 0) { foreach (byte b in recvBytes) { CurRcvData.Append(b.ToString("X2") + " "); } } //Thread.Sleep(20); } Log.WriteLogToLocal(CurTime + "接收" + comPort.PortRealName + "原始數據:\r\n" + CurRcvData + "\r\n------------------------\r\n", "Log\\IBO_LOG", comPort.PortDesc + "_Receive"); RcvData += CurRcvData; CurRcvData.Clear(); //接收到的數據進行處理,並存儲在串口對應的接收隊列中 //SerialPortFun.DealPortData(comPort, RcvData, CurTime); SerialPortFun.ConcatFullBag(comPort, comPort.RcvBag, RcvData, CurTime); comPort.LastRebackTime = CurTime; } } catch (Exception ex) { Log.WriteLogToLocal(CurTime + "接收" + comPort.PortRealName + "串口數據(" + RcvData + ")錯誤:(" + ex.Message+")"+ex.StackTrace + "\r\n", "Log\\IBO_LOG\\ErrorLog", comPort.PortDesc + "_Receive"); } finally { comPort.PortIsRcving = false;//接收串口數據完畢 } } } } #endregion /// <summary> /// 接收到的數據進行處理,並存儲在串口對應的接收隊列中 /// </summary> /// <param name="port"></param> /// <param name="Struct"></param> /// <param name="CurRcvData"></param> /// <param name="CurTime"></param> public static void ConcatFullBag(ComPort port, BagStruct Struct, string CurRcvData, DateTime CurTime) { Log.WriteLogToLocal(CurTime + "數據包處理" + port.PortRealName + "原始數據:\r\n" + CurRcvData + "\r\n------------------------\r\n", "Log\\IBO_LOG", port.PortDesc + "_Receive"); string temp = CurRcvData; while (true) { //校驗包頭,包尾 if (!temp.StartsWith(port.StartStr)) { int index = temp.IndexOf(port.StartStr); if (index == -1) temp = ""; else temp = temp.Remove(0, index); } if (temp.Length > 0) { int NoDataBagLen = Struct.Head.Length + Struct.Cmd.Length + Struct.Other.Length + Struct.DataLen.Length + Struct.VerifyLen + Struct.EndLen; int BagDataLen = 0; if (temp.Length > (Struct.DataLen.StartPosition * 3 + Struct.DataLen.Length * 3)) { #region 獲取數據包的業務數據長度 BagDataLen = GetBagDataLen(Struct, temp); //string CurDataLen = ""; //string CurDataLenOrg = temp.Substring(Struct.DataLen.StartPosition * 3, Struct.DataLen.Length * 3); //if (Struct.ByteOrder == 1)//高字節在后,低字節在前 //{ // CurDataLen = ReverseStr(CurDataLenOrg, 2); // BagDataLen = Convert.ToInt32(CurDataLen, 16);//16進制 //} //else //{ // CurDataLen = CurDataLenOrg; // BagDataLen = Convert.ToInt32(CurDataLen, 10);//華氣特殊 10進制 //} #endregion } if (BagDataLen > 0 && temp.Length / 3 >= NoDataBagLen + BagDataLen) { string FullBag = temp.Substring(0, (NoDataBagLen + BagDataLen) * 3); BagItemContent CurBagItem = new BagItemContent(); CurBagItem.Head = FullBag.Substring(Struct.Head.StartPosition * 3, Struct.Head.Length * 3).Replace(" ", ""); CurBagItem.Cmd = FullBag.Substring(Struct.Cmd.StartPosition * 3, Struct.Cmd.Length * 3).Replace(" ", ""); CurBagItem.Other = FullBag.Substring(Struct.Other.StartPosition * 3, Struct.Other.Length * 3).Replace(" ", ""); CurBagItem.DataLen = FullBag.Substring(Struct.DataLen.StartPosition * 3, Struct.DataLen.Length * 3).Replace(" ", ""); CurBagItem.RealData = FullBag.Substring(Struct.RealDataPos * 3, BagDataLen * 3); CurBagItem.Verify = FullBag.Substring(FullBag.Length - (Struct.VerifyLen + Struct.EndLen) * 3, Struct.VerifyLen * 3).Replace(" ", ""); CurBagItem.End = FullBag.Substring(FullBag.Length - Struct.EndLen * 3, Struct.EndLen * 3).Replace(" ", ""); if (Struct.ByteOrder == 1)//高字節在后,低字節在前 { CurBagItem.Head = Struct.Head.Length <= 1 ? CurBagItem.Head : ReverseStr(CurBagItem.Head, 2); CurBagItem.Cmd = (Struct.Cmd.Length <= 1 ? CurBagItem.Cmd : ReverseStr(CurBagItem.Cmd, 2)); CurBagItem.DataLen = Struct.DataLen.Length <= 1 ? CurBagItem.DataLen : ReverseStr(CurBagItem.DataLen, 2); //CurBagItem.RealData,CurBagItem.Other需根據具體情況解析暫不反轉 //CurBagItem.RealData = RealDataLen <= 1 ? CurBagItem.RealData : ReverseStr(CurBagItem.RealData, 2); //CurBagItem.Other = Struct.Other.Length <= 1 ? CurBagItem.Other : ReverseStr(CurBagItem.Other, 2); CurBagItem.Verify = Struct.VerifyLen <= 1 ? CurBagItem.Verify : ReverseStr(CurBagItem.Verify, 2); CurBagItem.End = Struct.EndLen <= 1 ? CurBagItem.End : ReverseStr(CurBagItem.End, 2); } PortReciveDataItem NewItem = new PortReciveDataItem(); NewItem.ReciveTime = CurTime; NewItem.ReciveDataItem = CurBagItem; NewItem.ReciveDataStr = FullBag; port.RcvLst.Add(NewItem); Log.WriteLogToLocal(CurTime + "加入" + port.PortRealName + "完整數據包:\r\n" + FullBag + "\r\n------------------------\r\n", "Log\\IBO_LOG", port.PortDesc + "_Receive"); temp = temp.Remove(0, FullBag.Length); if (temp.Length <= 0) { port.RcvData = ""; break; } // port.RcvData = temp.Substring((NoDataBagLen + BagDataLen) * 3);//剩余字符放入下個數據包 } else { port.RcvData = CurRcvData;//不是完整包,等待完整數據 break; } } else { //丟棄不符合的串口數據 port.RcvData = ""; Log.WriteLogToLocal(CurTime + "丟棄" + port.PortRealName + "原始數據:\r\n" + CurRcvData + "\r\n------------------------\r\n", "Log\\IBO_LOG", port.PortDesc + "_Receive"); break; } } } /// <summary> /// 獲取包長度 /// </summary> /// <param name="Struct"></param> /// <param name="AllBagData"></param> /// <returns></returns> public static int GetBagDataLen(BagStruct Struct, string AllBagData) { int BagDataLen = 0; string CurDataLen = ""; AllBagData = AllBagData.Replace(" ", ""); string CurDataLenOrg = AllBagData.Substring(Struct.DataLen.StartPosition * 2, Struct.DataLen.Length * 2); if (Struct.ByteOrder == 1)//高字節在后,低字節在前 { CurDataLen = ReverseStr(CurDataLenOrg, 2); BagDataLen = Convert.ToInt32(CurDataLen, 16);//16進制 需改成通用 } else { CurDataLen = CurDataLenOrg; BagDataLen = Convert.ToInt32(CurDataLen, 10);//華氣特殊 10進制 } return BagDataLen; } /// <summary> /// 校驗數據包 /// </summary> /// <param name="BagStr"></param> /// <returns></returns> public static bool CheckBag(string BagHead, string BagEnd, BagStruct Struct, string BagStr, out int RealDataLen) { bool IsRight = true; RealDataLen = 0; BagStr = BagStr.Replace(" ", ""); //校驗包頭,包尾 if (!(BagStr.StartsWith(BagHead) && BagStr.EndsWith(BagEnd))) { IsRight = IsRight && false; } #region 校驗包總長度 int CurTotalBagLen = Struct.Head.Length + Struct.Cmd.Length + Struct.Other.Length + Struct.DataLen.Length + Struct.VerifyLen + Struct.EndLen; if (BagStr.Length < Struct.DataLen.StartPosition + Struct.DataLen.Length) { IsRight = IsRight && false; } else { string CurDataLen = ""; string CurDataLenOrg = BagStr.Substring(Struct.DataLen.StartPosition * 2, Struct.DataLen.Length * 2); if (Struct.ByteOrder == 1)//高字節在后,低字節在前 { CurDataLen = ReverseStr(CurDataLenOrg, 2); //for (int i = Struct.DataLen.Length; i > 0; i--) //{ // CurDataLen += CurDataLenOrg.Substring((i - 1) * 2, 2); //} RealDataLen = Convert.ToInt32(CurDataLen, 16); } else { CurDataLen = CurDataLenOrg; RealDataLen = Convert.ToInt32(CurDataLen, 10);//華氣特殊 } CurTotalBagLen += RealDataLen; if (BagStr.Length / 2 != CurTotalBagLen) { IsRight = IsRight && false; } } #endregion return IsRight; } /// <summary> /// 反轉字符串 /// </summary> /// <param name="Str"></param> /// <param name="Len"></param> /// <returns></returns> public static string ReverseStr(string Str, int Len) { Str = Str.Replace(" ", ""); string NewStr = ""; List<string> ls = new List<string>(); for (int i = 0; i < Str.Length; i += Len) { ls.Add(Str.Substring(i, Len)); // 兩兩截取 } ls.Reverse();// 兩兩反轉 foreach (string item in ls) { NewStr += item; } return NewStr; } /// <summary> /// 16進制字符串轉字節數組 /// </summary> /// <param name="hexString"></param> /// <returns></returns> public static byte[] strToHexByte(string hexString) { hexString = hexString.Replace(" ", ""); if ((hexString.Length % 2) != 0) hexString += " "; byte[] returnBytes = new byte[hexString.Length / 2]; for (int i = 0; i < returnBytes.Length; i++) returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2).Trim(), 16); return returnBytes; } } }
創建數據接收
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Ports; namespace SerialPortSpace { /// <summary> /// 接收數據類 /// </summary> public class PortReciveDataItem { public BagItemContent ReciveDataItem { get; set; } public DateTime ReciveTime { get; set; } public string ReciveDataStr { get; set; } } /// <summary> /// 串口緩存數據 /// </summary> public class ComPort { /// <summary> /// 串口 /// </summary> public SerialPort Port { set; get; } /// <summary> /// 當前串口名稱 /// </summary> public string PortRealName { set; get; } /// <summary> /// 當前串口描述 /// </summary> public string PortDesc { set; get; } /// <summary> /// 當前串口是否正在接收數據---關閉串口判斷 /// </summary> public bool PortIsRcving { set; get; } /// <summary> /// 最近返回數據時間 /// </summary> public DateTime LastRebackTime { get; set; } /// <summary> /// 定義接收包結構 /// </summary> public BagStruct RcvBag { get; set; } /// <summary> /// 開始字符 /// </summary> public string StartStr { set; get; } /// <summary> /// 結束字符 /// </summary> public string EndStr { set; get; } /// <summary> /// 串口發送是指令集合 /// </summary> public List<string> SendOrder { set; get; } /// <summary> /// 存儲不完整包 臨時數據 /// </summary> public string RcvData { set; get; } /// <summary> /// 接收到的串口數據--完整包,臨時存放 /// </summary> public List<PortReciveDataItem> RcvLst { set; get; } } /// <summary> /// 接收包的結構定義 /// </summary> public class BagStruct { /// <summary> /// 字節順序約定 0--高字節在前,低字節在后,1--高字節在后,低字節在前 /// </summary> public int ByteOrder { set; get; } /// <summary> /// 包頭所占字節數 /// </summary> public BagItemPosition Head { set; get; } /// <summary> /// 命令字段所占字節數 /// </summary> public BagItemPosition Cmd { set; get; } /// <summary> /// 除實際數據包中數據長度及實際數據以外的其他數據項所占字節數 /// </summary> public BagItemPosition Other { set; get; } /// <summary> /// 數據字段長度項所占字節數 /// </summary> public BagItemPosition DataLen { set; get; } /// <summary> /// 實際數據所占字節數---可變的,可以根據實際數據包由DataLen計算所得 /// </summary> public int RealDataPos//開始位置 { set; get; } /// <summary> /// 校驗位所占字節數 /// </summary> public int VerifyLen { set; get; } /// <summary> /// 包尾所占字節數 /// </summary> public int EndLen { set; get; } } /// <summary> /// 定義數據包中每項的開始位置及所占字節數 /// </summary> public class BagItemPosition { /// <summary> /// 開始位置 /// </summary> public int StartPosition { set; get; } /// <summary> /// 所占字節數 /// </summary> public int Length { set; get; } } /// <summary> /// 接收包的結構內容 /// </summary> public class BagItemContent { /// <summary> /// 包頭 /// </summary> public string Head { set; get; } /// <summary> /// 命令字段 /// </summary> public string Cmd { set; get; } /// <summary> /// 除實際數據包中數據長度及實際數據以外的其他數據項 /// </summary> public string Other { set; get; } /// <summary> /// 數據字段長度項 /// </summary> public string DataLen { set; get; } /// <summary> /// 實際數據 /// </summary> public string RealData { set; get; } /// <summary> /// 校驗位 /// </summary> public string Verify { set; get; } /// <summary> /// 包尾 /// </summary> public string End { set; get; } } }