使用C# 實(shí)現(xiàn)串口撥號(hào)器的SIM卡通信[修正版]
在第一版 使用C# 實(shí)現(xiàn)串口撥號(hào)器的SIM卡通信 的代碼中,存在一些實(shí)際的問(wèn)題,經(jīng)過(guò)反復(fù)測(cè)試和實(shí)際使用,對(duì)原代碼進(jìn)行了一些改進(jìn)。
首先,博客園的ㄟ荖樹炪厊ㄖ同學(xué)提出將撥號(hào)指令A(yù)TD發(fā)出后,不必使用 Thread.Sleep(20 * 1000) 方法等待20秒后進(jìn)行掛機(jī),而改用AutoResetEvent來(lái)處理,不必讓線程死等,也能提高你程序的性能。但修改后效果并不理想,還是使用Thread.Sleep(20 * 1000) 方法快捷實(shí)用。
其次,由于撥號(hào)器以及服務(wù)器等硬件設(shè)備的差異,導(dǎo)致反饋信息的速度不一致,以前采用Thread.Sleep() 方法綁定固定秒數(shù)然后查看返回信息的方式存在一定的問(wèn)題,改用如下代碼就行:
int i = 0; while (read.Trim() == "" && i <15) { Thread.Sleep(100); i++; } if (read.Contains("ATH") && read.Contains("OK")) { ...... }
其余的不詳細(xì)介紹,看代碼即可。
MySerialPort 類
對(duì)每一個(gè)連接到COM 串行端口的撥號(hào)器實(shí)例化 MySerialPort 對(duì)象,代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Ports; using System.Threading; namespace Con_SerialPortExample { public class MySerialPort { private SerialPort com; public MySerialPort(string _portName) { this.com = new SerialPort(); //this.autoResetEvent_HasModem = new AutoResetEvent(true); //this.autoResetEvent_Dialing = new AutoResetEvent(false); //接收數(shù)據(jù)事件 this.com.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived); //串口名 com.PortName = _portName; this.PortName = _portName; // BaudRate 串行波特率 com.BaudRate = 9600; //默認(rèn)值 // 是否啟用請(qǐng)求發(fā)送 (RTS) 信號(hào)。 com.RtsEnable = true; //由計(jì)算機(jī)發(fā)送 Request To Send 信號(hào)到聯(lián)接的調(diào)制解調(diào)器,以請(qǐng)示允許發(fā)送數(shù)據(jù)。 // 是否使Data Terminal Ready (DTR)線有效。 xugang 2012.8.20 添加 //com.DtrEnable = true; //Data Terminal Ready 是計(jì)算機(jī)發(fā)送到調(diào)制解調(diào)器的信號(hào),指示計(jì)算機(jī)在等待接受傳輸。 try { com.Open(); } catch //(Exception) { Close(); } } public MySerialPort(string _portName, int _baudRate) : this(_portName) { if (_baudRate != 0) { // BaudRate 串行波特率 com.BaudRate = _baudRate; } } private string portName; //串口名稱 public string PortName { get { return portName; } set { portName = value; } } // BaudRate 串行波特率 public int BaudRate { get { return com.BaudRate; } set { com.BaudRate = value; } } private bool isWorking; //設(shè)置是否正在使用 public bool IsWorking { get { return isWorking; } set { isWorking = value; } } //private enum AutoResetEvent_Type //{ // None, HasModem, Dialing //} //private AutoResetEvent autoResetEvent_HasModem; //private AutoResetEvent autoResetEvent_Dialing; //private AutoResetEvent_Type autoResetEvent_Type = AutoResetEvent_Type.None; //private bool enableUse; private DateTime lastUseTime = DateTime.MinValue; //當(dāng)前撥號(hào)器能否使用 (解決連續(xù)使用撥號(hào)問(wèn)題) public bool EnableUse { get { TimeSpan span = DateTime.Now - lastUseTime; return span.TotalSeconds >= 60; } //set { enableUse = value; } } // 檢測(cè)當(dāng)前端口是否安裝有撥號(hào)器 public bool HasModem() { //this.autoResetEvent_Type = AutoResetEvent_Type.HasModem; //read = ""; //清空返回緩沖區(qū) WriteSerial("AT\r\n"); //Thread.Sleep(500); //this.autoResetEvent_HasModem.WaitOne(5 * 1000); //this.autoResetEvent_Type = AutoResetEvent_Type.None; int i = 0; while (read.Trim() == "" && i < 40) { Thread.Sleep(100); i++; } Console.WriteLine(read.TrimEnd('\r', '\n')); //Console.Write(read); //if (read.Contains("AT") && read.Contains("OK")) if (read.Contains("OK")) { Console.WriteLine(this.com.PortName + "端口能使用!"); return true; } else { Console.WriteLine(this.com.PortName + "端口無(wú)法使用!"); return false; } } //進(jìn)行撥號(hào),喚醒上位機(jī) public void Dialing(string _SIM) { IsWorking = true; //正在撥號(hào) try { //this.autoResetEvent_Type = AutoResetEvent_Type.Dialing; read = ""; //清空返回緩沖區(qū) StringBuilder sb = new StringBuilder(36); sb.Append(this.com.PortName); sb.Append("端口開始對(duì)SIM卡:"); sb.Append(_SIM); sb.Append(" 進(jìn)行撥號(hào)..."); Console.WriteLine(sb.ToString()); WriteSerial(string.Format("ATD{0};\r\n", _SIM)); System.Threading.Thread.Sleep(20 * 1000); //this.autoResetEvent_Dialing.WaitOne(20 * 1000); //this.autoResetEvent_Type = AutoResetEvent_Type.None; read = ""; //清空返回緩沖區(qū) WriteSerial("ATH\r\n"); //this.autoResetEvent.WaitOne(1000); int i = 0; while (read.Trim() == "" && i <15) { Thread.Sleep(100); i++; } Console.WriteLine(read); if (read.Contains("ATH") && read.Contains("OK")) { Console.WriteLine(this.com.PortName + "端口撥號(hào)已完成!"); } else { read = ""; //清空返回緩沖區(qū) //System.Threading.Thread.Sleep(1000); WriteSerial("ATH\r\n"); //this.autoResetEvent.WaitOne(1000); i = 0; while (read.Trim() == "" && i < 20) { Thread.Sleep(100); i++; } Console.WriteLine(read); if (read.Contains("ATH") && read.Contains("OK")) { Console.WriteLine(this.com.PortName + "端口撥號(hào)已完成!"); } else { //IsWorking = false; //撥號(hào)完成 string strExc = this.com.PortName + "撥號(hào)異常!"; Console.WriteLine(strExc); throw new Exception(strExc); } } } catch (Exception ex) { throw ex; } finally { IsWorking = false; //撥號(hào)完成 } } /// <summary> /// 向串口端發(fā)送命令! /// </summary> /// <param name="s">命令字符串</param> private void WriteSerial(string s) { //this.autoResetEvent_HasModem.WaitOne(20*1000); //mLogger.Info(s); byte[] buff = Encoding.ASCII.GetBytes(s); try { this.com.Write(buff, 0, buff.Length); } catch (Exception ex) { //WriteExecLog.Writing(ex); Console.WriteLine(ex.Message); } } //int n = 0; string read = ""; //接收數(shù)據(jù)事件方法 void com_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (sender is SerialPort) { try { Thread.Sleep(500); read = ""; //清空返回緩沖區(qū) SerialPort mySerial = sender as SerialPort; if (mySerial.IsOpen == false) { mySerial.Open(); } //方法一 //read += mySerial.ReadLine().Trim(); //Console.WriteLine(mySerial.PortName + " 第" + n.ToString() + "接收數(shù)據(jù):" + read); //方法二 byte[] readBuffer = new byte[1024]; int count = mySerial.Read(readBuffer, 0, 1023); if (count > 0) { read = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count); //Console.WriteLine(read); } //AT 返回 //if (autoResetEvent_Type == AutoResetEvent_Type.HasModem // && read.Contains("OK")) //{ // this.autoResetEvent_HasModem.Set(); //} //ATD 返回 //if (autoResetEvent_Type == AutoResetEvent_Type.Dialing // && (read.Contains("ERROR") || read.Contains("BUSY") || read.Contains("OK") || read.Contains("NO CARRIER")) // ) //{ // this.autoResetEvent_Dialing.Set(); //} } catch (TimeoutException) { return; //xg備忘:可以將異常寫入日志! } catch (Exception) { return; //xg備忘:可以將異常寫入日志! } //finally //{ // this.autoResetEvent_HasModem.Set(); //} } } //private string ReadSerial() //{ // while (_keepReading) // { // if (com.IsOpen) // { // //byte[] readBuffer = new byte[com.ReadBufferSize + 1]; // byte[] readBuffer = new byte[10]; // try // { // //int count = com.Read(readBuffer, 0, com.ReadBufferSize); // int count = com.Read(readBuffer, 0, 9); // String SerialIn = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count); // if (count != 0) // { // return SerialIn; // } // } // catch (TimeoutException) // { // return ""; // } // } // else // { // TimeSpan waitTime = new TimeSpan(0, 0, 0, 0, 50); // Thread.Sleep(waitTime); // } // } // return ""; //} //關(guān)閉 public void Close() { if (com != null) { com.Close(); com.Dispose(); } } } }
SerialPortList 類
定義一個(gè) SerialPortList 類,實(shí)現(xiàn)對(duì)所有連接上的撥號(hào)器 MySerialPort 對(duì)象進(jìn)行管理和調(diào)度使用。代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Ports; using System.Threading; namespace Con_SerialPortExample { public class SerialPortList { //已經(jīng)安裝了撥號(hào)器的串口對(duì)象 private List<MySerialPort> al = null; //可使用的撥號(hào)器個(gè)數(shù) public int WorkableCount { get { if (al == null) return 0; else return al.Count; } } private Dictionary<string, int> portBaudRate = null; //波特率配置列表 public Dictionary<string, int> PortBaudRate { get { return portBaudRate; } set { portBaudRate = value; } } private bool hasPort = false; //當(dāng)前有無(wú)可使用的串口撥號(hào)器 public bool HasPort { get { return hasPort; } //set { hasPort = value; } } private int reCheckMinutes = 30; //默認(rèn)30分鐘 //串口撥號(hào)器的重新檢測(cè)間隔分鐘 public int ReCheckMinutes { get { return reCheckMinutes; } set { reCheckMinutes = value; } } #region 構(gòu)造方法重載 public SerialPortList() { } public SerialPortList(Dictionary<string, int> _portBaudRate) { this.portBaudRate = _portBaudRate; } public SerialPortList(int _reCheckMinutes) { this.reCheckMinutes = _reCheckMinutes; } public SerialPortList(Dictionary<string, int> _portBaudRate, int _reCheckMinutes) { this.portBaudRate = _portBaudRate; this.reCheckMinutes = _reCheckMinutes; } #endregion 構(gòu)造方法重載 /// <summary> /// 獲得串口上已經(jīng)安裝了撥號(hào)器的對(duì)象 /// </summary> public void GetSerialPortList() { al = new List<MySerialPort>(); //步驟一: 獲得所有的串口名稱(列表) string[] ports = SerialPort.GetPortNames(); foreach (string port in ports) { MySerialPort mySerialPort = null; Console.WriteLine("正在檢測(cè):" + port); //測(cè)試使用 //是否設(shè)置波特率? if (portBaudRate != null && portBaudRate.ContainsKey(port) && portBaudRate[port] != 0) { mySerialPort = new MySerialPort(port, portBaudRate[port]); } else mySerialPort = new MySerialPort(port); bool ok = mySerialPort.HasModem(); if (ok) { al.Add(mySerialPort); } else { mySerialPort.Close(); mySerialPort = null; } } // 判斷當(dāng)前計(jì)算機(jī)有無(wú)可使用串口端 hasPort = al.Count <= 0 ? false : true; } /// <summary> /// 重新獲得串口上已經(jīng)安裝了撥號(hào)器的對(duì)象 /// </summary> public void ReGetSerialPortList() { if (al == null) GetSerialPortList(); else { //步驟一: 重新獲得所有的串口名稱(列表) string[] portsName_2 = SerialPort.GetPortNames(); //如果當(dāng)前串口數(shù)目 > 正在使用的COM if (portsName_2.Length > al.Count) { Console.WriteLine("正在重新檢測(cè)可以使用的撥號(hào)器!"); //測(cè)試使用 foreach (string pName_2 in portsName_2) { //當(dāng)前串口名是否存在撥號(hào)列表中 bool hasAt = al.Exists(delegate(MySerialPort port_1) { return pName_2 == port_1.PortName; }); //如果當(dāng)前串口名不存在撥號(hào)列表中,則重新檢測(cè)! if (!hasAt) { Console.WriteLine("正在重新檢測(cè):" + pName_2); //測(cè)試使用 MySerialPort mySerialPort = null; //是否設(shè)置波特率? if (portBaudRate != null && portBaudRate.ContainsKey(pName_2) && portBaudRate[pName_2] != 0) { mySerialPort = new MySerialPort(pName_2, portBaudRate[pName_2]); } else mySerialPort = new MySerialPort(pName_2); bool ok = mySerialPort.HasModem(); if (ok) { al.Add(mySerialPort); } else { mySerialPort.Close(); mySerialPort = null; } } } } } // 判斷當(dāng)前計(jì)算機(jī)有無(wú)可使用串口端 hasPort = al.Count <= 0 ? false : true; } /// <summary> /// 重新獲得串口上已經(jīng)安裝了撥號(hào)器的對(duì)象 (波特率使用默認(rèn)值) /// </summary> public void ReGetSerialPortList(int _reCheckMinutes) { //串口撥號(hào)器的重新檢測(cè)間隔分鐘 reCheckMinutes = _reCheckMinutes; ReGetSerialPortList();//波特率全部使用默認(rèn)值 } /// <summary> /// 釋放所有串口資源組件 /// </summary> public void ClearAllSerialPort() { if (al != null) { for (int i = 0; i < al.Count; i++) { al[i].Close(); al[i] = null; } al = null; } if (portBaudRate != null) { portBaudRate = null; } } private int index_Number = -1; //串口的調(diào)度號(hào) private int IndexNumber() { lock (this) { if (index_Number + 1 >= al.Count) { if (al.Count == 0) index_Number = -1; else index_Number = 0; } else { index_Number++; } return index_Number; } } /// <summary> /// 對(duì)已經(jīng)安裝了撥號(hào)器的串口調(diào)度使用 /// </summary> private void UseingSerialPort(string _SIM) { if (al == null) return; // 等待線程進(jìn)入 Monitor.Enter(al); MySerialPort getPort = null; try { //獲得當(dāng)前調(diào)用的串口對(duì)象的索引號(hào) int num = IndexNumber(); if (num >= 0) //判斷是否存在撥號(hào)器 { getPort = al[num]; if (getPort != null && !getPort.IsWorking) { getPort.Dialing(_SIM); //對(duì) SIM 進(jìn)行撥號(hào),喚醒上位機(jī) } } else { //沒有可使用的撥號(hào)器,則重新檢測(cè)。 this.ReGetSerialPortList(); } } catch { //再一次檢查該 COM 能否使用! (范工提議) if (getPort != null) { string re_PortName = getPort.PortName; al.Remove(getPort); //從可用列表去除 getPort.Close(); MySerialPort mySerialPort = new MySerialPort(re_PortName); bool ok = mySerialPort.HasModem(); if (ok) { al.Add(mySerialPort); //重新添加到列表 } else { mySerialPort.Close(); mySerialPort = null; } } } finally { // 通知其它對(duì)象 Monitor.Pulse(al); // 釋放對(duì)象鎖 Monitor.Exit(al); } } //重新檢測(cè)端口時(shí)間 private DateTime dtCheck = DateTime.Now; /// <summary> /// 調(diào)用撥號(hào)器 /// </summary> /// <param name="_SIM"></param> public void InvokingSerialPort(string _SIM) { if (hasPort == false) { // 獲得串口上已經(jīng)安裝了撥號(hào)器的對(duì)象 this.GetSerialPortList(); } if (hasPort == true) { this.UseingSerialPort(_SIM); //Thread.Sleep(5000); } //定期檢測(cè)串口列表 //if (dtCheck.AddMinutes(reCheckMinutes) <= DateTime.Now) if (dtCheck.AddMinutes(5) <= DateTime.Now) { // 重新獲得串口上已經(jīng)安裝了撥號(hào)器的對(duì)象 this.ReGetSerialPortList(); dtCheck = DateTime.Now; } } } }
測(cè)試代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; // //using System.IO.Ports; using System.Threading; //using System.Collections; namespace Con_SerialPortExample { class Program { static void Main(string[] args) { // 獲得串口上已經(jīng)安裝了撥號(hào)器的對(duì)象 (自定義波特率) Dictionary<string, int> _portBaudRate = new Dictionary<string, int>(); _portBaudRate["COM1"] = 9600; _portBaudRate["COM2"] = 9600; _portBaudRate["COM7"] = 9600; SerialPortList list = new SerialPortList(_portBaudRate,5); try { // 獲得串口上已經(jīng)安裝了撥號(hào)器的對(duì)象 list.GetSerialPortList(); if (list.HasPort == false) { Console.WriteLine("當(dāng)前計(jì)算機(jī)無(wú)可使用的串口撥號(hào)器!"); } while (list.HasPort) { // 調(diào)用撥號(hào)器 list.InvokingSerialPort("13500000000"); Thread.Sleep(5000); } Console.WriteLine("程序運(yùn)行結(jié)束!"); } finally { // 釋放所有串口資源組件 list.ClearAllSerialPort(); } Console.ReadLine(); } } }
| 作者: XuGang 網(wǎng)名:鋼鋼 |
| 出處: http://xugang.cnblogs.com |
| 聲明: 本文版權(quán)歸作者和博客園共有。轉(zhuǎn)載時(shí)必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接地址! |
posted on 2012-09-19 16:06 鋼鋼 閱讀(1785) 評(píng)論(7) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)