zoukankan      html  css  js  c++  java
  • 使用C# 实现串口拨号器的SIM卡通信

     参考网址:https://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
    ");
             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};
    ", _SIM));
    
            System.Threading.Thread.Sleep(20 * 1000);
    
            //Console.WriteLine(" {0}  ATH TO:{1}", DateTime.Now, _SIM);
    
            WriteSerial("ATH
    ");
    
            Thread.Sleep(500);
            Console.WriteLine(read);
            if (read.Contains("ATHOK"))
            {
                Console.WriteLine(this.com.PortName + "端口拨号已完成!");
            }
            else
            {
                //System.Threading.Thread.Sleep(1000);
                WriteSerial("ATH
    ");
                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();
        }
    }
    复制代码

    测试结果:

  • 相关阅读:
    后缀数组
    网络流 HOJ1087
    备用
    css-具有缩略图的材料列表
    正则匹配log行
    vue-cli的webpack打包,icon无法正确加载
    导出CSV,导出excel数字过长显示科学计数法解决方案
    js导出CSV
    git常见操作指令
    javascript原型的意义
  • 原文地址:https://www.cnblogs.com/bruce1992/p/14836809.html
Copyright © 2011-2022 走看看