276 lines
10 KiB
C#
276 lines
10 KiB
C#
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<HidDevice> 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<InputChangedEventHandler> 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;
|
|
}
|
|
|
|
}
|
|
}
|