c# SIM 卡拨号

引用:http://www.cnblogs.com/xugang/archive/2012/08/23/2652671.html

写此博客意为抛砖引玉,希望能和博客园的朋友们探讨一下关于.NET 在工业方面的应用,包括:物联网、无线通信、嵌入式开发、工业控制等等。欢迎探讨,多多指教!^_^

 

下面是我在开发中,使用C#代码实现对安装在COM 串行端口上的SIM卡拨号器的拨号调度程序。

 

应用场景:

在使用新能源的风光互补路灯远程管理系统中,通信服务器需要通过无线通信方式唤醒上位机。

> 上位机中内置GPRS 无线上网卡,被安装在风光互补路灯中。

> 通信服务器上扩展出4个COM 串行端口,分别安装有:西门子C35TS 拨号器和西门子MC52I 拨号器。

 

使用需求:

> 监控中心跟上位机进行通信前,对没有连接上的上位机先使用拨号器唤醒;

> 由于长时间连续使用拨号器进行拨号,将导致拨号器的宕机情况,所以采用轮番调用的线性方式使用4个拨号器;

> 实现自动检测服务器上的COM 串行端口,并自动识别可使用的拨号器;

> 增加拨号器后,程序能自动识别并添加使用;

> 拔出拨号器后,程序能自动识别并停止使用;

> 能克服拨号器的宕机、假死等异常情况,并在指定的间隔时间重新检测拨号器,并添加到服务器中使用;

> 让拨号器通过SIM卡,实现对上位机的拨号,挂机等功能;

 

程序实现:

程序中应用到AT 指令集,详细介绍请看百度百科。这里附上一些简单的AT 指令集:

复制代码
AT  回车换行  返回:OK



ATD13800000000;  回车换行  建立呼叫



ATA  回车换行  接听电话



ATH  回车换行  挂机



AT+IPR=9600  回车换行  设置模块波特率为9600



AT+CSCA=13800000000 回车换行  设置短信中心号码



AT+CMGF=1  回车换行  设置短信格式为文本方式(=0为PDU方式,用于发送数据和中文)



AT+CMGS  发送文本短信,具体如下:



AT+CMGS=13800000000

>0000123456789
复制代码

 

在程序项目中,需要引用如下程序集:

using System.IO.Ports;

using System.Threading;

using System.Collections;

并使用到了.NET 的串行端口资源 SerialPort 类

 

MySerialPort 类

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

复制代码
public class MySerialPort

{

    private SerialPort com;

    public MySerialPort(string _portName)

     {

         this.com = new SerialPort();



         //接收数据事件

         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; }

    }





     // 检测当前端口是否安装有拨号器

     public bool HasModem()

     {

         read = ""; //清空返回缓冲区

         WriteSerial("AT\r\n");

         Thread.Sleep(100);

         Console.WriteLine(read);

         if (read.Contains("ATOK"))

         {

             Console.WriteLine(this.com.PortName + "端口能使用!");

             return true;

         }

         else return false;

     }



     //进行拨号,唤醒上位机

     public void Dialing(string _SIM)

     {

        IsWorking = true; //正在拨号



        read = ""; //清空返回缓冲区



        WriteSerial(string.Format("ATD{0};\r\n", _SIM));



        System.Threading.Thread.Sleep(20 * 1000);



        //Console.WriteLine(" {0}  ATH TO:{1}", DateTime.Now, _SIM);



        WriteSerial("ATH\r\n");



        Thread.Sleep(500);

        Console.WriteLine(read);

        if (read.Contains("ATHOK"))

        {

            Console.WriteLine(this.com.PortName + "端口拨号已完成!");

        }

        else

        {

            //System.Threading.Thread.Sleep(1000);

            WriteSerial("ATH\r\n");

            Thread.Sleep(500);

            Console.WriteLine(read);



            if (read.Contains("ATHOK"))

            {

                Console.WriteLine(this.com.PortName + "端口拨号已完成!");

            }

            else

            {

                IsWorking = false; //拨号完成

                throw new Exception(this.com.PortName + "拨号异常!");

            }

        }



        IsWorking = false; //拨号完成

     }





    /// <summary>

    /// 向串口端发送命令!

    /// </summary>

    /// <param name="s">命令字符串</param>

    private void WriteSerial(string s)

    {

        //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

            {

                SerialPort mySerial = sender as SerialPort;

                read += mySerial.ReadLine().Trim();

                //Console.WriteLine(mySerial.PortName + " 第" + n.ToString() + "接收数据:" + read);

                //n++;

            }

            catch (TimeoutException)

            {

                return;    //xg备忘:可以将异常写入日志!

            }

            catch (Exception)

            {

                return;    //xg备忘:可以将异常写入日志!

            }

        }

    }



    //关闭

    public void Close()

    {

        if (com != null)

        {

            com.Close();

            com.Dispose();

        }

    }



    //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 "";

    //}



}
复制代码

 

SerialPortList 类

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

复制代码
public class SerialPortList

{

    //已经安装了拨号器的串口对象

    private List<MySerialPort> al = null;



    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 进行拨号,唤醒上位机

                }

            }

                

        }

        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();

        }

        else

        {

            this.UseingSerialPort(_SIM);

            //Thread.Sleep(5000);



            //定期检测串口列表

            if (dtCheck.AddMinutes(reCheckMinutes) <= DateTime.Now)

            {

                // 重新获得串口上已经安装了拨号器的对象

                this.ReGetSerialPortList();

                dtCheck = DateTime.Now;

            }

        }

    }

  

}
复制代码


测试代码如下:

复制代码
class Program

{

    static void Main(string[] args)

    {

        // 获得串口上已经安装了拨号器的对象 (自定义波特率)

        Dictionary<string, int> _portBaudRate = new Dictionary<string, int>();

        _portBaudRate["COM5"] = 9600;

        _portBaudRate["COM6"] = 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");  // 实际SIM卡号

                Thread.Sleep(5000);

            }

        }

        finally

        {

            // 释放所有串口资源组件

            list.ClearAllSerialPort();

        }



        Console.ReadLine();

    }

}
复制代码

测试结果:

c# SIM 卡拨号

 

参考资源:
在C# 中使用SerialPort 类实现串口通信(陆续更新)
AT 指令发送短信流程
短信 PDU 解码

你可能感兴趣的:(C#)