• 使用C# 实现串口拨号器的SIM卡通信[修正版]


     

    在第一版 使用C# 实现串口拨号器的SIM卡通信 的代码中,存在一些实际的问题,经过反复测试和实际使用,对原代码进行了一些改进。

     

    首先,博客园的ㄟ荖樹炪厊ㄖ同学提出将拨号指令ATD发出后,不必使用 Thread.Sleep(20 * 1000) 方法等待20秒后进行挂机,而改用AutoResetEvent来处理,不必让线程死等,也能提高你程序的性能。但修改后效果并不理想,还是使用Thread.Sleep(20 * 1000) 方法快捷实用。

     

    其次,由于拨号器以及服务器等硬件设备的差异,导致反馈信息的速度不一致,以前采用Thread.Sleep() 方法绑定固定秒数然后查看返回信息的方式存在一定的问题,改用如下代码就行:

    int i = 0;
    while (read.Trim() == "" && i <15)
    {
        Thread.Sleep(100);
        i++;
    }
    
    if (read.Contains("ATH") && read.Contains("OK"))
    {  ......  }

    其余的不详细介绍,看代码即可。

    MySerialPort 类

    对每一个连接到COM 串行端口的拨号器实例化 MySerialPort 对象,代码如下:

    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);
    
                //接收数据事件
                this.com.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);
                //串口名
                com.PortName = _portName;
                this.PortName = _portName;
    
                // BaudRate 串行波特率
                com.BaudRate = 9600; //默认值
    
                // 是否启用请求发送 (RTS) 信号。
                com.RtsEnable = true; //由计算机发送   Request   To   Send   信号到联接的调制解调器,以请示允许发送数据。
    
                // 是否使Data Terminal Ready (DTR)线有效。  xugang 2012.8.20 添加
                //com.DtrEnable = true; //Data   Terminal   Ready   是计算机发送到调制解调器的信号,指示计算机在等待接受传输。 
    
                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;
            //设置是否正在使用
            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;
            //当前拨号器能否使用 (解决连续使用拨号问题)
            public bool EnableUse
            {
                get 
                {
                    TimeSpan span = DateTime.Now - lastUseTime;
                    return span.TotalSeconds >= 60; 
                }
                //set { enableUse = value; }
            }
    
            // 检测当前端口是否安装有拨号器
            public bool HasModem()
            {
                //this.autoResetEvent_Type = AutoResetEvent_Type.HasModem;
    
                //read = ""; //清空返回缓冲区
                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 + "端口无法使用!");
                    return false;
                }
    
            }
    
            //进行拨号,唤醒上位机
            public void Dialing(string _SIM)
            {
                IsWorking = true; //正在拨号
                try
                {
                    //this.autoResetEvent_Type = AutoResetEvent_Type.Dialing;
    
                    read = ""; //清空返回缓冲区
    
                    StringBuilder sb = new StringBuilder(36);
                    sb.Append(this.com.PortName);
                    sb.Append("端口开始对SIM卡:");
                    sb.Append(_SIM);
                    sb.Append(" 进行拨号...");
                    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 = ""; //清空返回缓冲区
    
                    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 + "端口拨号已完成!");
                    }
                    else
                    {
                        read = ""; //清空返回缓冲区
    
                        //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 + "端口拨号已完成!");
                        }
                        else
                        {
                            //IsWorking = false; //拨号完成
    
                            string strExc = this.com.PortName + "拨号异常!";
                            Console.WriteLine(strExc);
    
                            throw new Exception(strExc);
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    IsWorking = false; //拨号完成
                }
            }
    
    
            /// <summary>
            /// 向串口端发送命令!
            /// </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 = "";
            //接收数据事件方法
            void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                if (sender is SerialPort)
                {
                    try
                    {
                        Thread.Sleep(500);
                        read = ""; //清空返回缓冲区
                        SerialPort mySerial = sender as SerialPort;
                        if (mySerial.IsOpen == false)
                        {
                            mySerial.Open();
                        }
    
                        //方法一
                        //read += mySerial.ReadLine().Trim();
                        //Console.WriteLine(mySerial.PortName + " 第" + n.ToString() + "接收数据:" + 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 "";
            //}
    
    
            //关闭
            public void Close()
            {
                if (com != null)
                {
                    com.Close();
                    com.Dispose();
                }
            }
    
        }
    
    }

    SerialPortList 类

    定义一个 SerialPortList 类,实现对所有连接上的拨号器 MySerialPort 对象进行管理和调度使用。代码如下:

    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
        {
            //已经安装了拨号器的串口对象
            private List<MySerialPort> al = null;
            //可使用的拨号器个数
            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;
            //当前有无可使用的串口拨号器
            public bool HasPort
            {
                get { return hasPort; }
                //set { hasPort = value; }
            }
    
            private int reCheckMinutes = 30; //默认30分钟
            //串口拨号器的重新检测间隔分钟
            public int ReCheckMinutes
            {
                get { return reCheckMinutes; }
                set { reCheckMinutes = value; }
            }
    
            #region  构造方法重载
            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  构造方法重载
    
            /// <summary>
            ///  获得串口上已经安装了拨号器的对象
            /// </summary>
            public void GetSerialPortList()
            {
                al = new List<MySerialPort>();
    
                //步骤一: 获得所有的串口名称(列表)
                string[] ports = SerialPort.GetPortNames();
    
                foreach (string port in ports)
                {
                    MySerialPort mySerialPort = null;
    
                    Console.WriteLine("正在检测:" + port); //测试使用
    
                    //是否设置波特率?
                    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;
                    }
                }
    
                // 判断当前计算机有无可使用串口端
                hasPort = al.Count <= 0 ? false : true;
            }
    
    
            /// <summary>
            /// 重新获得串口上已经安装了拨号器的对象
            /// </summary>
            public void ReGetSerialPortList()
            {
                if (al == null) GetSerialPortList();
                else
                {
                    //步骤一: 重新获得所有的串口名称(列表)
                    string[] portsName_2 = SerialPort.GetPortNames();
    
                    //如果当前串口数目 > 正在使用的COM
                    if (portsName_2.Length > al.Count)
                    {
                        Console.WriteLine("正在重新检测可以使用的拨号器!"); //测试使用
                        foreach (string pName_2 in portsName_2)
                        {
                            //当前串口名是否存在拨号列表中
                            bool hasAt = al.Exists(delegate(MySerialPort port_1)
                            {
                                return pName_2 == port_1.PortName;
                            });
    
                            //如果当前串口名不存在拨号列表中,则重新检测!
                            if (!hasAt)
                            {
                                Console.WriteLine("正在重新检测:" + pName_2); //测试使用
    
                                MySerialPort mySerialPort = null;
    
                                //是否设置波特率?
                                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;
                                }
                            }
                        }
                    }
                }
    
                // 判断当前计算机有无可使用串口端
                hasPort = al.Count <= 0 ? false : true;
            }
    
            /// <summary>
            /// 重新获得串口上已经安装了拨号器的对象 (波特率使用默认值)
            /// </summary>
            public void ReGetSerialPortList(int _reCheckMinutes)
            {
                //串口拨号器的重新检测间隔分钟
                reCheckMinutes = _reCheckMinutes;
    
                ReGetSerialPortList();//波特率全部使用默认值
            }
    
            /// <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;
            //串口的调度号
            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>
            /// 对已经安装了拨号器的串口调度使用
            /// </summary>
            private void UseingSerialPort(string _SIM)
            {
                if (al == null) return;
    
                // 等待线程进入 
                Monitor.Enter(al);
    
                MySerialPort getPort = null;
                try
                {
                    //获得当前调用的串口对象的索引号
                    int num = IndexNumber();
    
                    if (num >= 0) //判断是否存在拨号器
                    {
                        getPort = al[num];
                        if (getPort != null && !getPort.IsWorking)
                        {
                            getPort.Dialing(_SIM); //对 SIM 进行拨号,唤醒上位机
                        }
                    }
                    else
                    {
                        //没有可使用的拨号器,则重新检测。
                        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
                {
                    // 通知其它对象
                    Monitor.Pulse(al);
                    // 释放对象锁 
                    Monitor.Exit(al);
                }
            }
    
            //重新检测端口时间
            private DateTime dtCheck = DateTime.Now;
    
            /// <summary>
            /// 调用拨号器
            /// </summary>
            /// <param name="_SIM"></param>
            public void InvokingSerialPort(string _SIM)
            {
                if (hasPort == false)
                {
                    // 获得串口上已经安装了拨号器的对象
                    this.GetSerialPortList();
                }
                if (hasPort == true)
                {
                    this.UseingSerialPort(_SIM);
                    //Thread.Sleep(5000);
                }
    
                //定期检测串口列表
                //if (dtCheck.AddMinutes(reCheckMinutes) <= DateTime.Now)
                if (dtCheck.AddMinutes(5) <= DateTime.Now)
                {
                    // 重新获得串口上已经安装了拨号器的对象
                    this.ReGetSerialPortList();
                    dtCheck = DateTime.Now;
                }
            }
    
        }
    
    }

    测试代码如下:

    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)
            {
                // 获得串口上已经安装了拨号器的对象 (自定义波特率)
                Dictionary<string, int> _portBaudRate = new Dictionary<string, int>();
                _portBaudRate["COM1"] = 9600;
                _portBaudRate["COM2"] = 9600;
                _portBaudRate["COM7"] = 9600;
    
                SerialPortList list = new SerialPortList(_portBaudRate,5);
    
                try
                {
                    // 获得串口上已经安装了拨号器的对象
                    list.GetSerialPortList();
    
                    if (list.HasPort == false)
                    {
                        Console.WriteLine("当前计算机无可使用的串口拨号器!");
                    }
    
                    while (list.HasPort)
                    {
                        // 调用拨号器
                        list.InvokingSerialPort("13500000000"); 
                        Thread.Sleep(5000);
                    }
    
                    Console.WriteLine("程序运行结束!");
                }
                finally
                {
                    // 释放所有串口资源组件
                    list.ClearAllSerialPort();
                }
    
                Console.ReadLine();
            }
        }
    }
    作者: XuGang   网名:钢钢
    出处: http://xugang.cnblogs.com
    声明: 本文版权归作者和博客园共有。转载时必须保留此段声明,且在文章页面明显位置给出原文连接地址!
  • 相关阅读:
    【mysql】关于IO/内存方面的一些优化
    【mysql】使用tpcc-mysql进行压力测试
    Linux下使用iostat 监视I/O状态
    【JUC】JUC线程池框架综述
    【目录】JUC集合框架目录
    【JUC】JDK1.8源码分析之ConcurrentSkipListSet(八)
    【JUC】JDK1.8源码分析之CopyOnWriteArraySet(七)
    【JUC】JDK1.8源码分析之CopyOnWriteArrayList(六)
    【设计模式】策略模式
    【JUC】JDK1.8源码分析之ConcurrentLinkedQueue(五)
  • 原文地址:https://www.cnblogs.com/xugang/p/2693856.html
Copyright © 2020-2023  润新知