using ArduinoBluetoothAPI; using System; using UnityEngine; using System.Collections.Generic; using UnityEngine.UI; using DG.Tweening; /* 蓝牙瞄准模块 */ public class BluetoothAim : MonoBehaviour { SBLogger logger = new SBLogger(typeof(BluetoothAim).Name); readonly string targetDeviceName = "Bbow_20210501"; string targetDeviceService { get { if (CommonConfig.devicePlan == 0 || CommonConfig.devicePlan == 3) { #if UNITY_ANDROID return "0000fff0"; #else return "fff0"; #endif } return "6e400001"; } } string targetDeviceCharacteristicWrite { get { if (CommonConfig.devicePlan == 0 || CommonConfig.devicePlan == 3) { #if UNITY_ANDROID return "0000fff2"; #else return "fff2"; #endif } return "6e400002"; } } string targetDeviceCharacteristicNotify { get { if (CommonConfig.devicePlan == 0 || CommonConfig.devicePlan == 3) { #if UNITY_ANDROID return "0000fff1"; #else return "fff1"; #endif } return "6e400003"; } } BluetoothHelper bluetoothHelper; BluetoothHelperCharacteristic characteristicWrite; BluetoothHelperService bluetoothService; string deviceName = ""; bool canConnect = true; [SerializeField] Text textUI; public BluetoothStatusEnum status = BluetoothStatusEnum.Connect; public bool hasData = false; public long hasDataTime; public static bool scanLock = false; //防止同时扫描冲突 public static BluetoothAim ins; void Start() { ins = this; InitAutoDormancy(); #if UNITY_STANDALONE_WIN || UNITY_EDITOR new GameObject("BleUDP").AddComponent(); #endif } void OnDestroy() { DisconnectBleHelper(); } private bool userDoConnect = false; private bool doConnect = false; public Func action_DoConnectInterceptor; public void DoConnect() { if (action_DoConnectInterceptor != null) { if (action_DoConnectInterceptor.Invoke()) return; } if (status == BluetoothStatusEnum.Connect) { userDoConnect = true; doConnect = true; SetStatus(BluetoothStatusEnum.Connecting); } else if (status == BluetoothStatusEnum.ConnectSuccess) { userDoConnect = false; doConnect = false; OnDisconnect(); #if UNITY_STANDALONE_WIN || UNITY_EDITOR BleUDP.ins.Disconnect(); #else DisconnectBleHelper(); #endif } } void OnDisconnect() { hasData = false; canConnect = true; SetStatus(BluetoothStatusEnum.ConnectFail); BowCamera.isTouchMode = true; DestroyWhenDisconenct(); if (AimHandler.ins) AimHandler.ins.msOld = default; } void Update() { if (userDoConnect && status == BluetoothStatusEnum.Connect) { DoConnect(); } if (doConnect) Connect(); } void SetStatus(BluetoothStatusEnum statusValue) { status = statusValue; if (status == BluetoothStatusEnum.ConnectFail) { Sequence sequence = DOTween.Sequence(); sequence.AppendInterval(2f); sequence.AppendCallback(delegate () { if (status == BluetoothStatusEnum.ConnectFail) { status = BluetoothStatusEnum.Connect; } }); sequence.SetUpdate(true); DeviceReconnectView.Show(); SimulateMouseController.ins?.SetBleConnected(false); } else if (status == BluetoothStatusEnum.ConnectSuccess) { SimulateMouseController.ins?.SetBleConnected(true); } } void DisconnectBleHelper() { if (bluetoothHelper != null) bluetoothHelper.Disconnect(); } void Connect() { if (BluetoothShoot.scanLock) { return; } if (!canConnect) { return; } doConnect = false; scanLock = true; canConnect = false; SetStatus(BluetoothStatusEnum.Connecting); #if UNITY_STANDALONE_WIN || UNITY_EDITOR ConnectBleByUDP(); #else ConnectBleHelper(); #endif } void ConnectBleHelper() { try { BluetoothHelper.BLE = true; bluetoothHelper = BluetoothHelper.GetNewInstance(); bluetoothHelper.OnConnected += (BluetoothHelper helper) => { Log("连接成功\n" + helper.getDeviceName()); SetStatus(BluetoothStatusEnum.ConnectSuccess); BowCamera.isTouchMode = false; logger.Log("连接成功"); foreach (BluetoothHelperService service in helper.getGattServices()) { logger.Log("待检测的服务名:" + service.getName()); if (service.getName().ToLower().StartsWith(targetDeviceService)) { bluetoothService = service; logger.Warn("匹配的服务名:" + service.getName()); foreach (BluetoothHelperCharacteristic characteristic in service.getCharacteristics()) { logger.Log("待检测的特征:" + characteristic.getName()); if (characteristic.getName().ToLower().StartsWith(targetDeviceCharacteristicWrite)) { characteristicWrite = characteristic; logger.Warn("匹配的写入特征:" + characteristic.getName()); } else if (characteristic.getName().ToLower().StartsWith(targetDeviceCharacteristicNotify)) { logger.Warn("匹配的订阅特征(准备):" + characteristic.getName()); BluetoothHelperCharacteristic ch = new BluetoothHelperCharacteristic(characteristic.getName()); ch.setService(bluetoothService.getName()); bluetoothHelper.Subscribe(ch); logger.Warn("匹配的订阅特征(完成):" + characteristic.getName()); } } } } logger.Log("服务匹配结束"); // CallDelay(1, OpenInfrared); // CallDelay(2, OpenReceiveData); // CallDelay(3, RequestBattery); CallDelay(2, () => { if (status != BluetoothStatusEnum.ConnectSuccess) return; logger.Log("初始化自动发送CMD"); InitWhenConenct(); }); }; bluetoothHelper.OnConnectionFailed += (BluetoothHelper helper) => { Log("连接失败\n" + helper.getDeviceName()); OnDisconnect(); }; bluetoothHelper.OnCharacteristicChanged += (helper, value, characteristic) => { // logger.Log(String.Join(",", value)); if (!hasData) { hasDataTime = JCUnityLib.TimeUtils.GetTimestamp(); UploadMacAddress(value); } hasData = true; byte[] bytes = value; // Log(String.Join(",", bytes)); BluetoothClient.UploadData(0, bytes); if (AimHandler.ins) { AimHandler.ins.OnDataReceived(bytes); } }; int scanCount = 0; bluetoothHelper.OnScanEnded += (BluetoothHelper helper, LinkedList nearbyDevices) => { scanLock = false; foreach (BluetoothDevice device in nearbyDevices) { if (device.DeviceName == targetDeviceName) { deviceName = device.DeviceName; bluetoothHelper.setDeviceName(deviceName); bluetoothHelper.Connect(); Log("发现设备\n" + device.DeviceName); return; } } if (scanCount < 3) { //如果没扫描到,则重新扫描,达到延迟提示失败的效果 scanCount++; scanLock = true; bluetoothHelper.ScanNearbyDevices(); } else { canConnect = true; Log("没有发现设备"); SetStatus(BluetoothStatusEnum.ConnectFail); } }; bluetoothHelper.ScanNearbyDevices(); Log("正在扫描设备"); } catch (Exception e) { Debug.LogError(e.Message); Debug.LogError(e.StackTrace); scanLock = false; canConnect = true; // SetStatus(BluetoothStatusEnum.ConnectFail); status = BluetoothStatusEnum.Connect; userDoConnect = false; PopupMgr.ins.ShowTip(TextAutoLanguage2.GetTextByKey("ble-please-open-ble")); } } void ConnectBleByUDP() { try { BleUDP.ins.OnConnected = () => { Log("连接成功\n" + deviceName); SetStatus(BluetoothStatusEnum.ConnectSuccess); BowCamera.isTouchMode = false; InitWhenConenct(); }; BleUDP.ins.OnConnectionFailed = () => { Log("连接失败\n" + deviceName); OnDisconnect(); }; BleUDP.ins.OnCharacteristicChanged = (byte[] value) => { if (!hasData) { hasDataTime = JCUnityLib.TimeUtils.GetTimestamp(); UploadMacAddress(value); } hasData = true; byte[] bytes = value; // Log(String.Join(",", bytes)); BluetoothClient.UploadData(0, bytes); if (AimHandler.ins) { AimHandler.ins.OnDataReceived(bytes); } }; BleUDP.ins.OnScanEnded = () => { scanLock = false; deviceName = targetDeviceName; BleUDP.ins.Connect(); Log("发现设备\n" + deviceName); }; BleUDP.ins.ScanNearbyDevices(); } catch (Exception e) { Debug.LogError(e.Message); Debug.LogError(e.StackTrace); scanLock = false; canConnect = true; SetStatus(BluetoothStatusEnum.ConnectFail); } } #region 自动进入/退出休眠状态, 这里做程指令发送队列,为了控制连续发送指令的间隔,避免硬件收不到或处理不过来 class CmdToSend { public string[] cmds; public Action onComplete; public Func canDo; public CmdToSend(string[] cmds, Action onComplete, Func canDo) { this.cmds = cmds; this.onComplete = onComplete; this.canDo = canDo; } } Queue cmdWaitingList = new Queue(); bool isSendCmdLocked = false; bool canAutoDormancy = false; bool isStartUp = false; JCUnityLib.CountLock needModularAwake = new JCUnityLib.CountLock(); void CheckAndStartUp() { if (needModularAwake.IsLocked()) { StartUp(); } else { Dormancy(); } } void InitAutoDormancy() { // GlobalEventCenter.ins.onGameSceneLoad += () => { // needModularAwake.Lock(); // CheckAndStartUp(); // }; // GlobalEventCenter.ins.onGameSceneDestroy += () => { // needModularAwake.Unlock(); // CheckAndStartUp(); // }; // GlobalEventCenter.ins.onSimulateMouseAwakeChanged += (waked) => { // if (waked) needModularAwake.Lock(); // else needModularAwake.Unlock();; // CheckAndStartUp(); // }; // GlobalEventCenter.ins.onDeviceCalibrateViewAwakeChanged += (waked) => { // if (waked) needModularAwake.Lock(); // else needModularAwake.Unlock();; // CheckAndStartUp(); // }; //暂时关闭自动休眠,默认是需要模块保持激活 needModularAwake.Lock(); } void InitWhenConenct() { canAutoDormancy = true; List cmds = new List(); cmds.Add("M"); //获取Mac地址 cmds.Add("b"); //确保开启stm32 cmds.Add("b"); //获取初始电量 cmds.Add("1"); //开启发送逻辑 Action onComplete = null; if (needModularAwake.IsLocked()) { cmds.Add("w"); //红外灯开启 cmds.Add("3"); //九轴开启 onComplete = () => { isStartUp = true; }; } else { cmds.Add("s"); //红外灯关闭 cmds.Add("S"); //Stm32关闭 cmds.Add("4"); //九轴关闭 onComplete = () => { isStartUp = false; }; } SendCDM(null, onComplete, cmds.ToArray()); } void DestroyWhenDisconenct() { canAutoDormancy = false; sendCMD_CheckAndDoStop(null); } //启动 void StartUp() { SendCDM(() => { return !isStartUp; }, () => { isStartUp = true; }, "b", "w", "3"); } //休眠 void Dormancy() { SendCDM(() => { return isStartUp; }, () => { isStartUp = false; }, "4", "s", "S"); } void SendCDM(Func canDo, Action onComplete, params string[] cmds) { CmdToSend cmdToSend = new CmdToSend(cmds, onComplete, canDo); if (isSendCmdLocked) { cmdWaitingList.Enqueue(cmdToSend); return; } sendCMD_NotCheck(cmdToSend); } void sendCMD_NotCheck(CmdToSend cmdToSend) { if (cmdToSend.canDo != null && !cmdToSend.canDo.Invoke()) { sendCMD_CheckNext(); return; } isSendCmdLocked = true; Sequence sequence = DOTween.Sequence(); sequence.PrependInterval(0.3f); foreach (var cmd in cmdToSend.cmds) { sequence.AppendCallback(() => { bool stopped = sendCMD_CheckAndDoStop(sequence); if (!stopped) WriteData(cmd); }); sequence.AppendInterval(0.5f); } sequence.AppendCallback(() => { bool stopped = sendCMD_CheckAndDoStop(sequence); if (!stopped) { isSendCmdLocked = false; cmdToSend.onComplete?.Invoke(); sendCMD_CheckNext(); } }); sequence.SetUpdate(true); } void sendCMD_CheckNext() { if (cmdWaitingList.Count <= 0) return; CmdToSend cmdToSend = cmdWaitingList.Dequeue(); sendCMD_NotCheck(cmdToSend); } bool sendCMD_CheckAndDoStop(Sequence sequence) { if (canAutoDormancy) return false; isStartUp = false; isSendCmdLocked = false; cmdWaitingList.Clear(); if (sequence != null) sequence.Kill(); return true; } #endregion public void RequestBattery() { if (!isStartUp) return; if (isSendCmdLocked) return; WriteData("b"); } public void ReplyInfraredShoot() { if (isSendCmdLocked) return; WriteData("I"); } void CallDelay(float delayTime, TweenCallback callback) { Sequence sequence = DOTween.Sequence(); sequence.PrependInterval(delayTime).AppendCallback(callback); sequence.SetUpdate(true); } public void WriteData(string data) { logger.Log("发送CMD(准备):" + data); #if UNITY_STANDALONE_WIN || UNITY_EDITOR BleUDP.ins.SendMsg(data); #else if (DebugDeviceCMD.ins) DebugDeviceCMD.ins.ShowCMD(data); BluetoothHelperCharacteristic ch = new BluetoothHelperCharacteristic(characteristicWrite.getName()); ch.setService(bluetoothService.getName()); bluetoothHelper.WriteCharacteristic(ch, data); #endif logger.Log("发送CMD(完成):" + data); } void Log(string text) { if (textUI) { textUI.text = text; } } void UploadMacAddress(byte[] bytes) { string mac = System.Text.Encoding.ASCII.GetString(bytes); if (mac != null) mac = mac.Trim(); if (CheckIsMacValid(mac)) { LoginMgr.myUserInfo.mac = mac; SaveMac(mac); } } bool CheckIsMacValid(string mac) { if (mac == null) return false; if (!mac.StartsWith("{")) return false; if (!mac.EndsWith("}")) return false; if (!mac.Contains(":")) return false; char[] validChars = {'{','}',':','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; foreach (var c in mac.ToCharArray()) { if (Array.IndexOf(validChars, c) == -1) return false; } return true; } void SaveMac(string mac) { Action cb = (Newtonsoft.Json.Linq.JToken o) => { string gyrStr = o.Value("gyr"); string magStr = o.Value("mag"); AimHandler.ins.InitGyr(gyrStr); AimHandler.ins.InitMag(magStr); }; UserPlayer.ins.call("userComp.saveMac", new object[]{mac}, cb); } // #region 测试mac获取服务端校准记录 // void Awake() // { // StartCoroutine(qqq()); // } // System.Collections.IEnumerator qqq() { // while (LoginMgr.myUserInfo.id == 0) { // yield return null; // } // SaveMac("123"); // StartCoroutine(AimHandler.ins.SaveGyr()); // StartCoroutine(AimHandler.ins.SaveMag()); // } // #endregion }