| 
					
				 | 
			
			
				@@ -0,0 +1,538 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using System; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using System.Collections; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using System.Collections.Generic; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using System.Text; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using UnityEngine; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using System.Runtime.InteropServices; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using System.Linq; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+namespace SmartBowSDK 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Windows连接BluetoothLE 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// 我的扫描逻辑默认了读写特征都在同一服务下 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public class BleWinHelper : MonoBehaviour 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string LogTag = "[BleWinHelper]"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private  void Log(string text) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //Debug.Log(LogTag + text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            SmartBowLogger.Log(this, LogTag + text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private void Warn(string text) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //Debug.LogWarning(LogTag + text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            SmartBowLogger.Log(this, LogTag + text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private  void Error(string text) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //Debug.Log(LogTag + text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            SmartBowLogger.Log(this, LogTag + text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        //private string targetDeviceName = "Bbow_20210501 | ARTEMIS Pro | HOUYI Pro | Pistol | Pistol M9 | BGBox_202012"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        //private string targetDeviceNameHOUYIPro = "HOUYI Pro"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        //private string targetDeviceNameGun = "Pistol | Pistol M9 | Bbow_20210501"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string targetService = "{0000fff0-0000-1000-8000-00805f9b34fb}"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string targetCharacteristicsNotify = "{0000fff1-0000-1000-8000-00805f9b34fb}"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string targetCharacteristicsWrite = "{0000fff2-0000-1000-8000-00805f9b34fb}"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool isConnectLocking = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool isScanningDevices = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool isScanningServices = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool isScanningCharacteristics = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool isSubscribed = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool isSubscribing = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string lastError = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        [SerializeField] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string selectedDeviceId = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private Dictionary<string, Dictionary<string, string>> deviceList = new Dictionary<string, Dictionary<string, string>>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private List<string> serviceList = new List<string>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private List<string> characteristicsList = new List<string>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private float _connectedTime = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private float _receiveDataTime = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private float _heartBeatInterval = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private Action<bool> OnScanEnded; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private Action OnConnected; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 主动调用Disconnect()不会触发该委托 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private Action OnConnectionFailed; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private static Action<string, byte[]> OnCharacteristicChanged; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public SmartBowHelper smartBowHelper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public BluetoothAim_SDK bluetoothAim; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 注册window对象 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <param name="o">挂载对象</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <param name="bluetoothWindows">关联的BluetoothWindows</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <param name="logTip">提示的log标签</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <returns></returns> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public static BleWinHelper RegisterTo(SmartBowHelper _smartBowHelper, BluetoothWindows bluetoothWindows,string logTip = "first") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //if (_Instance) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //    Error("Register fail, because only one can be registered."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //    return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            string bleWinName = "BleWinHelper" + logTip; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            GameObject obj = new GameObject(bleWinName); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            obj.transform.SetParent(_smartBowHelper.transform); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleWinHelper bleWinHelper = obj.AddComponent<BleWinHelper>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bleWinHelper.smartBowHelper = _smartBowHelper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bleWinHelper.bluetoothAim = _smartBowHelper.bluetoothAim; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //日志名字 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bleWinHelper.LogTag = logTip + ": "; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bluetoothWindows.Connect = bleWinHelper.Connect; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bluetoothWindows.Disconnect = bleWinHelper.Disconnect; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bluetoothWindows.Write = bleWinHelper.Write; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bluetoothWindows.WriteByte = bleWinHelper.WriteByte; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // windos 通知 bluetoothAim  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bleWinHelper.OnScanEnded = (bool bSelectedDeviceId) => bluetoothWindows.OnScanEnded?.Invoke(bSelectedDeviceId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bleWinHelper.OnConnected = () => bluetoothWindows.OnConnected?.Invoke(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bleWinHelper.OnConnectionFailed = () => bluetoothWindows.OnConnectionFailed?.Invoke(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //多个定义共用一个OnCharacteristicChanged 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            OnCharacteristicChanged += (deviceID,bytes) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (deviceID == bleWinHelper.selectedDeviceId) {  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    bluetoothWindows.OnCharacteristicChanged?.Invoke(deviceID, bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return bleWinHelper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 设置心跳检测 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 1.每次收到的蓝牙数据都视为心跳 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 2.帮助触发蓝牙断开监听 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <param name="interval">心跳检测间隔</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public void SetHeartBeat(float interval) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            _heartBeatInterval = interval; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        //private static BleWinHelper _Instance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        void Awake() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           // _Instance = this; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        void OnDestroy() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          //  if (_Instance == this) _Instance = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.Quit(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        void Update() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.ScanStatus status; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isScanningDevices) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                BleApi.DeviceUpdate res = new BleApi.DeviceUpdate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    status = BleApi.PollDevice(ref res, false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (status == BleApi.ScanStatus.AVAILABLE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        // 如果存在 selectedDeviceId,不要重复 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (selectedDeviceId != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //Log($"当前的 selectedDeviceId: {selectedDeviceId}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            continue; // 跳过后续 AVAILABLE 的处理,等待 FINISHED 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (!deviceList.ContainsKey(res.id)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            deviceList[res.id] = new Dictionary<string, string>() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                { "name", "" }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                { "isConnectable", "False" } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //这里参考手机蓝牙模式 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (smartBowHelper.GetIsConnectName()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                //后续匹配名字 可以是多个设备 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                Log($"发现设备{res.name},is fileters empty:{ string.IsNullOrEmpty(smartBowHelper.GetFilters())},name:{smartBowHelper.GetFilters()}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                // 统一格式化:去掉非十六进制字符,转大写 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                string FormatMac(string mac) => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    string.Concat(mac.Where(c => Uri.IsHexDigit(c))).ToUpper(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                string searchMac = FormatMac(ExtractMacFromDeviceId(res.id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                string connectMac = FormatMac(smartBowHelper.GetConnectMacStr()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                Log($"发现设备 {searchMac},is connectMacStr:{connectMac },DeviceAddress:{searchMac}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        }   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (res.nameUpdated) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            deviceList[res.id]["name"] = res.name; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (res.isConnectableUpdated) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            deviceList[res.id]["isConnectable"] = res.isConnectable.ToString(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        //这里参考手机蓝牙模式 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (smartBowHelper.GetIsConnectName()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //后续匹配名字 可以是多个设备 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //Log( $"发现设备{res.name},is fileters empty:{ string.IsNullOrEmpty(smartBowHelper.GetFilters())},name:{smartBowHelper.GetFilters()}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            string _filters = string.IsNullOrEmpty(smartBowHelper.GetFilters())  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                ? bluetoothAim.deviceConfig.deviceName  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                : smartBowHelper.GetFilters(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            string[] filterArray = _filters.Split('|'); // 支持多个名字 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            foreach (var f in filterArray) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                string trimmedFilter = f.Trim(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                if (string.Equals(trimmedFilter, deviceList[res.id]["name"].Trim(), StringComparison.OrdinalIgnoreCase) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    && res.isConnectable) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    selectedDeviceId = res.id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    StopDeviceScan(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    Log($"Name匹配设备 {trimmedFilter}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        else  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            // 统一格式化:去掉非十六进制字符,转大写 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            string FormatMac(string mac) => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                string.Concat(mac.Where(c => Uri.IsHexDigit(c))).ToUpper(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            string searchMac = FormatMac(ExtractMacFromDeviceId(res.id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            string connectMac = FormatMac(smartBowHelper.GetConnectMacStr()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //Log($"发现设备 {searchMac},is connectMacStr:{connectMac },DeviceAddress:{searchMac}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //按mac地址匹配 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if (!string.IsNullOrEmpty(connectMac) && connectMac.Contains(searchMac) && res.isConnectable) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                selectedDeviceId = res.id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                StopDeviceScan(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                Log($"MAC匹配设备 {searchMac}"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    else if (status == BleApi.ScanStatus.FINISHED) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        StartCoroutine(ScanServiceAndCharacteristicsToSubscribe()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        Log($" ScanStatus FINISHED !"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        //停止扫描后通知? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        OnScanEnded?.Invoke(selectedDeviceId != null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } while (status == BleApi.ScanStatus.AVAILABLE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isScanningServices) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                BleApi.Service res; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    status = BleApi.PollService(out res, false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (status == BleApi.ScanStatus.AVAILABLE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        serviceList.Add(res.uuid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    else if (status == BleApi.ScanStatus.FINISHED) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        isScanningServices = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } while (status == BleApi.ScanStatus.AVAILABLE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isScanningCharacteristics) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                BleApi.Characteristic res; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    status = BleApi.PollCharacteristic(out res, false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (status == BleApi.ScanStatus.AVAILABLE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        Log("res.userDescription:"+ res.userDescription+ ",res.uuid:" + res.uuid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (res.userDescription == "no description available" ||  //旧设备 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            res.userDescription == "SPP Write Channel" ||   //新设备 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            res.userDescription == "SPP Read Channel") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            characteristicsList.Add(res.uuid); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            //跟着之前代码 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            characteristicsList.Add(res.userDescription); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        //string name = res.userDescription != "no description available" ? res.userDescription : res.uuid; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        //characteristicsList.Add(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    else if (status == BleApi.ScanStatus.FINISHED) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        isScanningCharacteristics = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } while (status == BleApi.ScanStatus.AVAILABLE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isSubscribed) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                BleApi.BLEData res; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                while (BleApi.PollData(out res, false)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    //string text = BitConverter.ToString(res.buf, 0, res.size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    // string text = Encoding.ASCII.GetString(res.buf, 0, res.size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    byte[] bytes = new byte[res.size]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Array.Copy(res.buf, bytes, res.size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    _receiveDataTime = Time.realtimeSinceStartup; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    OnCharacteristicChanged?.Invoke(res.deviceId, bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                BleApi.ErrorMessage res; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                BleApi.GetError(out res); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (lastError != res.msg) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Error(res.msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    lastError = res.msg; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    //对应设备才断开 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (lastError.Contains("SendDataAsync") && lastError.Contains(selectedDeviceId) && isSubscribed) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        HandleConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        void LateUpdate() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                _heartBeatInterval > 0 && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                isSubscribed && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Time.realtimeSinceStartup - _connectedTime >= _heartBeatInterval && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Time.realtimeSinceStartup - _receiveDataTime >= _heartBeatInterval 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                HandleConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool Connect() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isConnectLocking || isScanningDevices) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Warn("Connect Invalid, because is in connect."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            //开始连接时候,重置一下参数 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ReinitAfterConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.StartDeviceScan(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isConnectLocking = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isScanningDevices = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Log("Start Connect!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private void StopDeviceScan() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!isScanningDevices) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.StopDeviceScan(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isScanningDevices = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Log($"Stop DeviceScan!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private IEnumerator ScanServiceAndCharacteristicsToSubscribe() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isSubscribing = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (selectedDeviceId == null) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                HandleConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                smartBowHelper.InvokeOnBluetoothError(BluetoothError.Unknown, "选择设备 DeviceId 不存在!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                yield break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Log("SelectedDeviceId OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.ScanServices(selectedDeviceId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isScanningServices = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            serviceList.Clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            while (isScanningServices) yield return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bool findTargetService = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (string service in serviceList) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (service == targetService) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    findTargetService = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Log("FindTargetService OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!findTargetService) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                HandleConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //一般都不会出现这个问题,直接使用Unknown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                smartBowHelper.InvokeOnBluetoothError(BluetoothError.Unknown, "目标设备服务 service 不存在!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                yield break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.ScanCharacteristics(selectedDeviceId, targetService); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isScanningCharacteristics = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            characteristicsList.Clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            while (isScanningCharacteristics) yield return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bool findTargetCharacteristicsNotify = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bool findTargetCharacteristicsWrite = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (string characteristics in characteristicsList) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (characteristics == targetCharacteristicsNotify) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    findTargetCharacteristicsNotify = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Log("FindTargetCharacteristicsNotify OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else if (characteristics == targetCharacteristicsWrite) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    findTargetCharacteristicsWrite = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Log("FindTargetCharacteristicsWrite OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!findTargetCharacteristicsNotify || !findTargetCharacteristicsWrite) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                HandleConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                //一般都不会出现这个问题,直接使用Unknown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                smartBowHelper.InvokeOnBluetoothError(BluetoothError.Unknown, "目标设备特征值 Characteristics 不存在!"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                yield break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.SubscribeCharacteristic(selectedDeviceId, targetService, targetCharacteristicsNotify, false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isSubscribed = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isSubscribing = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Log("SubscribeCharacteristicNotify OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            _connectedTime = Time.realtimeSinceStartup; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            OnConnected?.Invoke(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 连接错误 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private void HandleConnectFail() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (selectedDeviceId != null) BleApi.Disconnect(selectedDeviceId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bool isLockBefore = isConnectLocking; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ReinitAfterConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isLockBefore) OnConnectionFailed?.Invoke(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private void ReinitAfterConnectFail() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isConnectLocking = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isScanningDevices = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isScanningServices = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isScanningCharacteristics = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isSubscribed = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isSubscribing = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            selectedDeviceId = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            deviceList.Clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            serviceList.Clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            characteristicsList.Clear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool Write(string text) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!isSubscribed) return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            byte[] payload = Encoding.ASCII.GetBytes(text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.BLEData data = new BleApi.BLEData(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.buf = new byte[512]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.size = (short)payload.Length; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.deviceId = selectedDeviceId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.serviceUuid = targetService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.characteristicUuid = targetCharacteristicsWrite; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (int i = 0; i < payload.Length; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data.buf[i] = payload[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // no error code available in non-blocking mode 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.SendData(in data, false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Log("Write(" + text + ")"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool WriteByte(byte[] payload) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!isSubscribed) return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.BLEData data = new BleApi.BLEData(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.buf = new byte[512]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.size = (short)payload.Length; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.deviceId = selectedDeviceId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.serviceUuid = targetService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data.characteristicUuid = targetCharacteristicsWrite; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (int i = 0; i < payload.Length; i++) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data.buf[i] = payload[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // no error code available in non-blocking mode 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            BleApi.SendData(in data, false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Log("Write(byte[])"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private bool Disconnect() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!isConnectLocking) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Warn("Disconnect Invalid, because not in connect."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isSubscribing) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Warn("Disconnect Invalid, because is subscribing."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isScanningDevices) StopDeviceScan(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (selectedDeviceId != null) BleApi.Disconnect(selectedDeviceId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ReinitAfterConnectFail(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Log("Disconnect OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 其实是 WinRT BLE API 的设备 ID 格式,它包含两部分: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 前面:BluetoothLE#BluetoothLE00:e0:4c:2a:12:97 → 设备的内部标识(适配器 + 随机 ID)。 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 后面:d3:5c:89:57:68:de → 真实的 MAC 地址。 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// 微软官方文档也说明了: WinRT 不直接给 BluetoothAddress,而是拼接在 Id 的末尾。 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <param name="deviceId">参考 BluetoothLE#BluetoothLE00:e0:4c:2a:12:97-d3:5c:89:57:68:de</param> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <returns></returns> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        string ExtractMacFromDeviceId(string deviceId) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(deviceId)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // WinRT 格式一般是:BluetoothLE#BluetoothLEXX:XX:XX:XX:XX:XX-YY:YY:YY:YY:YY:YY 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            int lastDash = deviceId.LastIndexOf('-'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (lastDash >= 0 && lastDash < deviceId.Length - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return deviceId.Substring(lastDash + 1); // 提取最后一段 MAC 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return deviceId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |