using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ANT_Managed_Library; using UnityEngine; namespace Assets.Scripts.Devices.Ant { public class AntConnector : IDisposable { private readonly byte[] ANTPLUS_NETWORK_KEY = new byte[] { //Insert the ANT+ network key here: 0xB9, 0xA5, 0x21, 0xFB, 0xBD, 0x72, 0xC3, 0x45 //Distribution of source code containing the ANT+ Network Key is prohibited. //You may not add the ANT+ Network Key to this source code and republish it. //The ANT+ Network Key is available to ANT+ Adopters. //Please refer to http://thisisant.com to become an ANT+ Adopter and access the key. }; private readonly static object _lock = new object(); private static ANT_Device _antDevice = null; private ANT_Channel searchChannel; private readonly List _channels = new List(); private int ChannelCount { get { //if (_antDevice == null) return 0; //return _antDevice.getNumChannels(); return _channels.Count; } } private bool IsBackgroundScanning { get; set; } //int searchingDeviceIndex = -1; /// /// 可以搜索的设备 /// private List deviceList = new List(); public bool IsAvailable = false; /// /// 设备号,频道编号 /// public readonly List usedChannels = new List(); /// /// 搜索到的设备 /// public readonly List discoveredDevices = new List(); private Action _action; private static AntConnector _antConnector; private Action _log; /// /// /// /// 探索到新的设备 /// public static AntConnector Instance(Action action = null, Action log = null) { if (_antConnector == null) { _antConnector = new AntConnector(); _antConnector._action = action; _antConnector._log = log; } return _antConnector; } //private DeviceService deviceService = new DeviceService(); private AntConnector() { if (_antDevice == null) { //ANT_Common.enableDebugLogs(); //findUsableAntDevice(); } //CheckStatus(); deviceList.Add(new FitDevice("")); deviceList.Add(new PowerDevice("")); deviceList.Add(new CadenceDevice("")); deviceList.Add(new HeartRateDevice("")); deviceList.Add(new BikeSpdCadDevice("")); deviceList.Add(new SpeedDevice("")); var timer = new System.Timers.Timer(1000); timer.AutoReset = true; timer.Elapsed += Timer_Elapsed; timer.Enabled = true; } private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { CheckStatus(); } public void CheckStatus() { if (_antDevice != null) return; //discoveredDevices.Add(new VirtualPowerDevice()); this.CreateAntDevice(); } private void CreateAntDevice() { try { if ((int)ANT_Common.getNumDetectedUSBDevices() == 0) return; _antDevice = new ANT_Device(); if (_antDevice.getNumChannels() <= 4) { _antDevice = null; return; } if (!_antDevice.setNetworkKey(0, ANTPLUS_NETWORK_KEY, 500)) throw new ApplicationException("Failed to set network key"); IsAvailable = true; //_antDevice.configureAdvancedBurstSplitting(true); _antDevice.deviceResponse += DeviceResponse; _antDevice.serialError += new ANT_Device.dSerialErrorHandler(antDevice_serialError); for (int i = 1; i < _antDevice.getNumChannels(); i++) { var channel = _antDevice.getChannel(i); _channels.Add(channel); channel.channelResponse += new dChannelResponseHandler(DeviceResponse); } if (IsAvailable) { usedChannels.Add(new UserdChannel { DeviceNumber = "scan", DeviceTypeId = "", Index = 0 }); StartNextSearch(); } } catch (Exception ex) { //ANT_Device.shutdownDeviceInstance(ref _antDevice); //Don't leave here with an invalid device ref //throw new Exception("Could not connect to valid USB2: " + ex.Message); //forward the exception //Log.ErrorLog(ex); //Debug.LogError(ex); //throw ex; Log(ex.Message); } finally { } } private void Log(string str) { //System.IO.File.AppendAllText(@"D:\work\PowerFun\PowerFun-Unity\Build\log.txt", str+"\r\n"); //Debug.LogError(str); } private void antDevice_serialError(ANT_Device sender, ANT_Device.serialErrorCode error, bool isCritical) { //throw new NotImplementedException(); Log("出错了"+ error.ToString()); if (error != ANT_Device.serialErrorCode.DeviceConnectionLost) return; if (_antDevice != null) { _antDevice.deviceResponse -= DeviceResponse; _antDevice.serialError -= antDevice_serialError; } _antDevice = null; IsAvailable = false; foreach (var item in _channels) { item.channelResponse -= DeviceResponse; } searchChannel.channelResponse -= antChannel_channelResponse_FeSearch; searchChannel = null; foreach (var item in discoveredDevices) { item.State = DeviceState.Disconnected; } discoveredDevices.Clear(); usedChannels.Clear(); IsBackgroundScanning = false; } void StartNextSearch() { //Console.WriteLine("startNextSearch"); if (searchChannel != null) { //searchChannel.Dispose(); } //Get new search channel if neccesary if (searchChannel == null) { //if (usedChannels.Count >= ChannelCount) // return; //no free channels //Find the first free channel and start the search //for (int i = 0; i < ChannelCount; ++i) //{ // if (!usedChannels.Values.Contains((byte)i)) // { // searchChannel = _antDevice.getChannel(i); // searchChannel.channelResponse += new dChannelResponseHandler(antChannel_channelResponse_FeSearch); // break; // } //} searchChannel = _antDevice.getChannel(0); // _antDevice.getChannel(usedChannels["scan"]); searchChannel.channelResponse += new dChannelResponseHandler(antChannel_channelResponse_FeSearch); } IsBackgroundScanning = true; ReSearch(); } void DeviceResponse(ANT_Response response) { //Debug.Log(response.responseID); var channelIndex = response.messageContents[0]; var antMessageId = (ANT_ReferenceLibrary.ANTMessageID)response.responseID; if (antMessageId == ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.RESPONSE_EVENT_0x40) { //Trace.WriteLine(string.Join(",", response.messageContents)); var antEventId = (ANT_ReferenceLibrary.ANTEventID)response.messageContents[2]; //Trace.WriteLine(antEventId.ToString() +", " + string.Join(",",response.messageContents)); if (antEventId == ANT_ReferenceLibrary.ANTEventID.EVENT_CHANNEL_CLOSED_0x07) { } //设备断开连接 else if (antEventId == ANT_ReferenceLibrary.ANTEventID.EVENT_RX_FAIL_GO_TO_SEARCH_0x08) { _log?.Invoke(antEventId.ToString() + ", " + string.Join(",", response.messageContents)); var cc1 = usedChannels.Where(d => d.Index == channelIndex).FirstOrDefault(); var dd1 = discoveredDevices.SingleOrDefault(d => d.searchProfile.deviceNumber.ToString() == cc1.DeviceNumber && d.searchProfile.deviceType.ToString() == cc1.DeviceTypeId); if (dd1 != null) { dd1.Disconnect(false); return; } } } else { //Trace.WriteLine(antMessageId + ", " + string.Join(",", response.messageContents));//EVENT_RX_FAIL_GO_TO_SEARCH_0x08 } //if (antMessageId == ANT_ReferenceLibrary.ANTMessageID.BURST_DATA_0x50) if (response.messageContents.Length == 3) return; var str = string.Join(",", response.messageContents); var cc = usedChannels.Where(d => d.Index == channelIndex).FirstOrDefault(); var dd = discoveredDevices.SingleOrDefault(d => d.searchProfile.deviceNumber.ToString() == cc.DeviceNumber && d.searchProfile.deviceType.ToString() == cc.DeviceTypeId); if (dd != null) { if (dd.State == DeviceState.Connecting) { dd.State = DeviceState.Connected; //IsBackgroundScanning = true; //ReSearch(); //StartNextSearch(); return; } dd.handleChannelResponse(response); } } public bool SendMessage(byte msgID, byte[] msgData) { return _antDevice.writeRawMessageToDevice(msgID, msgData); } public bool SendMessage(ANTMessage data) { return SendMessage(data.Id, data.Data); } void antChannel_channelResponse_FeSearch(ANT_Response response) { if (IsBackgroundScanning == false) { searchChannel.closeChannel(); return; } if (response.messageContents.Length == 3) return; var message = response.messageContents; if (response.messageContents.Length >= 14) { message = response.messageContents.Skip(10).Take(4).ToArray(); var deviceNumber = message[0] + (message[1] << 8); var deviceTypeId = message[2] & 127; var transmissionTypeId = message[3]; //Console.WriteLine($" {deviceNumber}, {deviceTypeId}"); var device2 = discoveredDevices.FirstOrDefault(d => d.searchProfile.deviceNumber == deviceNumber && d.searchProfile.deviceType == deviceTypeId); if (device2 != null) { //ReSearch(); _action?.Invoke(device2); if (device2.State == DeviceState.Connected) { device2.handleChannelResponse(response); return; } //else if (device2.State == DeviceState.Disconnected) //{ //var deviceInfo = deviceService.Get(App.CurrentUser.Id, $"{ device2.searchProfile.deviceNumber }:{ device2.searchProfile.deviceType }"); //if (deviceInfo != null && deviceInfo.Paired) //{ // device2.Connect(); //} //} return; } var device1 = deviceList.SingleOrDefault(d => d.searchProfile.deviceType == deviceTypeId); if (device1 != null) { var id = $"{ deviceNumber }:{ deviceTypeId}:{ transmissionTypeId }"; AbstractAntDevice device = null; switch (device1.Sensor) { case SensorType.None: break; case SensorType.Cadence: Debug.LogError("发现踏频设备"+id); device = new CadenceDevice(id); break; case SensorType.HeartRate: device = new HeartRateDevice(id); break; case SensorType.Power: device = new PowerDevice(id); break; case SensorType.Speed: device = new SpeedDevice(id); break; case SensorType.SpeedCadence: device = new BikeSpdCadDevice(id); break; case SensorType.Trainer: device = new FitDevice(id); break; case SensorType.VirtualPower: break; default: break; } if (device != null) { //device.searchProfile.deviceNumber = (ushort)deviceNumber; device.DeviceNumber = (ushort)deviceNumber; discoveredDevices.Add(device); } //ReSearch(); return; } } } private void ReSearch() { if (IsBackgroundScanning == false) return; //Console.WriteLine($"research,{ DateTime.Now }"); if (searchChannel != null) { //searchChannel.closeChannel(500); //searchChannel.unassignChannel(); //searchChannel.openChannel(); //return; } var channelIndex = (byte)0; //searchChannel.getChannelNum(); //Handle setting the search timeout byte timeout = 4; //default 4*2.5=10 seconds for each device //if (deviceList.Count - usedChannels.Count == 1) timeout = 255; //search forever if we only have one device to find; If one of the other devices resets it will startNextSearch again so we won't get stuck searchChannel.assignChannelExt(ANT_ReferenceLibrary.ChannelType.ADV_TxRx_Only_or_RxAlwaysWildCard_0x40, 0, ANT_ReferenceLibrary.ChannelTypeExtended.ADV_AlwaysSearch_0x01); //SendMessage((int)ANT_ReferenceLibrary.ANTMessageID.ASSIGN_CHANNEL_0x42, new byte[] { // channelIndex, // (int)ANT_ReferenceLibrary.ChannelType.ADV_TxRx_Only_or_RxAlwaysWildCard_0x40, // 0 // }); searchChannel.setChannelID(0, false, 0, 0); searchChannel.setChannelFreq(57); SendMessage(102, new byte[] { channelIndex, Convert.ToByte(true) }); searchChannel.setLowPrioritySearchTimeout(timeout); searchChannel.setChannelSearchTimeout(0); searchChannel.openChannel(500); } public void ConnectDevice(AbstractAntDevice device) { if (usedChannels.Any(u => u.DeviceNumber == device.searchProfile.deviceNumber.ToString() && u.DeviceTypeId == device.searchProfile.deviceType.ToString())) return; if (device.Sensor == SensorType.Trainer || device.Sensor == SensorType.Power) { if (discoveredDevices.Any(s => s.Sensor == device.Sensor && (s.State == DeviceState.Connected || s.State == DeviceState.Connecting))) { return; } } if (device.Sensor == SensorType.Cadence) { if (discoveredDevices.Any(s => s.Sensor == device.Sensor && (s.State == DeviceState.Connected || s.State == DeviceState.Connecting))) { return; } } if (device.Sensor == SensorType.HeartRate) { if (discoveredDevices.Any(s => s.Sensor == device.Sensor && (s.State == DeviceState.Connected || s.State == DeviceState.Connecting))) { return; } } for (int i = 0; i < ChannelCount; i++) { if (!usedChannels.Any(u => u.Index == (byte)i)) { usedChannels.Add(new UserdChannel { DeviceNumber = device.searchProfile.deviceNumber.ToString(), DeviceTypeId = device.searchProfile.deviceType.ToString(), Index = (byte)i }); break; } } //var deviceService = new DeviceService(); //var info = deviceService.Get(App.CurrentUser.Id, device.searchProfile.deviceNumber.ToString() + ":" + device.searchProfile.deviceType); //if (info != null && info.Paired == false) //{ // info.Paired = true; // deviceService.Update(info); //} #region 配对 device.State = DeviceState.Connecting; //var r = searchChannel.closeChannel(500); //if (!r) //{ // throw new Exception("关闭搜索频道出错"); //} //CloseChannel(searchChannel); //IsBackgroundScanning = false; var channelIndex = usedChannels.Single(c => c.DeviceNumber == device.searchProfile.deviceNumber.ToString() && c.DeviceTypeId == device.searchProfile.deviceType.ToString()).Index; var channel = _antDevice.getChannel(channelIndex); //channel.channelResponse += DeviceResponse; channel.assignChannel(ANT_ReferenceLibrary.ChannelType.BASE_Slave_Receive_0x00, 0); channel.setChannelID(device.searchProfile.deviceNumber, false, (byte)device.searchProfile.deviceType, device.searchProfile.transType); channel.setChannelPeriod(device.searchProfile.messagePeriod); channel.setChannelFreq(device.searchProfile.rfOffset); channel.openChannel(); //if (!r1) //{ // throw new Exception($"打开配对频道出错{ channelIndex }"); //} //Console.WriteLine($" 连接设备{ device.searchProfile.deviceNumber }, 频道{ channelIndex },{ DateTime.Now }"); //Task.Delay(2000).ContinueWith((t) => //{ // IsBackgroundScanning = true; // ReSearch(); //}); #endregion } public void DisconnectDevice(AbstractAntDevice device, bool save) { var deviceNumber = device.searchProfile.deviceNumber.ToString(); var deviceType = device.searchProfile.deviceType.ToString(); var item = usedChannels.SingleOrDefault(u => u.DeviceNumber == deviceNumber && u.DeviceTypeId == deviceType); if (item == null) return; device.State = DeviceState.Disconnecting; var channelIndex = item.Index; var channel = _antDevice.getChannel(channelIndex); CloseChannel(channel); usedChannels.Remove(item); device.State = DeviceState.Disconnected; if (save) { //var deviceService = new DeviceService(); //var info = deviceService.Get(App.CurrentUser.Id, device.searchProfile.deviceNumber.ToString() + ":" + device.searchProfile.deviceType); //if (info != null && info.Paired) //{ // info.Paired = false; // deviceService.Update(info); //} } } public void Dispose() { discoveredDevices.Clear(); deviceList.Clear(); _channels.Clear(); if (_antDevice != null) { _antDevice.Dispose(); } } public void CloseChannel(ANT_Channel channel) { channel.closeChannel(500); channel.unassignChannel(500); } } }