using Assets.Scripts.Ble; using Assets.Scripts.Ble.Win; using Assets.Scripts.Ble.Win.CPPBridge; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using UnityEngine; namespace Assets.Scripts.Devices.Ble.Win { internal class WclBleGattThread: WclBleThread { public delegate void GattCharacteristicChangedDelegate(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response); public delegate void GattCharacteristicReadDelegate(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response); public delegate void GattCharacteristicsDiscoveredDelegate(WclBleGattThread gattClient, BleServiceInfo service, BleResponse> response); public delegate void GattCharacteristicSubscribedDelegate(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response); public delegate void GattCharacteristicWroteDelegate(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response); private WclBleGattThread.GattCharacteristicChangedDelegate gattCharacteristicChanged; private WclBleGattThread.GattCharacteristicReadDelegate gattCharacteristicRead; // Token: 0x0400131E RID: 4894 private WclBleGattThread.GattCharacteristicsDiscoveredDelegate gattCharacteristicsDiscovered; // Token: 0x0400131F RID: 4895 private WclBleGattThread.GattCharacteristicSubscribedDelegate gattCharacteristicsSubscribed; private GattConnected gattConnected; // Token: 0x04001322 RID: 4898 private GattDisconnected gattDisconnected; private GattServicesDiscovered gattServicesDiscovered; public event WclBleGattThread.GattCharacteristicChangedDelegate GattCharacteristicChanged { add { this.gattCharacteristicChanged += value; } remove { this.gattCharacteristicChanged -= value; } } // Token: 0x14000039 RID: 57 // (add) Token: 0x060020AE RID: 8366 RVA: 0x00087BB3 File Offset: 0x00085DB3 // (remove) Token: 0x060020AF RID: 8367 RVA: 0x00087BC7 File Offset: 0x00085DC7 public event WclBleGattThread.GattCharacteristicReadDelegate GattCharacteristicRead { add { this.gattCharacteristicRead += value; } remove { this.gattCharacteristicRead -= value; } } // Token: 0x1400003A RID: 58 // (add) Token: 0x060020B0 RID: 8368 RVA: 0x00087BDB File Offset: 0x00085DDB // (remove) Token: 0x060020B1 RID: 8369 RVA: 0x00087BEF File Offset: 0x00085DEF public event WclBleGattThread.GattCharacteristicsDiscoveredDelegate GattCharacteristicsDiscovered { add { this.gattCharacteristicsDiscovered += value; } remove { this.gattCharacteristicsDiscovered -= value; } } // Token: 0x1400003B RID: 59 // (add) Token: 0x060020B2 RID: 8370 RVA: 0x00087C03 File Offset: 0x00085E03 // (remove) Token: 0x060020B3 RID: 8371 RVA: 0x00087C17 File Offset: 0x00085E17 public event WclBleGattThread.GattCharacteristicSubscribedDelegate GattCharacteristicSubscribed { add { this.gattCharacteristicsSubscribed += value; } remove { this.gattCharacteristicsSubscribed -= value; } } private WclBleGattThread.GattCharacteristicWroteDelegate gattCharacteristicWrote; public event WclBleGattThread.GattCharacteristicWroteDelegate GattCharacteristicWrote { add { this.gattCharacteristicWrote += value; } remove { this.gattCharacteristicWrote -= value; } } // Token: 0x1400003D RID: 61 // (add) Token: 0x060020B6 RID: 8374 RVA: 0x00087C53 File Offset: 0x00085E53 // (remove) Token: 0x060020B7 RID: 8375 RVA: 0x00087C67 File Offset: 0x00085E67 public event GattConnected GattConnected { add { this.gattConnected += value; } remove { this.gattConnected -= value; } } // Token: 0x1400003E RID: 62 // (add) Token: 0x060020B8 RID: 8376 RVA: 0x00087C7B File Offset: 0x00085E7B // (remove) Token: 0x060020B9 RID: 8377 RVA: 0x00087C8F File Offset: 0x00085E8F public event GattDisconnected GattDisconnected { add { this.gattDisconnected += value; } remove { this.gattDisconnected -= value; } } // Token: 0x1400003F RID: 63 // (add) Token: 0x060020BA RID: 8378 RVA: 0x00087CA3 File Offset: 0x00085EA3 // (remove) Token: 0x060020BB RID: 8379 RVA: 0x00087CB7 File Offset: 0x00085EB7 public event GattServicesDiscovered GattServicesDiscovered { add { this.gattServicesDiscovered += value; } remove { this.gattServicesDiscovered -= value; } } private readonly Dictionary charMapping = new Dictionary(); private readonly Dictionary servicesMapping = new Dictionary(); private readonly Dictionary subscribedCharHandles = new Dictionary(); private WclBleGattClient gatt; private IntPtr rPtr; private BlePeripheralInfo _bleDevice; private readonly long address; public BlePeripheralInfo Peripheral { get; } private Thread thread; private ConcurrentQueue actions = new ConcurrentQueue(); private bool start = false; private ManualResetEvent sEvent; private AutoResetEvent aEvent; private ManualResetEvent tEvent; internal WclBleGattThread(BlePeripheralInfo bleDevice, IntPtr radio, WclBleGattThread.WclBleGattThreadStoppedCallback stopCallback) { this.Peripheral = bleDevice; this.rPtr = radio; _bleDevice = bleDevice; this.address = long.Parse(_bleDevice.Address, System.Globalization.NumberStyles.HexNumber); this.threadStoppedCallback = stopCallback; } //private void ThreadProc() // { // this.SetUpWorkerThread(); // //this.sEvent.Set(); // //this.DoWorkerLoop(); // //this.CleanUpWorkerThread(); // //this.InternalCleanUp(); //} protected override void CleanUpWorkerThread() { this.gatt.Disconnect(); base.ProcessPendingAPCMessages(); this.gatt.Dispose(); this.gatt = null; this.threadStoppedCallback?.Invoke(this); } protected override void SetUpWorkerThread() { this.gatt = new WclBleGattClient(this.address, this.rPtr) { Connected = new WclBleGattClient.GattConnectionChanged(this.OnConnected), Disconnected = new WclBleGattClient.GattConnectionChanged(this.OnDisconnected), CharacteristicChanged = new WclBleGattClient.GattCharacteristicChanged(this.OnCharacteristicChanged) }; } private void DoWorkerLoop() { IntPtr[] handle = new IntPtr[] { this.tEvent.SafeWaitHandle.DangerousGetHandle(), this.aEvent.SafeWaitHandle.DangerousGetHandle() }; while (WclAlertableWait.Wait(handle, 2U, 4294967295U) != 0U) { //this.InvokeQueuedActions(); Action action; if (this.actions.TryDequeue(out action)) { if (action != null) { Debug.Log("连接设备"); action(); } } } } private void OnCharacteristicChanged(WclBleGattClient connection, ushort handle, byte[] value) { if (!this.subscribedCharHandles.ContainsKey(handle)) { return; } BleCharacteristicInfo characteristic = this.subscribedCharHandles[handle]; BleResponse response = new BleResponse { IsSuccess = true, Data = value }; if (!base.IsTerminating) { //Loom.QueueOnMainThread(() => //{ this.gattCharacteristicChanged?.Invoke(this, characteristic, response); //}); } } private void OnCharacteristicRead(BleCharacteristicInfo characteristic, int result, byte[] data) { BleResponse response = new BleResponse { IsSuccess = WclBleErrors.IsSuccessCode(result) }; if (response.IsSuccess) { response.Data = data; } else { response.Error = new BleHwInterfaceError(new int?(result), "WclBleGattClientErrorDomain", "Error when reading value"); } if (!base.IsTerminating) { //Loom.QueueOnMainThread(() => //{ this.gattCharacteristicRead.Invoke(this, characteristic, response); //}); } } private void OnCharacteristicsDiscovered(BleServiceInfo service, int result, GattCharacteristics characteristics) { BleResponse> response = new BleResponse> { IsSuccess = WclBleErrors.IsSuccessCode(result) }; if (response.IsSuccess) { response.Data = this.ProcessCharacteristics(service, characteristics); } else { // response.Error = new BleHwInterfaceError(new int?(result), "WclBleGattClientErrorDomain", "Error when reading BLE services"); } if (!base.IsTerminating) { //Loom.QueueOnMainThread(() => //{ this.gattCharacteristicsDiscovered(this, service, response); //}); } } private void OnConnected(WclBleGattClient sender, int error) { //Debug.Log("gatt thread onconnected"); if (base.IsTerminating) { return; } BleResponse response = new BleResponse { IsSuccess = WclBleErrors.IsSuccessCode(error), }; if (!response.IsSuccess) { response.Error = new BleHwInterfaceError(error, "", ""); } //Loom.QueueOnMainThread(() => //{ this.gattConnected?.Invoke(this, response); //}); } private void OnDisconnected(WclBleGattClient sender, int reason) { BleResponse response = new BleResponse { IsSuccess = true, Error = new BleHwInterfaceError(new int?(reason), "WclBleGattClientErrorDomain", string.Format("GATT disconnect reason is - {0}", reason)) }; //Loom.QueueOnMainThread(() => //{ this.gattDisconnected?.Invoke(this, response); //}); } public int Connect() { if (!base.CanLoadWork) { return WclBleErrors.WCL_E_CONNECTION_NOT_ACTIVE; } if(this.gatt.State != WclBleGattClientState.Disconnected) { return WclBleErrors.WCL_E_BLUETOOTH_CLIENT_CONNECTED; } base.ClearActionQueue(); base.EnqueueAction(() => { this.gatt.Connect(); }); return WclBleErrors.WCL_E_SUCCESS; } public int Discounect() { this.gatt.Disconnect(); return WclBleErrors.WCL_E_SUCCESS; } public int DiscoverServices() { base.EnqueueAction(() => { GattServices services; int result = this.gatt.DiscoverServices(out services); this.OnServicesDiscovered(result, services); }); return WclBleErrors.WCL_E_SUCCESS; } private void OnServicesDiscovered(int result, GattServices services) { BleResponse> response = new BleResponse> { IsSuccess = WclBleErrors.IsSuccessCode(result) }; if (response.IsSuccess) { response.Data = this.ProcessServices(services); } else { response.Error = new BleHwInterfaceError(new int?(result), "WclBleGattClientErrorDomain", "Error when reading BLE services"); } if (!base.IsTerminating) { //Loom.QueueOnMainThread(() => //{ gattServicesDiscovered(this, response); //}); } } private List ProcessCharacteristics(BleServiceInfo service, GattCharacteristics characteristics) { List list = new List((int)characteristics.Count); for (int i = 0; i < (int)characteristics.Count; i++) { GattCharacteristic gattCharacteristic = characteristics.Chars[i]; WclBleGattThread.WinBleCharacteristicInfo winBleCharacteristicInfo = new WclBleGattThread.WinBleCharacteristicInfo(service, gattCharacteristic.Uuid.WclGuidToNormalizedGuid()); this.charMapping[winBleCharacteristicInfo] = gattCharacteristic; list.Add(winBleCharacteristicInfo); } return list; } private List ProcessServices(GattServices services) { List list = new List((int)services.Count); for (int i = 0; i < (int)services.Count; i++) { GattService gattService = services.Services[i]; WclBleGattThread.WinBleServiceInfo winBleServiceInfo = new WclBleGattThread.WinBleServiceInfo(this._bleDevice, gattService.Uuid.WclGuidToNormalizedGuid()); this.servicesMapping[winBleServiceInfo] = gattService; list.Add(winBleServiceInfo); } return list; } public int DiscoverCharacteristics(BleServiceInfo service) { GattService ns = this.servicesMapping[service]; base.EnqueueAction(() => { GattCharacteristics characteristics; int result = this.gatt.DiscoverCharacteristics(ns, out characteristics); this.OnCharacteristicsDiscovered(service, result, characteristics); }); return WclBleErrors.WCL_E_SUCCESS; } public int ReadCharacteristicValue(BleCharacteristicInfo characteristic) { if (!charMapping.ContainsKey(characteristic)) { return WclBleErrors.WCL_E_CONNECTION_NOT_ACTIVE; } GattCharacteristic nCh = this.charMapping[characteristic]; base.EnqueueAction(() => { byte[] data; int result = this.gatt.ReadCharacteristicValue(nCh, out data); this.OnCharacteristicRead(characteristic, result, data); }); return WclBleErrors.WCL_E_SUCCESS; } public int SubscribeCharacteristic(BleCharacteristicInfo characteristic) { if (!this.charMapping.ContainsKey(characteristic)) { return WclBleErrors.WCL_E_CONNECTION_NOT_ACTIVE; } GattCharacteristic nCh = this.charMapping[characteristic]; base.EnqueueAction(() => { int result = this.gatt.SubscribeCharacteristic(nCh); this.OnCharacteristicsSubscribed(characteristic, nCh, result); }); return WclBleErrors.WCL_E_SUCCESS; } private void OnCharacteristicsSubscribed(BleCharacteristicInfo characteristic, GattCharacteristic nCharacteristic, int result) { //Debug.Log("char subscribed"); BleResponse response = new BleResponse { IsSuccess = WclBleErrors.IsSuccessCode(result) }; if (response.IsSuccess) { this.subscribedCharHandles[nCharacteristic.Handle] = characteristic; } else { response.Error = new BleHwInterfaceError(new int?(result), "WclBleGattClientErrorDomain", "Error when subscribing Characteristics"); } if (!base.IsTerminating) { //Loom.QueueOnMainThread(() => { this.gattCharacteristicsSubscribed(this, characteristic, response); //}); } } public int WriteCharacteristic(BleCharacteristicInfo characteristicInfo, byte[] data) { if (!this.charMapping.ContainsKey(characteristicInfo)) { return WclBleErrors.WCL_E_CONNECTION_NOT_ACTIVE; } GattCharacteristic gCh = this.charMapping[characteristicInfo]; base.EnqueueAction(() => { int result = this.gatt.WriteCharacteristic(gCh, data); this.OnCharactersisticsWrote(characteristicInfo, result); }); return WclBleErrors.WCL_E_SUCCESS; } private void OnCharactersisticsWrote(BleCharacteristicInfo characteristic, int result) { var response = new BleResponse { IsSuccess = WclBleErrors.IsSuccessCode(result), Data = BleCharacteristicWriteType.WriteWithResponse }; this.gattCharacteristicWrote?.Invoke(this, characteristic, response); } private WclBleGattThread.WclBleGattThreadStoppedCallback threadStoppedCallback; public delegate void WclBleGattThreadStoppedCallback(WclBleGattThread clientThread); private class WinBleCharacteristicInfo : BleCharacteristicInfo { // Token: 0x06003F85 RID: 16261 RVA: 0x000EA274 File Offset: 0x000E8474 public WinBleCharacteristicInfo(BleServiceInfo service, Guid id) : base(id, service, (BleCharacteristicProperties)0) { } } private class WinBleServiceInfo : BleServiceInfo { // Token: 0x06003F86 RID: 16262 RVA: 0x000EA27F File Offset: 0x000E847F public WinBleServiceInfo(BlePeripheralInfo peripheral, Guid id) : base(id, peripheral) { } } } }