00001
00002 using System;
00003 using System.IO;
00004 using System.IO.Ports;
00005 using System.Diagnostics;
00006 using System.Reflection;
00007 using System.Runtime.InteropServices;
00008 using Microsoft.Win32.SafeHandles;
00009
00010 using NewGamePhysics.Utilities;
00011
00012 namespace NewGamePhysics.Devices
00013 {
00018 public delegate void GeigerCounterEvent(double relativeTime);
00019
00024 public class AwareGeigerCounter : IDisposable
00025 {
00029 private const UInt32 MS_RING_ON = 0x0040;
00030
00034 private GeigerCounterEvent eventHandler;
00035
00039 private SerialPort serialPort;
00040
00044 private IntPtr serialPortHandle;
00045
00050 private bool lastRingOn = false;
00051
00057 public AwareGeigerCounter(string portReference)
00058 {
00059 if (string.IsNullOrEmpty(portReference))
00060 {
00061 throw new ArgumentNullException(
00062 "portReference",
00063 "The port reference cannot be null or empty");
00064 }
00065
00066 string[] portNames = System.IO.Ports.SerialPort.GetPortNames();
00067 string portName;
00068
00069 if ((portNames == null) || (portNames.Length == 0))
00070 {
00071 throw new ApplicationException(
00072 "No serial ports are available on this system");
00073 }
00074
00075 if (!Array.Exists(portNames, s => s.ToUpper().Contains(portReference.ToUpper())))
00076 {
00077 try
00078 {
00079 int portIndex = Convert.ToInt32(portReference);
00080
00081 if ((portIndex < 1) || (portIndex > portNames.Length))
00082 {
00083 SerialPortInfo(portNames);
00084 throw new ArgumentOutOfRangeException(
00085 "portIndex",
00086 "The port index must be in the range 1 to " + portNames.Length);
00087 }
00088
00089 portName = portNames[portIndex - 1];
00090 }
00091 catch (Exception)
00092 {
00093 SerialPortInfo(portNames);
00094 throw;
00095 }
00096 }
00097 else
00098 {
00099 portName = portReference;
00100 }
00101
00102 this.InitializePort(portName);
00103 }
00104
00109 public void Dispose()
00110 {
00111 if (this.serialPort != null)
00112 {
00113 this.serialPortHandle = (IntPtr)0;
00114 this.serialPort.PinChanged -= new SerialPinChangedEventHandler(PinChanged);
00115 this.serialPort.Close();
00116 this.serialPort.Dispose();
00117 this.serialPort = null;
00118 }
00119 }
00120
00124 ~AwareGeigerCounter()
00125 {
00126 this.Dispose();
00127 }
00128
00133 public GeigerCounterEvent EventHandler
00134 {
00135 set { eventHandler = value; }
00136 }
00140 private void SerialPortInfo(string[] portNames)
00141 {
00142 System.Console.WriteLine();
00143 System.Console.WriteLine("Available Serial Ports:");
00144 for (int i = 0; i < portNames.Length; i++)
00145 {
00146 System.Console.WriteLine(" port #" + (i + 1) + " on: " + portNames[i]);
00147 }
00148 System.Console.WriteLine();
00149 }
00150
00155 private void InitializePort(string portName)
00156 {
00157
00158 this.serialPort = new SerialPort(portName, 2400);
00159
00160
00161 this.serialPort.Open();
00162 this.serialPort.DtrEnable = true;
00163 this.serialPort.RtsEnable = false;
00164
00165
00166 FieldInfo internalStreamFieldInfo = typeof(SerialPort).GetField(
00167 "internalSerialStream",
00168 BindingFlags.NonPublic | BindingFlags.Instance);
00169 object internalStream = internalStreamFieldInfo.GetValue(this.serialPort);
00170 Type internalStreamType = internalStream.GetType();
00171 FieldInfo handleFieldInfo = internalStreamType.GetField(
00172 "_handle",
00173 BindingFlags.NonPublic | BindingFlags.Instance);
00174 object safeFileHandle = handleFieldInfo.GetValue(internalStream);
00175 this.serialPortHandle = ((SafeFileHandle)safeFileHandle).DangerousGetHandle();
00176
00177
00178 this.serialPort.PinChanged += new SerialPinChangedEventHandler(PinChanged);
00179 }
00180
00187 [DllImport("kernel32.dll")]
00188 private static extern Boolean GetCommModemStatus(IntPtr hFile, out UInt32 lpModemStat);
00189
00196 private void PinChanged(object sender, SerialPinChangedEventArgs e)
00197 {
00198 if (e.EventType == SerialPinChange.Ring)
00199 {
00200
00201 uint status;
00202 if (GetCommModemStatus(this.serialPortHandle, out status))
00203 {
00204 bool ringOn = (status & MS_RING_ON) != 0;
00205 if ((this.lastRingOn) && (!ringOn))
00206 {
00207 if (null != this.eventHandler)
00208 {
00209 this.eventHandler(HighResolutionTimer.Seconds());
00210 }
00211 }
00212
00213 this.lastRingOn = ringOn;
00214 }
00215 else
00216 {
00217 string message = "GetCommModemStatus() error: " +
00218 Marshal.GetLastWin32Error().ToString();
00219 throw new ApplicationException(message);
00220 }
00221 }
00222 }
00223 }
00224 }