September 7, 2008

GPS Serial Port Provider

For the actual serial port provider we have a base class. This takes care of all the serial port side of things
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Diagnostics;

namespace GpsTest
{
  public abstract class SerialPortGpsProviderBase : IGpsDataProvider
  {
    /// <summary>
    /// Fire this event when A GPS line of data has been received
    /// </summary>
    public event GpsDataReceivedEventHandler GpsDataReceived;

    #region Attributes

    protected SerialPort comPort = new SerialPort();

    #endregion

    public SerialPortGpsProviderBase()
    {
    }

    ~SerialPortGpsProviderBase()
    {
      Dispose(false);
    }

    #region IDisposable Members

    private bool m_disposed = false;

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (!m_disposed)
      {
        if (disposing)
        {
          // Dispose managed resources
        }

        // Dispose unmanaged resources
        comPort.Dispose();
      }
      m_disposed = true;
    }

    #endregion

    public bool IsOpen()
    {
      return comPort.IsOpen;
    }

    public void Open()
    {
      //set up the delegate used to read data in.......
      comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);

      try
      {
        //finally open the port....
        comPort.Open();
        Trace.WriteLine("Com port " + comPort.ToString() + " Open=" + comPort.IsOpen.ToString());
      }
      catch (System.Exception ex)
      {
        Trace.WriteLine(DateTime.Now.ToString() + 
           ": Failed to open COM port with exception: " + ex.Message);
      }
    }

    protected abstract void comPort_DataReceived(object sender, 
            SerialDataReceivedEventArgs e);

    protected void FireGpsDataReceived(string data)
    {
      try
      {
        if ((data.Length > 0) &&
            (GpsDataReceived != null))
        {
          GpsDataReceived(this, new GpsDataLineEventArgs(data));
        }
      }
      catch (System.Exception ex)
      {
        Trace.WriteLine(DateTime.Now + 
            ": Failed to extract GPS data in comPort_DataReceived: " +
             ex.Message);
      }
    }


    public void Close()
    {
      try
      {
        //check we have a valid COM port to close...
        if (comPort.IsOpen)
        {
          comPort.Close();
          Debug.WriteLine("Com port " + comPort.ToString() + " CLOSED");
        }
      }
      catch (System.Exception ex)
      {
        Trace.WriteLine(DateTime.Now.ToString() + 
                ": Failed to close COM port with exception: " +
                ex.Message);
      }
    }
  }

}

The actual provider just implements the code to read the data and parse it
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Diagnostics;

namespace GpsTest
{

  // First simple provider 
  public class GpsSerialPortDataProvider : SerialPortGpsProviderBase
  {
    #region Attributes

    private const string comPortName = "COM14"; 
    private const int baudRate = 4800;

    #endregion

    public GpsSerialPortDataProvider()
    {
      //set up the com port settings.....
      comPort.PortName = comPortName;
      comPort.BaudRate = baudRate;
      comPort.Parity = Parity.None;
      comPort.StopBits = StopBits.One;
      comPort.DataBits = 8;
      comPort.Handshake = Handshake.None;
      comPort.ReadTimeout = 1000;
      comPort.NewLine = "\r\n";
    }

    ~GpsSerialPortDataProvider()
    {
      Dispose(false);
    }

    protected override void comPort_DataReceived(
      object sender, 
      SerialDataReceivedEventArgs e)
    {
      //Read a line of GPS data (each is sent with a \r\n at the end)
      try
      {
        string data = comPort.ReadLine();
        FireGpsDataReceived(data);
      }
      catch (System.Exception ex)
      {
        Trace.WriteLine(DateTime.Now + 
          ": Failed to extract GPS data in comPort_DataReceived: "
          + ex.Message);
      }
    }

  }

}
Due to a driver fault on the device we were using we could not read the data a line at a time. We had to clear the buffer out every time we read it, so here is the alternative provider
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Diagnostics;

namespace GpsTest
{

public class GpsSerialPortDataProvider3 : SerialPortGpsProviderBase
{

  #region Attributes

  private const string comPortName = "COM14";
  private const int baudRate = 4800;

  #endregion

  public GpsSerialPortDataProvider3()
  {
    //Setup the COM port
    comPort.PortName = comPortName;
    comPort.BaudRate = baudRate;

    //set up the other settings.....
    comPort.Parity = Parity.None;
    comPort.StopBits = StopBits.One;
    comPort.DataBits = 8;
    comPort.Handshake = Handshake.None;
    comPort.ReadTimeout = 1000;
    comPort.ReceivedBytesThreshold = 256;
  }

  ~GpsSerialPortDataProvider3()
  {
    Dispose(false);
  }

  private string buffer = "";
  private object bufferLock = new object();

  protected override void comPort_DataReceived(object sender, 
           SerialDataReceivedEventArgs e)
  {
    //Read a line of GPS data (each is sent with a \r\n at the end)
    try
    {
      // Read out everything in the buffer
      string data = comPort.ReadExisting();
      if (data.Length > 0)
      {
        string ready = string.Empty;
        lock (bufferLock)
        {
          buffer += data;
          // Do we have any complete lines in the buffer
          int ix = buffer.LastIndexOf("\r\n");
          if (ix >= 0)
          {
            // Remove all the complete lines
            ready = buffer.Remove(ix);
            // Truncate the buffer any incomplete data at the end
            buffer = buffer.Remove(0, ix + 2);
          }
        }
        if (ready.Length > 0) // found some complete lines
        {
          // Extract them
          string[] lines = ready.Split(new string[] { "\r\n" },
               StringSplitOptions.RemoveEmptyEntries);
          foreach (string line in lines)
          {
            // Invoke the GpsDataReceived event with the complete line
            FireGpsDataReceived(line);
          }
        }

      }
    }
    catch (System.Exception ex)
    {
      Trace.WriteLine(DateTime.Now.ToString() + 
           ": Failed to extract GPS data in comPort_DataReceived: " +
            ex.Message);
    }
  }

}

}

No comments: