using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using HidSharp; using HidSharp.Reports; using HidSharp.Reports.Encodings; namespace arDev.Joystick { public class JoystickRaw : IDisposable { HidDevice _dev; HidStream _hidStream; public Boolean IsOpen { get; private set; } public event EventHandler Disconnected; public event EventHandler Connected; public event EventHandler Changed; public delegate void MessageHandler(string msg); public event MessageHandler Message; public int InputLength { get; private set; } public int OutputLength { get; private set; } public int FeatureLength { get; private set; } public int vid { get; set; } public int pid { get; set; } public Boolean[] Buttons = new bool[64]; //버튼정보를 기록한다 Boolean bListUpdate = false; public JoystickRaw() { vid = -1; pid = -1; this.InputLength = 0; this.OutputLength = 0; this.FeatureLength = 0; this.IsOpen = false; this._dev = null; this._hidStream = null; DeviceList.Local.Changed += Local_Changed; Job = new Task(JobMain, ct); Job.Start(); for (int i = 0; i < Buttons.Length; i++) Buttons[i] = false; } CancellationToken ct ; public IEnumerable GetHidDevices() { return DeviceList.Local.GetHidDevices(); } public void Connect(int vid, int pid) { this.vid = vid; this.pid = pid; } public void Connect(HidDevice device) { this.vid = device.VendorID; this.pid = device.ProductID; } Task Job = null; private bool disposed = false; DateTime LastConnTime = DateTime.Now; void JobMain() { //개체가 소멸하기전까지 진행한다 while (this.disposed == false) { if (IsOpen == false) { if (this.vid == -1 || this.pid == -1) { //아직설정되지 않았으니 대기를한다 System.Threading.Thread.Sleep(3000); } else { //연결작업을 시작해야한다 if (bListUpdate || (DateTime.Now - LastConnTime).TotalMilliseconds >= 3000) { LastConnTime = DateTime.Now; //연결작업 var list = this.GetHidDevices(); this._dev = list.Where(t => t.VendorID == this.vid && t.ProductID == this.pid).FirstOrDefault(); if (_dev != null) { try { IsOpen = _dev.TryOpen(out _hidStream); if (IsOpen) { this.InputLength = _dev.GetMaxInputReportLength(); this.OutputLength = _dev.GetMaxOutputReportLength(); this.FeatureLength = _dev.GetMaxFeatureReportLength(); this._hidStream.ReadTimeout = Timeout.Infinite; Connected?.Invoke(this, null); var rawReportDescriptor = _dev.GetRawReportDescriptor(); var msg = ("Report Descriptor:"); msg += string.Format(" {0} ({1} bytes)", string.Join(" ", rawReportDescriptor.Select(d => d.ToString("X2"))), rawReportDescriptor.Length); Message?.Invoke(msg); } else { if (this._hidStream != null) this._hidStream.Dispose(); } } catch { System.Threading.Thread.Sleep(1500); } } else System.Threading.Thread.Sleep(1500); } else System.Threading.Thread.Sleep(1500); } } else { //데이터를 가지고 온다 using (_hidStream) { try { reportDescriptor = _dev.GetReportDescriptor(); deviceItem = reportDescriptor.DeviceItems[0]; var inputReportBuffer = new byte[_dev.GetMaxInputReportLength()]; var inputReceiver = reportDescriptor.CreateHidDeviceInputReceiver(); var inputParser = deviceItem.CreateDeviceItemInputParser(); inputReceiver.Start(_hidStream); while (true) { if (!inputReceiver.IsRunning) { break; } // Disconnected? Report report; // while (inputReceiver.TryRead(inputReportBuffer, 0, out report)) { if (inputParser.TryParseReport(inputReportBuffer, 0, report)) { WriteDeviceItemInputParserResult(inputParser); } } System.Threading.Thread.Sleep(20); } inputReceiver = null; IsOpen = false; } catch (Exception ex) { Message?.Invoke(ex.Message); } finally { Disconnected?.Invoke(this, null); IsOpen = false; } } } } } void WriteDeviceItemInputParserResult(HidSharp.Reports.Input.DeviceItemInputParser parser) { while (parser.HasChanged) { int changedIndex = parser.GetNextChangedIndex(); var previousDataValue = parser.GetPreviousValue(changedIndex); var dataValue = parser.GetValue(changedIndex); var oVal = previousDataValue.GetPhysicalValue(); var nVal = dataValue.GetPhysicalValue(); if (double.IsNaN(oVal)) oVal = 0.0; if (double.IsNaN(nVal)) nVal = 0.0; var InputMethod = (Usage)dataValue.Usages.FirstOrDefault(); if (InputMethod.ToString().StartsWith("Button")) { var butNo = int.Parse(InputMethod.ToString().Substring(6)); this.Buttons[butNo - 1] = nVal > 0; //버튼값은 기록 210107 } //이벤트발생 InputChanged?.Invoke(this, new InputChangedEventHandler( InputMethod, oVal, nVal)); //메세지도 발생함 //var msg = (string.Format(" {0}: {1} -> {2}", // (Usage)dataValue.Usages.FirstOrDefault(), // previousDataValue.GetPhysicalValue(), // dataValue.GetPhysicalValue())); //Message?.Invoke(msg); } } public event EventHandler InputChanged; public class InputChangedEventHandler : EventArgs { public Usage input { get; set; } public double oldValue { get; set; } public double newValue { get; set; } public InputChangedEventHandler(Usage input_, double oValue_, double nValue_) { this.input = input_; this.oldValue = oValue_; this.newValue = nValue_; } } ReportDescriptor reportDescriptor; DeviceItem deviceItem; ~JoystickRaw() { Dispose(false); } public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. this._dev = null; } Job.Wait(); Job.Dispose(); // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. //CloseHandle(handle); //handle = IntPtr.Zero; // Note disposing has been done. DeviceList.Local.Changed -= Local_Changed; disposed = true; } } private void Local_Changed(object sender, DeviceListChangedEventArgs e) { Changed?.Invoke(sender, e); bListUpdate = true; } } }