BluetoothAim_SDK.cs 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using ArduinoBluetoothAPI;
  6. using System.Linq;
  7. using SmartBowSDK.CMD;
  8. namespace SmartBowSDK
  9. {
  10. public class BluetoothAim_SDK : MonoBehaviour
  11. {
  12. public SmartBowHelper smartBowHelper;
  13. public BluetoothDeviceConfig deviceConfig;
  14. private BluetoothHelper _bluetoothHelper;
  15. private BluetoothHelperService _bluetoothService;
  16. private BluetoothHelperCharacteristic _characteristicWrite;
  17. private static HashSet<object> _ScanLocker = new HashSet<object>();
  18. private int _receivedDataCount = 0;
  19. public string macAddress = null;
  20. //设备固件版本
  21. public string deviceVersion = null;
  22. public BluetoothStatusEnum bluetoothStatus;
  23. private BluetoothWindows _bluetoothWindows;
  24. /// <summary>
  25. /// 6轴
  26. /// </summary>
  27. private o06DOF.SixAxisFusion sixAxisFusion;
  28. /// <summary>
  29. /// 过滤名称
  30. /// </summary>
  31. public string filters { get; set; } = "";
  32. /// <summary>
  33. /// 是否使用mac过滤
  34. /// </summary>
  35. public bool isConnectName { get; set; } = true;//默认用名字连接
  36. /// <summary>
  37. /// 传入的mac
  38. /// </summary>
  39. public string connectMacStr { get; set; } = "";
  40. /// <summary>
  41. /// 当前的传感器类型
  42. /// </summary>
  43. public SensorAxisType sensorAxisType { get; set; } = SensorAxisType.NineAxisSensor;
  44. private void SetStatus(BluetoothStatusEnum newStatus)
  45. {
  46. BluetoothStatusEnum oldStatus = bluetoothStatus;
  47. if (oldStatus == newStatus) return;
  48. bluetoothStatus = newStatus;
  49. SmartBowLogger.Log(this, $" oldStatus:[{oldStatus}]=> newStatus:[{newStatus}]");
  50. if (newStatus == BluetoothStatusEnum.None)
  51. {
  52. _bluetoothHelper = null;
  53. _bluetoothService = null;
  54. _characteristicWrite = null;
  55. _receivedDataCount = 0;
  56. macAddress = null;
  57. deviceVersion = null;
  58. _battery = 0;
  59. moduleInited = false;
  60. }
  61. smartBowHelper.InvokeOnBluetoothStatusChanged(oldStatus, newStatus);
  62. }
  63. void Awake()
  64. {
  65. DontDestroyOnLoad(gameObject);
  66. deviceConfig = BluetoothDeviceConfig.GetDefault();
  67. }
  68. void Update()
  69. {
  70. LoopHandleCommands();
  71. }
  72. private static int _NoneToConnectingStep = 0;
  73. public BluetoothWindows CreateWindowBLE() {
  74. //如果是window环境,直接实例化一个BluetoothWindows
  75. if (BluetoothWindows.IsWindows())
  76. {
  77. _bluetoothWindows = new BluetoothWindows();
  78. _bluetoothWindows.OnScanEnded = OnScanEnded_windows;
  79. _bluetoothWindows.OnConnected = OnConnected_windows;
  80. _bluetoothWindows.OnConnectionFailed = OnConnectionFailed_windows;
  81. _bluetoothWindows.OnCharacteristicChanged = OnCharacteristicChanged_windows;
  82. return _bluetoothWindows;
  83. }
  84. return null;
  85. }
  86. /// <summary>
  87. /// 根据当前的 isUseCompanionDeviceManager 需求来连接
  88. /// </summary>
  89. public void ConnectFormType() {
  90. if (isUseCompanionDeviceManager)
  91. {
  92. ConnectCMD();
  93. }
  94. else {
  95. Connect();
  96. }
  97. }
  98. public void Connect()
  99. {
  100. if (BluetoothWindows.IsWindows())
  101. {
  102. if (bluetoothStatus == BluetoothStatusEnum.None)
  103. {
  104. //windos连接触发蓝牙
  105. _bluetoothWindows.Connect();
  106. SetStatus(BluetoothStatusEnum.Connecting);
  107. //初始化一次六轴解析
  108. if (sensorAxisType == SensorAxisType.SixAxisSensor && sixAxisFusion == null)
  109. {
  110. Vector3 accelOffset = Vector3.zero;
  111. Vector3 gyroOffset = Vector3.zero;
  112. float gForce = 1.0f;
  113. float gyroToDegree = 1.0f;
  114. sixAxisFusion = new o06DOF.SixAxisFusion(accelOffset, gyroOffset, gForce, gyroToDegree);
  115. }
  116. }
  117. return;
  118. }
  119. if (_ScanLocker.Count > 0) return;
  120. if (bluetoothStatus != BluetoothStatusEnum.None) return;
  121. if (_NoneToConnectingStep > 0) return;
  122. _NoneToConnectingStep = 1;
  123. if (Application.platform == RuntimePlatform.Android)
  124. {
  125. if (!BluetoothHelperAndroid_SDK.IsBluetoothEnabled())
  126. {
  127. HandleConnectError(BluetoothError.BluetoothNotEnabled, "蓝牙开关未打开");
  128. return;
  129. }
  130. if (BluetoothHelperAndroid_SDK.RequestBluetoothPermissions(() =>
  131. {
  132. _NoneToConnectingStep = 0;
  133. Connect();
  134. }, (permission) =>
  135. {
  136. if (permission.Contains("LOCATION"))
  137. {
  138. HandleConnectError(BluetoothError.LocationPermissionNotGranted, "尚未授予定位权限");
  139. }
  140. else if (permission.Contains("BLUETOOTH"))
  141. {
  142. HandleConnectError(BluetoothError.ScanPermissionNotGranted, "尚未授予扫描附近设备权限");
  143. }
  144. })) return;
  145. }
  146. try
  147. {
  148. BluetoothHelper.BLE = true;
  149. _bluetoothHelper = BluetoothHelper.GetNewInstance();
  150. _bluetoothHelper.OnConnected += OnConnected;
  151. _bluetoothHelper.OnConnectionFailed += OnConnectionFailed;
  152. SmartBowLogger.Log(this,"创建蓝牙的SensorAxisType类型:"+ sensorAxisType);
  153. if (sensorAxisType == SensorAxisType.SixAxisSensor)
  154. {
  155. //6轴时候,另外的解析操作
  156. _bluetoothHelper.OnCharacteristicChanged += OnCharacteristicChanged6Axis;
  157. //初始化一次六轴解析
  158. if (sixAxisFusion == null) {
  159. Vector3 accelOffset = Vector3.zero;
  160. Vector3 gyroOffset = Vector3.zero;
  161. float gForce = 1.0f;
  162. float gyroToDegree = 1.0f;
  163. sixAxisFusion = new o06DOF.SixAxisFusion(accelOffset, gyroOffset, gForce, gyroToDegree);
  164. }
  165. }
  166. else {
  167. //九轴的解析
  168. _bluetoothHelper.OnCharacteristicChanged += OnCharacteristicChanged;
  169. }
  170. _bluetoothHelper.OnScanEnded += OnScanEnded;
  171. _bluetoothHelper.ScanNearbyDevices();
  172. _NoneToConnectingStep = 2;
  173. }
  174. catch (Exception e)
  175. {
  176. HandleConnectError(BluetoothError.Unknown, e.ToString());
  177. }
  178. if (_NoneToConnectingStep == 2)
  179. {
  180. _NoneToConnectingStep = 0;
  181. _ScanLocker.Add(_bluetoothHelper);
  182. SetStatus(BluetoothStatusEnum.Connecting);
  183. }
  184. }
  185. private void HandleConnectError(BluetoothError error, string message)
  186. {
  187. _NoneToConnectingStep = 0;
  188. smartBowHelper.InvokeOnBluetoothError(error, message);
  189. }
  190. public void Disconnect()
  191. {
  192. //断开连接时候清除AimDeviceInfo
  193. ResetAimDeviceInfoToNull();
  194. if (isUseCompanionDeviceManager) {
  195. cmdManager.Disconnect();
  196. SetStatus(BluetoothStatusEnum.None);
  197. return;
  198. }
  199. if (BluetoothWindows.IsWindows())
  200. {
  201. if (_bluetoothWindows.Disconnect())
  202. SetStatus(BluetoothStatusEnum.None);
  203. return;
  204. }
  205. if (_bluetoothHelper != null)
  206. _bluetoothHelper.Disconnect();
  207. SetStatus(BluetoothStatusEnum.None);
  208. }
  209. void OnConnected(BluetoothHelper helper)
  210. {
  211. if (helper != _bluetoothHelper) return;
  212. SetStatus(BluetoothStatusEnum.Connected);
  213. foreach (BluetoothHelperService service in helper.getGattServices())
  214. {
  215. if (service.getName().ToLower().StartsWith(deviceConfig.service))
  216. {
  217. _bluetoothService = service;
  218. foreach (BluetoothHelperCharacteristic characteristic in service.getCharacteristics())
  219. {
  220. if (characteristic.getName().ToLower().StartsWith(deviceConfig.characteristicWrite))
  221. {
  222. _characteristicWrite = characteristic;
  223. }
  224. else if (characteristic.getName().ToLower().StartsWith(deviceConfig.characteristicNotify))
  225. {
  226. BluetoothHelperCharacteristic c = new BluetoothHelperCharacteristic(characteristic.getName());
  227. c.setService(_bluetoothService.getName());
  228. _bluetoothHelper.Subscribe(c);
  229. }
  230. }
  231. }
  232. }
  233. //指令区分
  234. if (sensorAxisType == SensorAxisType.SixAxisSensor)
  235. {
  236. //6轴指令
  237. StartCoroutine(InitWhenConnected6Axis());
  238. }
  239. else
  240. {
  241. //九轴
  242. StartCoroutine(InitWhenConnected());
  243. }
  244. }
  245. void OnConnected_windows()
  246. {
  247. SetStatus(BluetoothStatusEnum.Connected);
  248. if (sensorAxisType == SensorAxisType.SixAxisSensor)
  249. {
  250. //6轴指令
  251. StartCoroutine(InitWhenConnected6Axis());
  252. }
  253. else
  254. {
  255. //九轴
  256. StartCoroutine(InitWhenConnected());
  257. }
  258. }
  259. void OnConnectionFailed(BluetoothHelper helper)
  260. {
  261. if (helper != _bluetoothHelper) return;
  262. SetStatus(BluetoothStatusEnum.None);
  263. }
  264. void OnConnectionFailed_windows()
  265. {
  266. SetStatus(BluetoothStatusEnum.None);
  267. }
  268. /// <summary>
  269. /// 原九轴的传感器解析
  270. /// </summary>
  271. /// <param name="helper"></param>
  272. /// <param name="value"></param>
  273. /// <param name="characteristic"></param>
  274. void OnCharacteristicChanged(BluetoothHelper helper, byte[] value, BluetoothHelperCharacteristic characteristic)
  275. {
  276. if (helper != _bluetoothHelper) return;
  277. if (bluetoothStatus != BluetoothStatusEnum.Connected) return;
  278. byte[] bytes = value;
  279. //旧的是500,
  280. int limit = isUseCompanionDeviceManager ? 50 : 500;
  281. if (_receivedDataCount++ < limit) { //旧的逻辑
  282. if (macAddress == null) ParseMacAddress(value);
  283. //固件版本号
  284. if (deviceVersion == null) ParseDeviceVersion(value);
  285. }
  286. smartBowHelper.aimHandler.OnDataReceived(bytes);
  287. }
  288. /// <summary>
  289. /// 六轴传感器时候使用的解析
  290. /// </summary>
  291. /// <param name="helper"></param>
  292. /// <param name="value"></param>
  293. /// <param name="characteristic"></param>
  294. void OnCharacteristicChanged6Axis(BluetoothHelper helper, byte[] value, BluetoothHelperCharacteristic characteristic)
  295. {
  296. if (helper != _bluetoothHelper) return;
  297. if (bluetoothStatus != BluetoothStatusEnum.Connected) return;
  298. byte[] bytes = value;
  299. if (macAddress == null && _receivedDataCount++ < 500) ParseMacAddress(value);
  300. //smartBowHelper.aimHandler.OnDataReceived(bytes);
  301. //转字符串后就是 Bat
  302. if (bytes.Length == 20 && bytes[19] == 0x0a
  303. //Bat
  304. && bytes[2] == 0x42 && bytes[3] == 0x61 && bytes[4] == 0x74) {
  305. //第一步 解析电量?
  306. string betty = System.Text.Encoding.ASCII.GetString(bytes);
  307. // SmartBowLogger.Log(this, BitConverter.ToString(bytes).Replace("-", ""));
  308. //SmartBowLogger.Log(this, "betty:" + betty);
  309. // 第二步:解析JSON字符串
  310. Newtonsoft.Json.Linq.JObject json = Newtonsoft.Json.Linq.JObject.Parse(betty);
  311. // 第三步:提取"Bat"的值
  312. int batValue = (int)json["Bat"];
  313. SmartBowLogger.Log(this, "Bat Value: " + batValue); // 输出: 100
  314. battery = batValue;
  315. }
  316. //传感器数据
  317. if (bytes.Length == 20 && bytes[0] == 0x7b && bytes[19] == 0x7d)
  318. {
  319. o06DOF.SixData sixData = sixAxisFusion.getSixAxisByBytes(bytes);
  320. Quaternion quaternion = sixAxisFusion.tranUpdateData(sixData);
  321. //输出欧拉角 pitch yaw roll
  322. smartBowHelper.InvokeOnSixAxisRotationUpdate(quaternion.eulerAngles);
  323. //smartBowHelper.InvokeOnAxisDataUpdateEvent(bytes);
  324. }
  325. }
  326. /// <summary>
  327. /// 重置6轴identify
  328. /// </summary>
  329. public void ResetSixAxisFusion() {
  330. if (sixAxisFusion != null) {
  331. sixAxisFusion.ResetToInitialRotation();
  332. }
  333. }
  334. /// <summary>
  335. /// pc调用
  336. /// </summary>
  337. /// <param name="deviceId"></param>
  338. /// <param name="value"></param>
  339. void OnCharacteristicChanged_windows(string deviceId, byte[] value)
  340. {
  341. if (sensorAxisType == SensorAxisType.SixAxisSensor)
  342. {
  343. //6轴指令
  344. OnCharacteristicChanged6Axis(null, value, null);
  345. }
  346. else
  347. {
  348. //九轴
  349. OnCharacteristicChanged(null, value, null);
  350. }
  351. }
  352. void OnScanEnded(BluetoothHelper helper, LinkedList<BluetoothDevice> nearbyDevices)
  353. {
  354. _ScanLocker.Remove(helper);
  355. if (helper != _bluetoothHelper) return;
  356. foreach (BluetoothDevice device in nearbyDevices)
  357. {
  358. if (isConnectName)
  359. {
  360. //if (device.DeviceName == deviceConfig.deviceName)
  361. //后续匹配名字 可以是多个设备
  362. string _filters = string.IsNullOrEmpty(filters) ? deviceConfig.deviceName : filters;
  363. SmartBowLogger.Log(this, $"发现设备{device.DeviceName},is fileters empty:{ string.IsNullOrEmpty(filters)},current filters:{_filters}");
  364. //if (_filters.Contains(device.DeviceName))
  365. //{
  366. // _bluetoothHelper.setDeviceName(device.DeviceName);
  367. // _bluetoothHelper.Connect();
  368. // SmartBowLogger.Log(this, $"Name匹配设备{device.DeviceName}");
  369. // return;
  370. //}
  371. // 精确匹配,大小写敏感,多设备名字用 " | " 分隔
  372. string[] filterArray = _filters.Split('|'); // 支持多个名字
  373. foreach (var f in filterArray)
  374. {
  375. string trimmedFilter = f.Trim();
  376. if (string.Equals(trimmedFilter, device.DeviceName.Trim(), StringComparison.OrdinalIgnoreCase))
  377. {
  378. _bluetoothHelper.setDeviceName(device.DeviceName);
  379. _bluetoothHelper.Connect();
  380. SmartBowLogger.Log(this, $"Name匹配设备 {device.DeviceName}");
  381. return;
  382. }
  383. }
  384. }
  385. else
  386. {
  387. SmartBowLogger.Log(this, $"发现设备 {device.DeviceAddress},is connectMacStr:{ connectMacStr },DeviceAddress:{device.DeviceAddress}");
  388. // 统一格式化:去掉非十六进制字符,转大写
  389. string FormatMac(string mac) =>
  390. string.Concat(mac.Where(c => Uri.IsHexDigit(c))).ToUpper();
  391. string deviceMac = FormatMac(device.DeviceAddress);
  392. string targetMac = FormatMac(connectMacStr);
  393. if (targetMac.Contains(deviceMac))
  394. {
  395. _bluetoothHelper.setDeviceName(device.DeviceName);
  396. _bluetoothHelper.setDeviceAddress(device.DeviceAddress);
  397. _bluetoothHelper.Connect();
  398. SmartBowLogger.Log(this, $"Mac匹配设备{device.DeviceAddress}");
  399. return;
  400. }
  401. //按mac地址匹配
  402. //if (connectMacStr.Contains(device.DeviceAddress))
  403. //{
  404. // _bluetoothHelper.setDeviceName(device.DeviceName);
  405. // _bluetoothHelper.setDeviceAddress(device.DeviceAddress);
  406. // _bluetoothHelper.Connect();
  407. // SmartBowLogger.Log(this, $"Mac匹配设备{device.DeviceAddress}");
  408. // return;
  409. //}
  410. }
  411. }
  412. SetStatus(BluetoothStatusEnum.None);
  413. smartBowHelper.InvokeOnBluetoothError(BluetoothError.ScanNotFoundTargetDevice, "扫描结束后未发现目标设备");
  414. }
  415. void OnScanEnded_windows(bool bSelectedDeviceId) {
  416. SmartBowLogger.Log(this, $"ScanEnded bSelectedDeviceId:" + bSelectedDeviceId);
  417. if (!bSelectedDeviceId)
  418. {
  419. SetStatus(BluetoothStatusEnum.None);
  420. smartBowHelper.InvokeOnBluetoothError(BluetoothError.ScanNotFoundTargetDevice, "扫描结束后未发现目标设备");
  421. }
  422. }
  423. #region 使用CompanionDeviceManager API 管理连接
  424. public bool isUseCompanionDeviceManager { get; set; } = false;//默认用原本蓝牙扫描连接
  425. private CMDManager cmdManager;
  426. private bool isCMDScanning = false;
  427. private string CmdName = "";
  428. private string CmdMac = "";
  429. private int cmdScanTimeout = 10;
  430. /// <summary>
  431. /// 设置超时时间
  432. /// </summary>
  433. /// <param name="timeout"></param>
  434. //public void SetCompanionDeviceManagerScanTimeout(int timeout) {
  435. // cmdScanTimeout = timeout;
  436. //}
  437. /// <summary>
  438. /// 初始化 CompanionDeviceManager 连接
  439. /// </summary>
  440. public void InitCMDManager()
  441. {
  442. if (cmdManager != null) return;
  443. cmdManager = new CMDManager();
  444. cmdManager.SetUUIDs(deviceConfig.service,deviceConfig.characteristicWrite,deviceConfig.characteristicNotify);
  445. cmdManager.OnCMDDeviceFound += OnCMDScanFound;
  446. cmdManager.OnCMDScanFailed += OnCMDScanFailed;
  447. cmdManager.OnCMDBLEConnected += OnCMDBLEConnected;
  448. cmdManager.OnCMDBLEDisconnected += OnCMDBLEDisconnected;
  449. cmdManager.OnCMDBLEReady += OnCMDBLEReady;
  450. cmdManager.OnCMDBLENotify += OnCMDBLENotify;
  451. cmdManager.OnPermissionDenied += OnCMDPermissionDenied;
  452. cmdManager.OnPermissionDontAsk += OnCMDPermissionDontAsk;
  453. SmartBowLogger.Log(this, "CMDManager 已初始化。");
  454. }
  455. /// <summary>
  456. /// 清空 CompanionDeviceManager 信息
  457. /// </summary>
  458. public void ClearCMDManager() {
  459. if (cmdManager != null)
  460. {
  461. cmdManager.Cleanup(); // 调用 CMDManager 内部彻底清理方法
  462. cmdManager.OnCMDDeviceFound -= OnCMDScanFound;
  463. cmdManager.OnCMDScanFailed -= OnCMDScanFailed;
  464. cmdManager.OnCMDBLEConnected -= OnCMDBLEConnected;
  465. cmdManager.OnCMDBLEDisconnected -= OnCMDBLEDisconnected;
  466. cmdManager.OnCMDBLEReady -= OnCMDBLEReady;
  467. cmdManager.OnCMDBLENotify -= OnCMDBLENotify;
  468. cmdManager.OnPermissionDenied -= OnCMDPermissionDenied;
  469. cmdManager.OnPermissionDontAsk -= OnCMDPermissionDontAsk;
  470. cmdManager = null;
  471. SmartBowLogger.Log(this, "CMDManager 已清除。");
  472. }
  473. }
  474. /// <summary>
  475. /// 使用CompanionDeviceManager连接,仅是 Android 平台
  476. /// </summary>
  477. public void ConnectCMD()
  478. {
  479. if (!isUseCompanionDeviceManager)
  480. {
  481. SmartBowLogger.Log(this, $"使用CMD需要设置isUseCompanionDeviceManager");
  482. return;
  483. }
  484. if (isCMDScanning) return;
  485. if (bluetoothStatus != BluetoothStatusEnum.None) return;
  486. if (Application.platform == RuntimePlatform.Android)
  487. {
  488. if (!BluetoothHelperAndroid_SDK.IsBluetoothEnabled())
  489. {
  490. HandleConnectError(BluetoothError.BluetoothNotEnabled, "蓝牙开关未打开");
  491. return;
  492. }
  493. }
  494. try
  495. {
  496. //_bluetoothHelper.ScanNearbyDevices();
  497. //".*"
  498. if (isConnectName)
  499. {
  500. string _filters = string.IsNullOrEmpty(filters) ? deviceConfig.deviceNameRegex : filters;
  501. string regex = "(" + _filters + ")";
  502. //扫描时候设置一个时间
  503. //cmdManager.scanTimeout = cmdScanTimeout;
  504. cmdManager.StartScan(regex);
  505. }
  506. else {
  507. cmdManager.ConnectMac(FormatMacWithColon(connectMacStr));
  508. }
  509. isCMDScanning = true;
  510. SetStatus(BluetoothStatusEnum.Connecting);
  511. }
  512. catch (Exception e)
  513. {
  514. CMDHandleConnectError(CMDScanState.UNKNOWN_ERROR, e.ToString());
  515. }
  516. }
  517. /**
  518. * mac.Where(c => Uri.IsHexDigit(c))遍历 mac 字符串中的每一个字符 c。
  519. * Uri.IsHexDigit(c) 会判断这个字符是否是 16 进制数字(0-9、A-F、a-f)。
  520. * 所以这一步会 过滤掉所有非 16 进制的字符,比如 -、:、空格等。
  521. * string.Concat(...)
  522. * 将过滤后的字符重新拼接成一个新的字符串。
  523. * .ToUpper()
  524. * 将整个字符串转成大写。
  525. */
  526. string FormatMacWithColon(string mac)
  527. {
  528. var hex = string.Concat(mac.Where(c => Uri.IsHexDigit(c))).ToUpper();
  529. return string.Join(":", Enumerable.Range(0, hex.Length / 2)
  530. .Select(i => hex.Substring(i * 2, 2)));
  531. }
  532. private void OnCMDScanFound(string name,string mac)
  533. {
  534. //MAC = D3:5C:89:57:68:DE
  535. SmartBowLogger.Log(this, $"[SmartBow] CMD 扫描到设备 MAC = " + mac);
  536. // 监听蓝牙准备就绪
  537. CmdMac = mac;
  538. CmdName = name;
  539. //扫描之后也是这里直接连接
  540. StartCoroutine(DelayConnect(FormatMacWithColon(CmdMac)));
  541. }
  542. private IEnumerator DelayConnect(string mac)
  543. {
  544. yield return new WaitForSeconds(0.3f);
  545. cmdManager.ConnectMac(FormatMacWithColon(mac));
  546. }
  547. private void OnCMDScanFailed(CMDScanState state, string reason) {
  548. SmartBowLogger.Log(this, $"[SmartBow] CMD OnCMDScanFailed:"+ reason);
  549. SetStatus(BluetoothStatusEnum.None);
  550. isCMDScanning = false;
  551. StartCoroutine(SafeUICallback(state, reason));
  552. }
  553. IEnumerator SafeUICallback(CMDScanState state, string reason)
  554. {
  555. yield return new WaitForEndOfFrame();
  556. yield return null; // 再等一帧
  557. smartBowHelper.InvokeOnCMDState(state, reason);
  558. }
  559. private void OnCMDBLEDisconnected() {
  560. SmartBowLogger.Log(this, $"[SmartBow] CMD OnCMDBLEDisconnected.");
  561. Disconnect();
  562. isCMDScanning = false;
  563. }
  564. /// <summary>
  565. /// 连接上准备好
  566. /// </summary>
  567. private void OnCMDBLEConnected()
  568. {
  569. SmartBowLogger.Log(this, $"[SmartBow] CMD OnCMDBLEConnected.");
  570. }
  571. /// <summary>
  572. /// 获取到特征值时候触发
  573. /// </summary>
  574. private void OnCMDBLEReady() {
  575. SmartBowLogger.Log(this, $"[SmartBow] CMD OnCMDBLEReady.");
  576. SetStatus(BluetoothStatusEnum.Connected);
  577. isCMDScanning = false;
  578. //指令区分
  579. if (sensorAxisType == SensorAxisType.SixAxisSensor)
  580. {
  581. //6轴指令
  582. StartCoroutine(InitWhenConnected6Axis());
  583. }
  584. else
  585. {
  586. //九轴
  587. StartCoroutine(InitWhenConnected());
  588. }
  589. }
  590. private void OnCMDBLENotify(byte[] value) {
  591. if (sensorAxisType == SensorAxisType.SixAxisSensor)
  592. {
  593. // 6轴指令
  594. OnCharacteristicChanged6Axis(null, value, null);
  595. }
  596. else
  597. {
  598. // 九轴
  599. OnCharacteristicChanged(null, value, null);
  600. }
  601. }
  602. public void OnCMDPermissionDenied(string perm) {
  603. SmartBowLogger.LogError(this,$"[App] 用户拒绝权限:{perm}");
  604. CMDHandleConnectError(CMDScanState.CMDDenied, $"拒绝权限:{perm}");
  605. }
  606. public void OnCMDPermissionDontAsk(string perm)
  607. {
  608. SmartBowLogger.LogError(this, $"[App] 用户永久拒绝权限:{perm}");
  609. CMDHandleConnectError(CMDScanState.CMDDontAsk, $"永久拒绝权限:{perm}");
  610. }
  611. private void CMDHandleConnectError(CMDScanState state, string message)
  612. {
  613. smartBowHelper.InvokeOnCMDState(state, message);
  614. }
  615. #endregion
  616. private float _lastWriteDataTime;
  617. bool WriteData(string data)
  618. {
  619. try
  620. {
  621. if (isUseCompanionDeviceManager)
  622. {
  623. return cmdManager.WriteString(data);
  624. }
  625. if (BluetoothWindows.IsWindows())
  626. {
  627. return _bluetoothWindows.Write(data);
  628. }
  629. BluetoothHelperCharacteristic c = new BluetoothHelperCharacteristic(_characteristicWrite.getName());
  630. c.setService(_bluetoothService.getName());
  631. _bluetoothHelper.WriteCharacteristic(c, data);
  632. _lastWriteDataTime = Time.realtimeSinceStartup;
  633. return true;
  634. }
  635. catch (Exception e)
  636. {
  637. Debug.LogError(e);
  638. return false;
  639. }
  640. }
  641. bool WriteByteData(byte[] data)
  642. {
  643. try
  644. {
  645. if (isUseCompanionDeviceManager)
  646. {
  647. return cmdManager.WriteBytes(data);
  648. }
  649. if (BluetoothWindows.IsWindows())
  650. {
  651. return _bluetoothWindows.WriteByte(data);
  652. }
  653. BluetoothHelperCharacteristic c = new BluetoothHelperCharacteristic(_characteristicWrite.getName());
  654. c.setService(_bluetoothService.getName());
  655. _bluetoothHelper.WriteCharacteristic(c, data);
  656. _lastWriteDataTime = Time.realtimeSinceStartup;
  657. return true;
  658. }
  659. catch (Exception e)
  660. {
  661. Debug.LogError(e);
  662. return false;
  663. }
  664. }
  665. public bool moduleInited;
  666. private string _connectedHandlerID = "";
  667. IEnumerator InitWhenConnected()
  668. {
  669. string myConnectedHandlerID = Guid.NewGuid().ToString();
  670. _connectedHandlerID = myConnectedHandlerID;
  671. yield return new WaitForSecondsRealtime(BluetoothWindows.IsWindows() ? 0.3f : 2.2f);
  672. //获取Mac地址 获取设备固件版本号 获取设备信息(设备类型,系统类型) 获取初始电量 开启发送逻辑
  673. Queue<string> cmds = new Queue<string>(new string[] { "M","V","I", "b", "b", "1"});
  674. while (cmds.Count > 0)
  675. {
  676. if (bluetoothStatus != BluetoothStatusEnum.Connected || myConnectedHandlerID != _connectedHandlerID) yield break;
  677. string cmd = cmds.Dequeue();
  678. WriteData(cmd);
  679. yield return new WaitForSecondsRealtime(0.3f);
  680. }
  681. if (bluetoothStatus != BluetoothStatusEnum.Connected || myConnectedHandlerID != _connectedHandlerID) yield break;
  682. StartCoroutine(LoopRequestBattery(myConnectedHandlerID));
  683. moduleInited = true;
  684. smartBowHelper.InvokeOnBluetoothModuleInited();
  685. }
  686. /// <summary>
  687. /// 初始化6轴指令
  688. /// </summary>
  689. /// <returns></returns>
  690. IEnumerator InitWhenConnected6Axis()
  691. {
  692. string myConnectedHandlerID = Guid.NewGuid().ToString();
  693. _connectedHandlerID = myConnectedHandlerID;
  694. yield return new WaitForSecondsRealtime(BluetoothWindows.IsWindows() ? 0.3f : 2.2f);
  695. Queue<string> cmds = new Queue<string>(new string[] { "M", "B", "B" });
  696. while (cmds.Count > 0)
  697. {
  698. if (bluetoothStatus != BluetoothStatusEnum.Connected || myConnectedHandlerID != _connectedHandlerID) yield break;
  699. string cmd = cmds.Dequeue();
  700. WriteData(cmd);
  701. yield return new WaitForSecondsRealtime(0.3f);
  702. }
  703. if (bluetoothStatus != BluetoothStatusEnum.Connected || myConnectedHandlerID != _connectedHandlerID) yield break;
  704. StartCoroutine(LoopRequestBattery6Axis(myConnectedHandlerID));
  705. moduleInited = true;
  706. smartBowHelper.InvokeOnBluetoothModuleInited();
  707. }
  708. private int _battery = 0;
  709. public int battery
  710. {
  711. get => _battery;
  712. set
  713. {
  714. _battery = value;
  715. //SmartBowLogger.Log(this, "Battery缓存成功");
  716. }
  717. }
  718. /// <summary>
  719. /// 九轴原本的连接电池指令
  720. /// </summary>
  721. /// <param name="myConnectedHandlerID"></param>
  722. /// <returns></returns>
  723. IEnumerator LoopRequestBattery(string myConnectedHandlerID)
  724. {
  725. while (bluetoothStatus == BluetoothStatusEnum.Connected && myConnectedHandlerID == _connectedHandlerID)
  726. {
  727. yield return new WaitForSecondsRealtime(10);
  728. AddCommandToQueue("b");
  729. }
  730. }
  731. /// <summary>
  732. /// 6轴的连接电池指令
  733. /// </summary>
  734. /// <param name="myConnectedHandlerID"></param>
  735. /// <returns></returns>
  736. IEnumerator LoopRequestBattery6Axis(string myConnectedHandlerID)
  737. {
  738. while (bluetoothStatus == BluetoothStatusEnum.Connected && myConnectedHandlerID == _connectedHandlerID)
  739. {
  740. yield return new WaitForSecondsRealtime(10);
  741. AddCommandToQueue("B");
  742. }
  743. }
  744. private List<string> _commands = new List<string>();
  745. void LoopHandleCommands()
  746. {
  747. if (bluetoothStatus != BluetoothStatusEnum.Connected || !moduleInited)
  748. {
  749. if (_commands.Count > 0) _commands.Clear();
  750. return;
  751. }
  752. if (Time.realtimeSinceStartup - _lastWriteDataTime < 0.2f) return;
  753. if (_commands.Count == 0) return;
  754. string cmd = _commands[0];
  755. _commands.RemoveAt(0);
  756. WriteData(cmd);
  757. }
  758. bool AddCommandToQueue(string cmd)
  759. {
  760. if (bluetoothStatus != BluetoothStatusEnum.Connected || !moduleInited) return false;
  761. //如果待插入的指令跟队尾的指令一样,就不要插入了,因为冗余的指令无意义
  762. if (_commands.Count > 0 && _commands[_commands.Count - 1].Equals(cmd)) return true;
  763. _commands.Add(cmd);
  764. return true;
  765. }
  766. public void ReplyInfraredShoot()
  767. {
  768. if (bluetoothStatus != BluetoothStatusEnum.Connected || !moduleInited) return;
  769. WriteData("I");
  770. }
  771. public void ReplyByte(byte[] value)
  772. {
  773. if (bluetoothStatus != BluetoothStatusEnum.Connected || !moduleInited) return;
  774. WriteByteData(value);
  775. }
  776. public bool RequestOpen9Axis()
  777. {
  778. return AddCommandToQueue("3");
  779. }
  780. public bool RequestClose9Axis()
  781. {
  782. return AddCommandToQueue("4");
  783. }
  784. public bool RequestOpenInfrared()
  785. {
  786. return AddCommandToQueue("w");
  787. }
  788. public bool RequestCloseInfrared()
  789. {
  790. return AddCommandToQueue("s");
  791. }
  792. void ParseMacAddress(byte[] bytes)
  793. {
  794. string mac = System.Text.Encoding.ASCII.GetString(bytes);
  795. if (mac != null) mac = mac.Trim();
  796. if (CheckIsMacValid(mac))
  797. {
  798. #region 根据aimDeviceInfo存在添加一个判断
  799. if (aimDeviceInfo != null) {
  800. //需要增加一个判断,判断是否对应的mac设备。不是需要进行重新连接
  801. if (!aimDeviceInfo.bInitMac)
  802. {
  803. SmartBowLogger.Log(this, "设置设备mac:" + mac);
  804. SetAimDeviceMac(mac);
  805. }
  806. else if (aimDeviceInfo.mac != mac)
  807. {
  808. SmartBowLogger.LogError(this, "设备不一样,断开连接");
  809. Disconnect();
  810. //抛出一个错误,给用户处理其他业务流程
  811. smartBowHelper.InvokeOnBluetoothError(BluetoothError.MacAddressAndDeviceMismatch, "验证设备MAC和记录的MAC不匹配!");
  812. return;
  813. }
  814. }
  815. #endregion
  816. macAddress = mac;
  817. SmartBowLogger.Log(this, "MacAddress解析成功");
  818. //获取MacAddress对应的校准记录
  819. string macXXX = macAddress;
  820. smartBowHelper.smartBowNetwork.GetCalibrateRecord(macXXX, (record) =>
  821. {
  822. if (macAddress != macXXX) return;
  823. smartBowHelper.aimHandler.ResumeCalibrateRecord(record);
  824. });
  825. }
  826. else SmartBowLogger.LogWarning(this, "MacAddress解析失败");
  827. }
  828. bool CheckIsMacValid(string mac)
  829. {
  830. if (mac == null) return false;
  831. if (!mac.StartsWith("{")) return false;
  832. if (!mac.EndsWith("}")) return false;
  833. if (!mac.Contains(":")) return false;
  834. char[] validChars = { '{', '}', ':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  835. foreach (var c in mac.ToCharArray())
  836. {
  837. if (Array.IndexOf(validChars, c) == -1) return false;
  838. }
  839. if (mac.Length != 19) return false;
  840. string macNoneFrame = mac.Substring(1, mac.Length - 2);
  841. string[] macNoneFrameSplits = macNoneFrame.Split(':');
  842. if (macNoneFrameSplits.Length != 6) return false;
  843. foreach (var item in macNoneFrameSplits)
  844. {
  845. if (item.Length != 2) return false;
  846. foreach (var c in item.ToCharArray())
  847. if (Array.IndexOf(validChars, c) < 3) return false;
  848. }
  849. return true;
  850. }
  851. #region 校验固件版本信息
  852. bool CheckIsVersionValid(string version)
  853. {
  854. if (string.IsNullOrEmpty(version)) return false;
  855. if (!version.StartsWith("{")) return false;
  856. if (!version.EndsWith("}")) return false;
  857. // 去掉 { 和 }
  858. string versionInner = version.Substring(1, version.Length - 2);
  859. // 固件版本格式 Vx.y.z
  860. if (!versionInner.StartsWith("V")) return false;
  861. string[] parts = versionInner.Substring(1).Split('.');
  862. if (parts.Length != 3) return false;
  863. foreach (var part in parts)
  864. {
  865. if (!int.TryParse(part, out _)) return false;
  866. }
  867. return true;
  868. }
  869. void ParseDeviceVersion(byte[] bytes)
  870. {
  871. string version = System.Text.Encoding.ASCII.GetString(bytes);
  872. if (version != null) version = version.Trim();
  873. if (CheckIsVersionValid(version))
  874. {
  875. deviceVersion = version;
  876. SmartBowLogger.Log(this, "DeviceVersion解析成功:" + version);
  877. }
  878. //else
  879. //{
  880. // SmartBowLogger.LogWarning(this, "DeviceVersion解析失败");
  881. //}
  882. }
  883. #endregion
  884. #region 记录的设备处理部分
  885. //连接时候判断信息。存在对象则连接需要判断MAC
  886. //记录一个设备
  887. AimDeviceInfo aimDeviceInfo = null;
  888. //用户初始化一个保存数据的操作的id。
  889. //不设置情况下不给进行相关操作
  890. string keyPrefix = "aim-device-info-";
  891. string userTags = "";
  892. int deviceId = -1;
  893. //清空对应id的数据
  894. public void onClearAimDeviceInfoByDeviceId(string _userTags, int _deviceId)
  895. {
  896. string deviceInfo = PlayerPrefs.GetString(keyPrefix + _userTags + _deviceId, "");
  897. if (deviceInfo != "") {
  898. PlayerPrefs.DeleteKey(keyPrefix + _userTags + _deviceId);
  899. ResetAimDeviceInfoToNull();
  900. }
  901. }
  902. public AimDeviceInfo onGetAimDeviceInfos(string _userTags, int _deviceId)
  903. {
  904. OnGetAimDeviceInfo(_userTags, _deviceId);
  905. //SmartBowLogger.Log(this, $"onGetAimDeviceInfos[{JsonUtility.ToJson(aimDeviceInfo)}]");
  906. return aimDeviceInfo;
  907. }
  908. //根据设置的
  909. void OnGetAimDeviceInfo(string _userTags, int _deviceId)
  910. {
  911. string deviceInfo = PlayerPrefs.GetString(keyPrefix + _userTags + _deviceId, "");
  912. if (deviceInfo != "")
  913. {
  914. aimDeviceInfo = JsonUtility.FromJson<AimDeviceInfo>(deviceInfo);//这里的类是依据最外层{}决定的
  915. }
  916. }
  917. void OnSaveAimDeviceInfo(string _userTags, int _deviceId)
  918. {
  919. if (_userTags.Length == 0)
  920. {
  921. SmartBowLogger.LogError(this, $"存储标识不存在!");
  922. return;
  923. }
  924. SmartBowLogger.Log(this, $"OnSaveAimDeviceInfo [{JsonUtility.ToJson(aimDeviceInfo)}]");
  925. PlayerPrefs.SetString(keyPrefix + _userTags + _deviceId, JsonUtility.ToJson(aimDeviceInfo));
  926. }
  927. //连接蓝牙调用。连接时候创建一个连接信息,用于记录mac 是否要验证
  928. //传入一个id用于区分是哪个设备
  929. //bRecreateDeviceInfo 是否重新创建
  930. public void initAimDeviceInfo(string _userTags, int _deviceId, bool bRecreateDeviceInfo)
  931. {
  932. userTags = _userTags;
  933. deviceId = _deviceId;
  934. if (bRecreateDeviceInfo)
  935. {
  936. //直接覆盖设备信息
  937. onRecreateAimDeviceInfoById();
  938. }
  939. else
  940. {
  941. //获取设备信息。没有创建
  942. onCreateAimDeviceInfoById();
  943. }
  944. }
  945. //是否存在AimDeviceInfo,存在获取,不存在创建;
  946. void onCreateAimDeviceInfoById()
  947. {
  948. ResetAimDeviceInfoToNull();
  949. OnGetAimDeviceInfo(userTags, deviceId);
  950. if (aimDeviceInfo == null)
  951. {
  952. aimDeviceInfo = new AimDeviceInfo(deviceId);
  953. OnSaveAimDeviceInfo(userTags, deviceId);
  954. }
  955. }
  956. //直接覆盖新元素
  957. void onRecreateAimDeviceInfoById()
  958. {
  959. // 使用Lambda表达式删除特定条件的元素
  960. ResetAimDeviceInfoToNull();
  961. //添加一个新元素
  962. aimDeviceInfo = new AimDeviceInfo(deviceId);
  963. //更新信息
  964. OnSaveAimDeviceInfo(userTags, deviceId);
  965. }
  966. //APP连接需进行MAC地址的比对。
  967. void SetAimDeviceMac(string _mac)
  968. {
  969. aimDeviceInfo.setInitMac(_mac);
  970. OnSaveAimDeviceInfo(userTags, deviceId);
  971. }
  972. public void ResetAimDeviceInfoToNull()
  973. {
  974. aimDeviceInfo = null;
  975. }
  976. #endregion
  977. }
  978. #region 添加一个记录设备连接的信息,主要用于判断mac地址是否连接操作
  979. [Serializable]//需要在转换为json格式的类的上方添加序列化
  980. public class AimDeviceInfo
  981. {
  982. public int id; //区分用户设备对应的id
  983. public bool bInitMac = false; //是否初始化Mac
  984. public string mac; //记录当前
  985. public AimDeviceInfo(int _id)
  986. {
  987. this.id = _id;
  988. }
  989. public void setInitMac(string macTemp)
  990. {
  991. bInitMac = true;
  992. mac = macTemp;
  993. }
  994. public void resetInitMac()
  995. {
  996. bInitMac = false;
  997. mac = "";
  998. }
  999. }
  1000. #endregion
  1001. }