using Assets.Scripts.Ble.Scan; using Assets.Scripts.Ble.Win; using Assets.Scripts.Devices.Ant; using Assets.Scripts.Devices.Ble; using Assets.Scripts.Devices.Ble.Interfaces; using Assets.Scripts.Devices.Ble.Win; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; namespace Assets.Scripts.Ble { public sealed class BleWinHwInterface: IBleWinHwInterface { private static BleWinHwInterface hwInterface; private WclBleMainThread wclBleMainThread; private Dictionary _pCache; public Dictionary pCache { get { if (_pCache == null) { _pCache = new Dictionary(); } return _pCache; } set { _pCache = value; } } private Action _discoveredCallback; private Dictionary> callbacks = new Dictionary>(); private Dictionary>>> servicesCallbacks = new Dictionary>>>(); //private Dictionary>>> characteristicsCallbacks = new Dictionary>>>(); private Dictionary> characteristicNotificationCallbacks = new Dictionary>(); private Dictionary characteristicReadCallbacks = new Dictionary(); private Dictionary characteristicsDiscoveredCallbacks = new Dictionary(); private Dictionary disconnectedCallback = new Dictionary(); private CharacteristicReadCallback characteristicReadEvent; public event CharacteristicReadCallback CharacteristicReadEvent { add { this.characteristicReadEvent += value; } remove { this.characteristicReadEvent -= value; } } private BluetoothStateChangedCallback bluetoothStateChanged; public event BluetoothStateChangedCallback BluetoothStateChangedEvent { add { this.bluetoothStateChanged += value; } remove { this.bluetoothStateChanged -= value; } } private BleState nativeState; public BleState BleState { get { return this.nativeState; } set { if(this.nativeState != value) { this.nativeState = value; this.bluetoothStateChanged?.Invoke(this, this.nativeState); } } } private PeripheralDisconnectedCallback peripheralDisconnectedEvent; public event PeripheralDisconnectedCallback PeripheralDisconnectedEvent { add { this.peripheralDisconnectedEvent += value; } remove { this.peripheralDisconnectedEvent -= value; } } private BleWinHwInterface() { wclBleMainThread = new WclBleMainThread(); wclBleMainThread.ManagerStatusChanged += ManagerStatusChanged; wclBleMainThread.ScanInfoReceived += WatcherScanInfoReceived; //wclBleMainThread.gatt wclBleMainThread.Start(); } private void WatcherScanInfoReceived(WclBleMainThread sender, long address, string name, int rssi, CPPBridge.WclBleAdvertisementType packetType, Guid? service) { //if(address != 224160707349234) //{ //return; //} //Debug.Log($"address:{ address }, name:{ name }, service:{ (service == null ? "" : service.Value.ToString()) }"); SensorType sensor = SensorType.None; List services = new List(); if (service != null) { services = new List { service.Value }; foreach(var item in ServiceUuids.Services) { if(item.IdGuid != service.Value) { continue; } if(item.IdByteArray == ServiceUuids.Ftms) { sensor = SensorType.Trainer; } else if(item.IdByteArray == ServiceUuids.HeartRate) { sensor = SensorType.HeartRate; } else if(item.IdByteArray == ServiceUuids.CyclingPower) { sensor = SensorType.Power; //sensor = SensorType.Trainer; } else if(item.IdByteArray == ServiceUuids.CyclingSpeedCadence) { sensor = SensorType.SpeedCadence; } else if(item.IdByteArray == ServiceUuids.TacxBle) { sensor = SensorType.Trainer; } } }; var addressStr = address.ToString("X12"); if (!pCache.ContainsKey(addressStr)) { var device = new BleAdvertisementInfo(new WinBlePeripheralInfo(addressStr, name), rssi, true, services, null, sensor); pCache.Add(addressStr, device); //WclBleGattThread gattClient = this.SetUpGattClient(device.Peripheral); //this.ConnectInternal(gattClient); } if (!string.IsNullOrWhiteSpace(name)) { (pCache[addressStr].Peripheral as WinBlePeripheralInfo).SetName(name); } if(sensor == SensorType.None) { return; } pCache[addressStr].SensorType = sensor; pCache[addressStr].Rssi = rssi; //Debug.Log(sensor); //pCache[address.ToString()].SensorType = sensor; foreach (var item in services) { pCache[addressStr].TryAddService(item); } pCache[addressStr].Index++; if(pCache[addressStr].SensorType == SensorType.Power && pCache[addressStr].Services.Any(s=> s.Equals(ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid))) { pCache[addressStr].SensorType = SensorType.Trainer; //Debug.Log("纠正为trainer, "+ pCache[address.ToString()].Index); } if (pCache[addressStr].SensorType == SensorType.Power) { if (pCache[addressStr].Index > 4) { _discoveredCallback?.Invoke(pCache[addressStr]); } } else if(pCache[addressStr].SensorType != SensorType.None) { _discoveredCallback?.Invoke(pCache[addressStr]); } } private WclBleGattThread SetUpGattClient(BlePeripheralInfo peripheral) { WclBleGattThread wclBleGattThread = this.wclBleMainThread.CreateGattThread(peripheral); wclBleGattThread.GattConnected += this.GattConnected; wclBleGattThread.GattDisconnected += this.GattDisconnected; wclBleGattThread.GattServicesDiscovered += this.GattServicesDiscovered; wclBleGattThread.GattCharacteristicsDiscovered += this.GattCharacteristicsDiscovered; wclBleGattThread.GattCharacteristicSubscribed += this.GattCharacteristicSubscribed; wclBleGattThread.GattCharacteristicRead += this.GattCharacteristicRead; wclBleGattThread.GattCharacteristicWrote += this.GattCharacteristicWrote; wclBleGattThread.GattCharacteristicChanged += this.GattCharacteristicChanged; wclBleGattThread.Start(); return wclBleGattThread; } public void ConnectPeripheral(BlePeripheralInfo info, Action callback) { this.callbacks.Add(info, callback); WclBleGattThread wclBleGattThread = this.wclBleMainThread.GetGattThread(info); if(wclBleGattThread == null) { wclBleGattThread = this.SetUpGattClient(info); this.ConnectInternal(wclBleGattThread); return; } else { this.ConnectInternal(wclBleGattThread); } } private void ConnectInternal(WclBleGattThread gattClient) { //Task.Run(() => //{ int num = gattClient.Connect(); Debug.Log("连接设备返回" + num); //}); } public void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback) { var gattThread = this.wclBleMainThread.GetGattThread(peripheral); if(gattThread != null && gattThread.CanLoadWork) { this.callbacks.Remove(peripheral); this.servicesCallbacks.Remove(peripheral); this.characteristicNotificationCallbacks.Remove(peripheral); this.disconnectedCallback.Add(peripheral, callback); gattThread.Discounect(); } } public static BleWinHwInterface GetInterface() { if(hwInterface == null) { hwInterface = new BleWinHwInterface(); } return hwInterface; } public void StartScan(Action discoveredCallBack) { pCache.Clear(); _discoveredCallback = discoveredCallBack; this.wclBleMainThread.StartWatcher(); } private void ManagerStatusChanged(WclBleMainThread sender, WclBleManagerStatus status) { this.BleState = BleWinHwInterface.StateFromNativeState(status); //Debug.Log("win hw:" + status); } private void GattConnected(WclBleGattThread gattClient, BleResponse response) { Debug.Log($"gatt connected { response.ToString() }"); if (!response.IsSuccess) { gattClient.Stop(); this.callbacks[gattClient.Peripheral].Invoke(this, gattClient.Peripheral, response); this.callbacks.Remove(gattClient.Peripheral); return; } this.callbacks[gattClient.Peripheral].Invoke(this, gattClient.Peripheral, response); this.callbacks.Remove(gattClient.Peripheral); } private void CleanUpPeripheral(BlePeripheralInfo peripheralInfo) { //this.callbacks.Clear() } private void GattDisconnected(WclBleGattThread gattClient, BleResponse response) { Debug.Log($"gatt disconnected { gattClient.Peripheral.Name }"); this.callbacks.Remove(gattClient.Peripheral); this.servicesCallbacks.Remove(gattClient.Peripheral); this.characteristicNotificationCallbacks.Remove(gattClient.Peripheral); //App.MainDeviceAdapter.PrintStatus(); var manualDisconnect = disconnectedCallback.ContainsKey(gattClient.Peripheral); this.peripheralDisconnectedEvent(this, gattClient.Peripheral, response, manualDisconnect); //App.MainDeviceAdapter.PrintStatus(); if (disconnectedCallback.ContainsKey(gattClient.Peripheral)) { disconnectedCallback[gattClient.Peripheral].Invoke(); disconnectedCallback.Remove(gattClient.Peripheral); } //this.pCache.Remove(gattClient.Peripheral.Address); } private void GattServicesDiscovered(WclBleGattThread gattClient, BleResponse> response) { //Debug.Log("services discovered"); //this.callbacks[gattClient.Peripheral].Invoke(this, gattClient.Peripheral, response); //foreach (var item in response.Data) //{ // Debug.Log(item.ToString()); //} if (this.servicesCallbacks.ContainsKey(gattClient.Peripheral)) { this.servicesCallbacks[gattClient.Peripheral].Invoke(this, gattClient.Peripheral, response); } } private void GattCharacteristicsDiscovered(WclBleGattThread gattClient, BleServiceInfo service, BleResponse> response) { //Debug.Log("characteristics discovered"); if (this.characteristicsDiscoveredCallbacks.ContainsKey(service)) { this.characteristicsDiscoveredCallbacks[service].Invoke(this, service, response); } } private void GattCharacteristicSubscribed(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response) { //Debug.Log("characteristics subscribed"); if (this.characteristicNotificationCallbacks.ContainsKey(gattClient.Peripheral)) { this.characteristicNotificationCallbacks[gattClient.Peripheral].Invoke(this, characteristic, response); } } private void GattCharacteristicRead(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response) { Debug.Log("characteristic read"); if (this.characteristicReadCallbacks.ContainsKey(characteristic)) { this.characteristicReadCallbacks[characteristic].Invoke(this, characteristic, response); this.characteristicReadCallbacks.Remove(characteristic); } this.characteristicReadEvent.Invoke(this, characteristic, response); } private void GattCharacteristicWrote(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response) { Debug.Log("characteristic wrote"); } private void GattCharacteristicChanged(WclBleGattThread gattClient, BleCharacteristicInfo characteristic, BleResponse response) { //Debug.Log("characteristic changed"); if(this.wclBleMainThread.GetGattThread(characteristic.Peripheral) != null) { this.characteristicReadEvent.Invoke(this, characteristic, response); } } public void DiscoverServices(BlePeripheralInfo peripheral, Action>> callback) { WclBleGattThread gattThread = this.wclBleMainThread.GetGattThread(peripheral); if(gattThread == null) { return; } this.servicesCallbacks.Add(peripheral, callback); int num = gattThread.DiscoverServices(); if (WclBleErrors.IsSuccessCode(num)) { return; } BleResponse> response = new BleResponse> { IsSuccess = false, Error = new BleHwInterfaceError(new int?(num), "WclBleGattClientErrorDomain", string.Format("Error discovering services - {0}", num)) }; this.GattServicesDiscovered(gattThread, response); } public void DiscoverCharacteristic(BleServiceInfo service, CharacteristicsDiscoveredCallback callback) { WclBleGattThread gattThread = this.wclBleMainThread.GetGattThread(service.Peripheral); if(gattThread == null) { return; } if (!this.characteristicsDiscoveredCallbacks.ContainsKey(service)) { this.characteristicsDiscoveredCallbacks.Add(service, callback); } int num = gattThread.DiscoverCharacteristics(service); if (WclBleErrors.IsSuccessCode(num)) { return; } BleResponse> response = new BleResponse> { IsSuccess = false, Error = new BleHwInterfaceError(new int?(num), "WclBleGattClientErrorDomain", string.Format("Error discovering characteristics - {0}", num)) }; this.GattCharacteristicsDiscovered(gattThread, service, response); } public void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action callback) { WclBleGattThread gattThread = this.wclBleMainThread.GetGattThread(characteristic.Peripheral); this.characteristicNotificationCallbacks.Add(gattThread.Peripheral, callback); int num = gattThread.SubscribeCharacteristic(characteristic); if (WclBleErrors.IsSuccessCode(num)) { return; } BleResponse> response = new BleResponse> { IsSuccess = false, Error = new BleHwInterfaceError(new int?(num), "WclBleGattClientErrorDomain", string.Format("Error subscribing characteristic - {0}", num)) }; this.GattCharacteristicSubscribed(gattThread, characteristic, response); } public void WriteCharacteristic(BleCharacteristicInfo characteristic, byte[] data) { var gattThread = this.wclBleMainThread.GetGattThread(characteristic.Peripheral); if(gattThread == null) { return; } int num = gattThread.WriteCharacteristic(characteristic, data); if (WclBleErrors.IsSuccessCode(num)) { Debug.Log("设置命令成功"); return; } } public void Dispose() { this.wclBleMainThread.ManagerStatusChanged -= ManagerStatusChanged; this.wclBleMainThread.ScanInfoReceived -= WatcherScanInfoReceived; this.wclBleMainThread.Stop(); this.wclBleMainThread = null; hwInterface = null; pCache.Clear(); } public void ReadCharacteristic(BleCharacteristicInfo characteristic, CharacteristicReadCallback callback) { WclBleGattThread gattThread = this.wclBleMainThread.GetGattThread(characteristic.Peripheral); if(gattThread == null) { return; } this.characteristicReadCallbacks.Add(characteristic, callback); int num = gattThread.ReadCharacteristicValue(characteristic); if (WclBleErrors.IsSuccessCode(num)) { return; } } private static BleState StateFromNativeState(WclBleManagerStatus status) { switch (status) { case WclBleManagerStatus.RadioOff: return BleState.Off; case WclBleManagerStatus.RadioOn: return BleState.On; case WclBleManagerStatus.Unknown: return BleState.Unknown; default: return BleState.Unavailable; } } private class WinBlePeripheralInfo : BlePeripheralInfo { // Token: 0x06003F35 RID: 16181 RVA: 0x000E9FBF File Offset: 0x000E81BF public WinBlePeripheralInfo(string address, string name) : base(address, name) { } // Token: 0x06003F36 RID: 16182 RVA: 0x000E9FC9 File Offset: 0x000E81C9 public void SetName(string name) { base.Name = name; } } } }