接入基本设备
This commit is contained in:
1 parent
2f84f70a5f
commit
7729332f41
62 files changed
+2880
-86
No files matched your search
@@ -26,6 +26,20 @@ namespace Assets.Scripts.Devices
|
||||
get; protected set;
|
||||
}
|
||||
|
||||
public NetworkType Network { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 信号强度
|
||||
/// 信号从0dbm~97dbm,个人建议是0~30是强,30~70是中,70~97是弱。
|
||||
/// </summary>
|
||||
public int SignalStrength { get; internal set; }
|
||||
|
||||
protected AbstractDevice(NetworkType networkType)
|
||||
{
|
||||
this.Network = networkType;
|
||||
}
|
||||
|
||||
|
||||
public abstract void Connect();
|
||||
|
||||
public abstract void Disconnect(bool save = true);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Assets.Scripts.Devices.Ant
|
||||
public readonly byte uid;
|
||||
|
||||
|
||||
public DataSourceBase(racerSportType sportType, bool isHuman)
|
||||
public DataSourceBase(racerSportType sportType, bool isHuman):base(NetworkType.ANT)
|
||||
{
|
||||
this.sportType = sportType;
|
||||
this.isHuman = isHuman;
|
||||
|
||||
@@ -9,7 +9,7 @@ using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ant
|
||||
{
|
||||
public class FitDevice : AbstractAntDevice, ISpeedDevice, IPowerDevice, ICadenceDevice, IRequiresRiderWeight
|
||||
public class FitDevice : AbstractAntDevice, ISpeedDevice, IPowerDevice, ICadenceDevice, IRequiresRiderWeight, ITrainerDevice
|
||||
{
|
||||
public const int MAX_NO_EVENT_STOP_COUNT = 12;
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ant.Interfaces
|
||||
{
|
||||
public interface ITrainerDevice
|
||||
{
|
||||
void SetErgMode(int targetPower);
|
||||
|
||||
void SetResistanceMode(double value);
|
||||
|
||||
void SetWindResistance(double? height = null);
|
||||
|
||||
void SetTrackResistance(double grade);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 362cacfc69d803d478112e7c293cfa17
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -39,4 +39,10 @@ namespace Assets.Scripts.Devices.Ant
|
||||
Trainer,
|
||||
VirtualPower,
|
||||
}
|
||||
|
||||
public enum NetworkType
|
||||
{
|
||||
BLE,
|
||||
ANT
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
using Assets.Scripts.Ble.HeartRate;
|
||||
using Assets.Scripts.Ble.Service;
|
||||
using Assets.Scripts.Devices.Ant;
|
||||
using Assets.Scripts.Devices.Ble.Characteristic;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -22,17 +24,23 @@ namespace Assets.Scripts.Devices.Ble
|
||||
|
||||
public override string Name { get => peripheralInfo.Name; protected set => base.Name = value; }
|
||||
|
||||
public BleDevice(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface, SensorType sensor) //: base(peripheralInfo)
|
||||
public List<ICharacteristic> Characteristics { get; protected set; } = new List<ICharacteristic>();
|
||||
|
||||
public BleDevice(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface, SensorType sensor) : base(NetworkType.BLE)
|
||||
{
|
||||
this.hwInterface = bleWinHwInterface;
|
||||
this.hwInterface.BluetoothStateChangedEvent += BluetoothStateChangedEvent;
|
||||
this.peripheralInfo = peripheralInfo;
|
||||
|
||||
base.Sensor = sensor;
|
||||
|
||||
|
||||
//base.Name = this.peripheralInfo.Name;
|
||||
|
||||
//Debug.Log(base.Name + "," + sensor.ToString());
|
||||
|
||||
Characteristics.Add(new BatteryLevel());
|
||||
Characteristics.Add(new ManufacturerNameCharacteristic());
|
||||
Characteristics.Add(new ModelNumberCharacteristic());
|
||||
}
|
||||
|
||||
public void ConnectToPeripheralIfPossible()
|
||||
@@ -230,7 +238,7 @@ namespace Assets.Scripts.Devices.Ble
|
||||
public override void Disconnect(bool save = true)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
|
||||
this.hwInterface.DisconnectPeripheral(this.peripheralInfo, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,11 +43,19 @@ namespace Assets.Scripts.Devices.Ble
|
||||
{
|
||||
if (!discoveredDevices.ContainsKey(device.Peripheral.Address))
|
||||
{
|
||||
Debug.Log("发现设备" + device.Peripheral.Address + device.Peripheral.Name + ", type:" + device.SensorType);
|
||||
Debug.Log($"发现设备{ device.Peripheral.Address }, \t\tName:{ device.Peripheral.Name }, type:{ device.SensorType }");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(device.Peripheral.Name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(device.SensorType == Ant.SensorType.Trainer)
|
||||
{
|
||||
//var device1 = new Ftms(device.Peripheral, hwInterface);
|
||||
//discoveredDevices.Add(device.Peripheral.Address, device1);
|
||||
|
||||
var device1 = new Tacx(device.Peripheral, hwInterface);
|
||||
discoveredDevices.Add(device.Peripheral.Address, device1);
|
||||
}
|
||||
else if(device.SensorType == Ant.SensorType.HeartRate)
|
||||
{
|
||||
@@ -59,8 +67,18 @@ namespace Assets.Scripts.Devices.Ble
|
||||
var device1 = new CyclingPower(device.Peripheral, hwInterface);
|
||||
discoveredDevices.Add(device.Peripheral.Address, device1);
|
||||
}
|
||||
else if(device.SensorType == Ant.SensorType.SpeedCadence)
|
||||
{
|
||||
var device1 = new SpeedCadence(device.Peripheral, hwInterface);
|
||||
discoveredDevices.Add(device.Peripheral.Address, device1);
|
||||
}
|
||||
//discoveredDevices.Add(device.Peripheral.Address, new BleDevice(device.Peripheral, hwInterface, device.SensorType));
|
||||
}
|
||||
if (discoveredDevices.ContainsKey(device.Peripheral.Address))
|
||||
{
|
||||
discoveredDevices[device.Peripheral.Address].SignalStrength = device.Rssi;
|
||||
//Debug.Log($"设备{ device.Peripheral.Name }信号量:{ device.Rssi }");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class BatteryLevel : ICharacteristic
|
||||
{
|
||||
public Guid Uuid => ServiceUuids.Characteristics.BatteryLevel;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.Battery).IdGuid;
|
||||
|
||||
private ICharacteristic _characteristicUnavailable = null;
|
||||
public ICharacteristic CharacteristicUnavailable
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._characteristicUnavailable;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsOptional
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int BatteryLevelValue { get; private set; }
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
BatteryLevelValue = (int)data[0];
|
||||
|
||||
Debug.Log($"电量:{ BatteryLevelValue }");
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
this._characteristicUnavailable = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b654b3a52c37e6f47b9823953c8fd5d1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class CyclingPowerMeasurement: ICharacteristic
|
||||
{
|
||||
public Guid Uuid
|
||||
{
|
||||
get
|
||||
{
|
||||
return ServiceUuids.Characteristics.CyclingPowerMeasurement;
|
||||
}
|
||||
}
|
||||
|
||||
public Guid ServiceUuid
|
||||
{
|
||||
get
|
||||
{
|
||||
return ServiceUuids.Get(ServiceUuids.CyclingPower).IdGuid;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsOptional
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private double wheelCircumference;
|
||||
|
||||
public int Power { get; private set; }
|
||||
|
||||
public CyclingPowerMeasurement(double wheelCircumference)
|
||||
{
|
||||
this.wheelCircumference = wheelCircumference;
|
||||
}
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
|
||||
this.Power = BitConverter.ToInt16(data, 2);
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b0646295dbaa0a4e951de9bd391e475
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,271 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Assets.Scripts.Devices.Ble.Extension;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class CyclingSpeedCadenceMeasurement : ICharacteristic
|
||||
{
|
||||
private CyclingSpeedCadenceMeasurement.IUpdate _currentUpdate;
|
||||
private CyclingSpeedCadenceMeasurement.IUpdate _previousUpdate = new CyclingSpeedCadenceMeasurement.NullUpdate();
|
||||
private const byte MaxNoEvents = (byte)4;
|
||||
private byte _noCadenceEventCount;
|
||||
private byte _noSpeedEventCount;
|
||||
|
||||
|
||||
public Guid Uuid => ServiceUuids.Characteristics.CscMeasurement;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.CyclingSpeedCadence).IdGuid;
|
||||
|
||||
public bool IsOptional => false;
|
||||
|
||||
public double Speed { get; private set; }
|
||||
public int Cadence { get; private set; }
|
||||
|
||||
public double WheelCircumference { private get; set; }
|
||||
|
||||
public CyclingSpeedCadenceMeasurement(double wheelCircumference)
|
||||
{
|
||||
this.WheelCircumference = wheelCircumference;
|
||||
}
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
//Debug.Log(string.Join(",", data));
|
||||
|
||||
this._currentUpdate = new CyclingSpeedCadenceMeasurement.Update(data);
|
||||
this.HandleCadenceUpdate();
|
||||
this.HandleSpeedUpdate();
|
||||
this._previousUpdate = this._currentUpdate;
|
||||
}
|
||||
|
||||
private void HandleCadenceUpdate()
|
||||
{
|
||||
if (this._currentUpdate.HasCadence && (int)this._currentUpdate.CadenceEventCount != (int)this._previousUpdate.CadenceEventCount)
|
||||
{
|
||||
this._noCadenceEventCount = (byte)0;
|
||||
if (this.CadenceTimeDifference() <= 0)
|
||||
return;
|
||||
this.Cadence = this.CalculateCadence();
|
||||
}
|
||||
else
|
||||
{
|
||||
this._noCadenceEventCount = (byte)((uint)this._noCadenceEventCount + 1U);
|
||||
if ((int)this._noCadenceEventCount < 4)
|
||||
return;
|
||||
this.Cadence = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSpeedUpdate()
|
||||
{
|
||||
if (this._currentUpdate.HasSpeed && (int)this._currentUpdate.SpeedEventCount != (int)this._previousUpdate.SpeedEventCount)
|
||||
{
|
||||
this._noSpeedEventCount = (byte)0;
|
||||
double num = this.CalculateSpeed();
|
||||
if (num >= 150.0)
|
||||
return;
|
||||
this.Speed = num;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._noSpeedEventCount = (byte)((uint)this._noSpeedEventCount + 1U);
|
||||
if ((int)this._noSpeedEventCount < 4)
|
||||
return;
|
||||
this.Speed = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
private int CalculateCadence()
|
||||
{
|
||||
int num = this.CadenceEventDifference() * 61440 / this.CadenceTimeDifference();
|
||||
if (num > (int)byte.MaxValue)
|
||||
return (int)byte.MaxValue;
|
||||
if (num < 0)
|
||||
return 0;
|
||||
return num;
|
||||
}
|
||||
|
||||
private int CadenceTimeDifference()
|
||||
{
|
||||
if ((int)this._currentUpdate.CadenceTime1024 > (int)this._previousUpdate.CadenceTime1024)
|
||||
return (int)this._currentUpdate.CadenceTime1024 - (int)this._previousUpdate.CadenceTime1024;
|
||||
return (int)ushort.MaxValue - (int)this._previousUpdate.CadenceTime1024 + (int)this._currentUpdate.CadenceTime1024 + 1;
|
||||
}
|
||||
|
||||
private int CadenceEventDifference()
|
||||
{
|
||||
if ((int)this._currentUpdate.CadenceEventCount > (int)this._previousUpdate.CadenceEventCount)
|
||||
return (int)this._currentUpdate.CadenceEventCount - (int)this._previousUpdate.CadenceEventCount;
|
||||
return (int)ushort.MaxValue - (int)this._previousUpdate.CadenceEventCount + (int)this._currentUpdate.CadenceEventCount + 1;
|
||||
}
|
||||
|
||||
private double CalculateSpeed()
|
||||
{
|
||||
double num = (double)(ulong)(this.WheelCircumference / 10.0 * 36864.0 * (double)this.SpeedEventDifference() / (double)this.SpeedTimeDifference()) / 1000.0;
|
||||
if (num >= 0.0)
|
||||
return num;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
private int SpeedTimeDifference()
|
||||
{
|
||||
if ((int)this._currentUpdate.SpeedTime1024 > (int)this._previousUpdate.SpeedTime1024)
|
||||
return (int)this._currentUpdate.SpeedTime1024 - (int)this._previousUpdate.SpeedTime1024;
|
||||
return (int)ushort.MaxValue - (int)this._previousUpdate.SpeedTime1024 + (int)this._currentUpdate.SpeedTime1024 + 1;
|
||||
}
|
||||
|
||||
private uint SpeedEventDifference()
|
||||
{
|
||||
if (this._currentUpdate.SpeedEventCount > this._previousUpdate.SpeedEventCount)
|
||||
return this._currentUpdate.SpeedEventCount - this._previousUpdate.SpeedEventCount;
|
||||
return (uint)(-1 - (int)this._previousUpdate.SpeedEventCount + (int)this._currentUpdate.SpeedEventCount + 1);
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private interface IUpdate
|
||||
{
|
||||
bool HasCadence { get; }
|
||||
|
||||
bool HasSpeed { get; }
|
||||
|
||||
uint SpeedEventCount { get; }
|
||||
|
||||
ushort SpeedTime1024 { get; }
|
||||
|
||||
ushort CadenceEventCount { get; }
|
||||
|
||||
ushort CadenceTime1024 { get; }
|
||||
}
|
||||
|
||||
private class Update : CyclingSpeedCadenceMeasurement.IUpdate
|
||||
{
|
||||
private static readonly byte WheelRevolutionsFlagBit = (byte)0;
|
||||
private static readonly byte CrankRevolutionsFlagBit = (byte)1;
|
||||
private readonly byte[] _data;
|
||||
|
||||
private byte Flags
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._data[0];
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasCadence
|
||||
{
|
||||
get
|
||||
{
|
||||
return PrimitiveExtensions.IsBitSet(this.Flags, (int)CyclingSpeedCadenceMeasurement.Update.CrankRevolutionsFlagBit);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return PrimitiveExtensions.IsBitSet(this.Flags, (int)CyclingSpeedCadenceMeasurement.Update.WheelRevolutionsFlagBit);
|
||||
}
|
||||
}
|
||||
|
||||
public uint SpeedEventCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return BitConvertHelper.ToUInt32(this._data, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort SpeedTime1024
|
||||
{
|
||||
get
|
||||
{
|
||||
return BitConvertHelper.ToUInt16(this._data, 5);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort CadenceEventCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return BitConvertHelper.ToUInt16(this._data, this.HasSpeed ? 7 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort CadenceTime1024
|
||||
{
|
||||
get
|
||||
{
|
||||
return BitConvertHelper.ToUInt16(this._data, this.HasSpeed ? 9 : 3);
|
||||
}
|
||||
}
|
||||
|
||||
public Update(byte[] data)
|
||||
{
|
||||
this._data = data;
|
||||
}
|
||||
}
|
||||
|
||||
private class NullUpdate : CyclingSpeedCadenceMeasurement.IUpdate
|
||||
{
|
||||
public bool HasCadence
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public uint SpeedEventCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort SpeedTime1024
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ushort)0;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort CadenceEventCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ushort)0;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort CadenceTime1024
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ushort)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ea4d73295288ad4a9f561a37246110a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class FtmsFitnessMachineFeature : ICharacteristic
|
||||
{
|
||||
public Guid Uuid => ServiceUuids.Characteristics.FitnessMachineFeature;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.Ftms).IdGuid;
|
||||
|
||||
public bool IsOptional => false;
|
||||
|
||||
public bool IsInclinationSupported
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public bool IsInclinationTargetSettingSupported
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
if (data.Length != 8)
|
||||
return;
|
||||
this.IsInclinationSupported = ByteExtensions.IsFlagSet(data[0], (byte)8);
|
||||
//this.ResistanceLevelSupported = ByteExtensions.IsFlagSet(data[0], sbyte.MinValue);
|
||||
|
||||
this.IsInclinationTargetSettingSupported = ByteExtensions.IsFlagSet(data[4], 2);
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6095c96152da4c47870708061e1d5ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,220 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class FtmsIndoorBikeData : ICharacteristic
|
||||
{
|
||||
private readonly ushort? _instantSpeedSubject = new ushort?();
|
||||
private readonly ushort? _averageSpeedSubject = new ushort?();
|
||||
private readonly ushort? _instantCadenceSubject = new ushort?();
|
||||
private readonly ushort? _averageCadenceSubject = (new ushort?());
|
||||
private readonly uint? _totalDistanceSubject = (new uint?());
|
||||
private readonly short? _resistanceLevelSubject = (new short?());
|
||||
private readonly short? _instantPowerSubject = (new short?());
|
||||
private readonly short? _averagePowerSubject = (new short?());
|
||||
private readonly ushort? _totalEnergySubject = (new ushort?());
|
||||
private readonly ushort? _energyPerHourSubject = (new ushort?());
|
||||
private readonly byte? _energyPerMinuteSubject = (new byte?());
|
||||
private readonly byte? _heartRateSubject = (new byte?());
|
||||
private readonly byte? _metabolicEquivalentSubject = (new byte?());
|
||||
private readonly ushort? _elapsedTimeSubject = (new ushort?());
|
||||
private readonly ushort? _remainingTimeSubject = (new ushort?());
|
||||
|
||||
public short? InstantPower
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._instantPowerSubject;
|
||||
}
|
||||
}
|
||||
public ushort? InstantCadence
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._instantCadenceSubject;
|
||||
}
|
||||
}
|
||||
public ushort? InstantSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._instantSpeedSubject;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Guid Uuid => ServiceUuids.Characteristics.IndoorBikeData;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.Ftms).IdGuid;
|
||||
|
||||
public bool IsOptional => false;
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
if(data.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<FtmsIndoorBikeData.IndoorBikeDataField> list = new List<FtmsIndoorBikeData.IndoorBikeDataField>();
|
||||
if (!ByteExtensions.IsFlagSetAtPosition(data[0], (byte)0))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousSpeed);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)1))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.AverageSpeed);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)2))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousCadence);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)3))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.AverageCadence);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)4))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.TotalDistance);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)5))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.ResistanceLevel);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)6))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousPower);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)7))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.AveragePower);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)0))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.ExpendedEnergy);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)1))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.HeartRate);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)2))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.MetabolicEquivalent);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)3))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.ElapsedTime);
|
||||
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)4))
|
||||
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.RemainingTime);
|
||||
int offset = 2;
|
||||
foreach (FtmsIndoorBikeData.IndoorBikeDataField field in list)
|
||||
offset += this.ParseField(data, offset, field);
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private int ParseField(byte[] attribute, int offset, FtmsIndoorBikeData.IndoorBikeDataField field)
|
||||
{
|
||||
int fieldSize = FtmsIndoorBikeData.GetFieldSize(field);
|
||||
if (attribute.Length < fieldSize + offset)
|
||||
throw new ArgumentException("attribute");
|
||||
switch (field)
|
||||
{
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousSpeed:
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._instantSpeedSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.AverageSpeed:
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._averageSpeedSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousCadence:
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._instantCadenceSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.AverageCadence:
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._averageCadenceSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.TotalDistance:
|
||||
ICharacteristicExtensions.HandleUint24AttributeValue((ICharacteristic)this, attribute, offset, this._totalDistanceSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.ResistanceLevel:
|
||||
ICharacteristicExtensions.HandleShortAttributeValue((ICharacteristic)this, attribute, offset, this._resistanceLevelSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousPower:
|
||||
ICharacteristicExtensions.HandleShortAttributeValue((ICharacteristic)this, attribute, offset, this._instantPowerSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.AveragePower:
|
||||
ICharacteristicExtensions.HandleShortAttributeValue((ICharacteristic)this, attribute, offset, this._averagePowerSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.ExpendedEnergy:
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._totalEnergySubject);
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset + 2, this._energyPerHourSubject);
|
||||
ICharacteristicExtensions.HandleByteAttributeValue((ICharacteristic)this, attribute, offset + 4, this._energyPerMinuteSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.HeartRate:
|
||||
ICharacteristicExtensions.HandleByteAttributeValue((ICharacteristic)this, attribute, offset, this._heartRateSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.MetabolicEquivalent:
|
||||
ICharacteristicExtensions.HandleByteAttributeValue((ICharacteristic)this, attribute, offset, this._metabolicEquivalentSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.ElapsedTime:
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._elapsedTimeSubject);
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.RemainingTime:
|
||||
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._remainingTimeSubject);
|
||||
break;
|
||||
}
|
||||
return fieldSize;
|
||||
}
|
||||
|
||||
private static int GetFieldSize(FtmsIndoorBikeData.IndoorBikeDataField field)
|
||||
{
|
||||
int num = 0;
|
||||
switch (field)
|
||||
{
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousSpeed:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.AverageSpeed:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousCadence:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.AverageCadence:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.TotalDistance:
|
||||
num = 3;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.ResistanceLevel:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousPower:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.AveragePower:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.ExpendedEnergy:
|
||||
num = 5;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.HeartRate:
|
||||
num = 1;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.MetabolicEquivalent:
|
||||
num = 1;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.ElapsedTime:
|
||||
num = 2;
|
||||
break;
|
||||
case FtmsIndoorBikeData.IndoorBikeDataField.RemainingTime:
|
||||
num = 2;
|
||||
break;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
private enum IndoorBikeDataField
|
||||
{
|
||||
InstantaneousSpeed,
|
||||
AverageSpeed,
|
||||
InstantaneousCadence,
|
||||
AverageCadence,
|
||||
TotalDistance,
|
||||
ResistanceLevel,
|
||||
InstantaneousPower,
|
||||
AveragePower,
|
||||
ExpendedEnergy,
|
||||
HeartRate,
|
||||
MetabolicEquivalent,
|
||||
ElapsedTime,
|
||||
RemainingTime,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3577d5802131984c9463ba280183935
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,4 +1,5 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -8,7 +9,7 @@ using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class HeartRateMeasurement
|
||||
public class HeartRateMeasurement : ICharacteristic
|
||||
{
|
||||
public Guid Uuid
|
||||
{
|
||||
@@ -26,6 +27,13 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsOptional
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private HrmFormat Format { get; set; }
|
||||
|
||||
// Token: 0x170005EB RID: 1515
|
||||
@@ -39,7 +47,7 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
if (this.Format == HrmFormat.Short)
|
||||
{
|
||||
this.BeatsPerMinute = BitConvertHelper.ToUInt8(data, 1);
|
||||
Debug.Log("心率:" + this.BeatsPerMinute);
|
||||
//Debug.Log("心率:" + this.BeatsPerMinute);
|
||||
return;
|
||||
}
|
||||
if (data.Length < 3)
|
||||
@@ -49,6 +57,10 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
this.BeatsPerMinute = BitConvertHelper.ToUInt16(data, 1);
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum HrmFlags
|
||||
{
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public static class ICharacteristicExtensions
|
||||
{
|
||||
public static void HandleByteAttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, byte? subject)
|
||||
{
|
||||
if (attribute.Length < 1 + offset)
|
||||
throw new ArgumentException("attribute");
|
||||
byte num = attribute[offset];
|
||||
subject =new byte?(num);
|
||||
}
|
||||
|
||||
public static void HandleShortAttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, short? subject)
|
||||
{
|
||||
if (attribute.Length < 2 + offset)
|
||||
throw new ArgumentException("attribute");
|
||||
short num = BitConverter.ToInt16(attribute, offset);
|
||||
subject= new short?(num);
|
||||
}
|
||||
|
||||
public static void HandleUshortAttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, ushort? subject)
|
||||
{
|
||||
if (attribute.Length < 2 + offset)
|
||||
throw new ArgumentException("attribute");
|
||||
ushort num = BitConverter.ToUInt16(attribute, offset);
|
||||
subject =new ushort?(num);
|
||||
}
|
||||
|
||||
public static void HandleUint24AttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, uint? subject)
|
||||
{
|
||||
if (attribute.Length < 4 + offset - 1)
|
||||
throw new ArgumentException("attribute");
|
||||
byte[] numArray = new byte[4];
|
||||
Buffer.BlockCopy((Array)attribute, offset, (Array)numArray, 0, 3);
|
||||
uint num = BitConverter.ToUInt32(numArray, 0);
|
||||
subject = (new uint?(num));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 238bc68320174c9429b5958b5207b2c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class ManufacturerNameCharacteristic : ICharacteristic
|
||||
{
|
||||
public Guid Uuid => ServiceUuids.Characteristics.ManufactureNameString;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.DeviceInformation).IdGuid;
|
||||
|
||||
public bool IsOptional => true;
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
Debug.Log(Encoding.UTF8.GetString(data));
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f0a2af64ce8fe84cbdf0e5a14bbc8ae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class ModelNumberCharacteristic : ICharacteristic
|
||||
{
|
||||
public Guid Uuid => ServiceUuids.Characteristics.ModelNumber;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.DeviceInformation).IdGuid;
|
||||
|
||||
public bool IsOptional => true;
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
Debug.Log($"model number:\t{ Encoding.UTF8.GetString(data) }");
|
||||
}
|
||||
|
||||
public void SetUnavailable()
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef489f56607ba2540b8954b67e1a4d3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,205 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class TacxFecNotify : ICharacteristic
|
||||
{
|
||||
public Guid Uuid => ServiceUuids.Characteristics.TacxFecRead;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid;
|
||||
|
||||
public virtual bool IsOptional
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public double Speed { get; set; }
|
||||
|
||||
private int _Power;
|
||||
public int Power
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Power;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Power = DeviceValueFilter.Power(value);
|
||||
//Debug.Log(_Power);
|
||||
}
|
||||
}
|
||||
|
||||
private int _Cadence;
|
||||
public int Cadence
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Cadence;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Cadence = DeviceValueFilter.Cadence(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
var responseId = data[2];
|
||||
|
||||
if (responseId != (byte)ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var data1 = data.Skip(3).ToArray();
|
||||
var pageNumber = data1[1];
|
||||
switch (pageNumber)
|
||||
{
|
||||
case 16:
|
||||
{
|
||||
HandleGeneralDataPage(data1.Skip(1).ToArray());
|
||||
}
|
||||
break;
|
||||
case 19:
|
||||
{
|
||||
//lastInstCadence = data1[5];
|
||||
}
|
||||
break;
|
||||
case 21: //bike data
|
||||
case 22: //Row data, same format as bike for cad and power
|
||||
case 20: //Elliptical, same format as bike for cad and power
|
||||
case 24: //Nordic Skier, same format as bike for cad and power
|
||||
{
|
||||
//lastInstCadence = data1[5];
|
||||
//lastInstPower = (ushort)(data1[6] + (data1[7] << 8));
|
||||
//System.Console.Out.WriteLine("Cadence:" + response.messageContents[5] + ",Power:" + (response.messageContents[6] + (response.messageContents[7] << 8)));
|
||||
//System.Console.Out.WriteLine("SPM:"+response.messageContents[5]+"Power:"+(response.messageContents[6] + (response.messageContents[7] << 8)));
|
||||
}
|
||||
break;
|
||||
case 25:
|
||||
|
||||
HandleTrainerDataPage(data1.Skip(1).ToArray());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleGeneralDataPage(byte[] dataPayload)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte equipmentType = dataPayload[1];
|
||||
byte elapsedTime = dataPayload[2];
|
||||
byte distanceTraveled = dataPayload[3];
|
||||
|
||||
//byte heartRate = dataPayload[6];
|
||||
//FecPageHandler.HeartRateDataSource heartRateDataSource = (FecPageHandler.HeartRateDataSource)(dataPayload[7] & 3);
|
||||
//bool traveledEnabled = (dataPayload[7] & 4) == 1;
|
||||
//bool distanceTraveledEnabled = (dataPayload[7] & 8) == 1;
|
||||
//FecPageHandler.FeState feState = (FecPageHandler.FeState)(dataPayload[7] & 128);
|
||||
//bool lapToggle = (dataPayload[7] & 128) == 1;
|
||||
//this._fecGeneralData = new FecGeneralData(equipmentType, elapsedTime, distanceTraveled, (short)num, feState, lapToggle, heartRate, heartRateDataSource, traveledEnabled, distanceTraveledEnabled);
|
||||
bool traveledEnabled = ((int)dataPayload[7] & 4) == 1;
|
||||
bool distanceTraveledEnabled = ((int)dataPayload[7] & 8) == 1;
|
||||
|
||||
int num1 = (int)dataPayload[4] | (int)dataPayload[5] << 8; // Instantaneous speed, m/s
|
||||
Speed = (double)num1 / 1000.0 * 60.0 * 60.0 / 1000.0; //转换成公里/小时
|
||||
//Console.WriteLine(num1 +"-----"+Speed);
|
||||
//fecDevice.UpdateSpeed(new double?(value));
|
||||
//PubCommData.Speed = num2;
|
||||
|
||||
//byte? b2 = (heartRate == 255) ? null : new byte?(heartRate);
|
||||
//int? xl = b2.HasValue ? new int?((int)b2.GetValueOrDefault()) : 0;
|
||||
//Console.WriteLine("最新速度" + Speed);
|
||||
//Console.WriteLine("最新心率" + xl);
|
||||
|
||||
//string str = "";
|
||||
//for (int i = 0; i < response.messageContents.Length; i++)
|
||||
//{
|
||||
// str += "," + response.messageContents[i];
|
||||
//}
|
||||
//PubCommData.sp = "速度:" + str;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleTrainerDataPage(byte[] dataPayload)
|
||||
{
|
||||
//if (response.messageContents.Length < 11) break;
|
||||
//double value = 0;
|
||||
//for (int i = 0; i < response.messageContents.Length; i++)
|
||||
//{
|
||||
// if (i < 8)
|
||||
// {
|
||||
// value += response.messageContents[i];
|
||||
// }
|
||||
//}
|
||||
|
||||
//double value1 = response.messageContents[10] * 256 + response.messageContents[9];
|
||||
|
||||
|
||||
//double power = response.messageContents[7] * 256 + response.messageContents[6];
|
||||
//Console.WriteLine("power:" + power);
|
||||
//PubCommData.power = power;
|
||||
|
||||
try
|
||||
{
|
||||
if (dataPayload[5] > 0)
|
||||
{
|
||||
//Console.WriteLine(dataPayload[5] + "," + dataPayload[6]);//瞬时功率
|
||||
}
|
||||
byte num1 = dataPayload[1];
|
||||
byte num2 = dataPayload[2];
|
||||
int accumulatedPower = (int)dataPayload[3] | (int)dataPayload[4] << 8;
|
||||
int instantaneousPower = (int)dataPayload[5] | ((int)dataPayload[6] & 15) << 8; //Watts
|
||||
bool bikePowerCalibrationRequired = ((int)dataPayload[6] >> 4 & 1) == 1;
|
||||
bool resistanceCalibrationRequired = ((int)dataPayload[6] >> 5 & 1) == 1;
|
||||
bool userConfigurationRequired = ((int)dataPayload[6] >> 6 & 1) == 1;
|
||||
bool speedIsTooLow = ((int)dataPayload[7] & 1) == 1;
|
||||
bool speedIsTooHigh = ((int)dataPayload[7] & 2) == 1;
|
||||
|
||||
//PubCommData.power = instantaneousPower;
|
||||
Power = instantaneousPower;
|
||||
|
||||
//bool bikePowerCalibrationRequired = (response.messageContents[6] >> 4 & 1) == 1;
|
||||
//bool resistanceCalibrationRequired = (response.messageContents[6] >> 5 & 1) == 1;
|
||||
//bool flag = (response.messageContents[6] >> 6 & 1) == 1;
|
||||
//bool speedIsTooLow = (response.messageContents[7] & 1) == 1;
|
||||
//bool speedIsTooHigh = (response.messageContents[7] & 2) == 1;
|
||||
//FecPageHandler.FeState feState = (FecPageHandler.FeState)(dataPayload[7] & 112);
|
||||
//bool lapToggle = (dataPayload[7] & 128) == 1;
|
||||
//this._fecTrainerData = new FecTrainerData((int)eventCount, (int)b, accumulatedPower, num, bikePowerCalibrationRequired, resistanceCalibrationRequired, flag, speedIsTooLow, speedIsTooHigh, feState, lapToggle);
|
||||
//device.UpdatePower(new int?(num));
|
||||
byte? nullable = (int)num2 == (int)byte.MaxValue ? new byte?() : new byte?(num2);
|
||||
int? cadence = nullable.HasValue ? new int?((int)nullable.GetValueOrDefault()) : new int?();
|
||||
this.Cadence = cadence.GetValueOrDefault();
|
||||
|
||||
//Console.WriteLine($"power:{ instantaneousPower },Cadence:{ cadence }, data:{ string.Join(",", dataPayload) }");
|
||||
|
||||
if (!userConfigurationRequired)
|
||||
return;
|
||||
//UpdateUserConfiguration();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetUnavailable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 640f21041d0bf6347b1e48669c9a8691
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Characteristic
|
||||
{
|
||||
public class TacxFecWrite : ICharacteristic
|
||||
{
|
||||
public virtual bool IsOptional
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Guid Uuid => ServiceUuids.Characteristics.TacxFecRead;
|
||||
|
||||
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid;
|
||||
|
||||
public void HandleAttributeReceived(byte[] data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void WriteMessageToControlPoint()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void SetUnavailable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c9564a0124aa5946b25bdd1a97ade2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,6 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ant.Interfaces;
|
||||
using Assets.Scripts.Devices.Ble.Characteristic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -12,12 +13,16 @@ namespace Assets.Scripts.Devices.Ble.Devices
|
||||
public class CyclingPower : BleDevice, IPowerDevice
|
||||
{
|
||||
private List<BleServiceInfo> Services;
|
||||
private CyclingPowerMeasurement cyclingPowerMeasurement;
|
||||
public CyclingPower(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Power)
|
||||
{
|
||||
|
||||
cyclingPowerMeasurement = new CyclingPowerMeasurement(1920);
|
||||
base.Characteristics.Add(cyclingPowerMeasurement);
|
||||
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
|
||||
}
|
||||
|
||||
public int Power { get => 999; set => throw new NotImplementedException(); }
|
||||
public int Power { get => cyclingPowerMeasurement.Power; set => throw new NotImplementedException(); }
|
||||
|
||||
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
|
||||
{
|
||||
@@ -35,7 +40,7 @@ namespace Assets.Scripts.Devices.Ble.Devices
|
||||
|
||||
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
|
||||
{
|
||||
Debug.Log("1111111111111111111111");
|
||||
//Debug.Log("1111111111111111111111");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -46,7 +51,22 @@ namespace Assets.Scripts.Devices.Ble.Devices
|
||||
|
||||
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
|
||||
{
|
||||
Debug.Log("main call" + string.Join(",", response.Data));
|
||||
//Debug.Log("power main call" + string.Join(",", response.Data));
|
||||
|
||||
//Debug.Log(characteristic.MatchGuid(cyclingPowerMeasurement.Uuid));
|
||||
//Debug.Log(characteristic.Service.MatchGuid(cyclingPowerMeasurement.ServiceUuid));
|
||||
//if (characteristic.MatchGuid(cyclingPowerMeasurement.Uuid))
|
||||
//{
|
||||
// cyclingPowerMeasurement.HandleAttributeReceived(response.Data);
|
||||
|
||||
//}
|
||||
foreach (var item in base.Characteristics)
|
||||
{
|
||||
if (characteristic.MatchGuid(item.Uuid))
|
||||
{
|
||||
item.HandleAttributeReceived(response.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ant.Interfaces;
|
||||
using Assets.Scripts.Devices.Ble.Characteristic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -8,19 +9,119 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Devices
|
||||
{
|
||||
public class Ftms : BleDevice, IPowerDevice
|
||||
public class Ftms : BleDevice, ISpeedDevice, IPowerDevice, ICadenceDevice, ITrainerDevice
|
||||
{
|
||||
|
||||
public int Power { get => _ftmsIndoorBikeData.InstantPower.GetValueOrDefault(0); set => throw new NotImplementedException(); }
|
||||
public double Speed { get => _ftmsIndoorBikeData.InstantSpeed.GetValueOrDefault(0); set => throw new NotImplementedException(); }
|
||||
public int Cadence { get => _ftmsIndoorBikeData.InstantCadence.GetValueOrDefault(0); set => throw new NotImplementedException(); }
|
||||
|
||||
private FtmsIndoorBikeData _ftmsIndoorBikeData;
|
||||
|
||||
private List<BleServiceInfo> Services;
|
||||
private BleCharacteristicInfo controlPointCharacteristic;
|
||||
|
||||
public Ftms(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) :base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer)
|
||||
{
|
||||
this._ftmsIndoorBikeData = new FtmsIndoorBikeData();
|
||||
base.Characteristics.Add(this._ftmsIndoorBikeData);
|
||||
|
||||
base.Characteristics.Add(new FtmsFitnessMachineFeature());
|
||||
|
||||
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
|
||||
|
||||
}
|
||||
|
||||
public int Power { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
|
||||
{
|
||||
this.Services = discoveredServices;
|
||||
|
||||
foreach (var service in this.Services)
|
||||
{
|
||||
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
|
||||
{
|
||||
foreach (var character in response.Data)
|
||||
{
|
||||
if (character.MatchGuid(ServiceUuids.Characteristics.IndoorBikeData))
|
||||
{
|
||||
//Debug.Log("功率功能");
|
||||
|
||||
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
|
||||
{
|
||||
//Debug.Log("1111111111111111111111");
|
||||
});
|
||||
}
|
||||
else if (character.MatchGuid(ServiceUuids.Characteristics.FitnessMachineControlPoint))
|
||||
{
|
||||
this.controlPointCharacteristic = character;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
|
||||
{
|
||||
foreach (var item in base.Characteristics)
|
||||
{
|
||||
if (characteristic.MatchGuid(item.Uuid))
|
||||
{
|
||||
//Debug.Log(string.Join(",", response.Data));
|
||||
item.HandleAttributeReceived(response.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetErgMode(int targetPower)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
byte[] bytes = BitConverter.GetBytes(targetPower);
|
||||
this.hwInterface.WriteCharacteristic(this.controlPointCharacteristic, new byte[3] {
|
||||
(byte)5,
|
||||
bytes[0],
|
||||
bytes[1]
|
||||
});
|
||||
}
|
||||
|
||||
public void SetResistanceMode(double value)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
var data = new byte[]
|
||||
{
|
||||
(byte)4,
|
||||
(byte)0.1
|
||||
};
|
||||
}
|
||||
|
||||
public void SetWindResistance(double? height = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轨道阻力
|
||||
/// </summary>
|
||||
/// <param name="grade"></param>
|
||||
public void SetTrackResistance(double grade)
|
||||
{
|
||||
if (this.State != Ant.DeviceState.Connected)
|
||||
return;
|
||||
if (controlPointCharacteristic == null)
|
||||
return;
|
||||
|
||||
short windSpeed = 0;
|
||||
short value2 = (short)(grade);
|
||||
byte rollingResistanceCoefficient = (byte)(0.004 * 10000);
|
||||
byte windResistanceCoefficient = 0;
|
||||
var data = new List<byte>();// { 17, (byte)windSpeed, (byte)value2, rollingResistanceCoefficient, windResistanceCoefficient };
|
||||
data.Add(17);
|
||||
data.AddRange(BitConverter.GetBytes(windSpeed));
|
||||
data.AddRange(BitConverter.GetBytes(value2));
|
||||
data.AddRange(BitConverter.GetBytes(rollingResistanceCoefficient));
|
||||
data.AddRange(BitConverter.GetBytes(windResistanceCoefficient));
|
||||
|
||||
|
||||
this.hwInterface.WriteCharacteristic(this.controlPointCharacteristic, data.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,13 @@ namespace Assets.Scripts.Devices.Ble.Devices
|
||||
private HeartRateMeasurement heartRateMeasurement;
|
||||
public HeartRate(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.HeartRate)
|
||||
{
|
||||
Debug.Log("创建心率设备");
|
||||
heartRateMeasurement = new HeartRateMeasurement();
|
||||
|
||||
|
||||
base.Characteristics.Add(heartRateMeasurement);
|
||||
|
||||
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
@@ -33,18 +32,33 @@ namespace Assets.Scripts.Devices.Ble.Devices
|
||||
|
||||
foreach (var service in this.Services)
|
||||
{
|
||||
//Debug.Log($"11111 "+ service.Id.ToString());
|
||||
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
|
||||
{
|
||||
foreach (var character in response.Data)
|
||||
{
|
||||
if (character.MatchGuid(heartRateMeasurement.Uuid))
|
||||
{
|
||||
Debug.Log("心率功能");
|
||||
|
||||
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
|
||||
{
|
||||
Debug.Log("1111111111111111111111");
|
||||
//Debug.Log($"心率计subscribe char:{ }");
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
foreach (var item in Characteristics.Where(c=>c.IsOptional))
|
||||
{
|
||||
//Debug.Log(item.GetType() + "服务可用"+ item.Uuid.ToString() +", service:" + item.ServiceUuid);
|
||||
var ccc = response.Data.FirstOrDefault(r => r.MatchGuid(item.Uuid));
|
||||
if (ccc == null)
|
||||
{
|
||||
item.SetUnavailable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log(item.GetType() + "服务可用");
|
||||
|
||||
GetBatteryLevel(ccc);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -52,10 +66,26 @@ namespace Assets.Scripts.Devices.Ble.Devices
|
||||
|
||||
}
|
||||
|
||||
public void GetBatteryLevel(BleCharacteristicInfo bbbb)
|
||||
{
|
||||
this.hwInterface.ReadCharacteristic(bbbb, (hwInterface1, characteristic1, response1) => {
|
||||
Debug.Log("read收到消息:" + string.Join(",", response1));
|
||||
});
|
||||
}
|
||||
|
||||
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
|
||||
{
|
||||
Debug.Log("main call" + string.Join(",", response.Data));
|
||||
heartRateMeasurement.HandleAttributeReceived(response.Data);
|
||||
//Debug.Log("heart rate main call" + string.Join(",", response.Data));
|
||||
|
||||
foreach (var item in base.Characteristics)
|
||||
{
|
||||
if (characteristic.MatchGuid(item.Uuid))
|
||||
{
|
||||
item.HandleAttributeReceived(response.Data);
|
||||
}
|
||||
}
|
||||
|
||||
//heartRateMeasurement.HandleAttributeReceived(response.Data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ant.Interfaces;
|
||||
using Assets.Scripts.Devices.Ble.Characteristic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Devices
|
||||
{
|
||||
public class SpeedCadence : BleDevice, ISpeedDevice, ICadenceDevice
|
||||
{
|
||||
private CyclingSpeedCadenceMeasurement _cyclingSpeedCadenceMeasurement;
|
||||
public int Cadence { get => _cyclingSpeedCadenceMeasurement.Cadence; set => throw new NotImplementedException(); }
|
||||
public double Speed { get => _cyclingSpeedCadenceMeasurement.Speed; set => throw new NotImplementedException(); }
|
||||
|
||||
private double _wheelCircumference;
|
||||
private List<BleServiceInfo> Services;
|
||||
public SpeedCadence(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.SpeedCadence)
|
||||
{
|
||||
Debug.Log("创建速度踏频设备");
|
||||
_cyclingSpeedCadenceMeasurement = new CyclingSpeedCadenceMeasurement(this._wheelCircumference);
|
||||
base.Characteristics.Add(_cyclingSpeedCadenceMeasurement);
|
||||
|
||||
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
|
||||
}
|
||||
|
||||
public void SetWheelCircumference(double value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
|
||||
{
|
||||
this.Services = discoveredServices;
|
||||
|
||||
foreach (var service in this.Services)
|
||||
{
|
||||
//Debug.Log($"11111 "+ service.Id.ToString());
|
||||
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
|
||||
{
|
||||
Debug.Log($"设备{ this.Name }的char: { string.Join("\r\n", response.Data.Select(d=>d.Id)) }");
|
||||
foreach (var character in response.Data)
|
||||
{
|
||||
if (character.MatchGuid(_cyclingSpeedCadenceMeasurement.Uuid))
|
||||
{
|
||||
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
|
||||
{
|
||||
//Debug.Log("1111111111111111111111");
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
foreach (var item in Characteristics.Where(c => c.IsOptional))
|
||||
{
|
||||
//Debug.Log(item.GetType() + "服务可用"+ item.Uuid.ToString() +", service:" + item.ServiceUuid);
|
||||
var ccc = response.Data.FirstOrDefault(r => r.MatchGuid(item.Uuid));
|
||||
if (ccc == null)
|
||||
{
|
||||
item.SetUnavailable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log(item.GetType() + "服务可用");
|
||||
|
||||
GetBatteryLevel(ccc);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void GetBatteryLevel(BleCharacteristicInfo bbbb)
|
||||
{
|
||||
this.hwInterface.ReadCharacteristic(bbbb, (hwInterface1, characteristic1, response1) => {
|
||||
Debug.Log("read收到消息:" + string.Join(",", response1));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
|
||||
{
|
||||
//Debug.Log("power main call" + string.Join(",", response.Data));
|
||||
|
||||
//Debug.Log(characteristic.MatchGuid(cyclingPowerMeasurement.Uuid));
|
||||
//Debug.Log(characteristic.Service.MatchGuid(cyclingPowerMeasurement.ServiceUuid));
|
||||
//if (characteristic.MatchGuid(cyclingPowerMeasurement.Uuid))
|
||||
//{
|
||||
// cyclingPowerMeasurement.HandleAttributeReceived(response.Data);
|
||||
|
||||
//}
|
||||
foreach (var item in base.Characteristics)
|
||||
{
|
||||
if (characteristic.MatchGuid(item.Uuid))
|
||||
{
|
||||
item.HandleAttributeReceived(response.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fc83bb07e841d04bb491332cdfeb669
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,262 @@
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Devices.Ant;
|
||||
using Assets.Scripts.Devices.Ant.Interfaces;
|
||||
using Assets.Scripts.Devices.Ble.Characteristic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Devices
|
||||
{
|
||||
public class Tacx : BleDevice, IPowerDevice, ICadenceDevice, ISpeedDevice, ITrainerDevice
|
||||
{
|
||||
public int Power { get => tacxFecNotify.Power; set => throw new NotImplementedException(); }
|
||||
public int Cadence { get => tacxFecNotify.Cadence; set => throw new NotImplementedException(); }
|
||||
public double Speed { get => tacxFecNotify.Speed; set => throw new NotImplementedException(); }
|
||||
|
||||
private List<BleServiceInfo> Services;
|
||||
|
||||
private TacxFecNotify tacxFecNotify;
|
||||
private BleCharacteristicInfo tacxFecWriteCharacteristic;
|
||||
|
||||
private TacxFecWrite tacxFecWrite;
|
||||
|
||||
/// <summary>
|
||||
/// 当前坡度,当切换到其他模式的时候,需要把坡度设置为0
|
||||
/// </summary>
|
||||
private double _grade = 0;
|
||||
|
||||
public Tacx(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer)
|
||||
{
|
||||
tacxFecNotify = new TacxFecNotify();
|
||||
base.Characteristics.Add(tacxFecNotify);
|
||||
|
||||
tacxFecWrite = new TacxFecWrite();
|
||||
base.Characteristics.Add(tacxFecWrite);
|
||||
|
||||
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
|
||||
|
||||
//bleWinHwInterface.WriteCharacteristic()
|
||||
}
|
||||
|
||||
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
|
||||
{
|
||||
this.Services = discoveredServices;
|
||||
|
||||
foreach (var service in this.Services)
|
||||
{
|
||||
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
|
||||
{
|
||||
foreach (var character in response.Data)
|
||||
{
|
||||
if (character.MatchGuid(ServiceUuids.Characteristics.TacxFecRead))
|
||||
{
|
||||
//Debug.Log("功率功能");
|
||||
|
||||
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
|
||||
{
|
||||
//Debug.Log("1111111111111111111111");
|
||||
});
|
||||
}
|
||||
else if (character.MatchGuid(ServiceUuids.Characteristics.TacxFecWrite))
|
||||
{
|
||||
this.tacxFecWriteCharacteristic = character;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
|
||||
{
|
||||
foreach (var item in base.Characteristics)
|
||||
{
|
||||
if (characteristic.MatchGuid(item.Uuid))
|
||||
{
|
||||
//Debug.Log(string.Join(",", response.Data));
|
||||
item.HandleAttributeReceived(response.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetErgMode(int targetPower)
|
||||
{
|
||||
Debug.Log("目标功率1");
|
||||
if (this.State != Ant.DeviceState.Connected)
|
||||
return;
|
||||
if (tacxFecWriteCharacteristic == null)
|
||||
return;
|
||||
Debug.Log($"目标功率:{ targetPower }");
|
||||
List<byte> data = new List<byte> { 164, 9, 79, 5, 49,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
(byte)(targetPower & byte.MaxValue),
|
||||
(byte)(targetPower >> 8 & byte.MaxValue)
|
||||
};
|
||||
byte checksum = (byte)0;
|
||||
data.ForEach(b => checksum ^= b);
|
||||
data.Add(checksum);
|
||||
|
||||
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 阻力模式
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void SetResistanceMode(double value)
|
||||
{
|
||||
if (this.State != Ant.DeviceState.Connected)
|
||||
return;
|
||||
if (tacxFecWriteCharacteristic == null)
|
||||
return;
|
||||
if(_grade > 0)
|
||||
{
|
||||
this.SetTrackResistance(0);
|
||||
}
|
||||
|
||||
double resistance = value / 100 * 200;
|
||||
|
||||
List<byte> data = new List<byte> { 164, 9, 79, 5, 48,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
(byte)resistance
|
||||
};
|
||||
byte checksum = (byte)0;
|
||||
data.ForEach(b => checksum ^= b);
|
||||
data.Add(checksum);
|
||||
|
||||
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 风阻
|
||||
/// </summary>
|
||||
/// <param name="height">海拔高度,单位米</param>
|
||||
public void SetWindResistance(double? height = null)
|
||||
{
|
||||
if (this.State != Ant.DeviceState.Connected)
|
||||
return;
|
||||
if (tacxFecWriteCharacteristic == null)
|
||||
return;
|
||||
|
||||
if (_grade > 0)
|
||||
{
|
||||
this.SetTrackResistance(0);
|
||||
}
|
||||
|
||||
byte windResistance = 0;
|
||||
|
||||
if (height.HasValue)
|
||||
{
|
||||
//Wind Resistance Coefficient [kg/m] = Frontal Surface Area [m2] x Drag Coefficient x Air Density[kg / m3]
|
||||
var wr = 0.40 * 1.0 * AirDensity.GetAirDensity(height.Value);
|
||||
if (wr > 1.86)
|
||||
{
|
||||
wr = 1.86;
|
||||
}
|
||||
windResistance = (byte)(Convert.ToInt32(Math.Round(wr, 2) / 0.01));
|
||||
|
||||
Console.WriteLine($"风阻系数:{ windResistance }");
|
||||
}
|
||||
|
||||
byte id = 79;
|
||||
byte size = 9;
|
||||
byte channel = 5;
|
||||
List<byte> data = new List<byte> { 164, size, id, channel, 50,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
windResistance,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
};
|
||||
byte checksum = (byte)0;
|
||||
data.ForEach(b => checksum ^= b);
|
||||
data.Add(checksum);
|
||||
|
||||
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轨道阻力
|
||||
/// </summary>
|
||||
/// <param name="grade">坡度百分比的值,单位是%</param>
|
||||
public void SetTrackResistance(double grade)
|
||||
{
|
||||
if (this.State != Ant.DeviceState.Connected)
|
||||
return;
|
||||
if (tacxFecWriteCharacteristic == null)
|
||||
return;
|
||||
|
||||
_grade = grade;
|
||||
if (_grade > 15)
|
||||
{
|
||||
_grade = 15;
|
||||
}
|
||||
else if (_grade < -5)
|
||||
{
|
||||
_grade = -5;
|
||||
}
|
||||
|
||||
var gradeValue = Convert.ToInt32((grade + 200) * 100);
|
||||
var gradeBytes = BitConverter.GetBytes(gradeValue);
|
||||
|
||||
byte id = 79;
|
||||
byte size = 9;
|
||||
byte channel = 5;
|
||||
List<byte> data = new List<byte> { 164, size, id, channel, 51,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
byte.MaxValue,
|
||||
gradeBytes[0],
|
||||
gradeBytes[1],
|
||||
byte.MaxValue,
|
||||
};
|
||||
byte checksum = (byte)0;
|
||||
data.ForEach(b => checksum ^= b);
|
||||
data.Add(checksum);
|
||||
|
||||
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
|
||||
}
|
||||
|
||||
private void UpdateUserConfiguration(double weight)
|
||||
{
|
||||
var _weight = (int)(weight * 100);
|
||||
if (this.State != Ant.DeviceState.Connected)
|
||||
return;
|
||||
if (tacxFecWriteCharacteristic == null)
|
||||
return;
|
||||
|
||||
byte id = 79;
|
||||
byte size = 4;
|
||||
byte channel = 5;
|
||||
List<byte> data = new List<byte> { 164, size, id, channel, 55,
|
||||
(byte) (_weight & (int) byte.MaxValue),
|
||||
(byte) (_weight >> 8 & (int) byte.MaxValue),
|
||||
byte.MaxValue,
|
||||
(byte) 143,
|
||||
(byte) 12,
|
||||
(byte) 70,
|
||||
(byte) 0
|
||||
};
|
||||
byte checksum = (byte)0;
|
||||
data.ForEach(b => checksum ^= b);
|
||||
data.Add(checksum);
|
||||
|
||||
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d133d5406fc475841a781b88d91dc139
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Extension
|
||||
{
|
||||
public static class PrimitiveExtensions
|
||||
{
|
||||
public static bool IsBitSet(this byte value, int position)
|
||||
{
|
||||
return ((uint)value & (uint)(1 << position)) > 0U;
|
||||
}
|
||||
|
||||
public static bool IsBitSet(this ushort value, int position)
|
||||
{
|
||||
return ((uint)value & (uint)(1 << position)) > 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d49278e3c0c3a8a43bc1145abf67a5c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3f0cb374472e52419b47f6e6d582341
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Interfaces
|
||||
{
|
||||
public interface ICharacteristic
|
||||
{
|
||||
Guid Uuid { get; }
|
||||
Guid ServiceUuid { get; }
|
||||
|
||||
bool IsOptional { get; }
|
||||
|
||||
void HandleAttributeReceived(byte[] data);
|
||||
|
||||
void SetUnavailable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e333c9c3812a8654c87353be9e87e84e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -16,7 +16,7 @@ namespace Assets.Scripts.Ble.Scan
|
||||
public BlePeripheralInfo Peripheral { get; }
|
||||
public int Rssi { get; }
|
||||
|
||||
public SensorType SensorType { get; }
|
||||
public SensorType SensorType { get; internal set; }
|
||||
public BleAdvertisementInfo(BlePeripheralInfo peripheral, int rssi, bool connectible, List<Guid> services, byte[] manufactureData, SensorType sensor)
|
||||
{
|
||||
this.Peripheral = peripheral;
|
||||
@@ -26,6 +26,8 @@ namespace Assets.Scripts.Ble.Scan
|
||||
//this.ManufactureData = manufactureData;
|
||||
|
||||
this.SensorType = sensor;
|
||||
|
||||
Index = 1;
|
||||
}
|
||||
|
||||
private readonly List<Guid> services;
|
||||
@@ -36,5 +38,14 @@ namespace Assets.Scripts.Ble.Scan
|
||||
return this.services;
|
||||
}
|
||||
}
|
||||
|
||||
public void TryAddService(Guid service)
|
||||
{
|
||||
if (this.services.Contains(service))
|
||||
return;
|
||||
this.services.Add(service);
|
||||
}
|
||||
|
||||
public int Index { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
namespace Assets.Scripts.Ble
|
||||
{
|
||||
/// <summary>
|
||||
/// rouvy里的 WinBlePeripheralInfo类
|
||||
/// r里的 WinBlePeripheralInfo类
|
||||
/// </summary>
|
||||
public class BleDeviceProxy
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Assets.Scripts.Ble.Win;
|
||||
using Assets.Scripts.Devices.Ant;
|
||||
using Assets.Scripts.Devices.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Win;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -96,53 +97,90 @@ namespace Assets.Scripts.Ble
|
||||
|
||||
private void WatcherScanInfoReceived(WclBleMainThread sender, long address, string name, int rssi, CPPBridge.WclBleAdvertisementType packetType, Guid? service)
|
||||
{
|
||||
//Debug.Log($"address:{ address }, name:{ name }, service:{ (service == null ? "" : service.Value.ToString()) }");
|
||||
//if(address != 224160707349234)
|
||||
//{
|
||||
//return;
|
||||
//}
|
||||
if (service.HasValue)
|
||||
{
|
||||
//Debug.Log($"address:{ address }, name:{ name }, service:{ (service == null ? "" : service.Value.ToString()) }");
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
if (pCache.ContainsKey(address.ToString()))
|
||||
{
|
||||
((pCache[address.ToString()].Peripheral) as WinBlePeripheralInfo).SetName(name);
|
||||
(pCache[address.ToString()].Peripheral as WinBlePeripheralInfo).SetName(name);
|
||||
}
|
||||
}
|
||||
//Debug.Log("service:" + service.ToString()+",name:" + name);
|
||||
if (service.HasValue && ServiceUuids.Services.Select(s => s.IdGuid).Any(x => x.Equals(service.Value)))
|
||||
if (!service.HasValue || ServiceUuids.Services.Select(s => s.IdGuid).All(x => !x.Equals(service.Value)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SensorType sensor = SensorType.None;
|
||||
List<Guid> services = null;
|
||||
if (service != null)
|
||||
{
|
||||
SensorType sensor = SensorType.None;
|
||||
List<Guid> services = null;
|
||||
if (service != null)
|
||||
{
|
||||
|
||||
services = new List<Guid> { service.Value };
|
||||
foreach(var item in ServiceUuids.Services)
|
||||
services = new List<Guid> { service.Value };
|
||||
foreach(var item in ServiceUuids.Services)
|
||||
{
|
||||
if(item.IdGuid != service.Value)
|
||||
{
|
||||
if(item.IdGuid != service.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(item.IdByteArray == ServiceUuids.Ftms)
|
||||
{
|
||||
sensor = SensorType.Trainer;
|
||||
}
|
||||
else if(item.IdByteArray == ServiceUuids.HeartRate)
|
||||
{
|
||||
sensor = SensorType.HeartRate;
|
||||
}
|
||||
else if(item.IdByteArray == ServiceUuids.CyclingPower)
|
||||
{
|
||||
sensor = SensorType.Power;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if(item.IdByteArray == ServiceUuids.Ftms)
|
||||
{
|
||||
sensor = SensorType.Trainer;
|
||||
}
|
||||
else if(item.IdByteArray == ServiceUuids.HeartRate)
|
||||
{
|
||||
sensor = SensorType.HeartRate;
|
||||
}
|
||||
else if(item.IdByteArray == ServiceUuids.CyclingPower)
|
||||
{
|
||||
sensor = SensorType.Power;
|
||||
//sensor = SensorType.Trainer;
|
||||
}
|
||||
else if(item.IdByteArray == ServiceUuids.CyclingSpeedCadence)
|
||||
{
|
||||
sensor = SensorType.SpeedCadence;
|
||||
}
|
||||
else if(item.IdByteArray == ServiceUuids.TacxBle)
|
||||
{
|
||||
sensor = SensorType.Trainer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!pCache.ContainsKey(address.ToString())) {
|
||||
var device = new BleAdvertisementInfo(new WinBlePeripheralInfo(address.ToString(), name), rssi, true, services, null, sensor);
|
||||
pCache.Add(address.ToString(), device);
|
||||
if (!pCache.ContainsKey(address.ToString()))
|
||||
{
|
||||
var device = new BleAdvertisementInfo(new WinBlePeripheralInfo(address.ToString(), name), rssi, true, services, null, sensor);
|
||||
pCache.Add(address.ToString(), device);
|
||||
|
||||
WclBleGattThread gattClient = this.SetUpGattClient(device.Peripheral);
|
||||
//this.ConnectInternal(gattClient);
|
||||
WclBleGattThread gattClient = this.SetUpGattClient(device.Peripheral);
|
||||
//this.ConnectInternal(gattClient);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.Log(sensor);
|
||||
//pCache[address.ToString()].SensorType = sensor;
|
||||
foreach (var item in services)
|
||||
{
|
||||
pCache[address.ToString()].TryAddService(item);
|
||||
}
|
||||
pCache[address.ToString()].Index++;
|
||||
if(pCache[address.ToString()].SensorType == SensorType.Power && pCache[address.ToString()].Services.Any(s=> s.Equals(ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid)))
|
||||
{
|
||||
pCache[address.ToString()].SensorType = SensorType.Trainer;
|
||||
//Debug.Log("纠正为trainer, "+ pCache[address.ToString()].Index);
|
||||
}
|
||||
|
||||
_discoveredCallback?.Invoke(pCache[address.ToString()]);
|
||||
|
||||
if (pCache[address.ToString()].Index > 4 || pCache[address.ToString()].SensorType != SensorType.Power)
|
||||
{
|
||||
_discoveredCallback?.Invoke(pCache[address.ToString()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,8 +217,11 @@ namespace Assets.Scripts.Ble
|
||||
|
||||
private void ConnectInternal(WclBleGattThread gattClient)
|
||||
{
|
||||
int num = gattClient.Connect();
|
||||
Debug.Log("连接设备返回" + num);
|
||||
//Task.Run(() =>
|
||||
//{
|
||||
int num = gattClient.Connect();
|
||||
Debug.Log("连接设备返回" + num);
|
||||
//});
|
||||
}
|
||||
|
||||
internal void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback)
|
||||
@@ -323,7 +364,10 @@ namespace Assets.Scripts.Ble
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.characteristicsDiscoveredCallbacks.Add(service, callback);
|
||||
if (!this.characteristicsDiscoveredCallbacks.ContainsKey(service))
|
||||
{
|
||||
this.characteristicsDiscoveredCallbacks.Add(service, callback);
|
||||
}
|
||||
int num = gattThread.DiscoverCharacteristics(service);
|
||||
if (WclBleErrors.IsSuccessCode(num))
|
||||
{
|
||||
@@ -355,11 +399,29 @@ namespace Assets.Scripts.Ble
|
||||
this.GattCharacteristicSubscribed(gattThread, characteristic, response);
|
||||
}
|
||||
|
||||
public void WriteCharacteristic(BleCharacteristicInfo characteristic, byte[] data)
|
||||
{
|
||||
var gattThread = this.wclBleMainThread.GetGattThread(characteristic.Peripheral);
|
||||
if(gattThread == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int num = gattThread.WriteCharacteristic(characteristic, data);
|
||||
if (WclBleErrors.IsSuccessCode(num))
|
||||
{
|
||||
Debug.Log("设置命令成功");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.wclBleMainThread.Dispose();
|
||||
this.wclBleMainThread = null;
|
||||
hwInterface = null;
|
||||
pCache.Clear();
|
||||
}
|
||||
|
||||
public void ReadCharacteristic(BleCharacteristicInfo characteristic, CharacteristicReadCallback callback)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Assets.Scripts.Devices.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Win;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Assets.Scripts.Devices.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Win;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Win
|
||||
{
|
||||
internal class WclAlertableWait
|
||||
{
|
||||
[DllImport("WclBlePluginCPP.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "WCLWait")]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
public static extern uint Wait([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.SysInt, SizeParamIndex = 1)][In] IntPtr[] Handle, [MarshalAs(UnmanagedType.U4)][In] uint Cnt, [MarshalAs(UnmanagedType.U4)][In] uint Timeout);
|
||||
|
||||
// Token: 0x0600207E RID: 8318
|
||||
[DllImport("WclBlePluginCPP.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "WCLFlushApc")]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
public static extern uint FlushApc();
|
||||
|
||||
// Token: 0x0600207F RID: 8319
|
||||
[DllImport("WclBlePluginCPP.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "WCLSetApcSync")]
|
||||
public static extern void SetApcSync();
|
||||
|
||||
// Token: 0x04001189 RID: 4489
|
||||
public const uint WAIT_OBJECT_0 = 0U;
|
||||
|
||||
// Token: 0x0400118A RID: 4490
|
||||
public const uint WAIT_IO_COMPLETION = 192U;
|
||||
|
||||
// Token: 0x0400118B RID: 4491
|
||||
public const uint WAIT_TIMEOUT = 258U;
|
||||
|
||||
// Token: 0x0400118C RID: 4492
|
||||
public const uint WAIT_FAILED = 4294967295U;
|
||||
|
||||
// Token: 0x0400118D RID: 4493
|
||||
public const uint INFINITE = 4294967295U;
|
||||
|
||||
// Token: 0x0400118E RID: 4494
|
||||
public const string GuidTemplate = "0000XXXX-0000-1000-8000-00805F9B34FB";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ec3942a1d98efc4eb36cd00001109b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,15 +1,16 @@
|
||||
using Assets.Scripts.Ble.Win;
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Ble.Win;
|
||||
using Assets.Scripts.Ble.Win.CPPBridge;
|
||||
using Assets.Scripts.Devices.Ble;
|
||||
using Assets.Scripts.Devices.Ble.Win;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Ble
|
||||
namespace Assets.Scripts.Devices.Ble.Win
|
||||
{
|
||||
internal class WclBleGattThread
|
||||
{
|
||||
@@ -163,7 +164,12 @@ namespace Assets.Scripts.Ble
|
||||
private readonly long address;
|
||||
|
||||
public BlePeripheralInfo Peripheral { get; }
|
||||
|
||||
private Thread thread;
|
||||
private ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
|
||||
private bool start = false;
|
||||
private ManualResetEvent sEvent;
|
||||
private AutoResetEvent aEvent;
|
||||
private ManualResetEvent tEvent;
|
||||
internal WclBleGattThread(BlePeripheralInfo bleDevice, IntPtr radio)
|
||||
{
|
||||
this.Peripheral = bleDevice;
|
||||
@@ -175,10 +181,84 @@ namespace Assets.Scripts.Ble
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
this.SetUpWorkerThread();
|
||||
if (this.start)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
this.start = true;
|
||||
|
||||
this.sEvent = new ManualResetEvent(false);
|
||||
this.tEvent = new ManualResetEvent(false);
|
||||
this.aEvent = new AutoResetEvent(false);
|
||||
|
||||
//thread = new Thread(new ThreadStart(ThreadProc));
|
||||
// this.sEvent.Set();
|
||||
|
||||
// IntPtr[] handle = new IntPtr[]
|
||||
// {
|
||||
// this.tEvent.SafeWaitHandle.DangerousGetHandle(),
|
||||
// this.aEvent.SafeWaitHandle.DangerousGetHandle()
|
||||
// };
|
||||
// while (WclAlertableWait.Wait(handle, 2U, WclAlertableWait.INFINITE) != WclAlertableWait.WAIT_OBJECT_0)
|
||||
// {
|
||||
// Action action;
|
||||
// if (this.actions.TryDequeue(out action))
|
||||
// {
|
||||
// if (action != null)
|
||||
// {
|
||||
// Debug.Log("连接设备");
|
||||
// action();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// }));
|
||||
//thread.Start();
|
||||
|
||||
//this.sEvent.WaitOne();
|
||||
ThreadProc();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Debug.Log("停止");
|
||||
start = false;
|
||||
thread?.Abort();
|
||||
|
||||
}
|
||||
|
||||
private void ThreadProc()
|
||||
{
|
||||
this.SetUpWorkerThread();
|
||||
//this.sEvent.Set();
|
||||
//this.DoWorkerLoop();
|
||||
|
||||
//this.CleanUpWorkerThread();
|
||||
//this.InternalCleanUp();
|
||||
}
|
||||
|
||||
private void CleanUpWorkerThread()
|
||||
{
|
||||
this.gatt.Disconnect();
|
||||
this.gatt.Dispose();
|
||||
this.gatt = null;
|
||||
}
|
||||
private void InternalCleanUp()
|
||||
{
|
||||
this.sEvent.Close();
|
||||
this.sEvent.Dispose();
|
||||
this.sEvent = null;
|
||||
|
||||
this.aEvent.Close();
|
||||
this.aEvent.Dispose();
|
||||
this.aEvent = null;
|
||||
|
||||
this.tEvent.Close();
|
||||
this.tEvent.Dispose();
|
||||
this.tEvent = null;
|
||||
}
|
||||
|
||||
private void SetUpWorkerThread()
|
||||
{
|
||||
this.gatt = new WclBleGattClient(this.address, this.rPtr)
|
||||
@@ -189,6 +269,28 @@ namespace Assets.Scripts.Ble
|
||||
};
|
||||
}
|
||||
|
||||
private void DoWorkerLoop()
|
||||
{
|
||||
IntPtr[] handle = new IntPtr[]
|
||||
{
|
||||
this.tEvent.SafeWaitHandle.DangerousGetHandle(),
|
||||
this.aEvent.SafeWaitHandle.DangerousGetHandle()
|
||||
};
|
||||
while (WclAlertableWait.Wait(handle, 2U, 4294967295U) != 0U)
|
||||
{
|
||||
//this.InvokeQueuedActions();
|
||||
Action action;
|
||||
if (this.actions.TryDequeue(out action))
|
||||
{
|
||||
if (action != null)
|
||||
{
|
||||
Debug.Log("连接设备");
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCharacteristicChanged(WclBleGattClient connection, ushort handle, byte[] value)
|
||||
{
|
||||
if (!this.subscribedCharHandles.ContainsKey(handle)) {
|
||||
@@ -260,6 +362,10 @@ namespace Assets.Scripts.Ble
|
||||
public int Connect()
|
||||
{
|
||||
this.gatt.Connect();
|
||||
//this.actions.Enqueue(() =>
|
||||
//{
|
||||
// this.gatt.Connect();
|
||||
//});
|
||||
return WclBleErrors.WCL_E_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -381,6 +487,21 @@ namespace Assets.Scripts.Ble
|
||||
this.gattCharacteristicsSubscribed(this, characteristic, response);
|
||||
}
|
||||
|
||||
public int WriteCharacteristic(BleCharacteristicInfo characteristicInfo, byte[] data)
|
||||
{
|
||||
if (!this.charMapping.ContainsKey(characteristicInfo))
|
||||
{
|
||||
return WclBleErrors.WCL_E_CONNECTION_NOT_ACTIVE;
|
||||
}
|
||||
GattCharacteristic gCh = this.charMapping[characteristicInfo];
|
||||
|
||||
int result = this.gatt.WriteCharacteristic(gCh, data);
|
||||
|
||||
|
||||
return WclBleErrors.WCL_E_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
private class WinBleCharacteristicInfo : BleCharacteristicInfo
|
||||
{
|
||||
// Token: 0x06003F85 RID: 16261 RVA: 0x000EA274 File Offset: 0x000E8474
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Assets.Scripts.Ble.CPPBridge;
|
||||
using Assets.Scripts.Ble;
|
||||
using Assets.Scripts.Ble.CPPBridge;
|
||||
using Assets.Scripts.Devices.Ble;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -8,7 +9,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Ble
|
||||
namespace Assets.Scripts.Devices.Ble.Win
|
||||
{
|
||||
internal class WclBleMainThread
|
||||
{
|
||||
@@ -99,6 +100,12 @@ namespace Assets.Scripts.Ble
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Debug.Log("停止thread");
|
||||
foreach (var item in this.gattClients.Values)
|
||||
{
|
||||
item.Stop();
|
||||
}
|
||||
|
||||
wclBleManager.Dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Assets.Scripts.Devices.Ble.Win
|
||||
{
|
||||
internal abstract class WclBleThread
|
||||
{
|
||||
// Token: 0x170005E2 RID: 1506
|
||||
// (get) Token: 0x06002114 RID: 8468 RVA: 0x000890FE File Offset: 0x000872FE
|
||||
public bool IsRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.started || this.IsTerminating;
|
||||
}
|
||||
}
|
||||
|
||||
// Token: 0x170005E3 RID: 1507
|
||||
// (get) Token: 0x06002115 RID: 8469 RVA: 0x00089112 File Offset: 0x00087312
|
||||
public bool IsTerminating
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.terminating;
|
||||
}
|
||||
}
|
||||
|
||||
// Token: 0x170005E4 RID: 1508
|
||||
// (get) Token: 0x06002116 RID: 8470 RVA: 0x0008911C File Offset: 0x0008731C
|
||||
public bool CanLoadWork
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.IsRunning && !this.IsTerminating;
|
||||
}
|
||||
}
|
||||
|
||||
// Token: 0x06002117 RID: 8471
|
||||
protected abstract void SetUpWorkerThread();
|
||||
|
||||
// Token: 0x06002118 RID: 8472
|
||||
protected abstract void CleanUpWorkerThread();
|
||||
|
||||
// Token: 0x06002119 RID: 8473 RVA: 0x00089131 File Offset: 0x00087331
|
||||
protected void TreadProcInitialized()
|
||||
{
|
||||
this.sEvent.Set();
|
||||
}
|
||||
|
||||
// Token: 0x0600211A RID: 8474 RVA: 0x0008913F File Offset: 0x0008733F
|
||||
protected void ProcessPendingAPCMessages()
|
||||
{
|
||||
while (WclAlertableWait.FlushApc() != 0U)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Token: 0x0600211B RID: 8475 RVA: 0x00089148 File Offset: 0x00087348
|
||||
protected void InternalCleanUp()
|
||||
{
|
||||
this.sEvent.Close();
|
||||
this.sEvent.Dispose();
|
||||
this.sEvent = null;
|
||||
this.aEvent.Close();
|
||||
this.aEvent.Dispose();
|
||||
this.aEvent = null;
|
||||
this.tEvent.Close();
|
||||
this.tEvent.Dispose();
|
||||
this.tEvent = null;
|
||||
this.terminating = false;
|
||||
}
|
||||
|
||||
// Token: 0x0600211C RID: 8476 RVA: 0x000891B5 File Offset: 0x000873B5
|
||||
private void ThreadProc()
|
||||
{
|
||||
this.SetUpWorkerThread();
|
||||
this.sEvent.Set();
|
||||
this.DoWorkerLoop();
|
||||
this.CleanUpWorkerThread();
|
||||
this.InternalCleanUp();
|
||||
}
|
||||
|
||||
// Token: 0x0600211D RID: 8477 RVA: 0x000891DC File Offset: 0x000873DC
|
||||
private void DoWorkerLoop()
|
||||
{
|
||||
IntPtr[] handle = new IntPtr[]
|
||||
{
|
||||
this.tEvent.SafeWaitHandle.DangerousGetHandle(),
|
||||
this.aEvent.SafeWaitHandle.DangerousGetHandle()
|
||||
};
|
||||
while (WclAlertableWait.Wait(handle, 2U, 4294967295U) != 0U && !this.terminating)
|
||||
{
|
||||
this.InvokeQueuedActions();
|
||||
if (this.terminating)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Token: 0x0600211E RID: 8478 RVA: 0x0008923C File Offset: 0x0008743C
|
||||
public virtual bool Start()
|
||||
{
|
||||
if (this.IsRunning)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
this.sEvent = new ManualResetEvent(false);
|
||||
this.tEvent = new ManualResetEvent(false);
|
||||
this.aEvent = new AutoResetEvent(false);
|
||||
this.wThread = new Thread(new ThreadStart(this.ThreadProc));
|
||||
this.wThread.Start();
|
||||
this.sEvent.WaitOne();
|
||||
this.started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Token: 0x0600211F RID: 8479 RVA: 0x000892B0 File Offset: 0x000874B0
|
||||
public virtual bool Stop()
|
||||
{
|
||||
if (!this.started || this.IsTerminating)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
this.terminating = true;
|
||||
this.tEvent.Set();
|
||||
this.wThread = null;
|
||||
this.ClearActionQueue();
|
||||
this.started = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Token: 0x06002120 RID: 8480 RVA: 0x000892FD File Offset: 0x000874FD
|
||||
protected bool EnqueueAction(Action action)
|
||||
{
|
||||
if (this.CanLoadWork)
|
||||
{
|
||||
this.aQueue.Enqueue(action);
|
||||
this.aEvent.Set();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Token: 0x06002121 RID: 8481 RVA: 0x00089324 File Offset: 0x00087524
|
||||
protected void ClearActionQueue()
|
||||
{
|
||||
Action action;
|
||||
while (this.aQueue.TryDequeue(out action))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Token: 0x06002122 RID: 8482 RVA: 0x00089340 File Offset: 0x00087540
|
||||
protected void InvokeQueuedActions()
|
||||
{
|
||||
while (this.DequeueAdnInvokeAction())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Token: 0x06002123 RID: 8483 RVA: 0x0008934C File Offset: 0x0008754C
|
||||
protected bool DequeueAdnInvokeAction()
|
||||
{
|
||||
Action action = this.TryDequActione();
|
||||
if (action == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
action();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Token: 0x06002124 RID: 8484 RVA: 0x0008936C File Offset: 0x0008756C
|
||||
private Action TryDequActione()
|
||||
{
|
||||
if (!this.CanLoadWork)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Action result;
|
||||
if (!this.aQueue.TryDequeue(out result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Token: 0x04001341 RID: 4929
|
||||
protected ManualResetEvent tEvent;
|
||||
|
||||
// Token: 0x04001342 RID: 4930
|
||||
protected AutoResetEvent aEvent;
|
||||
|
||||
// Token: 0x04001343 RID: 4931
|
||||
private readonly ConcurrentQueue<Action> aQueue = new ConcurrentQueue<Action>();
|
||||
|
||||
// Token: 0x04001344 RID: 4932
|
||||
protected ManualResetEvent sEvent;
|
||||
|
||||
// Token: 0x04001345 RID: 4933
|
||||
protected Thread wThread;
|
||||
|
||||
// Token: 0x04001346 RID: 4934
|
||||
protected volatile bool terminating;
|
||||
|
||||
// Token: 0x04001347 RID: 4935
|
||||
private volatile bool started;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25f702c985f2a1b41990cc8fcdcb0450
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.Devices
|
||||
{
|
||||
@@ -45,6 +46,7 @@ namespace Assets.Scripts.Devices
|
||||
var adapter = adapters.FirstOrDefault(a => a.Interface == connectionInterface);
|
||||
if(adapter != null)
|
||||
{
|
||||
Debug.Log("bbbbbb " + (adapter.GetState().ToString()));
|
||||
return adapter.GetState();
|
||||
}
|
||||
return DeviceAdapterState.Unavailable;
|
||||
|
||||
Reference in new issue
Block a user