文章出處
View Code
View Code
View Code
View Code
View Code
View Code
View Code
文章列表
最近寫C#串口通信程序,系統是B/S架構。SerialPort類有一個DataReceived事件,用來接收串口返回的數據,但這種方式在C/S架構下很好用,但B/S就不好處理了。所以寫了一個同步模式接收返回數據的方法,不使用DataReceived事件。經過測試,可以正常使用。
一、MachineFactory類
為什么使用工廠類:售貨機由不止一個廠家提供,接口協議都不一樣。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.IO; using System.IO.Ports; namespace IMachineDll { /// <summary> /// 售貨機工廠類 /// </summary> public class MachineFactory { /// <summary> /// 貨機接口緩存 /// </summary> private static Dictionary<string, IMachine> dicMachine = new Dictionary<string, IMachine>(); /// <summary> /// 鎖變量 /// </summary> public static object _lock = new object(); /// <summary> /// 創建售貨機類 /// </summary> /// <param name="path">DLL物理路徑</param> /// <param name="dllName">DLL名稱(不含擴展名),命名空間必須為DLL名稱加“Dll”后綴,類名必須和DLL名稱相同</param> /// <param name="com">串口名稱,如:COM1</param> public static IMachine Create(string path, string dllName, string com) { if (!dicMachine.ContainsKey(dllName) || dicMachine[dllName] == null) { using (FileStream fs = new FileStream(path + dllName + ".dll", FileMode.Open, FileAccess.Read)) { using (MemoryStream ms = new MemoryStream()) { byte[] byteArray = new byte[4096]; while (fs.Read(byteArray, 0, byteArray.Length) > 0) { ms.Write(byteArray, 0, byteArray.Length); } Assembly assembly = Assembly.Load(ms.ToArray()); dicMachine[dllName] = (IMachine)assembly.CreateInstance(dllName + "Dll." + dllName, false, BindingFlags.Default, null, new object[] { com }, null, null); } } } return dicMachine[dllName]; } } }
二、Machine類
1、變量與構造函數

/// <summary> /// 串口資源 /// </summary> private SerialPort serialPort = null; public Machine(string com) { serialPort = new SerialPort(com, 9600, Parity.None, 8, StopBits.One); serialPort.ReadBufferSize = 1024; serialPort.WriteBufferSize = 1024; }
2、向串口發送數據,同步接收返回數據的方法:

/// <summary> /// 向串口發送數據,讀取返回數據 /// </summary> /// <param name="sendData">發送的數據</param> /// <returns>返回的數據</returns> private byte[] ReadPort(byte[] sendData) { lock (MachineFactory._lock) { //打開連接 if (!serialPort.IsOpen) serialPort.Open(); //發送數據 serialPort.Write(sendData, 0, sendData.Length); //讀取返回數據 DateTime dt = DateTime.Now; while (serialPort.BytesToRead == 0) { Thread.Sleep(1); if (DateTime.Now.Subtract(dt).TotalMilliseconds > 5000) //如果5秒后仍然無數據返回,則視為超時 { throw new Exception("主版無響應"); } } Thread.Sleep(50); byte[] recData = new byte[serialPort.BytesToRead]; serialPort.Read(recData, 0, recData.Length); //關閉連接 if (serialPort.IsOpen) serialPort.Close(); return recData; } }
優化版:

/// <summary> /// 向串口發送數據,讀取返回數據 /// </summary> /// <param name="sendData">發送的數據</param> /// <returns>返回的數據</returns> private byte[] ReadPort(byte[] sendData) { lock (MachineFactory._lock) { //打開連接 if (!serialPort.IsOpen) serialPort.Open(); //發送數據 serialPort.Write(sendData, 0, sendData.Length); //讀取返回數據 DateTime dt = DateTime.Now; while (serialPort.BytesToRead < 2) { Thread.Sleep(1); if (DateTime.Now.Subtract(dt).TotalMilliseconds > 5000) //如果5秒后仍然無數據返回,則視為超時 { throw new Exception("主版無響應"); } } List<byte> recList = new List<byte>(); byte[] recData = new byte[serialPort.BytesToRead]; serialPort.Read(recData, 0, recData.Length); recList.AddRange(recData); int length = recData[1] + 3; //報文數據總長度 while (recList.Count < length) { if (serialPort.BytesToRead > 0) { recData = new byte[serialPort.BytesToRead]; serialPort.Read(recData, 0, recData.Length); recList.AddRange(recData); } Thread.Sleep(1); } //關閉連接 if (serialPort.IsOpen) serialPort.Close(); return recList.ToArray(); } }
3、發送聯機指令:

/// <summary> /// 聯機 /// </summary> /// <param name="msg">傳出錯誤信息</param> /// <returns>聯機是否成功</returns> public bool Connect(out string msg) { byte[] sendData = new byte[] { 0x01, 0x01, 0x00, 0x00 }; CommonUtil.CalCheckCode(sendData); byte[] recData = ReadPort(sendData); if (recData.Length >= 4 && recData[0] == 0x01 && recData[1] == 0x02 && recData[2] == 0x00 && CommonUtil.ValidCheckCode(recData)) { switch (recData[3]) { case 0x00: msg = "控制主板正在重啟"; return false; case 0x01: msg = "聯機成功"; return true; case 0x02: msg = "控制主板正在維護"; return false; case 0x03: msg = "控制主板收到的數據格式不正確"; return false; default: msg = "未知狀態"; return false; } } else if (IsRunning(recData, out msg) || !IsConnected(recData, out msg)) { return false; } else { throw new Exception("貨機返回的數據格式不正確"); } }
三、如何使用
1、Controller層代碼(還不完善,僅測試,真實情況是根據硬件信息,確定調用哪個Dll使用哪個串口):

#region 創建售貨機接口 /// <summary> /// 創建售貨機接口 /// </summary> private IMachine CreateMachine() { //return MachineFactory.Create(Request.PhysicalApplicationPath + @"\bin\", "Machine", "COM1"); return MachineFactory.Create(@"D:\售藥機代碼\Reception\Machine\bin\Debug\", "Machine", "COM1"); } #endregion #region 聯機 /// <summary> /// 聯機 /// </summary> /// <param name="msg">錯誤信息</param> /// <returns>聯機是否成功</returns> private bool Connect(out string msg) { try { IMachine machine = CreateMachine(); DateTime dt1 = DateTime.Now; while (!machine.Connect(out msg)) //聯機 { if (DateTime.Now.Subtract(dt1).TotalMilliseconds > 20000) { msg = "聯機超時"; return false; } Thread.Sleep(50); } return true; } catch (Exception ex) { msg = ex.Message; return false; } } #endregion #region 單次聯機 /// <summary> /// 單次聯機 /// </summary> public ActionResult Conn() { string msg = null; Dictionary<string, object> dic = null; try { IMachine machine = CreateMachine(); if (machine.Connect(out msg)) //聯機成功 { dic = new Dictionary<string, object>(); dic["ok"] = true; dic["msg"] = "成功"; return Content(JsonConvert.SerializeObject(dic)); } else { dic = new Dictionary<string, object>(); dic["ok"] = false; dic["msg"] = "聯機失敗:" + msg; return Content(JsonConvert.SerializeObject(dic)); } } catch (Exception ex) { dic = new Dictionary<string, object>(); dic["ok"] = false; dic["msg"] = "錯誤:" + ex.Message; return Content(JsonConvert.SerializeObject(dic)); } } #endregion #region 聯機并使能硬紙幣器 /// <summary> /// 聯機并使能硬紙幣器 /// </summary> public ActionResult ConnectEnable() { string msg = null; Dictionary<string, object> dic = null; try { IMachine machine = CreateMachine(); if (Connect(out msg) && machine.CoinEnable(out msg) && machine.PaperMoneyEnable(out msg)) //聯機并使能硬紙幣器成功 { dic = new Dictionary<string, object>(); dic["ok"] = true; dic["msg"] = "成功"; return Content(JsonConvert.SerializeObject(dic)); } else { dic = new Dictionary<string, object>(); dic["ok"] = false; dic["msg"] = "硬幣器使能失敗:" + msg; return Content(JsonConvert.SerializeObject(dic)); } } catch (Exception ex) { dic = new Dictionary<string, object>(); dic["ok"] = false; dic["msg"] = "錯誤:" + ex.Message; return Content(JsonConvert.SerializeObject(dic)); } } #endregion
2、前臺代碼:

@{ ViewBag.Title = "貨機接口測試"; Layout = null; } <!DOCTYPE html> <html> <head> <title>@ViewBag.Title</title> <script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script> <script type="text/javascript" src="~/Scripts/LongPolling.js"></script> </head> <body> <div style="padding: 20px;"> <input type="button" value="聯機" onclick="connect()" /> <div style="font-size: 20px; line-height: 30px;"> <div style="padding: 20px;"> <span id="msg"> </span> </div> </div> </div> </body> </html> <script type="text/javascript"> //聯機 function connect() { commonAjax({ url: "@Url.Content("/MachineInterface/Conn")", callback: function (data) { if (data.ok) { var html = "聯機成功"; $("#msg").html(html); } else { alert(data.msg); } } }); } </script>
文章列表
全站熱搜