zoukankan      html  css  js  c++  java
  • 我的OO实践由GPS消息处理抽象出一通用命令处理类

    1. 实验要求

    (1)综合运用以前学到的控制语句、继承、封装、接口等知识,完成具有实际运用功能的程序。

    (2)通过运用学过的知识进一步的巩固和掌握学到的知识。

    1. 实验内容

      1. 使用GPS GATE软件模拟GPS卫星发出的GPS信号,编写程序对GPS GATE发出的信息进行接收、解析、处理。将处理好的信息按照固定的格式存储至文件中(经度、纬度、时间、速度、高度)。

        下面是主要用到的GPS信息的格式:

        1. GPS/TRANSIT Data(RMC)推荐定位信息 (在项目中就使用了这个报文的定位数据)

      $GPRMC,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)*hh(CR)(LF)

      (1)UTC时间,hhmmss(时分秒)格式
      (2)定位状态,A=有效定位,V=无效定位
      (3)纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
      (4)纬度半球N(北半球)或S(南半球)
      (5)经度dddmm.mmmm(度分)格式(前面的0也将被传输)
      (6)经度半球E(东经)或W(西经)
      (7)地面速率(000.0~999.9节,前面的0也将被传输)

      (8)地面航向(000.0~359.9度,以真北为参考基准,前面的0也将被传输) (9)UTC日期,ddmmyy(日月年)格式
      (10)磁偏角(000.0~180.0度,前面的0也将被传输)
      (11)磁偏角方向,E(东)或W(西)
      (12)模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)

      (13) hh(CR)(LF) hh是前面从第一个数据到最后一个数据的校验和,(CR)(LF)是回车换行,表示一个字符串的结束。

      具体示例:

      $GPRMC,121212.456,A,3232.1234,N,12121.3322,W,0.15,305.12,121299, ,*22

      2. GPS Fix Data(GGA)GPS定位信息

      $GPGGA,(1),(2),(3),(4),(5),(6),(7),(8),(9),M,(10),M,(11),(12)*hh(CR)(LF)

      (1)UTC时间,hhmmss(时分秒)格式
      (2)纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
      (3)纬度半球N(北半球)或S(南半球)
      (4)经度dddmm.mmmm(度分)格式(前面的0也将被传输)
      (5)经度半球E(东经)或W(西经)
      (6)GPS状态:0=未定位,1=非差分定位,2=差分定位,6=正在估算
      (7)正在使用解算位置的卫星数量(00~12)(前面的0也将被传输)
      (8)HDOP水平精度因子(0.5~99.9)
      (9)海拔高度(-9999.9~99999.9)
      (10)地球椭球面相对大地水准面的高度
      (11)差分时间(从最近一次接收到差分信号开始的秒数,如果不是差分定位将为空)
      (12)差分站ID号0000~1023(前面的0也将被传输,如果不是差分定位将为空)

      (13) hh(CR)(LF) hh是前面从第一个数据到最后一个数据的校验和,(CR)(LF)是回车换行,表示一个字符串的结束。

      具体示例:

    $GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F

    校验和是指这一行的所有非数字字符,按照"字母、空格、句点、正号= 0;负号=1"的规则换算成0和1后,将这一行中原来的全部数字加起来,以10为模计算后所得的和。校验和可以检查出90%的数据存储或传送错误。按十进制加起来的个位数字的校验和,用于精确纠正误差。

    1. 使用异常处理,对接收信息的过程中可能产生的异常进行处理。
    1. (选作)编写C#Winfrom 程序,能够将解析到的数据轨迹绘制到相应的地图上。
    1. 详细设计

        该实现主要是实践我在前文《谈谈我处理异常的一般方法》中提出的一些观点,以及一些面向对象设计的思想,和一些设计模式的运用。

        在看到这个问题的时候,首先要对需求进行分析。该问题的数据流程比较清晰,见Figure 1。

    Figure 1 数据流

        从个人经验来讲,我认为在指令的解析部分可以抽象出一个比较通用的方式,我们把GPS指令文本流替换为二进制流,将单条指令文本替换为单条指令的二进制信息。

    Figure 2 通用数据流

        我做的抽象框架如所示Figure 3 类视图所示。

    Figure 3 类视图

        首先我们应当有一个类来处理整个问题,称之为CommandProcessor。它负责从外部设备接收数据、并且将处理后的消息通过事件的方式向外部提供。该类还应当能够将单条指令从流中取出来,因而我在这个类中提供了一个虚的方法GetSingleCommandString(),并且进行了一个通用的简单实现、即每行为一条指令。这里参数都用string来表示了,用string来表示二进制数据也没太大问题,但对于纯文本的数据处理起来则比较方便。

        在将命令分出来之后,我们需要将一条单独的指令转换为一个强类型的命令。我们用ICommand来表示一个被分析后的命令,则问题就是如何由一个string产生一个ICommand。

        根据面向对象设计的一些原则,数据与实现应都放在一个类当中。那么对于特殊的Command,它自身才知道如何对它对应的string进行分析。我们在ICommand中定义Parse(string s)方法对string进行分析。

        ICommand的定义如下,关于DoCommand()方法在后文将有说明。

        

    public interface ICommand
        {
    
            /// <summary>
            /// If it is right command,return true
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            bool Parse(string str);
            void DoCommand();
        }
    

        那么如何找到string->ICommand的对应关系呢?

        比较简单的实现是我们维护一个ICommand的列表,然后用穷举的方式调用Parse方法,直到该方法返回true。如果没有方法返回true,则表示该string是一个未知的命令。这样实现是比较OO,在编程阶段添加ICommand比较方便。缺点是效率比较低,而且还需要维护一个已经实例化ICommand的列表。而有这样一个列表,在多线程编程时就会产生一些同步问题。在编程时需要在Parse(string s)方法中先对传入的string做一个初步检测,如果非该指令则立即返回false。而在当前的设计中,没法对子类重写Parse方法内的内容进行约束,如果实现不好则会更大的造成效率的降低。

        我现在的实现是,引入一个ICommandFactory对象,那么这个问题就成为了string->ICommandFactory->ICommand的问题了,由于实现接口ICommandFactory与ICommand都是同一开发者实现的,该开发者可以分析string的特殊性,在实现ICommandFactory接口的类中找到string->ICommand的关系。还有一个优点是,我们完全可以在自己定义的CommandFactory中实现在上一段提到的方法。以下是该部分具体的实现代码。

    public interface ICommandFactory
    {
    /// <summary>
    /// return an ICommand object from a string.
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    ICommand Parse(string str);
    }
    

    //实现该接口的一个具体示例

    public class GPSCommandFactory : ICommandFactory
        {
            public IUnityContainer UnityContainer { get; set; }
    
            private ICommand Parse<C>(string str)
                where C : ICommand
            {
                var cmd = UnityContainer.Resolve<C>();
                if (!cmd.Parse(str))
                {
                    throw new InvalidCommandStringException(str, null);
                }
                return cmd;
            }
    
            #region ICommandFactory Members
    
            public ICommand Parse(string str)
            {
                try
                {
                    string command;
                    command = str.Substring(0, str.IndexOf(','));
                    switch (command)
                    {
                        case "$GPRMC":
                            return Parse<GPRMCommand>(str);
                        case "$GPGGA":
                            return Parse<GPGGACommmand>(str);
                    }
                    throw new InvalidCommandStringException(str, null);
                }
                catch (ArgumentOutOfRangeException ex)
                {
                   …
                }
            }
    
            #endregion
        }
    
    

        在解决完命令分析的问题之后,最后的一个问题是如何将分析出的命令应用的问题。在这个设计中,我通过事件来通知其他类。

        在前文给出ICommand的描述中给出了一个DoCommand()方法。我们可以在需要应用的项目中重写该方法,做出相应的动作。这样就完全的实现了多态,在接收到CommandReceived事件的消息后直接调用DoCommand()方法就好了,不需要对Command类型进行显示分析。当然,我们需要借助IOC容器来进行这样的实现。这里不阐述IOC容器的具体功能,有兴趣Google下就好啦。当然这样实现也许还是有一些复杂性的,我们需要对每一个Command类进行重写,然后更改IOC容器的映射关系。

        还有一种实现是使用is操作,对ICommand对象进行测试,然后将ICommand中的信息进行利用。下面给出第二种实现方法的一个示例。

            void CommandProcessor_CommandRecevied(object sender, CommandEventArgs e)
            {
                Invoke(new ThreadStart(delegate()
                {
                    if (e.Command is GPGGACommmand)
                    {
                        ProcessCommand((GPGGACommmand)e.Command);
                    }
                    if (e.Command is GPRMCommand)
                    {
                        ProcessCommand((GPRMCommand)e.Command);
                    }
                }));
    
                //throw new NotImplementedException();
            }
    

        这样一个基本的命令分析器的Library工程就基本写完了。然后就应当着手解决GPS消息的问题了。在前面的示例中已经贴了一些实现。

        分析GPS的消息,很容易的发现每条命令是以$\.+?, 方式开始的,我们可以根据这样的特性实现ICommandFactory,这里再贴一下主要的实现代码

    string command;
    command = str.Substring(0, str.IndexOf(','));
    switch (command)
    {
    case "$GPRMC":
    return Parse<GPRMCommand>(str);
    case "$GPGGA":
    return Parse<GPGGACommmand>(str);
    }
    

        然后就可以产生Command了。我们根据要求建立了两个Command,分别是GPRMCommand和GPGGACommmand。然后根据对应的命令格式重写Parse()方法。而消息分割正好是每行一条命令的方式,因而就无需重写CommandProcessor的GetSingleCommand方法了。然后就完了,不需要写什么了,基本的东西都已经在我们之前谈到的项目中定义好了。

        具体视图如下:

    Figure 4 GPS类视图


    如果您有兴趣,可以在这里下载代码,如果有问题欢迎与我联系:)

    http://loningproject.googlecode.com/svn/trunk/cnblogs/gps.7z

  • 相关阅读:
    spring3 上配置quartz 任务调度
    读取.properties配置信息
    mybatis 返回值
    div根据鼠标的移入移除显示隐藏
    jquery日期控件+时分秒
    mysql 插入多条记录,重复值不插入
    Hadoop中Writable类
    Hadoop中WritableComparable 和 comparator
    Hadoop序列化
    Hadoop压缩之MapReduce中使用压缩
  • 原文地址:https://www.cnblogs.com/loning/p/1866715.html
Copyright © 2011-2022 走看看