实现连接设备界面

This commit is contained in:
suntao committed 2021-04-01 11:09:53 +08:00
1 parent 996f924719
commit ec7589096f
88 files changed
+11344 -156

No files matched your search

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Devices.Ant
{
@@ -91,6 +92,11 @@ namespace Assets.Scripts.Devices.Ant
public bool pairingEnabled;
}
public virtual int Priority
{
get; protected set;
}
//private readonly Rhino.PowerFun.Services.DeviceService deviceService;
//private readonly DeviceDetailService _deviceDetailService;
//private readonly AntManufacturerService antManufacturerService;
@@ -172,6 +178,7 @@ namespace Assets.Scripts.Devices.Ant
{
if (State == DeviceState.Connected || State == DeviceState.Connecting)
return;
Debug.Log("连接" + this.DeviceNumber);
AntConnector.Instance().ConnectDevice(this);
}
@@ -179,6 +186,7 @@ namespace Assets.Scripts.Devices.Ant
{
if (State == DeviceState.Disconnected || State == DeviceState.Disconnecting)
return;
Debug.Log(this.DeviceNumber + "断开连接");
AntConnector.Instance().DisconnectDevice(this, save);
}
+8 -8
View File
@@ -68,10 +68,10 @@ namespace Assets.Scripts.Devices.Ant
//CheckStatus();
deviceList.Add(new FitDevice());
//deviceList.Add(new ds_AntPlus_Power());
//deviceList.Add(new ds_AntPlus_Cadence());
//deviceList.Add(new ds_AntPlus_HeartRate());
//deviceList.Add(new ds_AntPlus_BikeSpdCad());
deviceList.Add(new PowerDevice());
deviceList.Add(new CadenceDevice());
deviceList.Add(new HeartRateDevice());
deviceList.Add(new BikeSpdCadDevice());
var timer = new System.Timers.Timer(1000);
timer.AutoReset = true;
@@ -322,18 +322,18 @@ namespace Assets.Scripts.Devices.Ant
case SensorType.None:
break;
case SensorType.Cadence:
//device = new ds_AntPlus_Cadence();
device = new CadenceDevice();
break;
case SensorType.HeartRate:
//device = new ds_AntPlus_HeartRate();
device = new HeartRateDevice();
break;
case SensorType.Power:
//device = new ds_AntPlus_Power();
device = new PowerDevice();
break;
case SensorType.Speed:
break;
case SensorType.SpeedCadence:
//device = new ds_AntPlus_BikeSpdCad();
device = new BikeSpdCadDevice();
break;
case SensorType.Trainer:
device = new FitDevice();
@@ -0,0 +1,87 @@
using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ant.LegacyPages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant
{
public class BikeSpdCadDevice : AbstractAntDevice, ISpeedDevice, ICadenceDevice
{
public const int MAX_NO_EVENT_STOP_COUNT = 12;
public const double DEFAULT_WHEEL_CIRCUMFERENCE_m = 2096; //Average 700cx23mm road tire
public readonly double wheelCircumfrence_m;
private RotationData _speedData = new RotationData();
private RotationData _cadenceData = new RotationData();
public BikeSpdCadDevice(double wheelCircumfrence_m = DEFAULT_WHEEL_CIRCUMFERENCE_m)
: base("Ant+ Speed&Cadence", racerSportType.Biking, SensorType.SpeedCadence)
{
Priority = 1;
this.wheelCircumfrence_m = wheelCircumfrence_m;
}
public double Speed { get; set; }
private int _Cadence;
public int Cadence
{
get
{
return _Cadence;
}
set
{
_Cadence = DeviceValueFilter.Cadence(value);
}
}
DateTime now = DateTime.Now;
protected override AntChannelProfile getDefaultSearchProfile()
{
return new AntChannelProfile()
{
rfOffset = 57,
transType = 0,
deviceType = 121,
deviceNumber = 0,
messagePeriod = 8086,
pairingEnabled = false,
};
}
public override void handleChannelResponse(ANT_Managed_Library.ANT_Response response)
{
//商品Manufacturer Information
if (response.messageContents[1] == 2)
{
//Console.WriteLine(string.Join(",",response.messageContents));
base.ManufacturerId = response.messageContents[2];
}
if (response.responseID == (byte)ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E)
{
var dataPayload = response.messageContents.Skip(1).ToArray();
int newUpdate1 = (int)dataPayload[0] | (int)dataPayload[1] << 8;
int newCount1 = (int)dataPayload[2] | (int)dataPayload[3] << 8;
int newUpdate2 = (int)dataPayload[4] | (int)dataPayload[5] << 8;
int newCount2 = (int)dataPayload[6] | (int)dataPayload[7] << 8;
var cadence = this._cadenceData.CalculateRpm(now, newUpdate1, newCount1);
if (cadence.HasValue)
{
Cadence = cadence.Value;
}
var a2 = this._speedData.CalculateRpm(now, newUpdate2, newCount2);
if (a2.HasValue)
{
Speed = DoubleExtensions.CalculateSpeed(a2.Value, this.wheelCircumfrence_m);
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c53c916c5ea88d42921b701c66a8b2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,96 @@
using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ant.LegacyPages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant
{
public class CadenceDevice : AbstractAntDevice, ICadenceDevice
{
public const int MAX_NO_EVENT_STOP_COUNT = 12;
private int _Cadence;
public int Cadence
{
get
{
return _Cadence;
}
set
{
_Cadence = DeviceValueFilter.Cadence(value);
}
}
private DateTime _now = DateTime.Now;
private RotationData _rotationData = new RotationData();
public CadenceDevice()
: base("Ant+ Cadence", racerSportType.Biking, SensorType.Cadence)
{
Priority = 2;
//if (speedSensor.isInUse)
// throw new ArgumentException("Can't use a Speed Sensor that is already in use.");
//speedSensor.customSourceName = "Spd used by Cad w/Spd";
//speedSensor.isInUse = true;
//this.speedSensor = speedSensor;
//speedSensor.start(newSpeedSensorPacket); //Ensure speed sensor is always running so we can always get the values
}
//public override void start(Action<DataSourcePacket> distanceUpdateHandler)
//{
// //Reset the speed sensor first so we get back to a zero distance
// //speedSensor.stop();
// //lastDistance = 0;
// //speedSensor.reset();
// //speedSensor.start(newSpeedSensorPacket);
// base.start(distanceUpdateHandler);
//}
//public void newSpeedSensorPacket(DataSourcePacket pckt)
//{
// double distDiff = pckt.distance - lastDistance;
// if(distDiff > 0)
// incrementDistanceAndUpdate(distDiff, speedMs: pckt.speed_ms, cadence: calculatedCadence);
// lastDistance = pckt.distance;
//}
protected override AntChannelProfile getDefaultSearchProfile()
{
return new AntChannelProfile()
{
rfOffset = 57,
transType = 0,
deviceType = 122,
deviceNumber = 0,
messagePeriod = 8102,
pairingEnabled = false,
};
}
public override void handleChannelResponse(ANT_Managed_Library.ANT_Response response)
{
//商品Manufacturer Information
if (response.messageContents[1] == 2)
{
//Console.WriteLine(string.Join(",",response.messageContents));
base.ManufacturerId = response.messageContents[2];
}
if (response.responseID == (byte)ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E)
{
//In this decode we ignore page change toggle and page type, since the info we need is transmitted on every page
var page = response.messageContents.Skip(1).ToArray();
var cadence = this._rotationData.CalculateRpm(_now, (int)page[4] | (int)page[5] << 8, (int)page[6] | (int)page[7] << 8);
Cadence = cadence.GetValueOrDefault(0);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 992e04a304393e841ad0d13498a74587
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+2 -2
View File
@@ -20,7 +20,6 @@ namespace Assets.Scripts.Devices.Ant
int lastInstCadence = -1;
ushort lastInstPower = 0xFFFF;
/// <summary>
/// 当前坡度,当切换到其他模式的时候,需要把坡度设置为0
/// </summary>
@@ -29,6 +28,7 @@ namespace Assets.Scripts.Devices.Ant
public FitDevice()
: base("Ant+ Trainer", racerSportType.Unknown, SensorType.Trainer)
{
Priority = 0;
this.StateChange = (state) =>
{
if (state == DeviceState.Connected)
@@ -85,7 +85,7 @@ namespace Assets.Scripts.Devices.Ant
set
{
_Power = DeviceValueFilter.Power(value);
Debug.Log(_Power);
//Debug.Log(_Power);
}
}
@@ -0,0 +1,66 @@
using Assets.Scripts.Devices.Ant.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant
{
public class HeartRateDevice : AbstractAntDevice, IHeartRateDevice
{
//public const int MAX_NO_EVENT_STOP_COUNT = 12;
private int _HeartRate;
public int HeartRate
{
get
{
return _HeartRate;
}
set
{
_HeartRate = DeviceValueFilter.HeartRate(value);
}
}
//ds_AntPlus_BikeSpd speedSensor;
public HeartRateDevice()
: base("Ant+ HeartRate", racerSportType.Unknown, SensorType.HeartRate)
{
Priority = 2;
}
protected override AntChannelProfile getDefaultSearchProfile()
{
return new AntChannelProfile()
{
rfOffset = 57,
transType = 0,
deviceType = 120,
deviceNumber = 0,
messagePeriod = 8070,
pairingEnabled = false,
};
}
public override void handleChannelResponse(ANT_Managed_Library.ANT_Response response)
{
//Console.WriteLine(string.Join(",", response.messageContents));
//商品Manufacturer Information
if (response.messageContents[1] == 2)
{
base.ManufacturerId = response.messageContents[2];
}
if (response.responseID == (byte)ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E)
{
var num = response.messageContents[8];
//PubCommData.HeartRate = num;
HeartRate = num;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab69ac4f4621acc409ac4c6a29893f9f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 93809400c8fb6fb41aa3cb44471bf185
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.LegacyPages
{
public class RotationData
{
private static readonly TimeSpan TimeBeforeRollover = TimeSpan.FromSeconds(63.0);
private const int MaxUpdateValue = 65535;
private const int MaxCountValue = 65535;
private int? _update;
private int? _count;
private DateTimeOffset? _lastUpdatedTime;
private int _rpm;
public int? CalculateRpm(DateTimeOffset now, int newUpdate, int newCount)
{
DateTimeOffset time = now;
if (!this._lastUpdatedTime.HasValue)
{
this._update = new int?(newUpdate);
this._count = new int?(newCount);
this._lastUpdatedTime = new DateTimeOffset?(now);
return new int?();
}
if (this.HasNotMoved(newUpdate, time))
return new int?(0);
if (this.HasMoreThanOneRolloverOccurred(time))
{
this._lastUpdatedTime = new DateTimeOffset?(time);
this._update = new int?(newUpdate);
this._count = new int?(newCount);
return new int?();
}
if (this.HasNotUpdated(newUpdate))
return new int?(this._rpm);
if (this.HasUpdateRolledOver(newUpdate))
{
int? nullable = this._update;
this._update = nullable.HasValue ? new int?(nullable.GetValueOrDefault() - (int)ushort.MaxValue) : new int?();
}
if (this.HasCountRolledOver(newCount))
{
int? nullable = this._count;
this._count = nullable.HasValue ? new int?(nullable.GetValueOrDefault() - (int)ushort.MaxValue) : new int?();
}
this._rpm = (int)Math.Round((double)(newCount - this._count.Value) / (double)(newUpdate - this._update.Value) * 1024.0 * 60.0);
this._lastUpdatedTime = new DateTimeOffset?(time);
this._update = new int?(newUpdate);
this._count = new int?(newCount);
return new int?(this._rpm);
}
private bool HasMoreThanOneRolloverOccurred(DateTimeOffset time)
{
DateTimeOffset dateTimeOffset = time;
DateTimeOffset? nullable1 = this._lastUpdatedTime;
TimeSpan? nullable2 = nullable1.HasValue ? new TimeSpan?(dateTimeOffset - nullable1.GetValueOrDefault()) : new TimeSpan?();
TimeSpan timeSpan = RotationData.TimeBeforeRollover;
if (!nullable2.HasValue)
return false;
return nullable2.GetValueOrDefault() > timeSpan;
}
private bool HasCountRolledOver(int newCount)
{
int num = newCount;
int? nullable = this._count;
int valueOrDefault = nullable.GetValueOrDefault();
if (num >= valueOrDefault)
return false;
return nullable.HasValue;
}
private bool HasUpdateRolledOver(int newUpdate)
{
int num = newUpdate;
int? nullable = this._update;
int valueOrDefault = nullable.GetValueOrDefault();
if (num >= valueOrDefault)
return false;
return nullable.HasValue;
}
private bool HasNotUpdated(int newUpdate)
{
int num = newUpdate;
int? nullable = this._update;
int valueOrDefault = nullable.GetValueOrDefault();
if (num != valueOrDefault)
return false;
return nullable.HasValue;
}
private bool HasNotMoved(int newUpdate, DateTimeOffset time)
{
DateTimeOffset dateTimeOffset = time - TimeSpan.FromSeconds(4.0);
DateTimeOffset? nullable1 = this._lastUpdatedTime;
if ((nullable1.HasValue ? (dateTimeOffset >= nullable1.GetValueOrDefault() ? 1 : 0) : 0) == 0)
return false;
int num = newUpdate;
int? nullable2 = this._update;
int valueOrDefault = nullable2.GetValueOrDefault();
if (num != valueOrDefault)
return false;
return nullable2.HasValue;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 057a1149cfd54d24bb329df54a9c9dfa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Messages
{
/// <summary>
/// 手动校准命令
/// </summary>
public class ManualCalibrate : BaseMessage
{
public ManualCalibrate(byte channel)
: base(channel)
{
}
public override ANTMessage GetMessage()
{
return new ANTMessage()
{
Id = (byte)79,
Size = (byte)3,
Data = new byte[3]
{
this.Channel,
(byte) 1,
(byte) 170
}
};
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 30adc878ef1c2cb4fb944e55b2d3d439
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
using Assets.Scripts.Devices.Ant.Pages.CalibrationPages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages
{
/// <summary>
/// 设备校准的处理函数
/// </summary>
public class CalibrationPageHandler : IPageHandler
{
private readonly IList<ICalibrationPage> _calibrationPageHandlers = new List<ICalibrationPage>()
{
new CtfCalibrationPage(),
new FecCalibrationPage(),
new FailureCalibrationPage(),
new SuccessCalibrationPage()
};
public bool CanHandle(byte pageNumber)
{
if ((int)pageNumber != 1)
{
return (int)pageNumber == 240;
}
return true;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
var calibrationPage = this._calibrationPageHandlers.FirstOrDefault(ph => ph.CanHandle(dataPayload[1]));
if (calibrationPage == null)
return;
calibrationPage.Handle(dataPayload, device);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75c5b210cd34db1498a311b6656f9ad4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages.CalibrationPages
{
public class CtfCalibrationPage : ICalibrationPage
{
public bool CanHandle(byte calibrationId)
{
return (int)calibrationId == 16;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
var powerDevice = device as PowerDevice;
if (powerDevice == null || (int)dataPayload[2] != 1)
return;
//参考类CtfCalibrationResult
var offset = (int)((int)dataPayload[7] | (int)dataPayload[6] << 8);
powerDevice.DoCalibrationSuccess(offset, AutoZeroStatus.NotSupported);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 14c60f86d0918cf4d9656359561b9c7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages.CalibrationPages
{
/// <summary>
/// 校准失败的处理函数
/// </summary>
public class FailureCalibrationPage : ICalibrationPage
{
public bool CanHandle(byte calibrationId)
{
return (int)calibrationId == 175;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
var powerDevice = device as PowerDevice;
if (powerDevice == null)
return;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87c2482dbcf031e488a6dd7cfc3b475b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages.CalibrationPages
{
/// <summary>
/// 校准成功的处理函数
/// </summary>
public class SuccessCalibrationPage : ICalibrationPage
{
public bool CanHandle(byte calibrationId)
{
return (int)calibrationId == 172;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
//throw new NotImplementedException();
var powerDevice = device as PowerDevice;
if (powerDevice == null)
return;
var autoZeroStatus = (AutoZeroStatus)dataPayload[2];
var offset = (int)((int)dataPayload[7] << 8 | (int)dataPayload[6]);
powerDevice.DoCalibrationSuccess(offset, autoZeroStatus);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c45f42f53396ece499b5153107ab46da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages
{
public class CrankPowerPageHandler : IPageHandler
{
private readonly TorquePowerDataCalculator _torquePowerDataCalculator = new TorquePowerDataCalculator();
public bool CanHandle(byte pageNumber)
{
return (int)pageNumber == 18;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
var powerDevice = device as PowerDevice;
if (powerDevice == null)
return;
byte eventCount = dataPayload[1];
int num = (int)dataPayload[3];
ushort period = (ushort)((uint)dataPayload[4] | (uint)dataPayload[5] << 8);
ushort accumulatedTorque = (ushort)(dataPayload[6] | (uint)dataPayload[7] << 8);
this._torquePowerDataCalculator.Update(eventCount, period, accumulatedTorque);
//powerDevice.UpdateCadence(new int?(this._torquePowerDataCalculator.Rpm));
//powerDevice.UpdatePower(new int?(this._torquePowerDataCalculator.Power));
powerDevice.Cadence = this._torquePowerDataCalculator.Rpm;
powerDevice.Power = this._torquePowerDataCalculator.Power;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cb07cd617b03b2b4eb8df137b07ae7fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages
{
/// <summary>
/// Crank Torque-Frequency Message 曲柄功率计的消息
/// </summary>
public class CtfPageHandler : IPageHandler
{
private readonly CrankTorqueFrequencyData _crankTorqueFrequency = new CrankTorqueFrequencyData();
public bool CanHandle(byte pageNumber)
{
return (int)pageNumber == 32;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
var powerDevice = device as PowerDevice;
if (powerDevice == null)
{
return;
}
byte num1 = dataPayload[1];
ushort num2 = (ushort)((uint)dataPayload[3] | (uint)dataPayload[2] << 8);
ushort num3 = (ushort)((uint)dataPayload[5] | (uint)dataPayload[4] << 8);
ushort num4 = (ushort)((uint)dataPayload[7] | (uint)dataPayload[6] << 8);
if (powerDevice.Offset.HasValue)
{
int offset = powerDevice.Offset.GetValueOrDefault(0);
CrankTorqueFrequencyData torqueFrequencyData = this._crankTorqueFrequency;
int num5 = (int)num1;
int num6 = (int)num3;
int num7 = (int)num4;
int num8 = (int)num2;
torqueFrequencyData.Update((byte)num5, (ushort)num6, (ushort)num7, (ushort)num8, offset);
Console.WriteLine("offset:" + offset);
powerDevice.Power = this._crankTorqueFrequency.Power.GetValueOrDefault();
}
powerDevice.Cadence = this._crankTorqueFrequency.Cadence;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 20f678fa75e9edb48a11bed1c1284daf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages
{
public class PowerOnlyPageHandler : IPageHandler
{
private DateTime _lastEventChange = DateTime.UtcNow;
private byte _previousEventCount;
public bool CanHandle(byte pageNumber)
{
return (int)pageNumber == 16;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
var powerDevice = device as PowerDevice;
if (powerDevice == null)
{
return;
}
//var dataPayload = response.messageContents.Skip(1).ToArray();
byte currentVal = dataPayload[1];
if ((int)ByteExtensions.GetDifference(currentVal, this._previousEventCount) > 0) //ByteExtensions
this._lastEventChange = DateTime.UtcNow;
this._previousEventCount = currentVal;
powerDevice.Power = this.IsNotMovingDueToNoEventChanges() ? 0 : (ushort)((uint)dataPayload[6] | (uint)dataPayload[7] << 8);
if ((int)dataPayload[3] != (int)byte.MaxValue)
powerDevice.Cadence = (this.IsNotMovingDueToNoEventChanges() ? 0 : (int)dataPayload[3]);
else
powerDevice.Cadence = 0;
//int num1 = (int)dataPayload[2];
//int num2 = 128;
//bool flag = (uint)(num1 & num2) > 0U;
//int num3 = (int)sbyte.MaxValue;
//int num4 = num1 & num3;
//int num5 = (int)byte.MaxValue;
}
private bool IsNotMovingDueToNoEventChanges()
{
return this._lastEventChange.AddSeconds(3.0) <= DateTime.UtcNow;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 267f065da5b080847b6c2354e6af58f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Pages
{
/// <summary>
/// 轮盘功率计
/// </summary>
public class WheelPowerPageHandler : IPageHandler
{
private readonly TorquePowerDataCalculator _torquePowerDataCalculator = new TorquePowerDataCalculator();
public bool CanHandle(byte pageNumber)
{
return pageNumber == 17;
}
public void Handle(byte[] dataPayload, AbstractAntDevice device)
{
//throw new NotImplementedException();
var powerDevice = device as PowerDevice;
if (powerDevice == null)
return;
var eventCount = dataPayload[1];
var num = dataPayload[3];
var period = (ushort)((uint)dataPayload[4] | (uint)dataPayload[5] << 8);
var accumulatedTorque = (ushort)((uint)dataPayload[6] | (uint)dataPayload[7] << 8);
_torquePowerDataCalculator.Update(eventCount, period, accumulatedTorque);
powerDevice.Power = _torquePowerDataCalculator.Power;
powerDevice.Cadence = num;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 399061526025e6148ba3f55a5800ed56
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+316
View File
@@ -0,0 +1,316 @@
using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ant.Messages;
using Assets.Scripts.Devices.Ant.Pages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace Assets.Scripts.Devices.Ant
{
/// <summary>
/// 功率计
/// </summary>
public class PowerDevice : AbstractAntDevice, IPowerDevice //, ICadenceDevice
{
private DateTime _lastEventChange = DateTime.UtcNow;
//private byte _previousEventCount;
private readonly TorquePowerDataCalculator _torquePowerDataCalculator = new TorquePowerDataCalculator();
private readonly CrankTorqueFrequencyData _crankTorqueFrequency = new CrankTorqueFrequencyData();
private System.Timers.Timer timer;
//List<IPageHandler> pageHandlers;
public PowerDevice()
: base("Ant+ Power", racerSportType.Biking, SensorType.Power)
{
Priority = 2;
//pageHandlers = new List<IPageHandler>();
//pageHandlers.Add(new ManufacturerDataPageHandler());
pageHandlers.Add(new PowerOnlyPageHandler());
pageHandlers.Add(new CrankPowerPageHandler());
pageHandlers.Add(new CtfPageHandler());
pageHandlers.Add(new WheelPowerPageHandler());
pageHandlers.Add(new CalibrationPageHandler());
this.StateChange = (state) =>
{
if (state == DeviceState.Connected)
{
Calibrate();
}
else if (state == DeviceState.Disconnected)
{
count = 0;
if (timer != null)
{
timer.Stop();
timer = null;
DoCalibrationFailure();
}
}
};
}
private int _Power;
public int Power
{
get
{
return _Power;
}
set
{
_Power = DeviceValueFilter.Power(value);
}
}
private int _Cadence;
public int Cadence
{
get
{
return _Cadence;
}
set
{
_Cadence = DeviceValueFilter.Cadence(value);
}
}
public DateTime LastCalibration { get; protected set; }
public int? Offset { get; set; }
public AutoZeroStatus AutoZeroStatus { get; private set; }
public bool IsCalibrating { get; set; }
public bool IsCalibrationReady { get; set; }
public bool CalibrationCompleted { get; set; }
protected override AntChannelProfile getDefaultSearchProfile()
{
return new AntChannelProfile()
{
rfOffset = 57,
transType = 0,
deviceType = 11,
deviceNumber = 0,
messagePeriod = 8182,
pairingEnabled = false,
};
}
//List<int> list = new List<int>();
public override void handleChannelResponse(ANT_Managed_Library.ANT_Response response)
{
//if (!list.Contains(response.messageContents[1]))
//{
// list.Add(response.messageContents[1]);
// Console.WriteLine(response.messageContents[1]);
//}
//Console.WriteLine(string.Join(",", response.messageContents));
if (response.responseID == (byte)ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E)
{
var pageNumber = response.messageContents[1];
foreach (var handler in pageHandlers)
{
if (handler.CanHandle(pageNumber))
{
handler.Handle(response.messageContents.Skip(1).ToArray(), this);
}
}
}
//System.IO.File.AppendAllText(System.Environment.CurrentDirectory + "\\data.txt", string.Join(",", response.messageContents) + ";" + Power+"\r\n");
//Console.WriteLine();
}
internal void DoCalibrationSuccess(int? offset, AutoZeroStatus autoZeroStatus)
{
this.AutoZeroStatus = autoZeroStatus;
this.LastCalibration = DateTime.UtcNow;
if (this.Offset.HasValue == false)
{
this.Offset = offset;
//this.Offset = 570;
}
this.IsCalibrating = false;
this.CalibrationCompleted = true;
//Trace.WriteLine($"校准成功:{ offset }");
}
internal void DoCalibrationFailure()
{
this.IsCalibrating = false;
this.CalibrationCompleted = false;
}
/// <summary>
/// 发送校准命令
/// </summary>
public void Calibrate()
{
if (this.IsCalibrating)
return;
this.IsCalibrating = true;
var channelId = GetChannelId();
base.SendMessage(new ManualCalibrate(channelId));
if (timer == null)
{
timer = new System.Timers.Timer(10000);
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
}
int count = 0;
/// <summary>
/// 检查是否已自动校准,自动发送3次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (count > 3)
{
timer.Stop();
return;
}
if (this.CalibrationCompleted)
{
timer.Stop();
return;
}
count++;
this.IsCalibrating = false;
this.Calibrate();
}
}
public class TorquePowerDataCalculator
{
private DateTime _lastEventChange = DateTime.UtcNow;
private byte _previousEventCount;
private ushort _previousPeriod;
private ushort _previousAccumulatedTorque;
public int Power { get; private set; }
public int Rpm { get; private set; }
public DateTime LastUpdatedTime { get; private set; }
public TorquePowerDataCalculator()
{
this.LastUpdatedTime = DateTime.MinValue;
}
public void Update(byte eventCount, ushort period, ushort accumulatedTorque)
{
byte difference1 = ByteExtensions.GetDifference(eventCount, this._previousEventCount);
ushort difference2 = UshortExtensions.GetDifference(period, this._previousPeriod);
if ((int)difference1 > 0)
this._lastEventChange = DateTime.UtcNow;
if (this.PeriodRolloverPossible() || this.IsNotMovingDueToNoEventChanges() || (int)difference2 == 0 && (int)difference1 > 0)
{
this.Power = 0;
this.Rpm = 0;
}
else if ((int)difference1 > 0)
{
this.Power = (int)(402.123859659494 * ((double)UshortExtensions.GetDifference(accumulatedTorque, this._previousAccumulatedTorque) / (double)difference2));
this.Rpm = 122880 * (int)difference1 / (int)difference2;
}
this.LastUpdatedTime = DateTime.UtcNow;
this._previousEventCount = eventCount;
this._previousPeriod = period;
this._previousAccumulatedTorque = accumulatedTorque;
}
private bool PeriodRolloverPossible()
{
return DateTime.UtcNow - this.LastUpdatedTime > TimeSpan.FromSeconds(32.0);
}
private bool IsNotMovingDueToNoEventChanges()
{
return this._lastEventChange.AddSeconds(3.0) <= DateTime.UtcNow;
}
}
public class CrankTorqueFrequencyData
{
private byte _previousEventCount;
private ushort _previousTimestamp;
private ushort _previousTorqueTicksStamp;
private int _noEventCount;
public int? Power { get; private set; }
public int Cadence { get; private set; }
public DateTime LastUpdatedTime { get; set; }
public CrankTorqueFrequencyData()
{
this.LastUpdatedTime = DateTime.MinValue;
}
public void Update(byte eventCount, ushort timestamp, ushort torqueTicksStamp, ushort slope, int offset)
{
byte difference1 = ByteExtensions.GetDifference(eventCount, this._previousEventCount);
ushort difference2 = UshortExtensions.GetDifference(timestamp, this._previousTimestamp);
ushort difference3 = UshortExtensions.GetDifference(torqueTicksStamp, this._previousTorqueTicksStamp);
this._previousEventCount = eventCount;
this._previousTimestamp = timestamp;
this._previousTorqueTicksStamp = torqueTicksStamp;
if (this.HasMoreThanOneRolloverOccured())
{
this.LastUpdatedTime = DateTime.UtcNow;
this.Power = new int?(0);
this.Cadence = 0;
}
else
{
this.LastUpdatedTime = DateTime.UtcNow;
if ((int)difference1 > 0)
{
this._noEventCount = 0;
double num = (double)difference2 / (double)difference1 * 0.0005;
if ((int)difference2 == 0)
return;
this.Cadence = (int)Math.Round(60.0 / num);
if ((int)difference3 == 0)
this.Power = new int?();
else
this.Power = new int?((int)((1.0 / ((double)difference2 * 0.0005 / (double)difference3) - (double)offset) / ((double)slope / 10.0) * (double)this.Cadence * 3.14159265358979 / 30.0));
}
else
{
int num = this._noEventCount + 1;
this._noEventCount = num;
if (num < 12)
return;
this.Power = new int?(0);
this.Cadence = 0;
}
}
}
private bool HasMoreThanOneRolloverOccured()
{
return DateTime.UtcNow - this.LastUpdatedTime > TimeSpan.FromSeconds(32.7);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ffde5f7de300274b8b12fe730cb7de1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: