forked from jperson2000/GPS.Net
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NetworkDevice.cs
377 lines (317 loc) · 13 KB
/
NetworkDevice.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
using System;
using System.Globalization;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Net;
using System.Net.Sockets;
using Microsoft.Win32;
#if !PocketPC || ICodeInAVacuum
using GeoFramework.Gps.Nmea;
using System.ComponentModel;
#endif
namespace GeoFramework.Gps.IO
{
/// <summary>
/// Represents a GPS device which is accessed via a socket connection.
/// </summary>
public class NetworkDevice : Device
{
private AddressFamily _AddressFamily;
private SocketType _SocketType;
private ProtocolType _ProtocolType;
private EndPoint _EndPoint;
private Socket _Socket;
private static TimeSpan _DefaultConnectTimeout = TimeSpan.FromSeconds(30);
#region Constants
private const string RootKeyName = Devices.RootKeyName + @"Network\";
#endregion
#region Constructors
public NetworkDevice(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
: this(addressFamily, socketType, protocolType, null)
{ }
public NetworkDevice(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, EndPoint endPoint)
{
_AddressFamily = addressFamily;
_SocketType = socketType;
_ProtocolType = protocolType;
_EndPoint = endPoint;
}
#endregion
#region Public Properties
/// <summary>
/// Returns the addressing scheme the socket can use.
/// </summary>
#if !PocketPC
[Category("Data")]
[Description("Returns the addressing scheme the socket can use. ")]
[Browsable(true)]
#endif
public AddressFamily AddressFamily
{
get
{
return _AddressFamily;
}
}
/// <summary>
/// Returns the type of protocol used by the socket.
/// </summary>
#if !PocketPC
[Category("Data")]
[Description("Returns the type of protocol used by the socket.")]
[Browsable(true)]
#endif
public SocketType SocketType
{
get
{
return _SocketType;
}
}
/// <summary>
/// Returns the protocol supported by the socket.
/// </summary>
#if !PocketPC
[Category("Data")]
[Description("Returns the protocol supported by the socket.")]
[Browsable(true)]
#endif
public ProtocolType ProtocolType
{
get
{
return _ProtocolType;
}
}
/// <summary>
/// Returns the network address for the device.
/// </summary>
#if !PocketPC
[Category("Data")]
[Description("Returns the network address for the device.")]
[Browsable(true)]
[TypeConverter(typeof(ExpandableObjectConverter))]
#endif
public EndPoint EndPoint
{
get
{
return _EndPoint;
}
set
{
_EndPoint = value;
}
}
/// <summary>
/// Returns the socket associated with this device.
/// </summary>
#if !PocketPC
[Browsable(false)]
#endif
protected Socket Socket
{
get { return _Socket; }
}
#endregion
#region Public Methods
/// <summary>
/// Creates a new connection to the specified endpoint.
/// </summary>
/// <param name="endPoint"></param>
public void Open(EndPoint endPoint)
{
// If we're already open, complain.
if (IsOpen)
throw new InvalidOperationException("The Open method was called on a device which is already connected. Please call the Close method before calling the Open method to ensure that connections are cleaned up properly.");
// Remember the endpoint
_EndPoint = endPoint;
// And continue opening a connection
Open();
}
#endregion
#region Static Properties
/// <summary>
/// Controls the amount of time allowed for a connection to be successfully opened.
/// </summary>
public static TimeSpan DefaultConnectTimeout
{
get { return _DefaultConnectTimeout; }
set
{
// The timeout must be greater than zero
if (value.TotalSeconds <= 0)
throw new ArgumentOutOfRangeException("DefaultConnectTimeout", "The default connect timeout for a network device must be greater than zero. A value of five to ten seconds is recommended.");
// Set the value
_DefaultConnectTimeout = value;
}
}
#endregion
#region Protected Methods
/// <summary>
/// Occurs immediately before a socket is opened.
/// </summary>
protected virtual void OnConfigureSocket()
{ }
#endregion
#region Overrides
public override string Name
{
get
{
return "Network Device";
}
}
protected override Stream OpenStream(FileAccess access, FileShare sharing)
{
// Give the consumer a chance to set socket options
OnConfigureSocket();
// Is the socket already connected?
if (_Socket == null || !_Socket.Connected)
{
// Close any exsisting socket
if (_Socket != null)
_Socket.Close();
// Create a socket
_Socket = new Socket(_AddressFamily, _SocketType, _ProtocolType);
#if !PocketPC
// A smaller buffer size reduces latency. We want to process data as soon as it's transmitted
_Socket.ReceiveBufferSize = NmeaReader.IdealNmeaBufferSize;
_Socket.SendBufferSize = NmeaReader.IdealNmeaBufferSize;
// Specify the timeout for read/write ops
_Socket.ReceiveTimeout = 10000; // (int)DefaultReadTimeout.TotalMilliseconds;
_Socket.SendTimeout = 10000; // = (int)DefaultWriteTimeout.TotalMilliseconds;
#endif
// Begin connecting asynchronously
IAsyncResult connectResult = _Socket.BeginConnect(_EndPoint, null, null);
// Wait up to the timeout for the connection to complete
if (!connectResult.AsyncWaitHandle.WaitOne((int)_DefaultConnectTimeout.TotalMilliseconds, false))
throw new TimeoutException(Name + " could not connect within the timeout period.");
_Socket.EndConnect(connectResult);
}
// Wrap a network stream around the socket.
return new NetworkStream(_Socket, access, true);
// We don't need to set the streams timeouts because they are implied by the
// socket timeouts set above.
/*
* This is documented (see Socket.Shutdown()). Closed sockets shouldn't be
* reopened. Above, a non null socket wasn't being closed, which caused errors
* suggesting that attempts were being made to reopen a socket.
*
* We also don't need to set the streams timeouts because they are implied by the
* socket timeouts.
*/
#region Flip-floppy ownership
//#if PocketPC
// /* EXTREMELY IMPORTANT
// *
// * The .NET implementation of the Socket and NetworkStream classes is such that
// * any attempt to close a socket will prevent all future connection attempts!
// * I'm not sure exactly why, but I believe that a simple close is inadvertantly
// * shutting down the entire Bluetooth socket system. This appears to only
// * affect the Compact Framework.
// *
// * As a result, it's very important to leave "ownsSocket" as FALSE.
// */
// return new NetworkStream(_Socket, access, false);
// // ^^^^^ very important to be False.
//#else
// // And wrap a network stream around it
// NetworkStream result = new NetworkStream(_Socket, access, true);
// // Specify read/write timeouts
// result.ReadTimeout = Convert.ToInt32(DefaultReadTimeout.TotalMilliseconds);
// result.WriteTimeout = Convert.ToInt32(DefaultWriteTimeout.TotalMilliseconds);
// return result;
//#endif
#endregion
}
protected override void Dispose(bool disposing)
{
if (_Socket != null)
{
// Close disconnects the socket and disposes it.
_Socket.Close();
}
// Dispose of the base, closing the stream
base.Dispose(disposing);
if (disposing)
{
_EndPoint = null;
_Socket = null;
}
}
protected override void OnCacheRemove()
{
try
{
// Endpoints are serialized in their raw byte form to improve compatibility
SocketAddress address = _EndPoint.Serialize();
StringBuilder hexEndPoint = new StringBuilder();
for (int index = 0; index < address.Size; index++)
hexEndPoint.Append(address[index].ToString("X2"));
Registry.LocalMachine.DeleteSubKeyTree(RootKeyName + hexEndPoint.ToString());
}
catch (UnauthorizedAccessException)
{ }
finally
{
// Reset the cache properties
SetSuccessfulDetectionCount(0);
SetFailedDetectionCount(0);
SetDateDetected(DateTime.MinValue);
SetDateConnected(DateTime.MinValue);
}
}
protected override void OnCacheWrite()
{
// Endpoints are serialized in their raw byte form to improve compatibility
SocketAddress address = _EndPoint.Serialize();
StringBuilder hexEndPoint = new StringBuilder();
for (int index = 0; index < address.Size; index++)
hexEndPoint.Append(address[index].ToString("X2"));
// Save device information
RegistryKey deviceKey = Registry.LocalMachine.CreateSubKey(RootKeyName + hexEndPoint.ToString());
if (deviceKey == null)
return;
// Update the success/fail statistics
deviceKey.SetValue("Number of Times Detected", SuccessfulDetectionCount);
deviceKey.SetValue("Number of Times Failed", FailedDetectionCount);
deviceKey.SetValue("Date Last Detected", DateDetected.ToString("R", CultureInfo.InvariantCulture));
deviceKey.SetValue("Date Last Connected", DateConnected.ToString("R", CultureInfo.InvariantCulture));
deviceKey.Close();
}
protected override void OnCacheRead()
{
// Endpoints are serialized in their raw byte form to improve compatibility
SocketAddress address = _EndPoint.Serialize();
StringBuilder hexEndPoint = new StringBuilder();
for (int index = 0; index < address.Size; index++)
hexEndPoint.Append(address[index].ToString("X2"));
// Save device stats
RegistryKey deviceKey = Registry.LocalMachine.OpenSubKey(RootKeyName + hexEndPoint.ToString(), false);
if (deviceKey == null)
return;
// Update the baud rate and etc.
foreach (string name in deviceKey.GetValueNames())
{
switch (name)
{
case "Number of Times Detected":
SetSuccessfulDetectionCount(Convert.ToInt32(deviceKey.GetValue(name), CultureInfo.InvariantCulture));
break;
case "Number of Times Failed":
SetFailedDetectionCount(Convert.ToInt32(deviceKey.GetValue(name), CultureInfo.InvariantCulture));
break;
case "Date Last Detected":
SetDateDetected(Convert.ToDateTime(deviceKey.GetValue(name), CultureInfo.InvariantCulture));
break;
case "Date Last Connected":
SetDateConnected(Convert.ToDateTime(deviceKey.GetValue(name), CultureInfo.InvariantCulture));
break;
}
}
}
#endregion
}
}