BluetoothAim.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. using ArduinoBluetoothAPI;
  2. using System;
  3. using UnityEngine;
  4. using System.Collections.Generic;
  5. using UnityEngine.UI;
  6. using DG.Tweening;
  7. using UnityEngine.SceneManagement;
  8. /* 蓝牙瞄准模块 */
  9. public class BluetoothAim : MonoBehaviour
  10. {
  11. BluetoothHelper bluetoothHelper;
  12. BluetoothHelperCharacteristic characteristicWrite;
  13. BluetoothHelperService bluetoothService;
  14. string targetDeviceName = "Bbow_20210501";
  15. string deviceName = "";
  16. bool canConnect = true;
  17. [SerializeField] Text textUI;
  18. public BluetoothStatusEnum status = BluetoothStatusEnum.Connect;
  19. public bool hasData = false;
  20. public long hasDataTime;
  21. public static bool scanLock = false; //防止同时扫描冲突
  22. public static BluetoothAim ins;
  23. void Start()
  24. {
  25. ins = this;
  26. InitAutoDormancy();
  27. #if UNITY_STANDALONE_WIN
  28. new GameObject("BleUDP").AddComponent<BleUDP>();
  29. #endif
  30. }
  31. void OnDestroy()
  32. {
  33. DisconnectBleHelper();
  34. }
  35. private bool userDoConnect = false;
  36. private bool doConnect = false;
  37. public void DoConnect()
  38. {
  39. if (status == BluetoothStatusEnum.Connect)
  40. {
  41. userDoConnect = true;
  42. doConnect = true;
  43. SetStatus(BluetoothStatusEnum.Connecting);
  44. }
  45. else if (status == BluetoothStatusEnum.ConnectSuccess)
  46. {
  47. userDoConnect = false;
  48. doConnect = false;
  49. OnDisconnect();
  50. #if UNITY_ANDROID
  51. DisconnectBleHelper();
  52. #elif UNITY_STANDALONE_WIN
  53. BleUDP.ins.Disconnect();
  54. #endif
  55. }
  56. }
  57. void OnDisconnect()
  58. {
  59. hasData = false;
  60. canConnect = true;
  61. SetStatus(BluetoothStatusEnum.ConnectFail);
  62. BowCamera.isTouchMode = true;
  63. DestroyWhenDisconenct();
  64. }
  65. void Update()
  66. {
  67. if (userDoConnect && status == BluetoothStatusEnum.Connect)
  68. {
  69. DoConnect();
  70. }
  71. if (doConnect) Connect();
  72. }
  73. void SetStatus(BluetoothStatusEnum statusValue)
  74. {
  75. status = statusValue;
  76. if (status == BluetoothStatusEnum.ConnectFail)
  77. {
  78. Sequence sequence = DOTween.Sequence();
  79. sequence.AppendInterval(2f);
  80. sequence.AppendCallback(delegate ()
  81. {
  82. if (status == BluetoothStatusEnum.ConnectFail)
  83. {
  84. status = BluetoothStatusEnum.Connect;
  85. }
  86. });
  87. sequence.SetUpdate(true);
  88. DeviceReconnectView.Show();
  89. }
  90. }
  91. void DisconnectBleHelper()
  92. {
  93. if (bluetoothHelper != null) bluetoothHelper.Disconnect();
  94. }
  95. void Connect()
  96. {
  97. if (BluetoothShoot.scanLock)
  98. {
  99. return;
  100. }
  101. if (!canConnect)
  102. {
  103. return;
  104. }
  105. doConnect = false;
  106. scanLock = true;
  107. canConnect = false;
  108. SetStatus(BluetoothStatusEnum.Connecting);
  109. #if UNITY_ANDROID
  110. ConnectBleHelper();
  111. #elif UNITY_STANDALONE_WIN
  112. ConnectBleByUDP();
  113. #endif
  114. }
  115. void ConnectBleHelper()
  116. {
  117. try
  118. {
  119. BluetoothHelper.BLE = true;
  120. bluetoothHelper = BluetoothHelper.GetNewInstance();
  121. bluetoothHelper.OnConnected += (BluetoothHelper helper) =>
  122. {
  123. Log("连接成功\n" + helper.getDeviceName());
  124. SetStatus(BluetoothStatusEnum.ConnectSuccess);
  125. BowCamera.isTouchMode = false;
  126. foreach (BluetoothHelperService service in helper.getGattServices())
  127. {
  128. if (service.getName().ToLower().StartsWith("0000fff0"))
  129. {
  130. bluetoothService = service;
  131. foreach (BluetoothHelperCharacteristic characteristic in service.getCharacteristics())
  132. {
  133. if (characteristic.getName().ToLower().StartsWith("0000fff2"))
  134. {
  135. characteristicWrite = characteristic;
  136. }
  137. else if (characteristic.getName().ToLower().StartsWith("0000fff1"))
  138. {
  139. BluetoothHelperCharacteristic ch = new BluetoothHelperCharacteristic(characteristic.getName());
  140. ch.setService(bluetoothService.getName());
  141. bluetoothHelper.Subscribe(ch);
  142. }
  143. }
  144. }
  145. }
  146. // CallDelay(1, OpenInfrared);
  147. // CallDelay(2, OpenReceiveData);
  148. // CallDelay(3, RequestBattery);
  149. CallDelay(2, () =>
  150. {
  151. if (status != BluetoothStatusEnum.ConnectSuccess) return;
  152. InitWhenConenct();
  153. });
  154. };
  155. bluetoothHelper.OnConnectionFailed += (BluetoothHelper helper) =>
  156. {
  157. Log("连接失败\n" + helper.getDeviceName());
  158. OnDisconnect();
  159. };
  160. bluetoothHelper.OnCharacteristicChanged += (helper, value, characteristic) =>
  161. {
  162. if (!hasData) hasDataTime = JC.CS.Utility.GetTimestamp();
  163. hasData = true;
  164. byte[] bytes = value;
  165. // Log(String.Join(",", bytes));
  166. BluetoothClient.UploadData(0, bytes);
  167. if (AimHandler.ins)
  168. {
  169. AimHandler.ins.OnDataReceived(bytes);
  170. }
  171. };
  172. int scanCount = 0;
  173. bluetoothHelper.OnScanEnded += (BluetoothHelper helper, LinkedList<BluetoothDevice> nearbyDevices) =>
  174. {
  175. scanLock = false;
  176. foreach (BluetoothDevice device in nearbyDevices)
  177. {
  178. if (device.DeviceName == targetDeviceName)
  179. {
  180. deviceName = device.DeviceName;
  181. bluetoothHelper.setDeviceName(deviceName);
  182. bluetoothHelper.Connect();
  183. Log("发现设备\n" + device.DeviceName);
  184. return;
  185. }
  186. }
  187. if (scanCount < 3)
  188. { //如果没扫描到,则重新扫描,达到延迟提示失败的效果
  189. scanCount++;
  190. scanLock = true;
  191. bluetoothHelper.ScanNearbyDevices();
  192. }
  193. else
  194. {
  195. canConnect = true;
  196. Log("没有发现设备");
  197. SetStatus(BluetoothStatusEnum.ConnectFail);
  198. }
  199. };
  200. bluetoothHelper.ScanNearbyDevices();
  201. Log("正在扫描设备");
  202. }
  203. catch (Exception e)
  204. {
  205. Debug.LogError(e.Message);
  206. Debug.LogError(e.StackTrace);
  207. scanLock = false;
  208. canConnect = true;
  209. SetStatus(BluetoothStatusEnum.ConnectFail);
  210. Log("请打开蓝牙");
  211. }
  212. }
  213. void ConnectBleByUDP()
  214. {
  215. try
  216. {
  217. BleUDP.ins.OnConnected = () =>
  218. {
  219. Log("连接成功\n" + deviceName);
  220. SetStatus(BluetoothStatusEnum.ConnectSuccess);
  221. BowCamera.isTouchMode = false;
  222. InitWhenConenct();
  223. };
  224. BleUDP.ins.OnConnectionFailed = () =>
  225. {
  226. Log("连接失败\n" + deviceName);
  227. OnDisconnect();
  228. };
  229. BleUDP.ins.OnCharacteristicChanged = (byte[] value) =>
  230. {
  231. if (!hasData) hasDataTime = JC.CS.Utility.GetTimestamp();
  232. hasData = true;
  233. byte[] bytes = value;
  234. // Log(String.Join(",", bytes));
  235. BluetoothClient.UploadData(0, bytes);
  236. if (AimHandler.ins)
  237. {
  238. AimHandler.ins.OnDataReceived(bytes);
  239. }
  240. };
  241. BleUDP.ins.OnScanEnded = () =>
  242. {
  243. scanLock = false;
  244. deviceName = targetDeviceName;
  245. BleUDP.ins.Connect();
  246. Log("发现设备\n" + deviceName);
  247. };
  248. BleUDP.ins.ScanNearbyDevices();
  249. }
  250. catch (Exception e)
  251. {
  252. Debug.LogError(e.Message);
  253. Debug.LogError(e.StackTrace);
  254. scanLock = false;
  255. canConnect = true;
  256. SetStatus(BluetoothStatusEnum.ConnectFail);
  257. }
  258. }
  259. #region 自动进入/退出休眠状态, 这里做程指令发送队列,为了控制连续发送指令的间隔,避免硬件收不到或处理不过来
  260. class CmdToSend
  261. {
  262. public string[] cmds;
  263. public Action onComplete;
  264. public Func<bool> canDo;
  265. public CmdToSend(string[] cmds, Action onComplete, Func<bool> canDo)
  266. {
  267. this.cmds = cmds;
  268. this.onComplete = onComplete;
  269. this.canDo = canDo;
  270. }
  271. }
  272. Queue<CmdToSend> cmdWaitingList = new Queue<CmdToSend>();
  273. bool isSendCmdLocked = false;
  274. bool canAutoDormancy = false;
  275. bool isStartUp = false;
  276. JC.CS.CountLocker needModularAwake = new JC.CS.CountLocker();
  277. void CheckAndStartUp()
  278. {
  279. if (needModularAwake.IsLocked())
  280. {
  281. StartUp();
  282. }
  283. else
  284. {
  285. Dormancy();
  286. }
  287. }
  288. void InitAutoDormancy()
  289. {
  290. // GlobalEventCenter.ins.onGameSceneLoad += () => {
  291. // needModularAwake.Lock();
  292. // CheckAndStartUp();
  293. // };
  294. // GlobalEventCenter.ins.onGameSceneDestroy += () => {
  295. // needModularAwake.Unlock();
  296. // CheckAndStartUp();
  297. // };
  298. // GlobalEventCenter.ins.onSimulateMouseAwakeChanged += (waked) => {
  299. // if (waked) needModularAwake.Lock();
  300. // else needModularAwake.Unlock();;
  301. // CheckAndStartUp();
  302. // };
  303. // GlobalEventCenter.ins.onDeviceCalibrateViewAwakeChanged += (waked) => {
  304. // if (waked) needModularAwake.Lock();
  305. // else needModularAwake.Unlock();;
  306. // CheckAndStartUp();
  307. // };
  308. //暂时关闭自动休眠,默认是需要模块保持激活
  309. needModularAwake.Lock();
  310. }
  311. void InitWhenConenct()
  312. {
  313. canAutoDormancy = true;
  314. List<string> cmds = new List<string>();
  315. cmds.Add("b"); //确保开启stm32
  316. cmds.Add("b"); //获取初始电量
  317. cmds.Add("1"); //开启发送逻辑
  318. Action onComplete = null;
  319. if (needModularAwake.IsLocked())
  320. {
  321. cmds.Add("w"); //红外灯开启
  322. cmds.Add("3"); //九轴开启
  323. onComplete = () =>
  324. {
  325. isStartUp = true;
  326. };
  327. }
  328. else
  329. {
  330. cmds.Add("s"); //红外灯关闭
  331. cmds.Add("S"); //Stm32关闭
  332. cmds.Add("4"); //九轴关闭
  333. onComplete = () =>
  334. {
  335. isStartUp = false;
  336. };
  337. }
  338. SendCDM(null, onComplete, cmds.ToArray());
  339. }
  340. void DestroyWhenDisconenct()
  341. {
  342. canAutoDormancy = false;
  343. sendCMD_CheckAndDoStop(null);
  344. }
  345. //启动
  346. void StartUp()
  347. {
  348. SendCDM(() =>
  349. {
  350. return !isStartUp;
  351. }, () =>
  352. {
  353. isStartUp = true;
  354. }, "b", "w", "3");
  355. }
  356. //休眠
  357. void Dormancy()
  358. {
  359. SendCDM(() =>
  360. {
  361. return isStartUp;
  362. }, () =>
  363. {
  364. isStartUp = false;
  365. }, "4", "s", "S");
  366. }
  367. void SendCDM(Func<bool> canDo, Action onComplete, params string[] cmds)
  368. {
  369. CmdToSend cmdToSend = new CmdToSend(cmds, onComplete, canDo);
  370. if (isSendCmdLocked)
  371. {
  372. cmdWaitingList.Enqueue(cmdToSend);
  373. return;
  374. }
  375. sendCMD_NotCheck(cmdToSend);
  376. }
  377. void sendCMD_NotCheck(CmdToSend cmdToSend)
  378. {
  379. if (cmdToSend.canDo != null && !cmdToSend.canDo.Invoke())
  380. {
  381. sendCMD_CheckNext();
  382. return;
  383. }
  384. isSendCmdLocked = true;
  385. Sequence sequence = DOTween.Sequence();
  386. sequence.PrependInterval(0.3f);
  387. foreach (var cmd in cmdToSend.cmds)
  388. {
  389. sequence.AppendCallback(() =>
  390. {
  391. bool stopped = sendCMD_CheckAndDoStop(sequence);
  392. if (!stopped) WriteData(cmd);
  393. });
  394. sequence.AppendInterval(0.5f);
  395. }
  396. sequence.AppendCallback(() =>
  397. {
  398. bool stopped = sendCMD_CheckAndDoStop(sequence);
  399. if (!stopped)
  400. {
  401. isSendCmdLocked = false;
  402. cmdToSend.onComplete?.Invoke();
  403. sendCMD_CheckNext();
  404. }
  405. });
  406. sequence.SetUpdate(true);
  407. }
  408. void sendCMD_CheckNext()
  409. {
  410. if (cmdWaitingList.Count <= 0) return;
  411. CmdToSend cmdToSend = cmdWaitingList.Dequeue();
  412. sendCMD_NotCheck(cmdToSend);
  413. }
  414. bool sendCMD_CheckAndDoStop(Sequence sequence)
  415. {
  416. if (canAutoDormancy) return false;
  417. isStartUp = false;
  418. isSendCmdLocked = false;
  419. cmdWaitingList.Clear();
  420. if (sequence != null) sequence.Kill();
  421. return true;
  422. }
  423. #endregion
  424. public void RequestBattery()
  425. {
  426. if (!isStartUp) return;
  427. if (isSendCmdLocked) return;
  428. WriteData("b");
  429. }
  430. public void ReplyInfraredShoot()
  431. {
  432. if (isSendCmdLocked) return;
  433. WriteData("I");
  434. }
  435. void CallDelay(float delayTime, TweenCallback callback)
  436. {
  437. Sequence sequence = DOTween.Sequence();
  438. sequence.PrependInterval(delayTime).AppendCallback(callback);
  439. sequence.SetUpdate(true);
  440. }
  441. public void WriteData(string data)
  442. {
  443. #if UNITY_ANDROID
  444. if (DebugDeviceCMD.ins) DebugDeviceCMD.ins.ShowCMD(data);
  445. BluetoothHelperCharacteristic ch = new BluetoothHelperCharacteristic(characteristicWrite.getName());
  446. ch.setService(bluetoothService.getName());
  447. bluetoothHelper.WriteCharacteristic(ch, data);
  448. #elif UNITY_STANDALONE_WIN
  449. BleUDP.ins.SendMsg(data);
  450. #endif
  451. }
  452. void Log(string text)
  453. {
  454. if (textUI)
  455. {
  456. textUI.text = text;
  457. }
  458. }
  459. }