本文共 9804 字,大约阅读时间需要 32 分钟。
在上篇文章 概述了NavsGo项目以及讲述了GPS监控功能的开发,GPS.net控件的使用,这篇文章讲述侦测功能的开发。
所谓GPS侦测功能就是扫描手机上所有可用的GPS设备(available GPS devices),把各个设备运行状态展现给用户,如果发现问题,通过友善的方式提示用户如果解决设备连通性问题。这些建议包括启动GPS设备,修改GPS Intermediate Driver的配置,启动蓝牙GPS设备等等。
这个模块是GPS.net 作者的一个demo程序,原程序可以到 下载。我做了少量修改加到NavsGo里面来了。
发现功能在上篇文章讲过,通过注册静态事件,然后回调相应的处理函数。
/* Hook into GPS device detection events. These events are static, allowing them to * be easily sunk by any other class or form. */ Devices.DeviceDetectionStarted += new EventHandler(Devices_DeviceDetectionStarted); Devices.DeviceDetectionCompleted += new EventHandler(Devices_DeviceDetectionCompleted); Devices.DeviceDetected += new EventHandler(Devices_DeviceDetected); Devices.DeviceDetectionAttempted += new EventHandler (Devices_DeviceDetectionAttempted); Devices.DeviceDetectionAttemptFailed += new EventHandler (Devices_DeviceDetectionAttemptFailed);
由于GPS.net是开源的,我们这次钻到他的源代码看看Detection的实现逻辑。
启动发现功能是在 Devices.BeginDetection() 函数里面。
public static void BeginDetection() { // Start detection on another thread. if (_IsDetectionInProgress) return; // Signal that detection is in progress _IsDetectionInProgress = true; // Start a thread for managing detection _DetectionThread = new Thread(new ThreadStart(DetectionThreadProc)); _DetectionThread.Name = "GPS.NET Device Detector (http://www.geoframeworks.com)"; _DetectionThread.IsBackground = true; #if !PocketPC // Do detection in the background _DetectionThread.Priority = ThreadPriority.Lowest; #endif _DetectionThread.Start(); #if PocketPC // Signal that the thread is alive (no Thread.IsAlive on the CF :P) _IsDetectionThreadAlive = true; #endif }
启动发现过程,系统会启动一个线程调用DetectionThreadProc()进行发现。下面是DetectionThreadProc()函数。
private static void DetectionThreadProc() { try { // Signal that it started OnDeviceDetectionStarted(); // Monitor this thread up to the timeout, then quit ThreadPool.QueueUserWorkItem(new WaitCallback(DetectionThreadProcWatcher)); #if PocketPC // Are we using the GPS Intermediate Driver? GpsIntermediateDriver gpsid = GpsIntermediateDriver.Current; // Is the GPSID supported? if (gpsid != null) { // Yes. Test it to be sure gpsid.BeginDetection(); // Wait for one device to get detected. Was it confirmed? if (gpsid.WaitForDetection()) { // Yes. If we only need one device, exit if(_IsOnlyFirstDeviceDetected) return; } } #endif /* If we get here, the GPS Intermediate Driver is not responding! */ int count; #region Detect Bluetooth devices // Is Bluetooth supported and turned on? if (IsBluetoothSupported && IsBluetoothEnabled) { // Start bluetooth detection for each device count = _BluetoothDevices.Count; for (int index = 0; index < count; index++) _BluetoothDevices[index].BeginDetection(); } #endregion #region Detect serial GPS devices if (AllowSerialConnections) { count = SerialDevices.Count; for (int index = 0; index < count; index++) _SerialDevices[index].BeginDetection(); /* If we're performing "exhaustive" detection, ports are scanned * even if there's no evidence they actually exist. This can happen in rare * cases, such as when a PCMCIA GPS device is plugged in and fails to create * a registry entry. */ if (_AllowExhaustiveSerialPortScanning) { // Try all ports from COM0: up to the maximum port number for (int index = 0; index < _MaximumSerialPortNumber; index++) { // Is this port already being checked? bool alreadyBeingScanned = false; for (int existingIndex = 0; existingIndex < _SerialDevices.Count; existingIndex++) { if (_SerialDevices[existingIndex].PortNumber.Equals(index)) { // Yes. Don't test it again alreadyBeingScanned = true; break; } // If it's already being scanned, stop if (alreadyBeingScanned) break; } // If it's already being scanned, skip to the next port if (alreadyBeingScanned) continue; // This is a new device. Scan it SerialDevice exhaustivePort = new SerialDevice("COM" + index.ToString() + ":"); exhaustivePort.BeginDetection(); } } } #endregion #region Discover new Bluetooth devices // Is Bluetooth supported and turned on? if (IsBluetoothSupported && IsBluetoothEnabled) { /* NOTE: For mobile devices, only one connection is allowed at a time. * As a result, we use a static SyncRoot to ensure that connections * and discovery happens in serial. For this reason, we will not attempt * to discover devices until *after* trying to detect existing ones. */ #if PocketPC // Wait for existing devices to be tested count = _BluetoothDevices.Count; for (int index = 0; index < count; index++) { // Complete detection for this device _BluetoothDevices[index].WaitForDetection(); } #endif // Begin searching for brand new devices BluetoothDevice.DiscoverDevices(true); // Block until that search completes BluetoothDevice.DeviceDiscoveryThread.Join(); } #endregion #region Wait for all devices to finish detection /* A list holds the wait handles of devices being detected. When it is empty, * detection has finished on all threads. */ while (_CurrentlyDetectingWaitHandles.Count != 0) { try { ManualResetEvent handle = _CurrentlyDetectingWaitHandles[0]; #if !PocketPC if (!handle.SafeWaitHandle.IsClosed) #endif handle.WaitOne(); } catch (ObjectDisposedException) { /* In some rare cases a device will get disposed of and nulled out. * So, regardless of what happens we can remove the item. */ } finally { _CurrentlyDetectingWaitHandles.RemoveAt(0); } } #endregion #if PocketPC #region Reconfigure the GPS Intermediate Driver (if necessary) /* The GPS Intermediate Driver may not have the right "Program Port" (actual GPS port/baud rate) * settings. Now that detection has completed, let's see if the GPSID needs configuration. * If it is flagged as NOT being a GPS device, then it could not connect. In this case, let's * find the most reliable serial device and use it. */ if ( // Is the GPSID supported? gpsid != null // Are we allowed to configure it? && gpsid.IsAutomaticallyConfigured // Is it currently NOT identified as a GPS device? (connections failed) && !gpsid.IsGpsDevice) { // Look through each confirmed GPS device count = _GpsDevices.Count; for (int index = 0; index < count; index++) { // Is it a serial device? SerialDevice device = _GpsDevices[index] as SerialDevice; if (device == null) continue; // Yes. Use it! try { gpsid.HardwarePort = device; // The GPSID is now working Add(gpsid); } catch (Exception ex) { // Notify of the error gracefully OnDeviceDetectionAttemptFailed(new DeviceDetectionException(gpsid, ex)); } // That's the best device, so quit break; } } #endregion #endif // Signal completion OnDeviceDetectionCompleted(); } catch (ThreadAbortException) { #region Abort detection for all devices #if PocketPC // Stop detection for the GPSID if(GpsIntermediateDriver.Current != null) GpsIntermediateDriver.Current.CancelDetection(); #endif // Stop detection for each Bluetooth device for (int index = 0; index < _BluetoothDevices.Count; index++) _BluetoothDevices[index].CancelDetection(); // Stop detection for each serial device for (int index = 0; index < _SerialDevices.Count; index++) _SerialDevices[index].CancelDetection(); #endregion // Wait for all the threads to die. Just... sit and watch. And wait. while (_CurrentlyDetectingWaitHandles.Count != 0) { try { _CurrentlyDetectingWaitHandles[0].WaitOne(); } catch { } finally { _CurrentlyDetectingWaitHandles.RemoveAt(0); } } // Signal the cancellation if (DeviceDetectionCanceled != null) DeviceDetectionCanceled(null, EventArgs.Empty); } finally { // Detection is no longer in progress _DetectionCompleteWaitHandle.Set(); _CurrentlyDetectingWaitHandles.Clear(); // <-- Already empty? _IsDetectionInProgress = false; #if PocketPC // Signal that the thread is alive (no Thread.IsAlive on the CF :P) _IsDetectionThreadAlive = false; #endif } }
DetectionThreadProc()负责整个发现过程,是一个很长的函数,有必要重构一下,把它分离(split)成几个小函数。他的处理逻辑是,检测超时,一旦发现发现过程超时,就好中途停止所有的处理。然后按顺序检测设备。检测的设备包括GPS Intermediate Driver设备(GpsIntermediateDriver),串口设备(SerialDevice)和蓝牙设备(BluetoothDevice)。支持的设备类图如下:
所有设备都是继承于父类Device,这样可以通过容器类Devices类统一管理所有设备的对象,通过多态的方式去调用各个具体设备的处理函数来实现发现过程。对于每个独立的设备,他们统一发现流程是启动一个线程,然后试图打开该设备,如果超时,认为设备不可用,如果在超时之前读取到数据就分析输出数据,如果数据是标准的NMEA就认为这个是GPS设备。NMEA相关的可以参考。
发现过程首先检查的是GPS Intermediate Driver设备。在Windows Mobile 5+和Wince 6+的系统下一般都内嵌GPS Intermediate Driver,关于GPS Intermediate Driver的开发可以参考 。我计划增加 GPS Intermediate Driver管理功能到NavsGo里面,后续会把GPS Intermediate Driver管理的开发写下来。
检测完GPS Intermediate Driver设备,就开始检查已经配对了的蓝牙设备(Paired Bluetooth Devices),这些已经配对了的蓝牙设备保存在注册表HKEY_LOCAL_MACHINE\SOFTWARE\GeoFrameworks\GPS.NET\3.0\Devices\Bluetooth\中。关于蓝牙配对也可以参考一下 。
检测完已经配对了的蓝牙设备后,就开始检查串口设备,这里的串口设备是指软件意义上的串口,不是仅仅只通过硬件串口线连接的设备。由于 的规范规定GPS设备的联通性通过波特率()为4800的串口设备。所以GPS设备厂商尽管使用其他联通方式,但是都可以转成软件上的串口设备。例如USB GPS设备可以通过驱动转成串口设备,蓝牙可以建立虚拟串口,关于虚拟串口可以参考 。总的来说,这里软件意义上的串口,真实的设备可能是 真正的串口线,USB,PCMCIA,蓝牙,红外等。
做完GPS Intermediate Driver设备,已经配对了的蓝牙设备和软件意义上的串口的发现流程后,进入了对新蓝牙设备的发现过程,这些蓝牙设备是不在已经配对了的蓝牙设备的范畴里面的,是手机周边新的蓝牙设备。由于手机对蓝牙的通信只能是一对一,也就是一个时间内一台手机只能和一个蓝牙设备进行通信,所以在发现新蓝牙设备之前,需要先等待已经配对了的蓝牙设备的发现过程的结束。关于蓝牙设备的开发和发现可以参考一下 和 。
一般来说移动设备都是通过GPS Intermediate Driver设备来对外部GPS程序提供服务的,有时候尽管GPS Intermediate Driver设备存在,并在运行,但是由于配置不正确也会导致外部GPS程序未能正确连接和使用GPS设备。所以最后一步是检查GPS Intermediate Driver设备的配置情况。我计划也在NavsGo增加GPS Intermediate Driver设备的配置管理功能。
最后的最后是清理所有资源,一个好的程序的习惯。
发现功能实现了整个GPS诊断模块的核心,建议功能也就是呈现发现功能缓存的信息。建议功能实现在SummaryForm和DeviceForm两个类里面,SummaryForm提示建议,而DeviceForm现实某个设备的检查情况。
建议功能的流程是,判断GPS Intermediate Driver设备是否可用,如果不可用可能是硬件端口配置错误,建议把可用的串口端口配置为GPS Intermediate Driver的硬件端口,如果没有可用的串口端口,建议使用蓝牙设备。
GPS.net提供日志发送功能,把设备发现和检查信息发送到服务器,方便开发者改进。这个功能很简单。
源码请看
本文转自Jake Lin博客园博客,原文链接:http://www.cnblogs.com/procoder/archive/2009/08/21/1551155.html,如需转载请自行联系原作者