diff --git a/src/HidLibrary/HidDevice.cs b/src/HidLibrary/HidDevice.cs index 03299ff..2869ba3 100644 --- a/src/HidLibrary/HidDevice.cs +++ b/src/HidLibrary/HidDevice.cs @@ -24,8 +24,8 @@ public class HidDevice : IHidDevice private bool _monitorDeviceEvents; protected delegate HidDeviceData ReadDelegate(int timeout); protected delegate HidReport ReadReportDelegate(int timeout); - private delegate bool WriteDelegate(byte[] data, int timeout); - private delegate bool WriteReportDelegate(HidReport report, int timeout); + protected delegate bool WriteDelegate(byte[] data, int timeout); + protected delegate bool WriteReportDelegate(HidReport report, int timeout); internal HidDevice(string devicePath, string description = null) { @@ -485,7 +485,7 @@ protected static void EndReadReport(IAsyncResult ar) if ((callbackDelegate != null)) callbackDelegate.Invoke(report); } - private static void EndWrite(IAsyncResult ar) + protected static void EndWrite(IAsyncResult ar) { var hidAsyncState = (HidAsyncState)ar.AsyncState; var callerDelegate = (WriteDelegate)hidAsyncState.CallerDelegate; @@ -495,7 +495,7 @@ private static void EndWrite(IAsyncResult ar) if ((callbackDelegate != null)) callbackDelegate.Invoke(result); } - private static void EndWriteReport(IAsyncResult ar) + protected static void EndWriteReport(IAsyncResult ar) { var hidAsyncState = (HidAsyncState)ar.AsyncState; var callerDelegate = (WriteReportDelegate)hidAsyncState.CallerDelegate; @@ -548,7 +548,7 @@ private static HidDeviceCapabilities GetDeviceCapabilities(IntPtr hidHandle) return new HidDeviceCapabilities(capabilities); } - private bool WriteData(byte[] data, int timeout) + protected bool WriteData(byte[] data, int timeout) { if (_deviceCapabilities.OutputReportByteLength <= 0) return false; diff --git a/src/HidLibrary/HidFastReadWriteDevice.cs b/src/HidLibrary/HidFastReadWriteDevice.cs new file mode 100644 index 0000000..e233f3f --- /dev/null +++ b/src/HidLibrary/HidFastReadWriteDevice.cs @@ -0,0 +1,83 @@ +using System.Threading.Tasks; + +namespace HidLibrary +{ + public class HidFastReadWriteDevice : HidFastReadDevice + { + internal HidFastReadWriteDevice(string devicePath, string description = null) + : base(devicePath, description) { } + + // FastWrite assumes that the device is connected, + // which could cause stability issues if hardware is + // disconnected during a write + public bool FastWrite(byte[] data) + { + return FastWrite(data, 0); + } + + public bool FastWrite(byte[] data, int timeout) + { + try + { + return WriteData(data, timeout); + } + catch + { + return false; + } + } + + public void FastWrite(byte[] data, WriteCallback callback) + { + FastWrite(data, callback, 0); + } + + public void FastWrite(byte[] data, WriteCallback callback, int timeout) + { + var writeDelegate = new WriteDelegate(WriteData); + var asyncState = new HidAsyncState(writeDelegate, callback); + writeDelegate.BeginInvoke(data, timeout, EndWrite, asyncState); + } + + public async Task FastWriteAsync(byte[] data, int timeout = 0) + { + var writeDelegate = new WriteDelegate(FastWrite); +#if NET20 || NET35 || NET5_0_OR_GREATER + return await Task.Factory.StartNew(() => writeDelegate.Invoke(data, timeout)); +#else + return await Task.Factory.FromAsync(writeDelegate.BeginInvoke, writeDelegate.EndInvoke, data, timeout, null); +#endif + } + + public bool FastWriteReport(HidReport report) + { + return FastWriteReport(report, 0); + } + + public bool FastWriteReport(HidReport report, int timeout) + { + return FastWrite(report.GetBytes(), timeout); + } + + public void FastWriteReport(HidReport report, WriteCallback callback) + { + FastWriteReport(report, callback, 0); + } + + public void FastWriteReport(HidReport report, WriteCallback callback, int timeout) + { + var writeReportDelegate = new WriteReportDelegate(FastWriteReport); + var asyncState = new HidAsyncState(writeReportDelegate, callback); + writeReportDelegate.BeginInvoke(report, timeout, EndWriteReport, asyncState); + } + + public async Task FastWriteReportAsync(HidReport report, int timeout = 0) + { + var writeReportDelegate = new WriteReportDelegate(FastWriteReport); +#if NET20 || NET35 || NET5_0_OR_GREATER + return await Task.Factory.StartNew(() => writeReportDelegate.Invoke(report, timeout)); +#else + return await Task.Factory.FromAsync(writeReportDelegate.BeginInvoke, writeReportDelegate.EndInvoke, report, timeout, null); +#endif + } } +} diff --git a/src/HidLibrary/HidFastReadWriteEnumerator.cs b/src/HidLibrary/HidFastReadWriteEnumerator.cs new file mode 100644 index 0000000..04b8aa4 --- /dev/null +++ b/src/HidLibrary/HidFastReadWriteEnumerator.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; + +namespace HidLibrary +{ + public class HidFastReadWriteEnumerator : IHidEnumerator + { + public bool IsConnected(string devicePath) + { + return HidDevices.IsConnected(devicePath); + } + + public IHidDevice GetDevice(string devicePath) + { + return Enumerate(devicePath).FirstOrDefault() as IHidDevice; + } + + public IEnumerable Enumerate() + { + return HidDevices.EnumerateDevices(). + Select(d => new HidFastReadWriteDevice(d.Path, d.Description) as IHidDevice); + } + + public IEnumerable Enumerate(string devicePath) + { + return HidDevices.EnumerateDevices().Where(x => x.Path == devicePath). + Select(d => new HidFastReadWriteDevice(d.Path, d.Description) as IHidDevice); + } + + public IEnumerable Enumerate(int vendorId, params int[] productIds) + { + return HidDevices.EnumerateDevices().Select(d => new HidFastReadWriteDevice(d.Path, d.Description)). + Where(f => f.Attributes.VendorId == vendorId && productIds.Contains(f.Attributes.ProductId)). + Select(d => d as IHidDevice); + } + + public IEnumerable Enumerate(int vendorId) + { + return HidDevices.EnumerateDevices().Select(d => new HidFastReadWriteDevice(d.Path, d.Description)). + Where(f => f.Attributes.VendorId == vendorId). + Select(d => d as IHidDevice); + } + } +} \ No newline at end of file diff --git a/src/Tests/HidFastReadWriteEnumerator.cs b/src/Tests/HidFastReadWriteEnumerator.cs new file mode 100644 index 0000000..09ea90d --- /dev/null +++ b/src/Tests/HidFastReadWriteEnumerator.cs @@ -0,0 +1,176 @@ +namespace HidLibrary.Tests +{ + using Xunit; + using System.Collections.Generic; + using System.Linq; + using System.Text.RegularExpressions; + using Should; + + namespace Tests + { + public class HidFastReadWriteEnumeratorTests + { + private HidFastReadWriteEnumerator enumerator; + private string devicePath; + + private void BeforeEach() + { + enumerator = new HidFastReadWriteEnumerator(); + var firstDevice = enumerator.Enumerate().FirstOrDefault(); + + if (firstDevice != null) + { + devicePath = firstDevice.DevicePath; + } + else + { + devicePath = ""; + } + } + + [Fact] + public void CanConstruct() + { + BeforeEach(); + enumerator.ShouldBeType(typeof(HidFastReadWriteEnumerator)); + } + + [Fact] + public void WrapsIsConnected() + { + BeforeEach(); + bool enumIsConnected = enumerator.IsConnected(devicePath); + bool hidIsConnected = HidDevices.IsConnected(devicePath); + enumIsConnected.ShouldEqual(hidIsConnected); + } + + [Fact] + public void WrapsGetDevice() + { + BeforeEach(); + IHidDevice enumDevice = enumerator.GetDevice(devicePath); + IHidDevice hidDevice = HidDevices.GetDevice(devicePath); + enumDevice.DevicePath.ShouldEqual(hidDevice.DevicePath); + } + + [Fact] + public void WrapsEnumerateDefault() + { + BeforeEach(); + IEnumerable enumDevices = enumerator.Enumerate(); + IEnumerable hidDevices = HidDevices.Enumerate(). + Select(d => d as IHidDevice); + + + AllDevicesTheSame(enumDevices, hidDevices).ShouldBeTrue(); + } + + [Fact] + public void WrapsEnumerateDevicePath() + { + BeforeEach(); + IEnumerable enumDevices = + enumerator.Enumerate(devicePath); + IEnumerable hidDevices = + HidDevices.Enumerate(devicePath). + Select(d => d as IHidDevice); + + + AllDevicesTheSame(enumDevices, hidDevices).ShouldBeTrue(); + } + + [Fact] + public void WrapsEnumerateVendorId() + { + BeforeEach(); + int vid = GetVid(); + + IEnumerable enumDevices = + enumerator.Enumerate(vid); + IEnumerable hidDevices = + HidDevices.Enumerate(vid). + Select(d => d as IHidDevice); + + + AllDevicesTheSame(enumDevices, hidDevices).ShouldBeTrue(); + } + + [Fact] + public void WrapsEnumerateVendorIdProductId() + { + BeforeEach(); + int vid = GetVid(); + int pid = GetPid(); + + IEnumerable enumDevices = + enumerator.Enumerate(vid, pid); + IEnumerable hidDevices = + HidDevices.Enumerate(vid, pid). + Select(d => d as IHidDevice); + + + AllDevicesTheSame(enumDevices, hidDevices).ShouldBeTrue(); + } + + [Fact] + public void DevicesAreFastRead() + { + BeforeEach(); + HidFastReadWriteDevice device = enumerator.GetDevice(devicePath) as HidFastReadWriteDevice; + device.ShouldNotBeNull(); + } + + private bool AllDevicesTheSame(IEnumerable a, + IEnumerable b) + { + if (a.Count() != b.Count()) + return false; + + bool allSame = true; + + var aList = a.ToList(); + var bList = b.ToList(); + + int numDevices = aList.Count; + + for (int i = 0; i < numDevices; i++) + { + if (aList[i].DevicePath != + bList[i].DevicePath) + { + allSame = false; + break; + } + } + + return allSame; + } + + private int GetVid() + { + return GetNumberFromRegex("vid_([0-9a-f]{4})"); + } + + private int GetPid() + { + return GetNumberFromRegex("pid_([0-9a-f]{3,4})"); + } + + private int GetNumberFromRegex(string pattern) + { + var match = Regex.Match(devicePath, pattern, + RegexOptions.IgnoreCase); + + int num = 0; + + if (match.Success) + { + num = int.Parse(match.Groups[1].Value, + System.Globalization.NumberStyles.HexNumber); + } + + return num; + } + } + } +}