65.9K
CodeProject 正在变化。 阅读更多。
Home

为智能手机存储和转发 GPS 数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (6投票s)

2009年7月28日

CPOL

3分钟阅读

viewsIcon

31389

downloadIcon

932

如何在互联网上异步地将GPS数据存储和转发到服务器,而无需队列。

mobstoreandforwardgpsdata/PhoneApp.jpg

引言

为智能手机(Windows Mobile 5/6)通过互联网存储和转发GPS数据的基于文件的交付,无需数据库或队列。有很多关于如何在移动设备上接收和处理GPS数据的优秀文章。在这篇文章中,我想分享我如何将数据从我的智能手机发送到服务器。本文是关于客户端-服务器应用程序的客户端代码,其中移动设备只是一个记录GPS数据并通过互联网HTTP协议将其传递给服务器的工具。

背景

服务器应用程序将执行您对GPS数据的所有需求。就我而言,我只是在地图上显示手机位置,并显示过去24小时的路线。智能手机的客户端应用程序可以在 http://vvx.webhop.net/gps/gps.CABhttp://vvx.webhop.net/gps/index.htm 下载。要查看地图上的设备,您可以访问 http://vvx.webhop.net/gps/map2.aspx

输入您的设备ID(电话号码)即可在地图上看到您的移动设备。手机ID或电话号码将自动填充,并可以在“配置”屏幕中看到。

最初,我以同步方式将数据传递给服务器,试图接近实时获取位置(延迟1-2秒,取决于连接)。在此过程中,我在将数据传递给服务器时遇到了许多困难,尤其是在信号非常弱的情况下。

为了解决这个问题,我采用了另一种方法,并将代码编写为“存储和转发”。所有GPS信息都被写入一个文件,另一个线程会将其发送到服务器。在发送数据之前,文件会被重命名并复制,以防止其他任何东西对其进行更改。

使用代码

设置

mobstoreandforwardgpsdata/ConfigScreen.jpg

您可以看到ID的错误读取。此屏幕截图来自模拟器,代码无法识别SIM卡,并显示了错误。

此代码是为连接到蓝牙GPS天线而编写的。COM端口设置存储在配置文件中,并以秒为间隔发送到服务器,默认为20秒。在应用程序启动时,我扫描手机所有可用的COM端口,并将该数据加载到下拉框中。

private void LoadConfig() {
    //discover the COM ports
    string[] sp = System.IO.Ports.SerialPort.GetPortNames();
    foreach (string s in sp) {
        ddlComPort.Items.Add(s.ToString());
    }
    ReadConfig();
}

总体思路是将所有传入数据写入一个文件,并由另一个线程异步发送数据。如果数据发送成功,则从设备驱动程序中删除数据。为了确保数据不被修改,我们需要在发送数据文件之前对其进行复制。

以下是我在互联网连接不可用时构建数据传递的示例。

来自MainFrm.cs的代码
delegate void WriteOutputDel(object txt);

public partial class MainFrm : Form {
    int buffersize = 1024;
    string comPort = "COM5";
    SerialPort sp = new SerialPort();
    StreamWriter swLogFile = null;
    Thread t;
    VVXGps.GPSWebService gpsSvc = null; //= new MobileGps.VVXGps.GPSWebService();
    string id = string.Empty;
    string ver = string.Empty;
    string msg = string.Empty;
    int sendInterval = 1000 * 10; //10sec

    public MainFrm() {
        try {
            InitializeComponent();
            Version v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
            ver = string.Format("Version:{0}.{1}.{2}.{3}\r\n", 
                                v.Major, v.Minor, v.Build, v.Revision);
            //msg += "Init\r\n";
            ReadConfig();
            //msg += "ReadConfig done\r\n";
            string logFile = Util.GetLogFile();
            msg += "log file directory:" + logFile + "\r\n";
            try {
                swLogFile = File.AppendText(logFile);
            } catch (Exception ioe) { msg += ioe.Message; }

                //msg += "Get LogFile Stream\r\n";
                gpsSvc = new MobileGps.VVXGps.GPSWebService();
               
                //msg += "Init gps service\r\n";
                lblOutput.Text = ver + msg;
               
            } catch (Exception ex) {
                lblOutput.Text = ver + "\r\n" + msg + ex.Message;
        }
    }

    private void ReadConfig() {
        try {
            XmlDocument xDoc = new XmlDocument();
            string fname = Util.GetConfigFile();
            if (string.IsNullOrEmpty(fname)) {
                Util.Reset();
                fname = Util.GetConfigFile();
            }
            xDoc.Load(fname);
            XmlNode comNode = xDoc.SelectSingleNode("//COM");
            if (comNode != null) {
                comPort = comNode.InnerText;
            }
            XmlNode idNode = xDoc.SelectSingleNode("//ID");
            if (idNode != null) {
                id = idNode.InnerText;
            }
            XmlNode sendNode = xDoc.SelectSingleNode("//SendInterval");
            if (sendNode != null) {
                sendInterval = Int32.Parse(sendNode.InnerText) * 1000;
            }
            //Start process of sending files.
            PickUpFileInput();
        } catch (Exception ex) {
            MessageBox.Show("Read Config error:\r\n" + ex.Message);
        }
    }

    private void PickUpFileInput() {
        Thread t = new Thread(new ThreadStart(GpsFileListner));
        t.Start();
        //Helper h = new Helper();
        //TimerCallback timeCB = new TimerCallback(h.CleanUpOldFiles);
        //System.Threading.Timer t = 
        //   new System.Threading.Timer(timeCB, null, 100, sendInterval);
    }

    private void GpsFileListner() {
        Helper h = new Helper();
        while (true) {
            h.CleanUpOldFiles(null);
            Thread.Sleep(sendInterval);
        }
    }

    private void mnuStart_Click(object sender, EventArgs e) {
        sp = new SerialPort(comPort);
//#if !DEBUG
        Connect(sp);
//#endif
        StartCOMPortListner();
    }

    void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) {
        WriteOutputDel del = new WriteOutputDel(WriteOut);
        Invoke(del, new object[] { sender });
        SerialData sd = e.EventType;
        //if (swLogFile != null)
        //    LogData(string.Format("Sender {0}\r\ne:{1}\r\nSize{2}", 
        //          sender.ToString(), e.ToString(), sd.ToString()));
    }

    private void WriteOut(object obj) {
        if (InvokeRequired) {
            WriteOutputDel del = new WriteOutputDel(WriteOut);
            Invoke(del, obj);
            return;
        }
        string s = obj.ToString();
        lblOutput.Text = s;
    }

    public bool Connect(SerialPort serialport) {
        if (!this.OpenSerialPort(serialport)) return false; //open a serial port           
        return true;
    }

    public bool OpenSerialPort(System.IO.Ports.SerialPort serialPort1) {
        try {
            if (serialPort1.IsOpen) serialPort1.Close();
            serialPort1.ReadBufferSize = buffersize;
            serialPort1.Open();
            return true;
        } catch (Exception ex) {
            MessageBox.Show("Cann't open serial port." + ex.Message);
            return false;
        }
    }

    private void StartCOMPortListner() {
        t = new Thread(new ThreadStart(Start));
        t.Start();
    }

    private void Start() {
        string line;
        WriteOutputDel del = new WriteOutputDel(WriteOut);
        while (true) {
            try {
                Thread.Sleep(500);
               line = sp.ReadLine();

                if (line != null) {
                    try {
                        if (line.StartsWith("$GPRMC")) {
                            Util.SaveToBatchSend(line);
                            Invoke(del, line);
                        }
                    } catch (Exception ex) {
                        Invoke(del, "Error in Start function:\n" + ex.Message);
                    }                      
                }
            } catch (Exception ex) {
                Invoke(del, "Read COM Port input Error");
                Thread.Sleep(1000);
            }
        }
    }
.......

Helper类会异步查询Web服务,并在数据成功交付后删除文件。

 public class Helper {
    static VVXGps.GPSWebService gpsSvc = 
           new MobileGps.VVXGps.GPSWebService();
    private static object lockObj = new object();
    Thread t = null;

    internal void CleanUpOldFiles(object state) {
        try {
            lock (lockObj) {
                string batchfile = Util.GetBatchFile();
                long tick = DateTime.Now.Ticks;
                string tmpFile = batchfile + "." + tick + ".vvx";

                try {
                    File.Copy(batchfile, tmpFile, false);
                    try { File.Delete(batchfile); } catch { }
                    File.CreateText(batchfile);
                } catch { }

                string dir = Path.GetDirectoryName(
                  Assembly.GetExecutingAssembly().GetName().CodeBase);
                MobileGps.Config cnf = new MobileGps.Config();
                string id = cnf.GetPhoneId();
                AsyncCallback callBack = new AsyncCallback(ProcessServiceInformation);
                string[] files = Directory.GetFiles(dir);

                foreach (string s in files) {
                    if (s.EndsWith(".vvx")) {
                        string[] data = new string[] { };

                        using (StreamReader sr = File.OpenText(s)) {
                            data = sr.ReadToEnd().Split(new char[] { '\n', '\r' });
                            sr.Close();
                        }
                        try {
                            //Make sure we send it
                            IAsyncResult arg = 
                              gpsSvc.BeginRecordBatchData(data, id, callBack, s);
                            while (!arg.IsCompleted) {
                                Thread.Sleep(10);
                            }
                        } catch {
                            return;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            Util.SaveGPSLog(ex.Message + "\n\r" + ex.StackTrace);
        }
    }


    void ProcessServiceInformation(IAsyncResult status) {
        try {
            if (status.IsCompleted) {
                try {
                    gpsSvc.EndRecordBatchData(status);
                    File.Delete((string)status.AsyncState);
                } catch (Exception x) {
                    //Util.SaveGPSLog("Error deleting file:" +
                    //        status.AsyncState + " \n" + x.Message);
                }
            }
            t.Abort();
        } catch (Exception ex) {

        }
    }
}

如果服务调用无法完成,ProcessServiceInformation函数将会抛出异常

gpsSvc.EndRecordBatchData(status);

在这种情况下,我们的文件将不会被删除,并且会重试。

历史

这段代码最初编写于两年多前。这是第二个版本。服务器端代码将很快发布。

© . All rights reserved.