短信设备通过C#来实现API函数接口

分享

通过上几篇短信设备的串口类学习,PDU编码分析和PDU码解码后今日这篇文章是实现一个API接口的;只是简单的实现了,还没有完善的一个程序,只供参考使用,
设备类目前可以完成发送、接收、读取和删除短信,需要其他功能可以自行添加。
本程序使用用前两篇实现PDU编解码类,在此基础上完成短信的发送和读取
PDU编解码参考:《C#实现PUD编码 》《C#实现PDU格式解码程序 》
下面正式开始解说实现API:

       类名:GSMModem,提供短信猫API,完成短信的发送与接收等。直接使用串口类SerialPort,通过实例化了的串口对象sp完成向设备发送AT指令及读取设备返回字符串,从而完成对“猫”的控制及数据交换。

       属性:ComPort、BaudRate、IsOpen。ComPort,串口号,此属性用来对串口sp的PortName访问,为设备所连接的串口号;BaudRate,波特率,此属性用来访问串口实例sp的BaudRate属性,是PC与短信猫通信的波特率;IsOpen,是否打开,此属性用来访问串口实例sp的IsOpen属性,指示设备是否打开。

       方法:Open、Close、SendAT、SendMsg、ReadMsgByIndex、DeleteMsgByIndex。Open:设备打开;Close:设备关闭;SendAT:发送AT指令;SendMsg:发送短信;ReadMsgByIndex:按序号读短信;DeleteMsgByIndex:按序号删除短信。
事件:OnRecieved,收到新消息触发此事件,应在事件调用读信息函数。

构造函数:

        public GSMModem()
        {
            sp = new SerialPort();

            sp.ReadTimeout = 2500;      //读超时时间 发送短信时间的需要
            sp.RtsEnable = true;        //必须为true 这样串口才能接收到数据

            sp.ReceivedBytesThreshold = 6;      //引发事件DataReceived前的字符数
            sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
        }
        public GSMModem(string comPort, int baudRate)
        {
            sp = new SerialPort();

            sp.PortName = comPort;      //
            sp.BaudRate = baudRate;
            sp.ReadTimeout = 2500;      //读超时时间 发送短信时间的需要
            sp.RtsEnable = true;        //必须为true 这样串口才能接收到数据

            sp.ReceivedBytesThreshold = 6;      //引发事件DataReceived前的字符数
            sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
        }

      完成sp的实例化与初始化,值得一提的sp.RtsEnable = true; 我写串口调试器的时候,刚开始没设此属性,总是收不到数据,后来好不容易找到这个问题,具体原因不知道,加上就是了。
       sp.ReadTimeout属性设为2000ms时,发短信时读取超时(发送短信用时比较长),2500可以正常了。最好是3秒3000

      属性:属性代码比较简单,在此只列出一个,其他与这个类似,详见附件。

        public string ComPort
        {
            get
            {
                return sp.PortName;
            }
            set
            {
                try
                {
                    sp.PortName = value;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

      属性代码只提供对类中串口实例的属性访问,比较简单,不多讲解了。

方法:        
      

  • Open,Close 比较简单,仅仅调用的sp的打开与关闭函数

      更新:添加设备初始部分,设置关回显,PDU模式,收到短信提醒
      更新2:添加设备连接识别,解决上个更新后未连接设备或串口号或波特率设置不正确时连接时间太长(30s),而且错误的连接成功问题

        /// <summary>
        /// 设备打开函数,无法时打开将引发异常
        /// </summary>
        public void Open()
        {
            try
            {
                sp.Open();
            }
            catch (Exception ex)
            {
                throw ex;
            }

            //初始化设备
            if (sp.IsOpen)
            {
                sp.DataReceived -= sp_DataReceived;

                //更新添加连接识别
                sp.Write("AT\r");
                Thread.Sleep(50);
                string s = sp.ReadExisting().Trim();
                s = s.Substring(s.Length - 2, 2);           //有回显时 去后两位有效字符
                if (s != "OK")
                {
                    throw new Exception("硬件连接错误");    //硬件未连接、串口或是波特率设置错误
                }

                try
                {
                    SendAT("ATE0");
                    SendAT("AT+CMGF=0");
                    SendAT("AT+CNMI=2,1");
                }
                catch { }
            }
        }


  • Close函数和此类似,但仅有关闭函数,不再贴出来了。


  • SendAT,完成AT指令的发送,并返回设备返回的字符串。

        /// <summary>
        /// 发送AT指令 逐条发送AT指令 调用一次发送一条指令
        /// 能返回一个OK或ERROR算一条指令
        /// </summary>
        /// <param name="ATCom">AT指令</param>
        /// <returns>发送指令后返回的字符串</returns>
        public string SendAT(string ATCom)
        {
            string result = string.Empty;
            //忽略接收缓冲区内容,准备发送
            sp.DiscardInBuffer();

            //注销事件关联,为发送做准备
            sp.DataReceived -= sp_DataReceived;

            //发送AT指令
            try
            {
                sp.Write(ATCom + "\r");
            }
            catch (Exception ex)
            {
                sp.DataReceived += sp_DataReceived;
                throw ex;
            }

            //接收数据 循环读取数据 直至收到“OK”或“ERROR”
            try
            {
                string temp = string.Empty;
                while (temp.Trim() != "OK" && temp.Trim() != "ERROR")
                {
                    temp = sp.ReadLine();
                    result += temp;
                }
                return result;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                //事件重新绑定 正常监视串口数据
                sp.DataReceived += sp_DataReceived;
            }
        }

       发送AT指令的过程:先清空缓冲区,注销事件关联(收到字符不再自动调用sp_DataReceived函数,这样发送AT指令后方能正常接收数据);发送AT指令,接收返回数据;最后重新绑定事件,以监视串口数据,以正常引发OnRecieved事件。

  • SendMsg 发送短信,参数:phone 手机号,msg 短信内容。



        此方法调用SendAT方法,发送指令 实例化PDUEncoding类完成编码,从而完成短信的发送
更新:发送短信的SendAT同时发送AT指令和短信PDU串,有的短信猫处理会出现一定问题,改成分开发送,收到”>”后再发PDU串

        /// <summary>
        /// 发送短信
        /// 发送失败将引发异常
        /// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="msg">短信内容</param>
        public void SendMsg(string phone, string msg)
        {
            PDUEncoding pe = new PDUEncoding();
            string temp = pe.PDUUSC2Encoder(phone, msg);

            int len = (temp.Length - Convert.ToInt32(temp.Substring(0, 2), 16) * 2 - 2) / 2;  //计算长度
            try
            {
                //注销事件关联,为发送做准备
                sp.DataReceived -= sp_DataReceived;

                sp.Write("AT+CMGS=" + len.ToString() + "\r");
                sp.ReadTo(">");
                sp.DiscardInBuffer();

                //事件重新绑定 正常监视串口数据
                sp.DataReceived += sp_DataReceived;

                temp = SendAT(temp + (char)(26));  //26 Ctrl+Z ascii码
            }
            catch (Exception)
            {
                throw new Exception("短信发送失败");
            }
            finally
            {
            }

            if (temp.Substring(temp.Length - 4, 3).Trim() == "OK")
            {
                return;
            }

            throw new Exception("短信发送失败");
        }

     发送过程:实例化PDUEncoding类,完成PDU的编码工作;计算长度(AT+CMGS=<LEN><cr>中的LEN)发送指令;查看是否发送成功,不成功则引发异常。

  • ReadMsgByIndex 按序号读取短信

        /// <summary>
        /// 按序号读取短信
        /// </summary>
        /// <param name="index">序号</param>
        /// <param name="msgCenter">短信中心</param>
        /// <param name="phone">发送方手机号码</param>
        /// <param name="msg">短信内容</param>
        /// <param name="time">时间字符串</param>
        public void ReadMsgByIndex(int index, out string msgCenter, out string phone, out string msg, out string time)
        {
            string temp = string.Empty;
            PDUEncoding pe = new PDUEncoding();
            try
            {
                temp = SendAT("AT+CMGR=" + index.ToString() + "\r");
            }
            catch (Exception ex)
            {
                throw ex;
            }

            if (temp.Trim() == "ERROR")
            {
                throw new Exception("没有此短信");
            }
            temp = temp.Split((char)(13))[2];       //取出PDU串(char)(13)为0x0a即
 按
分为多个字符串 第3个是PDU串

            pe.PDUDecoder(temp, out msgCenter, out phone, out msg, out time);
        }

      按序号读短信,实例化PDUEncoding;发送AT指令;提取PDU串;PDU解码。

  • DeleteMsgByIndex 参数 index 序号,删除指定序号的短信(短信在SIM卡中是按序号存储的)。

        public void DeleteMsgByIndex(int index)
        {
            if (SendAT("AT+CMGD=" + index.ToString() + "\r").Trim() == "OK")
            {
                return;
            }

            throw new Exception("删除失败");
        }
    }

    删除过程:发送AT指令;查看是否成功;成功则返回,否则引发异常。

事件:OnRecieved  收到短信后引发事件。
      事件声明:  

  /// <summary>
  /// 创建事件收到信息的委托
   /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
   public delegate void OnRecievedHandler(object sender, EventArgs e);
   
  /// <summary>
  /// 收到短信息事件 OnRecieved 
  /// 收到短信将引发此事件
   /// </summary>
   public event OnRecievedHandler OnRecieved;

事件由串口事件引发,检测出有短信,则引发事件

  void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
  {
      string temp = sp.ReadLine();
      if (temp.Length > 8)
      {
          if (temp.Substring(0, 6) == "+CMTI:")  //收到短信标记
          {
              NewMsgIndex = Convert.ToInt32(temp.Split(',')[1]);
              OnRecieved(this, e);
          }
      }
  }



      事件使用方式:

gm.OnRecieved += new GSMModem.OnRecievedHandler(gm_OnRecieved); //添加事件绑定


      程序引发事件,事件函数

  void gm_OnRecieved(object sender, EventArgs e)
  {
      string s1, s2, s3, s4;
      gm.ReadMsgByIndex(gm.NewMsgIndex, out s1, out s2, out s3, out s4);
      textBox1.Text = s1 + s2 + s3 + s4;  //此处引发异常(此处不属于主进程)  须回调函数才能改变此属性
   }

      一般还需要一个回调函数,这样才能改变Form中的控件属性,详细参考MSDN
委托与事件可参考:《C#事件(event)解析

类库简化版完成,功能尚不完善,加入自己程序时要做一定更改,让代码更安全


附件:

GSMModem.rar


短信设备二次开发 2022-04-05 0 0

admin

  • 注册时间 : 2022-03-30 04:08:47
  • 邮箱地址 : admin@tenghengkeji.com
  • 此页面用于展示用户的基本上资料信息

回帖 ( 0 )