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 BleMobileInterface : IBleWinHwInterface { private static BleMobileInterface hwInterface; private BleMobileThread bleMobileThread; 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 BleMobileInterface() { bleMobileThread = new BleMobileThread(); bleMobileThread.ManagerInitialized += BleMobileThread_ManagerInitialized; bleMobileThread.ManagerStatusChanged += ManagerStatusChanged; bleMobileThread.ScanInfoReceived += WatcherScanInfoReceived; } private void BleMobileThread_ManagerInitialized(BleMobileThread thread) { this.BleState = BleState.On; } private void ManagerStatusChanged(BleMobileThread sender, WclBleManagerStatus status) { this.BleState = BleMobileInterface.StateFromNativeState(status); } private void WatcherScanInfoReceived(BleMobileThread sender, string address, string name, int rssi,string[] uuids) { SensorType sensor = SensorType.None; List services = new List(); if (uuids != null && uuids.Length > 0) { foreach (var item1 in uuids) { services.Add(new Guid(item1)); } foreach (var item in ServiceUuids.Services) { if (!services.Contains(item.IdGuid)) { continue; } //Debug.Log($"設備信息,{item.IdGuid},{string.Join(";",item.IdByteArray)}" ); if (item.IdByteArray == ServiceUuids.Ftms) { if (App.IsRowerMode == true) { sensor = SensorType.Rower; } else { 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; if (!pCache.ContainsKey(addressStr)) { var device = new BleAdvertisementInfo(new WinBlePeripheralInfo(addressStr, name), rssi, true, services, null, sensor); pCache.Add(addressStr, device); } 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 uuids) { pCache[addressStr].TryAddService(new Guid(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]); } } List servicelist = new List(); List characteristilist = new List(); public void ConnectPeripheral(BlePeripheralInfo info, Action callback) { if (!this.callbacks.ContainsKey(info)) { this.callbacks.Add(info, callback); } Debug.Log("try connect device" + info.Name + this.callbacks.Count.ToString()); if (this.callbacks.Count == 1) { BleResponse s = new BleResponse(); s.IsSuccess = true; s.Error = null; var self = this; BluetoothLEHardwareInterface.ConnectToPeripheral(info.Address, (address) => { callback?.Invoke(self, info, s); this.callbacks.Remove(info); Debug.Log("连接成功!"+info.Name); Devices.Ble.Characteristic.C2RowerData.IsEnabled = info.Name.Contains("PM"); if (this.callbacks.Any()) { this.ConnectPeripheral(this.callbacks.First().Key, this.callbacks.First().Value); } }, (address, service) => { ServicesDiscovered(address, service); }, (address, service, characteristic) => { CharacteristicsDiscovered(address, service, characteristic); }, (address) => { Debug.Log("disconnect device:" + address); PeripheralDisconnected(address, info); }); } } #region 私有方法 //服务发现 private void ServicesDiscovered(string address, string service) { BluetoothLEHardwareInterface.Log(address+"FOUNDFSERVICE:" + service); if (servicesCallbacks.Where(c => c.Key.Address == address).Any()) { var serviceCallback = servicesCallbacks.Where(c => c.Key.Address == address).FirstOrDefault(); List servicelist = new List(); servicelist.Add(new WinBleServiceInfo(serviceCallback.Key, new Guid(service))); BleResponse> response = new BleResponse> { IsSuccess = true, Error = null, Data = servicelist, }; serviceCallback.Value?.Invoke(this, serviceCallback.Key, response); } } //特征值发现 private void CharacteristicsDiscovered(string address, string service, string characteristic) { BluetoothLEHardwareInterface.Log(address + "发现特征值:" + characteristic); if (characteristicsDiscoveredCallbacks.Where(c => c.Key.Peripheral.Address == address).Any()) { var characteristicCallback = characteristicsDiscoveredCallbacks.Where(c => c.Key.Peripheral.Address == address).FirstOrDefault(); List characteristiclist = new List(); characteristiclist.Add(new WinBleCharacteristicInfo(new Guid(characteristic), new WinBleServiceInfo(characteristicCallback.Key.Peripheral, new Guid(service)), 0)); BleResponse> response = new BleResponse> { IsSuccess = true, Error = null, Data = characteristiclist, }; characteristicCallback.Value?.Invoke(this, characteristicCallback.Key, response); } } //设备主动断开处理 private void PeripheralDisconnected(string address, BlePeripheralInfo info) { var currentCallback = this.callbacks.Where(c => c.Key.MatchAddress(address)); if (currentCallback.Any()) { this.callbacks.Remove(currentCallback.First().Key); } var characteristicCallback = characteristicNotificationCallbacks.Where(c => c.Key.MatchAddress(address)); if (characteristicCallback.Any()) { characteristicNotificationCallbacks.Remove(characteristicCallback.First().Key); } var serviceCallback = servicesCallbacks.Where(c => c.Key.MatchAddress(address)); if (serviceCallback.Any()) { servicesCallbacks.Remove(serviceCallback.First().Key); } var characteristicsDiscoveredCallback = characteristicsDiscoveredCallbacks.Where(c => c.Key.MatchAddress(address)); if (serviceCallback.Any()) { characteristicsDiscoveredCallbacks.Remove(characteristicsDiscoveredCallback.First().Key); } peripheralDisconnectedEvent(this, info, null, false); } #endregion //设备断开连接 public void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback) { BluetoothLEHardwareInterface.DisconnectPeripheral(peripheral.Address, (address) => { Debug.Log("断开回调"+ address); peripheralDisconnectedEvent(this, peripheral, null, true); callback?.Invoke(); }); } public static BleMobileInterface GetInterface() { if(hwInterface == null) { hwInterface = new BleMobileInterface(); } return hwInterface; } //扫描设备 public void StartScan(Action discoveredCallBack) { pCache.Clear(); _discoveredCallback = discoveredCallBack; bleMobileThread.StartWatcher(); } public void StopScan() { bleMobileThread?.Stop(); } //发现服务列表 public void DiscoverServices(BlePeripheralInfo peripheral, Action>> callback) { if (!this.servicesCallbacks.ContainsKey(peripheral)) { this.servicesCallbacks.Add(peripheral, callback); } } //发现特征值 public void DiscoverCharacteristic(BleServiceInfo service, CharacteristicsDiscoveredCallback callback) { if (!this.characteristicsDiscoveredCallbacks.ContainsKey(service)) { this.characteristicsDiscoveredCallbacks.Add(service, callback); } } //订阅特征值 public void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action callback) { if (!this.characteristicNotificationCallbacks.ContainsKey(characteristic.Peripheral)) { this.characteristicNotificationCallbacks.Add(characteristic.Peripheral, callback); } Debug.Log("SubscribeCharacteristic" + characteristic.Peripheral.Name + characteristic.Service.ToString() + characteristic.Service.ToString()); BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(characteristic.Peripheral.Address, characteristic.Service.ToString(), characteristic.Id.ToString(), (a,b)=> { Debug.Log("返回特征值," + a + "," + b); }, (deviceAddress, characteristric, bytes) => { if (characteristicReadEvent != null) { BleResponse response = new BleResponse { IsSuccess = true, Error = null, Data = bytes, }; characteristicReadEvent.Invoke(this, characteristic, response); } }); } //写入特征值 public void WriteCharacteristic(BleCharacteristicInfo characteristic, byte[] data) { bool withResponse = false; #if UNITY_IOS withResponse = characteristic.Id.ToString().ToLower().Contains("2AD9".ToLower()); #endif BluetoothLEHardwareInterface.WriteCharacteristic(characteristic.Peripheral.Address, characteristic.Service.Id.ToString(), characteristic.Id.ToString(), data, data.Length, withResponse, (characteristicUUID) => { Debug.Log("相应" + characteristicUUID); BluetoothLEHardwareInterface.Log("Write Succeeded"); }); } public void Dispose() { bleMobileThread.ManagerInitialized -= BleMobileThread_ManagerInitialized; bleMobileThread.ManagerStatusChanged -= ManagerStatusChanged; bleMobileThread.ScanInfoReceived -= WatcherScanInfoReceived; bleMobileThread.Dispose(); bleMobileThread = null; hwInterface = null; pCache.Clear(); } public void ReadCharacteristic(BleCharacteristicInfo characteristic, CharacteristicReadCallback callback) { BluetoothLEHardwareInterface.ReadCharacteristic(characteristic.Peripheral.Address, characteristic.Service.Id.ToString(), characteristic.Id.ToString(), (c, bytes) => { BleResponse response = new BleResponse { IsSuccess = true, Error = null, Data = bytes, }; callback?.Invoke(this, characteristic, response); }); } 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; } } private class WinBleServiceInfo : BleServiceInfo { // Token: 0x06003F86 RID: 16262 RVA: 0x000EA27F File Offset: 0x000E847F public WinBleServiceInfo(BlePeripheralInfo peripheral, Guid id) : base(id, peripheral) { } } private class WinBleCharacteristicInfo : BleCharacteristicInfo { // Token: 0x06003F86 RID: 16262 RVA: 0x000EA27F File Offset: 0x000E847F public WinBleCharacteristicInfo(Guid id, BleServiceInfo service, BleCharacteristicProperties properties) : base(id,service, properties) { } } } }