479 lines
20 KiB
C#
479 lines
20 KiB
C#
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<string, BleAdvertisementInfo> _pCache;
|
||
public Dictionary<string, BleAdvertisementInfo> pCache
|
||
{
|
||
get
|
||
{
|
||
if (_pCache == null)
|
||
{
|
||
_pCache = new Dictionary<string, BleAdvertisementInfo>();
|
||
}
|
||
return _pCache;
|
||
}
|
||
set { _pCache = value; }
|
||
}
|
||
private Action<BleAdvertisementInfo> _discoveredCallback;
|
||
|
||
private Dictionary<BlePeripheralInfo, Action<IBleWinHwInterface, BlePeripheralInfo, BleResponse>> callbacks = new Dictionary<BlePeripheralInfo, Action<IBleWinHwInterface, BlePeripheralInfo, BleResponse>>();
|
||
private Dictionary<BlePeripheralInfo, Action<IBleWinHwInterface, BlePeripheralInfo, BleResponse<List<BleServiceInfo>>>> servicesCallbacks = new Dictionary<BlePeripheralInfo, Action<IBleWinHwInterface, BlePeripheralInfo, BleResponse<List<BleServiceInfo>>>>();
|
||
//private Dictionary<BlePeripheralInfo, Action<BleWinHwInterface, BleServiceInfo, BleResponse<List<BleCharacteristicInfo>>>> characteristicsCallbacks = new Dictionary<BlePeripheralInfo, Action<BleWinHwInterface, BleServiceInfo, BleResponse<List<BleCharacteristicInfo>>>>();
|
||
private Dictionary<BlePeripheralInfo, Action<IBleWinHwInterface, BleCharacteristicInfo, BleResponse>> characteristicNotificationCallbacks = new Dictionary<BlePeripheralInfo, Action<IBleWinHwInterface, BleCharacteristicInfo, BleResponse>>();
|
||
private Dictionary<BleCharacteristicInfo, CharacteristicReadCallback> characteristicReadCallbacks = new Dictionary<BleCharacteristicInfo, CharacteristicReadCallback>();
|
||
private Dictionary<BleServiceInfo, CharacteristicsDiscoveredCallback> characteristicsDiscoveredCallbacks = new Dictionary<BleServiceInfo, CharacteristicsDiscoveredCallback>();
|
||
|
||
private Dictionary<BlePeripheralInfo, Action> disconnectedCallback = new Dictionary<BlePeripheralInfo, Action>();
|
||
|
||
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<Guid> services = new List<Guid>();
|
||
|
||
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<BleServiceInfo> servicelist = new List<BleServiceInfo>();
|
||
List<BleCharacteristicInfo> characteristilist = new List<BleCharacteristicInfo>();
|
||
public void ConnectPeripheral(BlePeripheralInfo info, Action<IBleWinHwInterface, BlePeripheralInfo, BleResponse> 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<BleServiceInfo> servicelist = new List<BleServiceInfo>();
|
||
servicelist.Add(new WinBleServiceInfo(serviceCallback.Key, new Guid(service)));
|
||
BleResponse<List<BleServiceInfo>> response = new BleResponse<List<BleServiceInfo>>
|
||
{
|
||
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<BleCharacteristicInfo> characteristiclist = new List<BleCharacteristicInfo>();
|
||
characteristiclist.Add(new WinBleCharacteristicInfo(new Guid(characteristic), new WinBleServiceInfo(characteristicCallback.Key.Peripheral, new Guid(service)), 0));
|
||
BleResponse<List<BleCharacteristicInfo>> response = new BleResponse<List<BleCharacteristicInfo>>
|
||
{
|
||
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<BleAdvertisementInfo> discoveredCallBack)
|
||
{
|
||
pCache.Clear();
|
||
_discoveredCallback = discoveredCallBack;
|
||
bleMobileThread.StartWatcher();
|
||
}
|
||
|
||
public void StopScan()
|
||
{
|
||
bleMobileThread?.Stop();
|
||
}
|
||
|
||
//发现服务列表
|
||
public void DiscoverServices(BlePeripheralInfo peripheral, Action<IBleWinHwInterface, BlePeripheralInfo, BleResponse<List<BleServiceInfo>>> 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<IBleWinHwInterface, BleCharacteristicInfo, BleResponse> 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<byte[]> response = new BleResponse<byte[]>
|
||
{
|
||
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<byte[]> response = new BleResponse<byte[]>
|
||
{
|
||
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)
|
||
{
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|