diff --git a/src/Device.Net - All.sln b/src/Device.Net - All.sln index 314122a3..a1aeedb4 100644 --- a/src/Device.Net - All.sln +++ b/src/Device.Net - All.sln @@ -72,6 +72,8 @@ Global {8E29E247-78B7-4FC8-AF1C-DA273050978C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E29E247-78B7-4FC8-AF1C-DA273050978C}.Release|Any CPU.Build.0 = Release|Any CPU {63695F84-3EC6-4F6A-8C8C-5219D75EDB68}.Debug|Any CPU.ActiveCfg = Debug|x86 + {63695F84-3EC6-4F6A-8C8C-5219D75EDB68}.Debug|Any CPU.Build.0 = Debug|x86 + {63695F84-3EC6-4F6A-8C8C-5219D75EDB68}.Debug|Any CPU.Deploy.0 = Debug|x86 {63695F84-3EC6-4F6A-8C8C-5219D75EDB68}.Release|Any CPU.ActiveCfg = Release|x86 {63695F84-3EC6-4F6A-8C8C-5219D75EDB68}.Release|Any CPU.Build.0 = Release|x86 {63695F84-3EC6-4F6A-8C8C-5219D75EDB68}.Release|Any CPU.Deploy.0 = Release|x86 diff --git a/src/Device.Net - All.sln.DotSettings b/src/Device.Net - All.sln.DotSettings new file mode 100644 index 00000000..3c2eccfe --- /dev/null +++ b/src/Device.Net - All.sln.DotSettings @@ -0,0 +1,5 @@ + + True + True + True + True \ No newline at end of file diff --git a/src/Device.Net.LibUsb/LibUsbDevice.cs b/src/Device.Net.LibUsb/LibUsbDevice.cs index ad0c9712..bf43bac7 100644 --- a/src/Device.Net.LibUsb/LibUsbDevice.cs +++ b/src/Device.Net.LibUsb/LibUsbDevice.cs @@ -3,19 +3,21 @@ using LibUsbDotNet.Main; using LibUsbDotNet.WinUsb; using System; +using System.IO; using System.Threading; using System.Threading.Tasks; namespace Device.Net.LibUsb { - public class LibUsbDevice : IDevice + public class LibUsbDevice : DeviceBase, IDevice { #region Fields private UsbEndpointReader _UsbEndpointReader; private UsbEndpointWriter _UsbEndpointWriter; private int ReadPacketSize; - private SemaphoreSlim _WriteAndReadLock = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _WriteAndReadLock = new SemaphoreSlim(1, 1); private bool disposed; + private bool _IsInitialized; #endregion #region Public Properties @@ -23,19 +25,17 @@ public class LibUsbDevice : IDevice public int VendorId => GetVendorId(UsbDevice); public int ProductId => GetProductId(UsbDevice); public int Timeout { get; } - public bool IsInitialized { get; private set; } - public ConnectedDeviceDefinitionBase ConnectedDeviceDefinition => throw new NotImplementedException(); - public string DeviceId => UsbDevice.DevicePath; - public ILogger Logger { get; set; } - #endregion - - #region Events - public event EventHandler Connected; - public event EventHandler Disconnected; + public override bool IsInitialized => _IsInitialized; + public override ushort WriteBufferSize => throw new NotImplementedException(); + public override ushort ReadBufferSize => throw new NotImplementedException(); #endregion #region Constructor - public LibUsbDevice(UsbDevice usbDevice, int timeout) + public LibUsbDevice(UsbDevice usbDevice, int timeout) : this(usbDevice, timeout, null, null) + { + } + + public LibUsbDevice(UsbDevice usbDevice, int timeout, ILogger logger, ITracer tracer) : base(logger, tracer) { UsbDevice = usbDevice; Timeout = timeout; @@ -48,7 +48,7 @@ public void Close() UsbDevice?.Close(); } - public void Dispose() + public override void Dispose() { if (disposed) return; disposed = true; @@ -56,6 +56,10 @@ public void Dispose() _WriteAndReadLock.Dispose(); Close(); + + base.Dispose(); + + GC.SuppressFinalize(this); } public async Task InitializeAsync() @@ -86,11 +90,11 @@ public async Task InitializeAsync() _UsbEndpointReader = UsbDevice.OpenEndpointReader(ReadEndpointID.Ep01); ReadPacketSize = _UsbEndpointReader.EndpointInfo.Descriptor.MaxPacketSize; - IsInitialized = true; + _IsInitialized = true; }); } - public async Task ReadAsync() + public override async Task ReadAsync() { await _WriteAndReadLock.WaitAsync(); @@ -98,11 +102,13 @@ public async Task ReadAsync() { return await Task.Run(() => { - var buffer = new byte[ReadPacketSize]; + var data = new byte[ReadPacketSize]; + + _UsbEndpointReader.Read(data, Timeout, out var bytesRead); - _UsbEndpointReader.Read(buffer, Timeout, out var bytesRead); + Tracer?.Trace(false, data); - return buffer; + return data; }); } finally @@ -111,7 +117,7 @@ public async Task ReadAsync() } } - public async Task WriteAsync(byte[] data) + public override async Task WriteAsync(byte[] data) { await _WriteAndReadLock.WaitAsync(); @@ -119,7 +125,17 @@ public async Task WriteAsync(byte[] data) { await Task.Run(() => { - _UsbEndpointWriter.Write(data, Timeout, out var bytesWritten); + var errorCode = _UsbEndpointWriter.Write(data, Timeout, out var bytesWritten); + if (errorCode == ErrorCode.Ok || errorCode == ErrorCode.Success) + { + Tracer?.Trace(true, data); + } + else + { + var message = $"Error. Write error code: {errorCode}"; + Logger?.Log(message, GetType().Name, null, LogLevel.Error); + throw new IOException(message); + } }); } finally @@ -128,11 +144,6 @@ public async Task WriteAsync(byte[] data) } } - public async Task WriteAndReadAsync(byte[] writeBuffer) - { - await WriteAsync(writeBuffer); - return await ReadAsync(); - } #endregion #region Public Static Methods diff --git a/src/Device.Net.LibUsb/LibUsbDeviceFactoryBase.cs b/src/Device.Net.LibUsb/LibUsbDeviceFactoryBase.cs index cce9b530..26589a2f 100644 --- a/src/Device.Net.LibUsb/LibUsbDeviceFactoryBase.cs +++ b/src/Device.Net.LibUsb/LibUsbDeviceFactoryBase.cs @@ -9,7 +9,8 @@ namespace Device.Net.LibUsb public abstract class LibUsbDeviceFactoryBase : IDeviceFactory { #region Public Properties - public ILogger Logger { get; set; } + public ILogger Logger { get; } + public ITracer Tracer { get; } #endregion #region Public Abstraction Properties @@ -23,32 +24,26 @@ public async Task> GetConnectedDeviceDefi { IEnumerable devices = UsbDevice.AllDevices; - if (deviceDefinition != null) - { - if (deviceDefinition.VendorId.HasValue) - { - devices = devices.Where(d => d.Vid == deviceDefinition.VendorId.Value); - } + if (deviceDefinition == null) - if (deviceDefinition.VendorId.HasValue) + return devices.Select(usbRegistry => new ConnectedDeviceDefinition(usbRegistry.DevicePath) { - devices = devices.Where(d => d.Pid == deviceDefinition.ProductId.Value); - } - } + VendorId = (uint) usbRegistry.Vid, + ProductId = (uint) usbRegistry.Pid, + DeviceType = DeviceType + }).ToList(); - var retVal = new List(); + if (deviceDefinition.VendorId.HasValue) + { + devices = devices.Where(d => d.Vid == deviceDefinition.VendorId.Value); + } - foreach (var usbRegistry in devices) + if (deviceDefinition.VendorId.HasValue) { - retVal.Add(new ConnectedDeviceDefinition(usbRegistry.DevicePath) - { - VendorId = (uint)usbRegistry.Vid, - ProductId = (uint)usbRegistry.Pid, - DeviceType = DeviceType - }); + devices = devices.Where(d => d.Pid == deviceDefinition.ProductId.Value); } - return retVal; + return devices.Select(usbRegistry => new ConnectedDeviceDefinition(usbRegistry.DevicePath) {VendorId = (uint) usbRegistry.Vid, ProductId = (uint) usbRegistry.Pid, DeviceType = DeviceType}).ToList(); }); } @@ -56,7 +51,15 @@ public IDevice GetDevice(ConnectedDeviceDefinition deviceDefinition) { var usbDeviceFinder = new UsbDeviceFinder((int)deviceDefinition.VendorId.Value, (int)deviceDefinition.ProductId.Value); var usbDevice = UsbDevice.OpenUsbDevice(usbDeviceFinder); - return usbDevice != null ? new LibUsbDevice(usbDevice, 3000) { Logger = Logger } : null; + return usbDevice != null ? new LibUsbDevice(usbDevice, 3000, Logger, Tracer) : null; + } + #endregion + + #region Constructor + protected LibUsbDeviceFactoryBase(ILogger logger, ITracer tracer) + { + Logger = logger; + Tracer = tracer; } #endregion } diff --git a/src/Device.Net.LibUsb/LibUsbUsbDeviceFactory.cs b/src/Device.Net.LibUsb/LibUsbUsbDeviceFactory.cs index 933e5686..2296806d 100644 --- a/src/Device.Net.LibUsb/LibUsbUsbDeviceFactory.cs +++ b/src/Device.Net.LibUsb/LibUsbUsbDeviceFactory.cs @@ -2,16 +2,18 @@ { public class LibUsbUsbDeviceFactory : LibUsbDeviceFactoryBase { - public override DeviceType DeviceType => DeviceType.Usb; - - public static void Register() + public LibUsbUsbDeviceFactory(ILogger logger, ITracer tracer) : base(logger, tracer) { - Register(null); } - public static void Register(ILogger logger) + public override DeviceType DeviceType => DeviceType.Usb; + + /// + /// Register the factory for enumerating USB devices. + /// + public static void Register(ILogger logger, ITracer tracer) { - DeviceManager.Current.DeviceFactories.Add(new LibUsbUsbDeviceFactory() { Logger = logger }); + DeviceManager.Current.DeviceFactories.Add(new LibUsbUsbDeviceFactory(logger, tracer)); } } } diff --git a/src/Device.Net.UWP/Device.Net.UWP.csproj b/src/Device.Net.UWP/Device.Net.UWP.csproj index 68519f57..6c5bf740 100644 --- a/src/Device.Net.UWP/Device.Net.UWP.csproj +++ b/src/Device.Net.UWP/Device.Net.UWP.csproj @@ -27,11 +27,11 @@ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP prompt 4 - true + false ../Device.Net/Device.Net.pfx - true + true AnyCPU pdbonly true @@ -54,7 +54,7 @@ - 2.6.3 + 2.9.3 runtime; build; native; contentfiles; analyzers all diff --git a/src/Device.Net.UWP/UWPDeviceBase - Generic.cs b/src/Device.Net.UWP/UWPDeviceBase - Generic.cs index f111e36d..ad8c58d3 100644 --- a/src/Device.Net.UWP/UWPDeviceBase - Generic.cs +++ b/src/Device.Net.UWP/UWPDeviceBase - Generic.cs @@ -1,4 +1,5 @@ -using System; +using Device.Net.Exceptions; +using System; using System.Threading.Tasks; using Windows.Foundation; @@ -12,16 +13,11 @@ public abstract class UWPDeviceBase : UWPDeviceBase, IDevice #region Protected Properties protected T ConnectedDevice { get; private set; } - protected bool Disposed { get; private set; } = false; + protected bool Disposed { get; private set; } #endregion #region Constructor - protected UWPDeviceBase() - { - - } - - protected UWPDeviceBase(string deviceId) + protected UWPDeviceBase(string deviceId, ILogger logger, ITracer tracer) : base(logger, tracer) { DeviceId = deviceId; } @@ -45,7 +41,7 @@ public override async Task ReadAsync() { if (IsReading) { - throw new Exception("Reentry"); + throw new AsyncException(Messages.ErrorMessageReentry); } //TODO: this should be a semaphore not a lock @@ -53,16 +49,19 @@ public override async Task ReadAsync() { if (Chunks.Count > 0) { - var retVal = Chunks[0]; - Tracer?.Trace(false, retVal); + var data2 = Chunks[0]; + Logger?.Log("Received data from device", GetType().Name, null, LogLevel.Information); Chunks.RemoveAt(0); - return retVal; + Tracer?.Trace(false, data2); + return data2; } } IsReading = true; ReadChunkTaskCompletionSource = new TaskCompletionSource(); - return await ReadChunkTaskCompletionSource.Task; + var data = await ReadChunkTaskCompletionSource.Task; + Tracer?.Trace(false, data); + return data; } #endregion diff --git a/src/Device.Net.UWP/UWPDeviceBase.cs b/src/Device.Net.UWP/UWPDeviceBase.cs index 03f66396..5e3863bb 100644 --- a/src/Device.Net.UWP/UWPDeviceBase.cs +++ b/src/Device.Net.UWP/UWPDeviceBase.cs @@ -35,5 +35,11 @@ protected void HandleDataReceived(byte[] bytes) #region Public Abstract Methods public abstract Task InitializeAsync(); #endregion + + #region Constructor + protected UWPDeviceBase(ILogger logger, ITracer tracer) : base(logger, tracer) + { + } + #endregion } } diff --git a/src/Device.Net.UWP/UWPDeviceFactoryBase.cs b/src/Device.Net.UWP/UWPDeviceFactoryBase.cs index 46ce9f4e..e883de88 100644 --- a/src/Device.Net.UWP/UWPDeviceFactoryBase.cs +++ b/src/Device.Net.UWP/UWPDeviceFactoryBase.cs @@ -23,7 +23,8 @@ public abstract class UWPDeviceFactoryBase #endregion #region Public Properties - public ILogger Logger { get; set; } + public ILogger Logger { get; } + public ITracer Tracer { get; } #endregion #region Public Abstract Properties @@ -34,6 +35,14 @@ public abstract class UWPDeviceFactoryBase protected abstract string GetAqsFilter(uint? vendorId, uint? productId); #endregion + #region Constructor + protected UWPDeviceFactoryBase(ILogger logger, ITracer tracer) + { + Logger = logger; + Tracer = tracer; + } + #endregion + #region Abstraction Methods protected string GetVendorPart(uint? vendorId) { @@ -101,6 +110,8 @@ public async Task> GetConnectedDeviceDefi #region Public Static Methods public static ConnectedDeviceDefinition GetDeviceInformation(wde.DeviceInformation deviceInformation, DeviceType deviceType) { + if (deviceInformation == null) throw new ArgumentNullException(nameof(deviceInformation)); + var retVal = WindowsDeviceFactoryBase.GetDeviceDefinitionFromWindowsDeviceId(deviceInformation.Id, deviceType); //foreach (var keyValuePair in deviceInformation.Properties) diff --git a/src/Device.Net.UnitTests/MockDeviceBase.cs b/src/Device.Net.UnitTests/MockDeviceBase.cs index e41b4680..4a9a448e 100644 --- a/src/Device.Net.UnitTests/MockDeviceBase.cs +++ b/src/Device.Net.UnitTests/MockDeviceBase.cs @@ -11,6 +11,11 @@ public abstract class MockDeviceBase : DeviceBase, IDevice public override bool IsInitialized => _IsInitialized; + protected MockDeviceBase(ILogger logger, ITracer tracer) : base(logger, tracer) + { + + } + public void Close() { } @@ -23,16 +28,22 @@ public Task InitializeAsync() private byte[] LastWrittenBuffer; - public async override Task ReadAsync() + public override async Task ReadAsync() { - if (LastWrittenBuffer != null) return LastWrittenBuffer; - - return await Task.FromResult(new byte[64] { 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + if (LastWrittenBuffer != null) + { + Tracer.Trace(false, LastWrittenBuffer); + return LastWrittenBuffer; + } + var data = new byte[64] { 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + Tracer.Trace(false, data); + return await Task.FromResult(data); } public override Task WriteAsync(byte[] data) { LastWrittenBuffer = data; + Tracer.Trace(true, data); return Task.FromResult(true); } } diff --git a/src/Device.Net.UnitTests/MockFactoryBase.cs b/src/Device.Net.UnitTests/MockFactoryBase.cs index 47b79f5b..50f677cb 100644 --- a/src/Device.Net.UnitTests/MockFactoryBase.cs +++ b/src/Device.Net.UnitTests/MockFactoryBase.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; namespace Device.Net.UnitTests @@ -12,7 +11,14 @@ public abstract class MockFactoryBase : IDeviceFactory public abstract DeviceType DeviceType { get; } - public ILogger Logger { get; protected set; } + public ILogger Logger { get; } + public ITracer Tracer { get; } + + protected MockFactoryBase(ILogger logger, ITracer tracer) + { + Logger = logger; + Tracer = tracer; + } public abstract uint ProductId { get; } public abstract uint VendorId { get; } @@ -21,14 +27,19 @@ public Task> GetConnectedDeviceDefinition { var result = new List(); - var mockConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId) { ProductId = ProductId, VendorId = VendorId }; + var mockConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId) + { + ProductId = ProductId, + VendorId = VendorId, + DeviceType = this is MockHidFactory ? DeviceType.Hid : DeviceType.Usb + }; + + + if (!IsConnected) return Task.FromResult>(result); - if (IsConnected) + if (DeviceManager.IsDefinitionMatch(deviceDefinition, mockConnectedDeviceDefinition)) { - if (DeviceManager.IsDefinitionMatch(deviceDefinition, mockConnectedDeviceDefinition)) - { - result.Add(mockConnectedDeviceDefinition); - } + result.Add(mockConnectedDeviceDefinition); } return Task.FromResult>(result); diff --git a/src/Device.Net.UnitTests/MockHidDevice.cs b/src/Device.Net.UnitTests/MockHidDevice.cs index 0331988f..efdfb8d7 100644 --- a/src/Device.Net.UnitTests/MockHidDevice.cs +++ b/src/Device.Net.UnitTests/MockHidDevice.cs @@ -1,16 +1,15 @@ namespace Device.Net.UnitTests { - public class MockHidDevice : MockDeviceBase, IDevice + public class MockHidDevice : MockDeviceBase { public const uint ProductId = 1; public const uint VendorId = 1; public const string MockedDeviceId = "123"; - public MockHidDevice() + public MockHidDevice(ILogger logger, ITracer tracer) : base(logger, tracer) { DeviceId = MockedDeviceId; - ConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId) { ProductId = ProductId, VendorId = VendorId }; - Logger = new DebugLogger { LogToConsole = true }; + ConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId) { ProductId = ProductId, VendorId = VendorId, DeviceType = DeviceType.Hid }; } } } diff --git a/src/Device.Net.UnitTests/MockHidFactory.cs b/src/Device.Net.UnitTests/MockHidFactory.cs index 02438ada..69bacb56 100644 --- a/src/Device.Net.UnitTests/MockHidFactory.cs +++ b/src/Device.Net.UnitTests/MockHidFactory.cs @@ -1,12 +1,11 @@ -using System; - -namespace Device.Net.UnitTests +namespace Device.Net.UnitTests { - public class MockHidFactory : MockFactoryBase, IDeviceFactory + public class MockHidFactory : MockFactoryBase { - public MockHidFactory() + public const string FoundMessage = "Found device {0}"; + + public MockHidFactory(ILogger logger, ITracer tracer) : base(logger, tracer) { - Logger = new DebugLogger { LogToConsole = true }; } public override string DeviceId => MockHidDevice.MockedDeviceId; @@ -21,22 +20,22 @@ public MockHidFactory() public override uint VendorId => MockHidDevice.VendorId; - public static void Register(ILogger logger) + public static void Register(ILogger logger, ITracer tracer) { - DeviceManager.Current.DeviceFactories.Add(new MockHidFactory() { Logger = logger }); + DeviceManager.Current.DeviceFactories.Add(new MockHidFactory(logger, tracer)); } public override IDevice GetDevice(ConnectedDeviceDefinition deviceDefinition) { - if (deviceDefinition != null) - { - if (deviceDefinition.DeviceId == DeviceId) - { - if (!deviceDefinition.DeviceType.HasValue || deviceDefinition.DeviceType == DeviceType.Hid) return new MockHidDevice(); - } - } - - throw new Exception("Couldn't get a device"); + if (deviceDefinition == null) return null; + + if (deviceDefinition.DeviceId != DeviceId) return null; + + if (deviceDefinition.DeviceType.HasValue && deviceDefinition.DeviceType != DeviceType.Hid) return null; + + Logger?.Log(string.Format(FoundMessage, DeviceId), nameof(MockHidFactory), null, LogLevel.Information); + + return new MockHidDevice(Logger, Tracer); } } } diff --git a/src/Device.Net.UnitTests/MockLogger.cs b/src/Device.Net.UnitTests/MockLogger.cs new file mode 100644 index 00000000..7253df34 --- /dev/null +++ b/src/Device.Net.UnitTests/MockLogger.cs @@ -0,0 +1,17 @@ +using System; +using System.Text; + +namespace Device.Net.UnitTests +{ + internal class MockLogger : ILogger + { + readonly StringBuilder _stringBuilder = new StringBuilder(); + + public string LogText => _stringBuilder.ToString(); + + public void Log(string message, string region, Exception ex, LogLevel logLevel) + { + _stringBuilder.Append(message); + } + } +} \ No newline at end of file diff --git a/src/Device.Net.UnitTests/MockTracer.cs b/src/Device.Net.UnitTests/MockTracer.cs new file mode 100644 index 00000000..ff4e471d --- /dev/null +++ b/src/Device.Net.UnitTests/MockTracer.cs @@ -0,0 +1,17 @@ +using System; + +namespace Device.Net.UnitTests +{ + public class MockTracer : ITracer + { + public int WriteCount { get; private set; } + public int ReadCount { get; private set; } + + public void Trace(bool isWrite, byte[] data) + { + if (data == null) throw new Exception("data must not be null"); + if (isWrite) WriteCount++; + if (!isWrite) ReadCount++; + } + } +} diff --git a/src/Device.Net.UnitTests/MockUsbDevice.cs b/src/Device.Net.UnitTests/MockUsbDevice.cs index 0623e60c..a95c78fa 100644 --- a/src/Device.Net.UnitTests/MockUsbDevice.cs +++ b/src/Device.Net.UnitTests/MockUsbDevice.cs @@ -1,16 +1,15 @@ namespace Device.Net.UnitTests { - public class MockUsbDevice : MockDeviceBase, IDevice + public class MockUsbDevice : MockDeviceBase { public const uint ProductId = 2; public const uint VendorId = 2; public const string MockedDeviceId = "321"; - public MockUsbDevice() + public MockUsbDevice(ILogger logger, ITracer tracer) : base(logger, tracer) { DeviceId = MockedDeviceId; - ConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId) { ProductId = ProductId, VendorId = VendorId }; - Logger = new DebugLogger { LogToConsole = true }; + ConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId) { ProductId = ProductId, VendorId = VendorId, DeviceType = DeviceType.Usb }; } } } diff --git a/src/Device.Net.UnitTests/MockUsbFactory.cs b/src/Device.Net.UnitTests/MockUsbFactory.cs index ffec9a99..9286f12a 100644 --- a/src/Device.Net.UnitTests/MockUsbFactory.cs +++ b/src/Device.Net.UnitTests/MockUsbFactory.cs @@ -2,11 +2,10 @@ namespace Device.Net.UnitTests { - public class MockUsbFactory : MockFactoryBase, IDeviceFactory + public class MockUsbFactory : MockFactoryBase { - public MockUsbFactory() + public MockUsbFactory(ILogger logger, ITracer tracer) : base(logger, tracer) { - Logger = new DebugLogger { LogToConsole = true }; } public override string DeviceId => MockUsbDevice.MockedDeviceId; @@ -21,22 +20,24 @@ public MockUsbFactory() public override uint VendorId => MockUsbDevice.VendorId; - public static void Register(ILogger logger) + public const string FoundMessage = "Found device {0}"; + + public static void Register(ILogger logger, ITracer tracer) { - DeviceManager.Current.DeviceFactories.Add(new MockUsbFactory() { Logger = logger }); + DeviceManager.Current.DeviceFactories.Add(new MockUsbFactory(logger, tracer)); } public override IDevice GetDevice(ConnectedDeviceDefinition deviceDefinition) { - if (deviceDefinition != null) - { - if (deviceDefinition.DeviceId == DeviceId) - { - if (!deviceDefinition.DeviceType.HasValue || deviceDefinition.DeviceType == DeviceType.Usb) return new MockUsbDevice() ; - } - } - - throw new Exception("Couldn't get a device"); + if (deviceDefinition == null) throw new Exception("Couldn't get a device"); + + if (deviceDefinition.DeviceId != DeviceId) throw new Exception("Couldn't get a device"); + + if (deviceDefinition.DeviceType.HasValue && deviceDefinition.DeviceType != DeviceType.Usb) throw new Exception("Couldn't get a device"); + + Logger?.Log(string.Format(FoundMessage, DeviceId), nameof(MockUsbFactory), null, LogLevel.Information); + + return new MockUsbDevice(Logger, Tracer); } } } diff --git a/src/Device.Net.UnitTests/UnitTests.cs b/src/Device.Net.UnitTests/UnitTests.cs index bf29560a..7fc2b249 100644 --- a/src/Device.Net.UnitTests/UnitTests.cs +++ b/src/Device.Net.UnitTests/UnitTests.cs @@ -1,3 +1,4 @@ +using Device.Net.Exceptions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; @@ -10,12 +11,15 @@ namespace Device.Net.UnitTests [TestClass] public class UnitTests { + private static readonly MockLogger logger = new MockLogger(); + private static readonly MockTracer tracer = new MockTracer(); + #region Tests [TestInitialize] public void Startup() { - MockHidFactory.Register(null); - MockUsbFactory.Register(null); + MockHidFactory.Register(logger, tracer); + MockUsbFactory.Register(logger, tracer); } [TestMethod] @@ -33,6 +37,25 @@ public async Task TestWithMatchedFilterAsync(bool isHidConnected, bool isUsbConn MockHidFactory.IsConnectedStatic = isHidConnected; MockUsbFactory.IsConnectedStatic = isUsbConnected; var connectedDeviceDefinitions = (await DeviceManager.Current.GetConnectedDeviceDefinitionsAsync(new FilterDeviceDefinition { ProductId = pid, VendorId = vid })).ToList(); + + if (connectedDeviceDefinitions.Count > 0) + { + foreach (var connectedDeviceDefinition in connectedDeviceDefinitions) + { + var device = DeviceManager.Current.GetDevice(connectedDeviceDefinition); + + if (device != null && connectedDeviceDefinition.DeviceType == DeviceType.Hid) + { + Assert.IsTrue(logger.LogText.Contains(string.Format(MockHidFactory.FoundMessage, connectedDeviceDefinition.DeviceId))); + } + + if (device != null && connectedDeviceDefinition.DeviceType == DeviceType.Usb) + { + Assert.IsTrue(logger.LogText.Contains(string.Format(MockUsbFactory.FoundMessage, connectedDeviceDefinition.DeviceId))); + } + } + } + Assert.IsNotNull(connectedDeviceDefinitions); Assert.AreEqual(expectedCount, connectedDeviceDefinitions.Count); } @@ -40,20 +63,25 @@ public async Task TestWithMatchedFilterAsync(bool isHidConnected, bool isUsbConn [TestMethod] [DataRow(true, true, MockHidDevice.VendorId, MockHidDevice.ProductId)] [DataRow(true, false, MockHidDevice.VendorId, MockHidDevice.ProductId)] - //[DataRow(true, true, MockUsbDevice.VendorId, MockUsbDevice.ProductId)] - //[DataRow(false, true, MockUsbDevice.VendorId, MockUsbDevice.ProductId)] public async Task TestWriteAndReadThreadSafety(bool isHidConnected, bool isUsbConnected, uint vid, uint pid) { + var readtraceCount = tracer.ReadCount; + var writetraceCount = tracer.WriteCount; + MockHidFactory.IsConnectedStatic = isHidConnected; MockUsbFactory.IsConnectedStatic = isUsbConnected; var connectedDeviceDefinition = (await DeviceManager.Current.GetConnectedDeviceDefinitionsAsync(new FilterDeviceDefinition { ProductId = pid, VendorId = vid })).ToList().First(); - var mockHidDevice = new MockHidDevice() { DeviceId = connectedDeviceDefinition.DeviceId }; + + + var mockHidDevice = new MockHidDevice(logger, tracer) { DeviceId = connectedDeviceDefinition.DeviceId }; var writeAndReadTasks = new List>(); //TODO: Does this properly test thread safety? - for (byte i = 0; i < 10; i++) + const int count = 10; + + for (byte i = 0; i < count; i++) { writeAndReadTasks.Add(mockHidDevice.WriteAndReadAsync(new byte[64] { i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })); } @@ -65,6 +93,11 @@ public async Task TestWriteAndReadThreadSafety(bool isHidConnected, bool isUsbCo var result = results[i]; Assert.IsTrue(result[0] == i); } + + Assert.AreEqual(readtraceCount + count, tracer.ReadCount); + Assert.AreEqual(writetraceCount + count, tracer.WriteCount); + + Assert.IsTrue(logger.LogText.Contains(Messages.SuccessMessageWriteAndReadCalled)); } [TestMethod] @@ -120,7 +153,7 @@ public async Task TestDeviceFactoriesNotRegisteredException() try { - var connectedDevices = await DeviceManager.Current.GetConnectedDeviceDefinitionsAsync(null); + await DeviceManager.Current.GetConnectedDeviceDefinitionsAsync(null); } catch (DeviceFactoriesNotRegisteredException) { diff --git a/src/Device.Net/BulkIOService.cs b/src/Device.Net/BulkIOService.cs new file mode 100644 index 00000000..d97b86f8 --- /dev/null +++ b/src/Device.Net/BulkIOService.cs @@ -0,0 +1,99 @@ +using Device.Net.Exceptions; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Device.Net +{ + /// + /// Wraps a device and allows it to be used like a stream + /// + public class BulkIOService + { + #region Public Properties + public IDevice Device { get; } + public int ReadBufferSize { get; } + #endregion + + #region Constructor + public BulkIOService(IDevice device, int readBufferSize) + { + Device = device; + ReadBufferSize = readBufferSize; + } + #endregion + + #region Public Methods + public async Task ReadAsync(byte[] buff, int offset, int len) + { + if (buff == null) throw new ArgumentNullException(nameof(buff)); + + if (buff.Length - offset < len) + { + throw new ValidationException("Index out of bounds"); + } + + var totalRead = 0; + + while (totalRead < len) + { + // Getting some data. + var data = await Device.ReadAsync(); + + if (data.Length > len - totalRead) + { + // Out of bounds. + // Cut the overflowing result. + data = data.Take(len - totalRead).ToArray(); + } + + // Copying buffer. + for (var i = 0; i < data.Length; ++i) + { + buff[totalRead + offset] = data[i]; + ++totalRead; + } + + if (data.Length < ReadBufferSize) + { + // Last buffer. + break; + } + } + + return totalRead; + } + + public async Task WriteAsync(byte[] data, int offset, int len) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + + // Some checking. + if (data.Length - offset < len) + { + throw new ValidationException("Index out of bounds"); + } + + // Determining buffer size. + byte[] writeBuff; + + if (offset == 0 && len == data.Length) + { + writeBuff = data; + } + else + { + writeBuff = new byte[len]; + + // Copying buffer. + for (var i = 0; i < len; ++i) + { + writeBuff[i] = data[offset + i]; + } + } + + await Device.WriteAsync(writeBuff); + } + #endregion + } +} diff --git a/src/Device.Net/CodeRules.ruleset b/src/Device.Net/CodeRules.ruleset index 348f6ffc..4a74e7af 100644 --- a/src/Device.Net/CodeRules.ruleset +++ b/src/Device.Net/CodeRules.ruleset @@ -20,6 +20,7 @@ + @@ -33,7 +34,7 @@ - + @@ -60,14 +61,16 @@ + + - + @@ -75,6 +78,7 @@ + @@ -102,6 +106,7 @@ + @@ -130,6 +135,7 @@ + @@ -191,6 +197,7 @@ + @@ -223,6 +230,7 @@ + @@ -858,7 +866,7 @@ - + @@ -1156,7 +1164,6 @@ - @@ -1167,7 +1174,6 @@ - @@ -1184,7 +1190,6 @@ - @@ -1234,7 +1239,6 @@ - @@ -1275,7 +1279,7 @@ - + diff --git a/src/Device.Net/DebugTracer.cs b/src/Device.Net/DebugTracer.cs index 0f8f242d..82604ba6 100644 --- a/src/Device.Net/DebugTracer.cs +++ b/src/Device.Net/DebugTracer.cs @@ -6,7 +6,9 @@ public class DebugTracer : ITracer { public void Trace(bool isWrite, byte[] data) { - Debug.WriteLine($"({string.Join(",", data)}) - {(isWrite ? "Write" : "Read")} ({data.Length})"); + if (data == null) return; + + Debug.WriteLine($"{(isWrite ? "Write" : "Read")}: ({string.Join(",", data)}) ({data.Length})"); } } } diff --git a/src/Device.Net/Device.Net.csproj b/src/Device.Net/Device.Net.csproj index 3251c1d6..0c9f2633 100644 --- a/src/Device.Net/Device.Net.csproj +++ b/src/Device.Net/Device.Net.csproj @@ -15,7 +15,7 @@ https://github.com/MelbourneDeveloper/Device.Net Hid USB Android UWP Windows C# MacOS Linux NU5125 - true + false CodeRules.ruleset @@ -39,7 +39,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Device.Net/DeviceBase.cs b/src/Device.Net/DeviceBase.cs index 7876bf6d..681cfa23 100644 --- a/src/Device.Net/DeviceBase.cs +++ b/src/Device.Net/DeviceBase.cs @@ -8,8 +8,8 @@ namespace Device.Net public abstract class DeviceBase : IDisposable { #region Fields - private SemaphoreSlim _WriteAndReadLock = new SemaphoreSlim(1, 1); - private bool disposed = false; + private readonly SemaphoreSlim _WriteAndReadLock = new SemaphoreSlim(1, 1); + private bool disposed; public const string DeviceDisposedErrorMessage = "This device has already been disposed"; private string _LogRegion; #endregion @@ -21,10 +21,18 @@ public abstract class DeviceBase : IDisposable #endregion #region Public Properties - public ITracer Tracer { get; set; } public ConnectedDeviceDefinitionBase ConnectedDeviceDefinition { get; set; } public string DeviceId { get; set; } - public ILogger Logger { get; set; } + public ILogger Logger { get; } + public ITracer Tracer { get; } + #endregion + + #region Constructor + protected DeviceBase(ILogger logger, ITracer tracer) + { + Tracer = tracer; + Logger = logger; + } #endregion #region Private Methods @@ -75,12 +83,12 @@ public async Task WriteAndReadAsync(byte[] writeBuffer) { await WriteAsync(writeBuffer); var retVal = await ReadAsync(); - Log($"Successfully called {nameof(WriteAndReadAsync)}"); + Log(Messages.SuccessMessageWriteAndReadCalled); return retVal; } catch (Exception ex) { - Log("Read/Write Error", ex); + Log(Messages.ErrorMessageReadWrite, ex); throw; } finally @@ -94,6 +102,8 @@ public async Task WriteAndReadAsync(byte[] writeBuffer) /// public static byte[] RemoveFirstByte(byte[] bytes) { + if (bytes == null) throw new ArgumentNullException(nameof(bytes)); + var length = bytes.Length - 1; var retVal = new byte[length]; diff --git a/src/Device.Net/DeviceFactoriesNotRegisteredException.cs b/src/Device.Net/DeviceFactoriesNotRegisteredException.cs deleted file mode 100644 index d89e683b..00000000 --- a/src/Device.Net/DeviceFactoriesNotRegisteredException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Device.Net -{ -#pragma warning disable CA2229 -#pragma warning disable CA1032 - public class DeviceFactoriesNotRegisteredException : Exception -#pragma warning restore CA1032 -#pragma warning restore CA2229 - { - public DeviceFactoriesNotRegisteredException() : base("No device factories have been registered") - { - } - } -} \ No newline at end of file diff --git a/src/Device.Net/DeviceListener.cs b/src/Device.Net/DeviceListener.cs index 30c0eb3d..fe2e026a 100644 --- a/src/Device.Net/DeviceListener.cs +++ b/src/Device.Net/DeviceListener.cs @@ -1,4 +1,5 @@ -using System; +using Device.Net.Exceptions; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -72,7 +73,7 @@ public void Start() { if (_PollTimer == null) { - throw new Exception("Polling is not enabled. Please specify pollMilliseconds in the constructor"); + throw new ValidationException(Messages.ErrorMessagePollingNotEnabled); } if (DeviceManager.Current.DeviceFactories.Count == 0) throw new DeviceFactoriesNotRegisteredException(); @@ -124,7 +125,7 @@ public async Task CheckForDevicesAsync() //Let listeners know a registered device was initialized DeviceInitialized?.Invoke(this, new DeviceEventArgs(device)); - Log("Device connected", null); + Log(Messages.InformationMessageDeviceConnected, null); } } @@ -149,7 +150,7 @@ public async Task CheckForDevicesAsync() removeDefs.Add(filteredDeviceDefinitionKey); - Log("Disconnected", null); + Log(Messages.InformationMessageDeviceListenerDisconnected, null); } } } @@ -159,12 +160,12 @@ public async Task CheckForDevicesAsync() _CreatedDevicesByDefinition.Remove(removeDef); } - Log("Poll complete", null); + Log(Messages.InformationMessageDeviceListenerPollingComplete, null); } catch (Exception ex) { - Log("Hid polling error", ex); + Log(Messages.ErrorMessagePollingError, ex); //TODO: What else to do here? } @@ -186,6 +187,8 @@ public void Dispose() Stop(); + _PollTimer?.Dispose(); + foreach (var key in _CreatedDevicesByDefinition.Keys) { _CreatedDevicesByDefinition[key].Dispose(); diff --git a/src/Device.Net/DeviceManager.cs b/src/Device.Net/DeviceManager.cs index f64d7c99..736ebfc2 100644 --- a/src/Device.Net/DeviceManager.cs +++ b/src/Device.Net/DeviceManager.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using Device.Net.Exceptions; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -40,17 +42,21 @@ public async Task> GetConnectedDeviceDefi //TODO: Duplicate code here... public IDevice GetDevice(ConnectedDeviceDefinition connectedDeviceDefinition) { + if (connectedDeviceDefinition == null) throw new ArgumentNullException(nameof(connectedDeviceDefinition)); + foreach (var deviceFactory in DeviceFactories) { if (connectedDeviceDefinition.DeviceType.HasValue && (deviceFactory.DeviceType != connectedDeviceDefinition.DeviceType)) continue; return deviceFactory.GetDevice(connectedDeviceDefinition); } - throw new System.Exception("Couldn't get a device"); + throw new DeviceException(Messages.ErrorMessageCouldntGetDevice); } public async Task> GetDevicesAsync(IList deviceDefinitions) { + if (deviceDefinitions == null) throw new ArgumentNullException(nameof(deviceDefinitions), $"{nameof(GetConnectedDeviceDefinitionsAsync)} can be used to enumerate all devices without specifying definitions."); + var retVal = new List(); foreach (var deviceFactory in DeviceFactories) @@ -81,6 +87,8 @@ public async Task> GetDevicesAsync(IList d #region Public Static Methods public static bool IsDefinitionMatch(FilterDeviceDefinition filterDevice, ConnectedDeviceDefinition actualDevice) { + if (actualDevice == null) throw new ArgumentNullException(nameof(actualDevice)); + if (filterDevice == null) return true; var vendorIdPasses = !filterDevice.VendorId.HasValue || filterDevice.VendorId == actualDevice.VendorId; diff --git a/src/Device.Net/Exceptions/ApiException.cs b/src/Device.Net/Exceptions/ApiException.cs new file mode 100644 index 00000000..d249eecf --- /dev/null +++ b/src/Device.Net/Exceptions/ApiException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace Device.Net.Exceptions +{ + public class ApiException : Exception + { + public ApiException() + { + } + + public ApiException(string message) : base(message) + { + } + + public ApiException(string message, Exception innerException) : base(message, innerException) + { + } + + protected ApiException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Device.Net/Exceptions/AsyncException.cs b/src/Device.Net/Exceptions/AsyncException.cs new file mode 100644 index 00000000..1b428001 --- /dev/null +++ b/src/Device.Net/Exceptions/AsyncException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Device.Net.Exceptions +{ + public class AsyncException : Exception + { + public AsyncException(string message) : base(message) + { + } + + public AsyncException() + { + } + + public AsyncException(string message, Exception innerException) : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/Device.Net/DeviceException.cs b/src/Device.Net/Exceptions/DeviceException.cs similarity index 91% rename from src/Device.Net/DeviceException.cs rename to src/Device.Net/Exceptions/DeviceException.cs index 2e77e5fe..89cb1cbb 100644 --- a/src/Device.Net/DeviceException.cs +++ b/src/Device.Net/Exceptions/DeviceException.cs @@ -1,6 +1,6 @@ using System; -namespace Device.Net +namespace Device.Net.Exceptions { public class DeviceException : Exception { diff --git a/src/Device.Net/Exceptions/DeviceFactoriesNotRegisteredException.cs b/src/Device.Net/Exceptions/DeviceFactoriesNotRegisteredException.cs new file mode 100644 index 00000000..9d0743af --- /dev/null +++ b/src/Device.Net/Exceptions/DeviceFactoriesNotRegisteredException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Device.Net.Exceptions +{ + public class DeviceFactoriesNotRegisteredException : Exception + { + public DeviceFactoriesNotRegisteredException() : base(Messages.ErrorMessageNoDeviceFactoriesRegistered) + { + } + + public DeviceFactoriesNotRegisteredException(string message) : base(message) + { + } + + public DeviceFactoriesNotRegisteredException(string message, Exception innerException) : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/Device.Net/NotInitializedException.cs b/src/Device.Net/Exceptions/NotInitializedException.cs similarity index 94% rename from src/Device.Net/NotInitializedException.cs rename to src/Device.Net/Exceptions/NotInitializedException.cs index d2e244f1..ca023c03 100644 --- a/src/Device.Net/NotInitializedException.cs +++ b/src/Device.Net/Exceptions/NotInitializedException.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; -namespace Device.Net +namespace Device.Net.Windows { public class NotInitializedException : Exception { diff --git a/src/Device.Net/Exceptions/ValidationException.cs b/src/Device.Net/Exceptions/ValidationException.cs new file mode 100644 index 00000000..727ff61b --- /dev/null +++ b/src/Device.Net/Exceptions/ValidationException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace Device.Net.Exceptions +{ + public class ValidationException : Exception + { + public ValidationException() + { + } + + public ValidationException(string message) : base(message) + { + } + + public ValidationException(string message, Exception innerException) : base(message, innerException) + { + } + + protected ValidationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/Device.Net/Messages.cs b/src/Device.Net/Messages.cs new file mode 100644 index 00000000..067ab851 --- /dev/null +++ b/src/Device.Net/Messages.cs @@ -0,0 +1,40 @@ +namespace Device.Net +{ + public static class Messages + { + #region Device Initialization + public const string ErrorMessageNotInitialized = "The device has not been initialized."; + public const string ErrorMessageCouldntIntializeDevice = "Couldn't initialize device"; + public const string ErrorMessageCantOpenWrite = "Could not open connection for writing"; + public const string ErrorMessageCantOpenRead = "Could not open connection for reading"; + #endregion + + #region Misc + public const string ErrorMessageReentry = "Reentry. This method is not thread safe"; + public static string SuccessMessageWriteAndReadCalled => $"Successfully called {nameof(DeviceBase.WriteAndReadAsync)}"; + #endregion + + #region IO + public static string GetErrorMessageInvalidWriteLength(int length, uint count) + { + return $"Write failure. {length} bytes were sent to the device but it claims that {count} were sent."; + } + + public const string ErrorMessageReadWrite = "Read/Write Error"; + #endregion + + #region Polling + public const string InformationMessageDeviceListenerPollingComplete = "Poll complete"; + public const string InformationMessageDeviceListenerDisconnected = "Disconnected"; + public const string ErrorMessagePollingError = "Hid polling error"; + public const string InformationMessageDeviceConnected = "Device connected"; + public const string ErrorMessagePollingNotEnabled = "Polling is not enabled. Please specify pollMilliseconds in the constructor"; + #endregion + + #region Factories + public const string ErrorMessageNoDeviceFactoriesRegistered = "No device factories have been registered"; + public const string ErrorMessageCouldntGetDevice = "Couldn't get a device"; + #endregion + + } +} diff --git a/src/Device.Net/Windows/APICalls.cs b/src/Device.Net/Windows/APICalls.cs index 92bbf3ca..904bfb87 100644 --- a/src/Device.Net/Windows/APICalls.cs +++ b/src/Device.Net/Windows/APICalls.cs @@ -1,9 +1,8 @@ -using Device.Net.Windows; -using System; -using Microsoft.Win32.SafeHandles; +using System; using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; -namespace Device.Net +namespace Device.Net.Windows { public static class APICalls { diff --git a/src/Device.Net/Windows/WindowsDeviceBase.cs b/src/Device.Net/Windows/WindowsDeviceBase.cs index 3947b7f5..f021acb5 100644 --- a/src/Device.Net/Windows/WindowsDeviceBase.cs +++ b/src/Device.Net/Windows/WindowsDeviceBase.cs @@ -1,4 +1,4 @@ -using System; +using Device.Net.Exceptions; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -14,7 +14,7 @@ public abstract class WindowsDeviceBase : DeviceBase #endregion #region Constructor - protected WindowsDeviceBase(string deviceId) + protected WindowsDeviceBase(string deviceId, ILogger logger, ITracer tracer) : base(logger, tracer) { DeviceId = deviceId; } @@ -33,7 +33,7 @@ public static void HandleError(bool isSuccess, string message) //TODO: Loggin if (errorCode == 0) return; - throw new Exception($"{message}. Error code: {errorCode}"); + throw new ApiException($"{message}. Error code: {errorCode}"); } #endregion } diff --git a/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs b/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs index 93fcde8c..08933d38 100644 --- a/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs +++ b/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs @@ -14,8 +14,8 @@ namespace Device.Net.Windows public abstract class WindowsDeviceFactoryBase { #region Public Properties - public ILogger Logger { get; set; } - public ITracer Tracer { get; set; } + public ILogger Logger { get; } + public ITracer Tracer { get; } #endregion #region Public Abstract Properties @@ -27,6 +27,14 @@ public abstract class WindowsDeviceFactoryBase protected abstract Guid GetClassGuid(); #endregion + #region Constructor + protected WindowsDeviceFactoryBase(ILogger logger, ITracer tracer) + { + Logger = logger; + Tracer = tracer; + } + #endregion + #region Public Methods public async Task> GetConnectedDeviceDefinitionsAsync(FilterDeviceDefinition filterDeviceDefinition) { @@ -147,6 +155,8 @@ protected void Log(string message, string region, Exception ex, LogLevel logLeve #region Private Static Methods private static uint GetNumberFromDeviceId(string deviceId, string searchString) { + if (deviceId == null) throw new ArgumentNullException(nameof(deviceId)); + var indexOfSearchString = deviceId.IndexOf(searchString, StringComparison.OrdinalIgnoreCase); string hexString = null; if (indexOfSearchString > -1) @@ -168,10 +178,8 @@ public static ConnectedDeviceDefinition GetDeviceDefinitionFromWindowsDeviceId(s vid = GetNumberFromDeviceId(deviceId, "vid_"); pid = GetNumberFromDeviceId(deviceId, "pid_"); } - catch (Exception) + catch { - //TODO: Logging - //We really need the Vid/Pid here for polling etc. so not sure if swallowing errors it the way to go } return new ConnectedDeviceDefinition(deviceId) { DeviceType = deviceType, VendorId = vid, ProductId = pid }; diff --git a/src/Device.Net/Windows/WindowsException.cs b/src/Device.Net/Windows/WindowsException.cs deleted file mode 100644 index 458c209c..00000000 --- a/src/Device.Net/Windows/WindowsException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Device.Net.Windows -{ - public class WindowsException : Exception - { - public WindowsException(string message) : base(message) - { - - } - - public WindowsException() - { - } - - public WindowsException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/src/Hid.Net.UWP/Hid.Net.UWP.csproj b/src/Hid.Net.UWP/Hid.Net.UWP.csproj index 32049279..85851fe3 100644 --- a/src/Hid.Net.UWP/Hid.Net.UWP.csproj +++ b/src/Hid.Net.UWP/Hid.Net.UWP.csproj @@ -17,9 +17,9 @@ 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} ../Device.Net/CodeRules.ruleset - true - 7.3 - PackageReference + false + 7.3 + PackageReference AnyCPU @@ -38,16 +38,16 @@ TRACE;NETFX_CORE;WINDOWS_UWP prompt true - ../Device.Net/Device.Net.pfx + ../Device.Net/Device.Net.pfx - + - 2.6.3 + 2.9.3 runtime; build; native; contentfiles; analyzers all diff --git a/src/Hid.Net.UWP/Properties/AssemblyInfo.cs b/src/Hid.Net.UWP/Properties/AssemblyInfo.cs index 08be974c..08b8c98a 100644 --- a/src/Hid.Net.UWP/Properties/AssemblyInfo.cs +++ b/src/Hid.Net.UWP/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Hid.Net.UWP")] -[assembly: AssemblyCopyright("Copyright © Christian Findlay 2018")] +[assembly: AssemblyCopyright("Copyright © Christian Findlay 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/src/Hid.Net.UWP/UWPHidDevice.cs b/src/Hid.Net.UWP/UWPHidDevice.cs index 21d67df1..135ad776 100644 --- a/src/Hid.Net.UWP/UWPHidDevice.cs +++ b/src/Hid.Net.UWP/UWPHidDevice.cs @@ -1,5 +1,8 @@ -using Device.Net.UWP; +using Device.Net; +using Device.Net.Exceptions; +using Device.Net.UWP; using System; +using System.IO; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using Windows.Devices.HumanInterfaceDevice; @@ -34,11 +37,15 @@ private void _HidDevice_InputReportReceived(HidDevice sender, HidInputReportRece #endregion #region Constructors - public UWPHidDevice() + public UWPHidDevice(ILogger logger, ITracer tracer) : this(null, logger, tracer) { } - public UWPHidDevice(string deviceId) : base(deviceId) + public UWPHidDevice(string deviceId) : this(deviceId, null, null) + { + } + + public UWPHidDevice(string deviceId, ILogger logger, ITracer tracer) : base(deviceId, logger, tracer) { } #endregion @@ -48,7 +55,7 @@ public override async Task InitializeAsync() { //TODO: Put a lock here to stop reentrancy of multiple calls - if (Disposed) throw new Exception(DeviceDisposedErrorMessage); + if (Disposed) throw new ValidationException(DeviceDisposedErrorMessage); Log("Initializing Hid device", null); @@ -60,7 +67,7 @@ public override async Task InitializeAsync() } else { - throw new Exception($"The device {DeviceId} failed to initialize"); + throw new DeviceException($"The device {DeviceId} failed to initialize"); } } @@ -92,6 +99,8 @@ public override Task WriteAsync(byte[] data) public async Task WriteReportAsync(byte[] data, byte? reportId) { + if (data == null) throw new ArgumentNullException(nameof(data)); + byte[] bytes; if (DataHasExtraByte) { @@ -111,19 +120,30 @@ public async Task WriteReportAsync(byte[] data, byte? reportId) try { var operation = ConnectedDevice.SendOutputReportAsync(outReport); - await operation.AsTask(); - Tracer?.Trace(false, bytes); + var count = await operation.AsTask(); + if (count == bytes.Length) + { + Tracer?.Trace(true, bytes); + } + else + { + var message = Messages.GetErrorMessageInvalidWriteLength(bytes.Length, count); + Logger?.Log(message, GetType().Name, null, LogLevel.Error); + throw new IOException(message); + } } catch (ArgumentException ex) { //TODO: Check the string is nasty. Validation on the size of the array being sent should be done earlier anyway if (string.Equals(ex.Message, "Value does not fall within the expected range.", StringComparison.Ordinal)) { - throw new Exception("It seems that the data being sent to the device does not match the accepted size. Have you checked DataHasExtraByte?", ex); + throw new IOException("It seems that the data being sent to the device does not match the accepted size. Have you checked DataHasExtraByte?", ex); } throw; } } + + #endregion #region Public Overrides @@ -143,7 +163,9 @@ public async Task ReadReportAsync() public override async Task ReadAsync() { - return (await ReadReportAsync()).Data; + var data = (await ReadReportAsync()).Data; + Tracer?.Trace(false, data); + return data; } #endregion diff --git a/src/Hid.Net.UWP/UWPHidDeviceFactory.cs b/src/Hid.Net.UWP/UWPHidDeviceFactory.cs index c4ac8c29..8c05bf2b 100644 --- a/src/Hid.Net.UWP/UWPHidDeviceFactory.cs +++ b/src/Hid.Net.UWP/UWPHidDeviceFactory.cs @@ -11,7 +11,7 @@ public sealed class UWPHidDeviceFactory : UWPDeviceFactoryBase, IDeviceFactory, { #region Fields private readonly SemaphoreSlim _TestConnectionSemaphore = new SemaphoreSlim(1, 1); - private Dictionary _ConnectionTestedDeviceIds = new Dictionary(); + private readonly Dictionary _ConnectionTestedDeviceIds = new Dictionary(); private bool disposed; #endregion @@ -64,10 +64,19 @@ public override async Task TestConnection(string deviceId) } #endregion + #region Constructor + public UWPHidDeviceFactory(ILogger logger, ITracer tracer) : base(logger, tracer) + { + + } + #endregion + #region Public Methods public IDevice GetDevice(ConnectedDeviceDefinition deviceDefinition) { - return deviceDefinition.DeviceType == DeviceType.Usb ? null : new UWPHidDevice(deviceDefinition.DeviceId) { Logger = Logger }; + if (deviceDefinition == null) throw new ArgumentNullException(nameof(deviceDefinition)); + + return deviceDefinition.DeviceType == DeviceType.Usb ? null : new UWPHidDevice(deviceDefinition.DeviceId, Logger, Tracer); } public void Dispose() @@ -82,19 +91,17 @@ public void Dispose() #endregion #region Public Static Methods - public static void Register() - { - Register(null); - } - - public static void Register(ILogger logger) + /// + /// Register the factory for enumerating Hid devices on UWP. + /// + public static void Register(ILogger logger, ITracer tracer) { foreach (var deviceFactory in DeviceManager.Current.DeviceFactories) { if (deviceFactory is UWPHidDeviceFactory) return; } - DeviceManager.Current.DeviceFactories.Add(new UWPHidDeviceFactory() { Logger = logger }); + DeviceManager.Current.DeviceFactories.Add(new UWPHidDeviceFactory(logger, tracer)); } #endregion diff --git a/src/Hid.Net/Hid.Net.csproj b/src/Hid.Net/Hid.Net.csproj index 36f34ff9..1df08c2d 100644 --- a/src/Hid.Net/Hid.Net.csproj +++ b/src/Hid.Net/Hid.Net.csproj @@ -20,7 +20,7 @@ Supports .NET, Android, and UWP out of the box. Other platforms can be addedChristian Findlay https://github.com/MelbourneDeveloper/Device.Net Hid.Net - true + false ../Device.Net/CodeRules.ruleset 1701;1702;NU5125 @@ -50,7 +50,7 @@ Supports .NET, Android, and UWP out of the box. Other platforms can be added - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Hid.Net/Windows/HidAPICalls.cs b/src/Hid.Net/Windows/HidAPICalls.cs index 52eba999..6e353e09 100644 --- a/src/Hid.Net/Windows/HidAPICalls.cs +++ b/src/Hid.Net/Windows/HidAPICalls.cs @@ -1,4 +1,5 @@ -using Device.Net.Windows; +using Device.Net.Exceptions; +using Device.Net.Windows; using Microsoft.Win32.SafeHandles; using System; using System.Runtime.InteropServices; @@ -72,7 +73,7 @@ public static HidCollectionCapabilities GetHidCapabilities(SafeFileHandle readSa var result = HidP_GetCaps(pointerToPreParsedData, out var hidCollectionCapabilities); if (result != HIDP_STATUS_SUCCESS) { - throw new Exception($"Could not get Hid capabilities. Return code: {result}"); + throw new ApiException($"Could not get Hid capabilities. Return code: {result}"); } isSuccess = HidD_FreePreparsedData(ref pointerToPreParsedData); diff --git a/src/Hid.Net/Windows/WindowsHIDDevice.cs b/src/Hid.Net/Windows/WindowsHIDDevice.cs index 4c4c57ca..22930396 100644 --- a/src/Hid.Net/Windows/WindowsHIDDevice.cs +++ b/src/Hid.Net/Windows/WindowsHIDDevice.cs @@ -1,4 +1,5 @@ using Device.Net; +using Device.Net.Exceptions; using Device.Net.Windows; using Microsoft.Win32.SafeHandles; using System; @@ -16,7 +17,7 @@ public sealed class WindowsHidDevice : WindowsDeviceBase, IHidDevice private SafeFileHandle _ReadSafeFileHandle; private SafeFileHandle _WriteSafeFileHandle; private bool _IsClosing; - private bool disposed = false; + private bool disposed; private readonly ushort? _WriteBufferSize; private readonly ushort? _ReadBufferSize; @@ -41,11 +42,15 @@ public sealed class WindowsHidDevice : WindowsDeviceBase, IHidDevice #endregion #region Constructor - public WindowsHidDevice(string deviceId) : this(deviceId, null, null) + public WindowsHidDevice(string deviceId) : this(deviceId, null, null, null, null) { } - public WindowsHidDevice(string deviceId, ushort? writeBufferSize, ushort? readBufferSize) : base(deviceId) + public WindowsHidDevice(string deviceId, ILogger logger, ITracer tracer) : this(deviceId, null, null, logger, tracer) + { + } + + public WindowsHidDevice(string deviceId, ushort? writeBufferSize, ushort? readBufferSize, ILogger logger, ITracer tracer) : base(deviceId, logger, tracer) { _WriteBufferSize = writeBufferSize; _ReadBufferSize = readBufferSize; @@ -61,7 +66,7 @@ private bool Initialize() if (string.IsNullOrEmpty(DeviceId)) { - throw new WindowsHidException($"{nameof(DeviceId)} must be specified before {nameof(Initialize)} can be called."); + throw new ValidationException($"{nameof(DeviceId)} must be specified before {nameof(Initialize)} can be called."); } _ReadSafeFileHandle = APICalls.CreateFile(DeviceId, APICalls.GenericRead | APICalls.GenericWrite, APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, 0, IntPtr.Zero); @@ -69,12 +74,12 @@ private bool Initialize() if (_ReadSafeFileHandle.IsInvalid) { - throw new Exception("Could not open connection for reading"); + throw new ApiException(Messages.ErrorMessageCantOpenRead); } if (_WriteSafeFileHandle.IsInvalid) { - throw new Exception("Could not open connection for writing"); + throw new ApiException(Messages.ErrorMessageCantOpenWrite); } ConnectedDeviceDefinition = WindowsHidDeviceFactory.GetDeviceDefinition(DeviceId, _ReadSafeFileHandle); @@ -84,12 +89,12 @@ private bool Initialize() if (readBufferSize == 0) { - throw new WindowsHidException($"{nameof(ReadBufferSize)} must be specified. HidD_GetAttributes may have failed or returned an InputReportByteLength of 0. Please specify this argument in the constructor"); + throw new ValidationException($"{nameof(ReadBufferSize)} must be specified. HidD_GetAttributes may have failed or returned an InputReportByteLength of 0. Please specify this argument in the constructor"); } if (writeBufferSize == 0) { - throw new WindowsHidException($"{nameof(WriteBufferSize)} must be specified. HidD_GetAttributes may have failed or returned an OutputReportByteLength of 0. Please specify this argument in the constructor. Note: Hid devices are always opened in write mode. If you need to open in read mode, please log an issue here: https://github.com/MelbourneDeveloper/Device.Net/issues"); + throw new ValidationException($"{nameof(WriteBufferSize)} must be specified. HidD_GetAttributes may have failed or returned an OutputReportByteLength of 0. Please specify this argument in the constructor. Note: Hid devices are always opened in write mode. If you need to open in read mode, please log an issue here: https://github.com/MelbourneDeveloper/Device.Net/issues"); } _ReadFileStream = new FileStream(_ReadSafeFileHandle, FileAccess.ReadWrite, readBufferSize, false); @@ -155,14 +160,16 @@ public override void Dispose() public override async Task InitializeAsync() { - if (disposed) throw new Exception(DeviceDisposedErrorMessage); + if (disposed) throw new ValidationException(DeviceDisposedErrorMessage); await Task.Run(() => Initialize()); } public override async Task ReadAsync() { - return (await ReadReportAsync()).Data; + var data = (await ReadReportAsync()).Data; + Tracer?.Trace(false, data); + return data; } public async Task ReadReportAsync() @@ -171,7 +178,7 @@ public async Task ReadReportAsync() if (_ReadFileStream == null) { - throw new Exception("The device has not been initialized"); + throw new NotInitializedException("The device has not been initialized"); } var bytes = new byte[ReadBufferSize]; @@ -190,8 +197,6 @@ public async Task ReadReportAsync() var retVal = ReadBufferHasReportId ? RemoveFirstByte(bytes) : bytes; - Tracer?.Trace(false, retVal); - return new ReadReport(reportId, retVal); } @@ -202,14 +207,16 @@ public override Task WriteAsync(byte[] data) public async Task WriteReportAsync(byte[] data, byte? reportId) { + if (data == null) throw new ArgumentNullException(nameof(data)); + if (_WriteFileStream == null) { - throw new Exception("The device has not been initialized"); + throw new NotInitializedException("The device has not been initialized"); } if (data.Length > WriteBufferSize) { - throw new Exception($"Data is longer than {WriteBufferSize - 1} bytes which is the device's OutputReportByteLength."); + throw new ValidationException($"Data is longer than {WriteBufferSize - 1} bytes which is the device's OutputReportByteLength."); } byte[] bytes; @@ -234,14 +241,13 @@ public async Task WriteReportAsync(byte[] data, byte? reportId) try { await _WriteFileStream.WriteAsync(bytes, 0, bytes.Length); + Tracer?.Trace(true, bytes); } catch (Exception ex) { Log(Helpers.WriteErrorMessage, ex); throw new IOException(Helpers.WriteErrorMessage, ex); } - - Tracer?.Trace(true, bytes); } else { diff --git a/src/Hid.Net/Windows/WindowsHidDeviceFactory.cs b/src/Hid.Net/Windows/WindowsHidDeviceFactory.cs index b856148c..26511b46 100644 --- a/src/Hid.Net/Windows/WindowsHidDeviceFactory.cs +++ b/src/Hid.Net/Windows/WindowsHidDeviceFactory.cs @@ -36,10 +36,19 @@ protected override Guid GetClassGuid() #endregion + #region Constructor + public WindowsHidDeviceFactory(ILogger logger, ITracer tracer) : base(logger, tracer) + { + + } + #endregion + #region Public Methods public IDevice GetDevice(ConnectedDeviceDefinition deviceDefinition) { - return deviceDefinition.DeviceType != DeviceType ? null : new WindowsHidDevice(deviceDefinition.DeviceId) { Logger = Logger }; + if (deviceDefinition == null) throw new ArgumentNullException(nameof(deviceDefinition)); + + return deviceDefinition.DeviceType != DeviceType ? null : new WindowsHidDevice(deviceDefinition.DeviceId, Logger, Tracer); } #endregion @@ -70,14 +79,12 @@ public static ConnectedDeviceDefinition GetDeviceDefinition(string deviceId, Saf #endregion #region Public Static Methods - public static void Register() - { - Register(null); - } - - public static void Register(ILogger logger) + /// + /// Register the factory for enumerating Hid devices on UWP. + /// + public static void Register(ILogger logger, ITracer tracer) { - DeviceManager.Current.DeviceFactories.Add(new WindowsHidDeviceFactory() { Logger = logger }); + DeviceManager.Current.DeviceFactories.Add(new WindowsHidDeviceFactory(logger, tracer)); } #endregion } diff --git a/src/Hid.Net/Windows/WindowsHidException.cs b/src/Hid.Net/Windows/WindowsHidException.cs deleted file mode 100644 index 44eff5a7..00000000 --- a/src/Hid.Net/Windows/WindowsHidException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Hid.Net.Windows -{ - public class WindowsHidException : Exception - { - public WindowsHidException(string message) : base(message) - { - - } - - public WindowsHidException() - { - } - - public WindowsHidException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/src/Usb.Net.Android/AndroidUsbDevice.cs b/src/Usb.Net.Android/AndroidUsbDevice.cs index 511d2673..bc2db736 100644 --- a/src/Usb.Net.Android/AndroidUsbDevice.cs +++ b/src/Usb.Net.Android/AndroidUsbDevice.cs @@ -1,11 +1,10 @@ -using Usb.Net; -using Usb.Net.Android; +using Device.Net; -namespace Device.Net +namespace Usb.Net.Android { - public class AndroidUsbDevice : UsbDevice, IUsbDevice + public class AndroidUsbDevice : UsbDevice { - public AndroidUsbDevice(AndroidUsbDeviceHandler androidUsbDeviceHandler) : base(androidUsbDeviceHandler) + public AndroidUsbDevice(AndroidUsbDeviceHandler androidUsbDeviceHandler, ILogger logger, ITracer tracer) : base(androidUsbDeviceHandler, logger, tracer) { } } diff --git a/src/Usb.Net.Android/AndroidUsbDeviceFactory.cs b/src/Usb.Net.Android/AndroidUsbDeviceFactory.cs index f6b8444d..aef5a7f3 100644 --- a/src/Usb.Net.Android/AndroidUsbDeviceFactory.cs +++ b/src/Usb.Net.Android/AndroidUsbDeviceFactory.cs @@ -1,12 +1,12 @@ -using Android.Content; -using Android.Hardware.Usb; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Usb.Net.Android; +using Android.Content; +using Android.Hardware.Usb; +using Device.Net; -namespace Device.Net +namespace Usb.Net.Android { /// /// TODO: Merge this factory class with other factory classes @@ -16,8 +16,8 @@ public class AndroidUsbDeviceFactory : IDeviceFactory #region Public Properties public UsbManager UsbManager { get; } public Context Context { get; } - public ILogger Logger { get; set; } - public ITracer Tracer { get; set; } + public ILogger Logger { get; } + public ITracer Tracer { get; } public ushort? ReadBufferSize { get; set; } public ushort? WriteBufferSize { get; set; } #endregion @@ -27,10 +27,12 @@ public class AndroidUsbDeviceFactory : IDeviceFactory #endregion #region Constructor - public AndroidUsbDeviceFactory(UsbManager usbManager, Context context) + public AndroidUsbDeviceFactory(UsbManager usbManager, Context context, ILogger logger, ITracer tracer) { UsbManager = usbManager; Context = context; + Logger = logger; + Tracer = tracer; } #endregion @@ -63,12 +65,12 @@ public IDevice GetDevice(ConnectedDeviceDefinition deviceDefinition) throw new Exception($"The device Id '{deviceDefinition.DeviceId}' is not a valid integer"); } - return new AndroidUsbDevice(new AndroidUsbDeviceHandler(UsbManager, Context, deviceId, Logger, Tracer, ReadBufferSize, WriteBufferSize)); + return new AndroidUsbDevice(new AndroidUsbDeviceHandler(UsbManager, Context, deviceId, Logger, Tracer, ReadBufferSize, WriteBufferSize), Logger, Tracer); } #endregion #region Public Static Methods - public static ConnectedDeviceDefinition GetAndroidDeviceDefinition(UsbDevice usbDevice) + public static ConnectedDeviceDefinition GetAndroidDeviceDefinition(global::Android.Hardware.Usb.UsbDevice usbDevice) { var deviceId = usbDevice.DeviceId.ToString(Helpers.ParsingCulture); @@ -83,14 +85,12 @@ public static ConnectedDeviceDefinition GetAndroidDeviceDefinition(UsbDevice usb }; } - public static void Register(UsbManager usbManager, Context context) - { - Register(usbManager, context, null); - } - - public static void Register(UsbManager usbManager, Context context, ILogger logger) + /// + /// Register the factory for enumerating USB devices on Android. + /// + public static void Register(UsbManager usbManager, Context context, ILogger logger, ITracer tracer) { - DeviceManager.Current.DeviceFactories.Add(new AndroidUsbDeviceFactory(usbManager, context) { Logger = logger }); + DeviceManager.Current.DeviceFactories.Add(new AndroidUsbDeviceFactory(usbManager, context, logger, tracer)); } #endregion } diff --git a/src/Usb.Net.Android/AndroidUsbDeviceHandler.cs b/src/Usb.Net.Android/AndroidUsbDeviceHandler.cs index c6192898..b12eb134 100644 --- a/src/Usb.Net.Android/AndroidUsbDeviceHandler.cs +++ b/src/Usb.Net.Android/AndroidUsbDeviceHandler.cs @@ -15,7 +15,7 @@ public class AndroidUsbDeviceHandler : UsbDeviceHandlerBase, IUsbDeviceHandler #region Fields private UsbDeviceConnection _UsbDeviceConnection; private usbDevice _UsbDevice; - private SemaphoreSlim _InitializingSemaphoreSlim = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _InitializingSemaphoreSlim = new SemaphoreSlim(1, 1); private bool _IsClosing; private bool disposed; #endregion @@ -35,16 +35,15 @@ public class AndroidUsbDeviceHandler : UsbDeviceHandlerBase, IUsbDeviceHandler #region Constructor public AndroidUsbDeviceHandler(UsbManager usbManager, Context androidContext, int deviceNumberId, ILogger logger, ITracer tracer, ushort? readBufferLength, ushort? writeBufferLength) : base(logger, tracer, readBufferLength, writeBufferLength) { - DeviceNumberId = deviceNumberId; - UsbManager = usbManager; - AndroidContext = androidContext; + UsbManager = usbManager ?? throw new ArgumentNullException(nameof(usbManager)); + AndroidContext = androidContext ?? throw new ArgumentNullException(nameof(androidContext)); DeviceNumberId = deviceNumberId; } #endregion #region Public Methods - public void Dispose() + public override void Dispose() { if (disposed) return; disposed = true; @@ -53,6 +52,8 @@ public void Dispose() _InitializingSemaphoreSlim.Dispose(); + base.Dispose(); + GC.SuppressFinalize(this); } @@ -81,13 +82,11 @@ public void Close() _IsClosing = false; } - //TODO: Make async properly public Task ReadAsync() { return ReadUsbInterface.ReadAsync(ReadBufferSize); } - //TODO: Perhaps we should implement Batch Begin/Complete so that the UsbRequest is not created again and again. This will be expensive public Task WriteAsync(byte[] data) { return WriteUsbInterface.WriteAsync(data); @@ -154,7 +153,7 @@ public async Task InitializeAsync() //TODO: This is the default interface but other interfaces might be needed so this needs to be changed. var usbInterface = _UsbDevice.GetInterface(x); - var androidUsbInterface = new AndroidUsbInterface(usbInterface, _UsbDeviceConnection, Logger, Tracer, _ReadBufferSize, _WriteBufferSize); + var androidUsbInterface = new AndroidUsbInterface(usbInterface, _UsbDeviceConnection, Logger, Tracer); UsbInterfaces.Add(androidUsbInterface); for (var y = 0; y < usbInterface.EndpointCount; y++) @@ -240,6 +239,9 @@ public Task GetConnectedDeviceDefinitionAsync() #endregion #region Finalizer + /// + /// What's this then? + /// ~AndroidUsbDeviceHandler() { Dispose(); diff --git a/src/Usb.Net.Android/AndroidUsbInterface.cs b/src/Usb.Net.Android/AndroidUsbInterface.cs index 3bd48289..5701dfdb 100644 --- a/src/Usb.Net.Android/AndroidUsbInterface.cs +++ b/src/Usb.Net.Android/AndroidUsbInterface.cs @@ -9,20 +9,21 @@ namespace Usb.Net.Android { internal class AndroidUsbInterface : UsbInterfaceBase, IUsbInterface { + #region Fields + private bool disposed; private readonly UsbInterface _UsbInterface; private readonly UsbDeviceConnection _UsbDeviceConnection; + #endregion - public AndroidUsbInterface(UsbInterface usbInterface, UsbDeviceConnection usbDeviceConnection, ILogger logger, ITracer tracer, ushort? readBufferSize, ushort? writeBufferSize) : base(logger, tracer,readBufferSize, writeBufferSize ) + #region Constructor + public AndroidUsbInterface(UsbInterface usbInterface, UsbDeviceConnection usbDeviceConnection, ILogger logger, ITracer tracer) : base(logger, tracer ) { - _UsbInterface = usbInterface; - _UsbDeviceConnection = usbDeviceConnection; - } - - public void Dispose() - { - throw new NotImplementedException(); + _UsbInterface = usbInterface ?? throw new ArgumentNullException(nameof(usbInterface)); + _UsbDeviceConnection = usbDeviceConnection ?? throw new ArgumentNullException(nameof(usbDeviceConnection)); } + #endregion + #region Public Methods public async Task ReadAsync(uint bufferLength) { return await Task.Run(async () => @@ -72,7 +73,9 @@ public async Task WriteAsync(byte[] data) { try { - var request = new UsbRequest(); + //TODO: Perhaps we should implement Batch Begin/Complete so that the UsbRequest is not created again and again. This will be expensive + + var request = new UsbRequest(); var endpoint = ((AndroidUsbEndpoint)WriteEndpoint).UsbEndpoint; request.Initialize(_UsbDeviceConnection, endpoint); var byteBuffer = ByteBuffer.Wrap(data); @@ -91,5 +94,15 @@ public async Task WriteAsync(byte[] data) } }); } + + public void Dispose() + { + if (disposed) return; + disposed = true; + + _UsbInterface.Dispose(); + _UsbDeviceConnection.Dispose(); + } + #endregion } } \ No newline at end of file diff --git a/src/Usb.Net.Android/Usb.Net.Android.csproj b/src/Usb.Net.Android/Usb.Net.Android.csproj index b8faa00d..5ee295c0 100644 --- a/src/Usb.Net.Android/Usb.Net.Android.csproj +++ b/src/Usb.Net.Android/Usb.Net.Android.csproj @@ -1,10 +1,11 @@  - + + + + + - - - Debug AnyCPU @@ -32,7 +33,7 @@ DEBUG;TRACE prompt 4 - true + false pdbonly @@ -77,13 +78,7 @@ - - - - - - - + @@ -92,11 +87,12 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + + +