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 { /// /// 功率计 /// 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 pageHandlers; public PowerDevice(string id) : base(id, "Ant+ Power", racerSportType.Biking, SensorType.Power) { Priority = 2; //pageHandlers = new List(); //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 list = new List(); 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; } /// /// 发送校准命令 /// 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; /// /// 检查是否已自动校准,自动发送3次 /// /// /// 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); } } }