diff --git a/AntPlus.Extensions.Hosting/AntCollection.cs b/AntPlus.Extensions.Hosting/AntCollection.cs index 257f4f5b..f9ecf65d 100644 --- a/AntPlus.Extensions.Hosting/AntCollection.cs +++ b/AntPlus.Extensions.Hosting/AntCollection.cs @@ -70,8 +70,6 @@ public async Task StartScanning() /// The ANT response. private void MessageHandler(object? sender, AntResponse e) { - _logger.LogAntResponse(LogLevel.Trace, e); - // check for a valid payload if (e.Payload == null || e.Payload.Length < 3) { diff --git a/AntPlus.Extensions.Hosting/Hosting.csproj b/AntPlus.Extensions.Hosting/Hosting.csproj index 619b7e3e..072ceed3 100644 --- a/AntPlus.Extensions.Hosting/Hosting.csproj +++ b/AntPlus.Extensions.Hosting/Hosting.csproj @@ -17,11 +17,12 @@ PackageLogo.png readme.md - Updated NuGet dependencies. + 1. Removed unnecessary logging. + 2. Updated NuGet dependencies. True snupkg - 2.0.6 + 2.0.7 https://stephenhidem.github.io/AntPlus https://github.com/StephenHidem/AntPlus Debug;Release @@ -41,8 +42,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/AntPlus/AntDeviceCollection.cs b/AntPlus/AntDeviceCollection.cs index 2639b3fe..93d85aa0 100644 --- a/AntPlus/AntDeviceCollection.cs +++ b/AntPlus/AntDeviceCollection.cs @@ -81,8 +81,6 @@ public async Task StartScanning() private void MessageHandler(object? sender, AntResponse e) { - _logger.LogAntResponse(LogLevel.Trace, e); - // check for a valid payload if (e.Payload == null || e.Payload.Length < 3) { diff --git a/AntPlus/AntPlus.csproj b/AntPlus/AntPlus.csproj index 1fedd599..3dfe1601 100644 --- a/AntPlus/AntPlus.csproj +++ b/AntPlus/AntPlus.csproj @@ -5,7 +5,7 @@ True SmallEarthTech.$(MSBuildProjectName.Replace(" ", "_")) SmallEarthTech.$(AssemblyName) - 6.2.0 + 6.2.1 ANT+ Class Library https://stephenhidem.github.io/AntPlus Stephen Hidem @@ -23,7 +23,7 @@ PackageLogo.png readme.md - 1. Added UnknownDataPageReceived event to AntDevice class. This allows applications to receive data pages that are not defined in the library. This is useful for receiving custom data pages or data pages that have not yet been added to the library. + 1. Improved logging with ANT device number scopes. 2. Updated NuGet dependencies. Debug;Release @@ -95,9 +95,9 @@ - - - + + + diff --git a/AntPlus/DeviceProfiles/AssetTracker/Tracker.cs b/AntPlus/DeviceProfiles/AssetTracker/Tracker.cs index c4a02781..c933df72 100644 --- a/AntPlus/DeviceProfiles/AssetTracker/Tracker.cs +++ b/AntPlus/DeviceProfiles/AssetTracker/Tracker.cs @@ -1,6 +1,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.Extensions.Logging; using SmallEarthTech.AntRadioInterface; +using System; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -50,7 +51,7 @@ public enum DataPage /// /// This ensures changes to the collection are thread safe and marshalled on the UI thread. /// - public object CollectionLock = new object(); + public readonly object CollectionLock = new(); /// Gets the collection of assets being tracked. public ObservableCollection Assets { get; } = new ObservableCollection(); @@ -89,48 +90,51 @@ public Tracker(ChannelId channelId, IAntChannel antChannel, ILogger log /// The asset is removed from the collection if the flag is set. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - switch ((DataPage)dataPage[0]) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - case DataPage.AssetLocation1: - Asset asset = GetAsset(dataPage); - asset.ParseLocation1(dataPage); - if (asset.Status.HasFlag(Asset.AssetStatus.RemoveAsset)) - { - lock (CollectionLock) + base.Parse(dataPage); + switch ((DataPage)dataPage[0]) + { + case DataPage.AssetLocation1: + Asset asset = GetAsset(dataPage); + asset.ParseLocation1(dataPage); + if (asset.Status.HasFlag(Asset.AssetStatus.RemoveAsset)) + { + lock (CollectionLock) + { + Assets.Remove(asset); + } + } + break; + case DataPage.AssetLocation2: + GetAsset(dataPage).ParseLocation2(dataPage); + break; + case DataPage.NoAssets: + if (Assets.Count > 0) { - Assets.Remove(asset); + lock (CollectionLock) + { + Assets.Clear(); + } } - } - break; - case DataPage.AssetLocation2: - GetAsset(dataPage).ParseLocation2(dataPage); - break; - case DataPage.NoAssets: - if (Assets.Count > 0) - { - lock (CollectionLock) + break; + case DataPage.AssetId1: + GetAsset(dataPage).ParseIdPage1(dataPage); + break; + case DataPage.AssetId2: + GetAsset(dataPage).ParseIdPage2(dataPage); + break; + case DataPage.DisconnectCommand: + Disconnected = true; + break; + default: + // Handle common data pages and unknown data pages. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) { - Assets.Clear(); + OnUnknownDataPageReceived(dataPage); } - } - break; - case DataPage.AssetId1: - GetAsset(dataPage).ParseIdPage1(dataPage); - break; - case DataPage.AssetId2: - GetAsset(dataPage).ParseIdPage2(dataPage); - break; - case DataPage.DisconnectCommand: - Disconnected = true; - break; - default: - // Handle common data pages and unknown data pages. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); - } - break; + break; + } } } diff --git a/AntPlus/DeviceProfiles/BicyclePower/BicyclePower.cs b/AntPlus/DeviceProfiles/BicyclePower/BicyclePower.cs index 9e8d96f8..7a8fa7e1 100644 --- a/AntPlus/DeviceProfiles/BicyclePower/BicyclePower.cs +++ b/AntPlus/DeviceProfiles/BicyclePower/BicyclePower.cs @@ -101,13 +101,13 @@ public static BicyclePower GetBicyclePowerSensor(byte[] dataPage, ChannelId chan if ((DataPage)dataPage[0] == DataPage.CrankTorqueFrequency) { // return CTF sensor - CrankTorqueFrequencySensor sensor = new CrankTorqueFrequencySensor(channelId, antChannel, loggerFactory.CreateLogger(), timeout); + CrankTorqueFrequencySensor sensor = new(channelId, antChannel, loggerFactory.CreateLogger(), timeout); sensor.Parse(dataPage); return sensor; } else { - StandardPowerSensor sensor = new StandardPowerSensor(channelId, antChannel, loggerFactory.CreateLogger(), timeout); + StandardPowerSensor sensor = new(channelId, antChannel, loggerFactory.CreateLogger(), timeout); sensor.Parse(dataPage); return sensor; } diff --git a/AntPlus/DeviceProfiles/BicyclePower/Calibration.cs b/AntPlus/DeviceProfiles/BicyclePower/Calibration.cs index 452f74f3..aeb16e7f 100644 --- a/AntPlus/DeviceProfiles/BicyclePower/Calibration.cs +++ b/AntPlus/DeviceProfiles/BicyclePower/Calibration.cs @@ -63,7 +63,7 @@ public partial class StandardPowerSensor /// /// This ensures changes to the collection are thread safe and marshalled on the UI thread. /// - public object CollectionLock = new object(); + public readonly object CollectionLock = new(); /// Gets a value indicating whether automatic zero is supported. [ObservableProperty] private bool autoZeroSupported; diff --git a/AntPlus/DeviceProfiles/BicyclePower/CrankTorqueFrequencySensor.cs b/AntPlus/DeviceProfiles/BicyclePower/CrankTorqueFrequencySensor.cs index 59a0c26c..9d752009 100644 --- a/AntPlus/DeviceProfiles/BicyclePower/CrankTorqueFrequencySensor.cs +++ b/AntPlus/DeviceProfiles/BicyclePower/CrankTorqueFrequencySensor.cs @@ -74,18 +74,21 @@ public CrankTorqueFrequencySensor(ChannelId channelId, IAntChannel antChannel, I /// The data page. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - switch ((DataPage)dataPage[0]) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - case DataPage.Calibration: - ParseCalibrationMessage(dataPage); - break; - case DataPage.CrankTorqueFrequency: - ParseCTFMessage(dataPage); - break; - default: - OnUnknownDataPageReceived(dataPage); - break; + base.Parse(dataPage); + switch ((DataPage)dataPage[0]) + { + case DataPage.Calibration: + ParseCalibrationMessage(dataPage); + break; + case DataPage.CrankTorqueFrequency: + ParseCTFMessage(dataPage); + break; + default: + OnUnknownDataPageReceived(dataPage); + break; + } } } diff --git a/AntPlus/DeviceProfiles/BicyclePower/StandardPowerSensor.cs b/AntPlus/DeviceProfiles/BicyclePower/StandardPowerSensor.cs index dcfc4a7e..ddb88e53 100644 --- a/AntPlus/DeviceProfiles/BicyclePower/StandardPowerSensor.cs +++ b/AntPlus/DeviceProfiles/BicyclePower/StandardPowerSensor.cs @@ -85,52 +85,55 @@ public StandardPowerSensor(ChannelId channelId, IAntChannel antChannel, ILogger< /// public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - switch ((DataPage)dataPage[0]) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - case DataPage.Calibration: - ParseCalibrationPage(dataPage); - break; - case DataPage.GetSetParameters: - ParseParameters(dataPage); - break; - case DataPage.MeasurementOutput: - ParseMeasurementOutputData(dataPage); - break; - case DataPage.PowerOnly: - ParsePowerOnly(dataPage); - break; - case DataPage.WheelTorque: - TorqueSensor ??= new StandardWheelTorqueSensor(_logger); - TorqueSensor.ParseTorque(dataPage); - break; - case DataPage.CrankTorque: - TorqueSensor ??= new StandardCrankTorqueSensor(_logger); - TorqueSensor.ParseTorque(dataPage); - break; - case DataPage.TorqueEffectivenessAndPedalSmoothness: - ParseTEPS(dataPage); - break; - case DataPage.TorqueBarycenter: - case DataPage.RightForceAngle: - case DataPage.LeftForceAngle: - case DataPage.PedalPosition: - if (TorqueSensor is StandardCrankTorqueSensor sensor) - { - sensor.ParseCyclingDynamics(dataPage); - } - else - { - _logger.LogIgnoredDataPage(dataPage); - } - break; - default: - // attempt to parse common data pages, otherwise log as unknown - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); - } - break; + base.Parse(dataPage); + switch ((DataPage)dataPage[0]) + { + case DataPage.Calibration: + ParseCalibrationPage(dataPage); + break; + case DataPage.GetSetParameters: + ParseParameters(dataPage); + break; + case DataPage.MeasurementOutput: + ParseMeasurementOutputData(dataPage); + break; + case DataPage.PowerOnly: + ParsePowerOnly(dataPage); + break; + case DataPage.WheelTorque: + TorqueSensor ??= new StandardWheelTorqueSensor(_logger); + TorqueSensor.ParseTorque(dataPage); + break; + case DataPage.CrankTorque: + TorqueSensor ??= new StandardCrankTorqueSensor(_logger); + TorqueSensor.ParseTorque(dataPage); + break; + case DataPage.TorqueEffectivenessAndPedalSmoothness: + ParseTEPS(dataPage); + break; + case DataPage.TorqueBarycenter: + case DataPage.RightForceAngle: + case DataPage.LeftForceAngle: + case DataPage.PedalPosition: + if (TorqueSensor is StandardCrankTorqueSensor sensor) + { + sensor.ParseCyclingDynamics(dataPage); + } + else + { + _logger.LogIgnoredDataPage(dataPage); + } + break; + default: + // attempt to parse common data pages, otherwise log as unknown + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } + break; + } } } diff --git a/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CombinedSpeedAndCadenceSensor.cs b/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CombinedSpeedAndCadenceSensor.cs index c84a13a8..ba4bc924 100644 --- a/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CombinedSpeedAndCadenceSensor.cs +++ b/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CombinedSpeedAndCadenceSensor.cs @@ -67,38 +67,41 @@ public override void Parse(byte[] dataPage) int deltaEventTime; int deltaRevCount; - base.Parse(dataPage); - - if (isFirstDataMessage) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - isFirstDataMessage = false; - prevSpeedEventTime = BitConverter.ToUInt16(dataPage, 4); - prevCadenceEventTime = BitConverter.ToUInt16(dataPage, 0); - prevSpeedRevCount = BitConverter.ToUInt16(dataPage, 6); - prevCadenceRevCount = BitConverter.ToUInt16(dataPage, 2); - lastDataPage = dataPage; - return; - } + base.Parse(dataPage); - // ignore duplicate/unchanged data pages - if (lastDataPage.SequenceEqual(dataPage)) - { - return; - } + if (isFirstDataMessage) + { + isFirstDataMessage = false; + prevSpeedEventTime = BitConverter.ToUInt16(dataPage, 4); + prevCadenceEventTime = BitConverter.ToUInt16(dataPage, 0); + prevSpeedRevCount = BitConverter.ToUInt16(dataPage, 6); + prevCadenceRevCount = BitConverter.ToUInt16(dataPage, 2); + lastDataPage = dataPage; + return; + } - deltaEventTime = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 0), ref prevCadenceEventTime); - if (deltaEventTime != 0) - { - deltaRevCount = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 2), ref prevCadenceRevCount); - InstantaneousCadence = 60.0 * deltaRevCount * 1024.0 / deltaEventTime; - } + // ignore duplicate/unchanged data pages + if (lastDataPage.SequenceEqual(dataPage)) + { + return; + } - deltaEventTime = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 4), ref prevSpeedEventTime); - if (deltaEventTime != 0) - { - deltaRevCount = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 6), ref prevSpeedRevCount); - InstantaneousSpeed = WheelCircumference * deltaRevCount * 1024.0 / deltaEventTime; - AccumulatedDistance += WheelCircumference * deltaRevCount; + deltaEventTime = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 0), ref prevCadenceEventTime); + if (deltaEventTime != 0) + { + deltaRevCount = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 2), ref prevCadenceRevCount); + InstantaneousCadence = 60.0 * deltaRevCount * 1024.0 / deltaEventTime; + } + + deltaEventTime = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 4), ref prevSpeedEventTime); + if (deltaEventTime != 0) + { + deltaRevCount = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 6), ref prevSpeedRevCount); + InstantaneousSpeed = WheelCircumference * deltaRevCount * 1024.0 / deltaEventTime; + AccumulatedDistance += WheelCircumference * deltaRevCount; + } } } diff --git a/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CommonSpeedCadence.cs b/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CommonSpeedCadence.cs index 6f9a7ec6..e76e7b96 100644 --- a/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CommonSpeedCadence.cs +++ b/AntPlus/DeviceProfiles/BikeSpeedAndCadence/CommonSpeedCadence.cs @@ -134,55 +134,58 @@ public CommonSpeedCadence(ChannelId channelId, IAntChannel antChannel, ILogger l /// public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - - if (isFirstDataMessage) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - isFirstDataMessage = false; - observedToggle = dataPage[0] & 0x80; - prevEventTime = BitConverter.ToUInt16(dataPage, 4); - prevRevCount = BitConverter.ToUInt16(dataPage, 6); + base.Parse(dataPage); + + if (isFirstDataMessage) + { + isFirstDataMessage = false; + observedToggle = dataPage[0] & 0x80; + prevEventTime = BitConverter.ToUInt16(dataPage, 4); + prevRevCount = BitConverter.ToUInt16(dataPage, 6); + lastDataPage = dataPage; + return; + } + + // ignore duplicate/unchanged data pages + if (lastDataPage.SequenceEqual(dataPage)) + { + return; + } lastDataPage = dataPage; - return; - } - - // ignore duplicate/unchanged data pages - if (lastDataPage.SequenceEqual(dataPage)) - { - return; - } - lastDataPage = dataPage; - // handle data page toggle - if (!pageToggle) - { - pageToggle = (dataPage[0] & 0x80) != observedToggle; - if (!pageToggle) return; - } - - switch ((DataPage)(dataPage[0] & 0x7F)) - { - case DataPage.Default: - // the default page is handled by the derived classes - break; - case DataPage.CumulativeOperatingTime: - CumulativeOperatingTime = TimeSpan.FromSeconds((BitConverter.ToUInt32(dataPage, 1) & 0x00FFFFFF) * 2.0); - break; - case DataPage.ManufacturerInfo: - ManufacturerInfo = new ManufacturerInfoPage(dataPage, ChannelId.DeviceNumber); - break; - case DataPage.ProductInfo: - ProductInfo = new ProductInfoPage(dataPage); - break; - case DataPage.BatteryStatus: - BatteryStatus = new BatteryStatusPage(dataPage); - break; - case DataPage.Motion: - Stopped = dataPage[1] == 0x01; - break; - default: - OnUnknownDataPageReceived(dataPage); - break; + // handle data page toggle + if (!pageToggle) + { + pageToggle = (dataPage[0] & 0x80) != observedToggle; + if (!pageToggle) return; + } + + switch ((DataPage)(dataPage[0] & 0x7F)) + { + case DataPage.Default: + // the default page is handled by the derived classes + break; + case DataPage.CumulativeOperatingTime: + CumulativeOperatingTime = TimeSpan.FromSeconds((BitConverter.ToUInt32(dataPage, 1) & 0x00FFFFFF) * 2.0); + break; + case DataPage.ManufacturerInfo: + ManufacturerInfo = new ManufacturerInfoPage(dataPage, ChannelId.DeviceNumber); + break; + case DataPage.ProductInfo: + ProductInfo = new ProductInfoPage(dataPage); + break; + case DataPage.BatteryStatus: + BatteryStatus = new BatteryStatusPage(dataPage); + break; + case DataPage.Motion: + Stopped = dataPage[1] == 0x01; + break; + default: + OnUnknownDataPageReceived(dataPage); + break; + } } } } diff --git a/AntPlus/DeviceProfiles/FitnessEquipment/Climber.cs b/AntPlus/DeviceProfiles/FitnessEquipment/Climber.cs index d8837084..304a0626 100644 --- a/AntPlus/DeviceProfiles/FitnessEquipment/Climber.cs +++ b/AntPlus/DeviceProfiles/FitnessEquipment/Climber.cs @@ -54,31 +54,34 @@ public enum CapabilityFlags /// The data page. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (handledPage) return; - - if ((DataPage)dataPage[0] == DataPage.ClimberData) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - HandleFEState(dataPage); - if (isFirstDataMessage) + base.Parse(dataPage); + if (handledPage) return; + + if ((DataPage)dataPage[0] == DataPage.ClimberData) { - isFirstDataMessage = false; - prevStride = dataPage[3]; + HandleFEState(dataPage); + if (isFirstDataMessage) + { + isFirstDataMessage = false; + prevStride = dataPage[3]; + } + else + { + StrideCycles += Utils.CalculateDelta(dataPage[3], ref prevStride); + } + Cadence = dataPage[4]; + InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); + Capabilities = (CapabilityFlags)(dataPage[7] & 0x01); } else { - StrideCycles += Utils.CalculateDelta(dataPage[3], ref prevStride); - } - Cadence = dataPage[4]; - InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); - Capabilities = (CapabilityFlags)(dataPage[7] & 0x01); - } - else - { - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } } } } diff --git a/AntPlus/DeviceProfiles/FitnessEquipment/Elliptical.cs b/AntPlus/DeviceProfiles/FitnessEquipment/Elliptical.cs index 9b4bc498..f847d09c 100644 --- a/AntPlus/DeviceProfiles/FitnessEquipment/Elliptical.cs +++ b/AntPlus/DeviceProfiles/FitnessEquipment/Elliptical.cs @@ -63,33 +63,36 @@ public enum CapabilityFlags /// The data page. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (handledPage) return; - - if ((DataPage)dataPage[0] == DataPage.EllipticalData) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - HandleFEState(dataPage); - if (isFirstDataMessage) + base.Parse(dataPage); + if (handledPage) return; + + if ((DataPage)dataPage[0] == DataPage.EllipticalData) { - isFirstDataMessage = false; - prevPos = dataPage[2]; - prevStride = dataPage[3]; + HandleFEState(dataPage); + if (isFirstDataMessage) + { + isFirstDataMessage = false; + prevPos = dataPage[2]; + prevStride = dataPage[3]; + } + else + { + PosVerticalDistance += Utils.CalculateDelta(dataPage[2], ref prevPos) / 10.0; + StrideCount += Utils.CalculateDelta(dataPage[3], ref prevStride); + } + Cadence = dataPage[4]; + InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); + Capabilities = (CapabilityFlags)(dataPage[7] & 0x03); } else { - PosVerticalDistance += Utils.CalculateDelta(dataPage[2], ref prevPos) / 10.0; - StrideCount += Utils.CalculateDelta(dataPage[3], ref prevStride); - } - Cadence = dataPage[4]; - InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); - Capabilities = (CapabilityFlags)(dataPage[7] & 0x03); - } - else - { - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } } } } diff --git a/AntPlus/DeviceProfiles/FitnessEquipment/NordicSkier.cs b/AntPlus/DeviceProfiles/FitnessEquipment/NordicSkier.cs index 6a5b213c..edf751fa 100644 --- a/AntPlus/DeviceProfiles/FitnessEquipment/NordicSkier.cs +++ b/AntPlus/DeviceProfiles/FitnessEquipment/NordicSkier.cs @@ -56,31 +56,34 @@ public enum CapabilityFlags /// The data page. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (handledPage) return; - - if ((DataPage)dataPage[0] == DataPage.NordicSkierData) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - HandleFEState(dataPage); - if (isFirstDataMessage) + base.Parse(dataPage); + if (handledPage) return; + + if ((DataPage)dataPage[0] == DataPage.NordicSkierData) { - isFirstDataMessage = false; - prevStride = dataPage[3]; + HandleFEState(dataPage); + if (isFirstDataMessage) + { + isFirstDataMessage = false; + prevStride = dataPage[3]; + } + else + { + StrideCount += Utils.CalculateDelta(dataPage[3], ref prevStride); + } + Cadence = dataPage[4]; + InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); + Capabilities = (CapabilityFlags)(dataPage[7] & 0x01); } else { - StrideCount += Utils.CalculateDelta(dataPage[3], ref prevStride); - } - Cadence = dataPage[4]; - InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); - Capabilities = (CapabilityFlags)(dataPage[7] & 0x01); - } - else - { - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } } } } diff --git a/AntPlus/DeviceProfiles/FitnessEquipment/Rower.cs b/AntPlus/DeviceProfiles/FitnessEquipment/Rower.cs index 876a411e..9ab5280f 100644 --- a/AntPlus/DeviceProfiles/FitnessEquipment/Rower.cs +++ b/AntPlus/DeviceProfiles/FitnessEquipment/Rower.cs @@ -56,31 +56,34 @@ public enum CapabilityFlags /// The data page. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (handledPage) return; - - if ((DataPage)dataPage[0] == DataPage.RowerData) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - HandleFEState(dataPage); - if (isFirstDataMessage) + base.Parse(dataPage); + if (handledPage) return; + + if ((DataPage)dataPage[0] == DataPage.RowerData) { - isFirstDataMessage = false; - prevStroke = dataPage[3]; + HandleFEState(dataPage); + if (isFirstDataMessage) + { + isFirstDataMessage = false; + prevStroke = dataPage[3]; + } + else + { + StrokeCount += Utils.CalculateDelta(dataPage[3], ref prevStroke); + } + Cadence = dataPage[4]; + InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); + Capabilities = (CapabilityFlags)(dataPage[7] & 0x01); } else { - StrokeCount += Utils.CalculateDelta(dataPage[3], ref prevStroke); - } - Cadence = dataPage[4]; - InstantaneousPower = BitConverter.ToUInt16(dataPage, 5); - Capabilities = (CapabilityFlags)(dataPage[7] & 0x01); - } - else - { - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } } } } diff --git a/AntPlus/DeviceProfiles/FitnessEquipment/StationaryBike.cs b/AntPlus/DeviceProfiles/FitnessEquipment/StationaryBike.cs index 6b90e5d8..9935b745 100644 --- a/AntPlus/DeviceProfiles/FitnessEquipment/StationaryBike.cs +++ b/AntPlus/DeviceProfiles/FitnessEquipment/StationaryBike.cs @@ -40,21 +40,24 @@ public StationaryBike(ChannelId channelId, IAntChannel antChannel, ILogger logge /// A byte array containing the data page to parse. override public void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (handledPage) return; - - if ((DataPage)dataPage[0] == DataPage.StationaryBikeData) - { - HandleFEState(dataPage); - Cadence = dataPage[4]; - InstantaneousPower = BitConverter.ToInt16(dataPage, 5); - } - else + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) + base.Parse(dataPage); + if (handledPage) return; + + if ((DataPage)dataPage[0] == DataPage.StationaryBikeData) + { + HandleFEState(dataPage); + Cadence = dataPage[4]; + InstantaneousPower = BitConverter.ToInt16(dataPage, 5); + } + else { - OnUnknownDataPageReceived(dataPage); + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } } } } diff --git a/AntPlus/DeviceProfiles/FitnessEquipment/TrainerStationaryBike.cs b/AntPlus/DeviceProfiles/FitnessEquipment/TrainerStationaryBike.cs index 988a8bdc..4be39e0c 100644 --- a/AntPlus/DeviceProfiles/FitnessEquipment/TrainerStationaryBike.cs +++ b/AntPlus/DeviceProfiles/FitnessEquipment/TrainerStationaryBike.cs @@ -129,7 +129,7 @@ public enum SpeedCondition private TargetPowerLimit targetPower; /// Gets the trainer torque. [ObservableProperty] - private TrainerTorque trainerTorque = new TrainerTorque(); + private TrainerTorque trainerTorque = new(); /// Gets the result of the requested calibration. /// The rhe result of calibration @@ -175,86 +175,89 @@ public enum SpeedCondition /// The data page. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (handledPage) return; - - switch ((DataPage)dataPage[0]) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - case DataPage.CalRequestResponse: - CalibrationStatus = (dataPage[1] == (byte)_request) ? CalibrationResult.Success : 0; + base.Parse(dataPage); + if (handledPage) return; + + switch ((DataPage)dataPage[0]) + { + case DataPage.CalRequestResponse: + CalibrationStatus = (dataPage[1] == (byte)_request) ? CalibrationResult.Success : 0; - if (dataPage[3] != 0xFF) - { - CalibrationStatus |= CalibrationResult.TemperatureValid; - Temperature = Math.Round(dataPage[3] * 0.5 - 25, 1); - } + if (dataPage[3] != 0xFF) + { + CalibrationStatus |= CalibrationResult.TemperatureValid; + Temperature = Math.Round(dataPage[3] * 0.5 - 25, 1); + } - if (BitConverter.ToUInt16(dataPage, 4) != 0xFFFF) - { - CalibrationStatus |= CalibrationResult.ZeroOffsetValid; - ZeroOffset = BitConverter.ToUInt16(dataPage, 4); - } + if (BitConverter.ToUInt16(dataPage, 4) != 0xFFFF) + { + CalibrationStatus |= CalibrationResult.ZeroOffsetValid; + ZeroOffset = BitConverter.ToUInt16(dataPage, 4); + } - if (BitConverter.ToUInt16(dataPage, 6) != 0xFFFF) - { - CalibrationStatus |= CalibrationResult.SpinDownValid; - SpinDownTime = BitConverter.ToUInt16(dataPage, 6); - } - break; - case DataPage.CalProgress: - TemperatureStatus = (TemperatureCondition)(dataPage[2] & 0x30); - SpeedStatus = (SpeedCondition)(dataPage[2] & 0xC0); + if (BitConverter.ToUInt16(dataPage, 6) != 0xFFFF) + { + CalibrationStatus |= CalibrationResult.SpinDownValid; + SpinDownTime = BitConverter.ToUInt16(dataPage, 6); + } + break; + case DataPage.CalProgress: + TemperatureStatus = (TemperatureCondition)(dataPage[2] & 0x30); + SpeedStatus = (SpeedCondition)(dataPage[2] & 0xC0); - if (dataPage[3] != 0xFF) - { - CurrentTemperature = Math.Round(dataPage[3] * 0.5 - 25, 1); - } + if (dataPage[3] != 0xFF) + { + CurrentTemperature = Math.Round(dataPage[3] * 0.5 - 25, 1); + } - if (BitConverter.ToUInt16(dataPage, 4) != 0xFFFF) - { - TargetSpeed = Math.Round(BitConverter.ToUInt16(dataPage, 4) * 0.001, 3); - } + if (BitConverter.ToUInt16(dataPage, 4) != 0xFFFF) + { + TargetSpeed = Math.Round(BitConverter.ToUInt16(dataPage, 4) * 0.001, 3); + } - if (BitConverter.ToUInt16(dataPage, 6) != 0xFFFF) - { - TargetSpinDownTime = BitConverter.ToUInt16(dataPage, 6); - } - break; - case DataPage.TrainerStationaryBikeData: - HandleFEState(dataPage); - InstantaneousCadence = dataPage[2]; - InstantaneousPower = BitConverter.ToUInt16(dataPage, 5) & 0x0FFF; - TrainerStatus = (TrainerStatusField)(dataPage[6] & 0x70); - TargetPower = (TargetPowerLimit)(dataPage[7] & 0x0F); + if (BitConverter.ToUInt16(dataPage, 6) != 0xFFFF) + { + TargetSpinDownTime = BitConverter.ToUInt16(dataPage, 6); + } + break; + case DataPage.TrainerStationaryBikeData: + HandleFEState(dataPage); + InstantaneousCadence = dataPage[2]; + InstantaneousPower = BitConverter.ToUInt16(dataPage, 5) & 0x0FFF; + TrainerStatus = (TrainerStatusField)(dataPage[6] & 0x70); + TargetPower = (TargetPowerLimit)(dataPage[7] & 0x0F); - if (isFirstDataMessage) - { - // initialize if first data message - isFirstDataMessage = false; - lastEventCount = dataPage[1]; - lastPower = BitConverter.ToUInt16(dataPage, 3); - return; - } + if (isFirstDataMessage) + { + // initialize if first data message + isFirstDataMessage = false; + lastEventCount = dataPage[1]; + lastPower = BitConverter.ToUInt16(dataPage, 3); + return; + } - if (dataPage[1] != lastEventCount) - { - // handle new events - int deltaEventCount = Utils.CalculateDelta(dataPage[1], ref lastEventCount); - int deltaPower = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 3), ref lastPower); - AveragePower = deltaPower / deltaEventCount; - } - break; - case DataPage.TrainerTorqueData: - HandleFEState(dataPage); - TrainerTorque.Parse(dataPage); - break; - default: - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); - } - break; + if (dataPage[1] != lastEventCount) + { + // handle new events + int deltaEventCount = Utils.CalculateDelta(dataPage[1], ref lastEventCount); + int deltaPower = Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 3), ref lastPower); + AveragePower = deltaPower / deltaEventCount; + } + break; + case DataPage.TrainerTorqueData: + HandleFEState(dataPage); + TrainerTorque.Parse(dataPage); + break; + default: + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } + break; + } } } diff --git a/AntPlus/DeviceProfiles/FitnessEquipment/Treadmill.cs b/AntPlus/DeviceProfiles/FitnessEquipment/Treadmill.cs index 09c4ae7b..4da59ebe 100644 --- a/AntPlus/DeviceProfiles/FitnessEquipment/Treadmill.cs +++ b/AntPlus/DeviceProfiles/FitnessEquipment/Treadmill.cs @@ -60,32 +60,35 @@ public enum CapabilityFlags /// The data page. public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (handledPage) return; - - if ((DataPage)dataPage[0] == DataPage.TreadmillData) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - HandleFEState(dataPage); - if (isFirstDataMessage) + base.Parse(dataPage); + if (handledPage) return; + + if ((DataPage)dataPage[0] == DataPage.TreadmillData) { - prevNeg = dataPage[5]; - prevPos = dataPage[6]; - isFirstDataMessage = false; + HandleFEState(dataPage); + if (isFirstDataMessage) + { + prevNeg = dataPage[5]; + prevPos = dataPage[6]; + isFirstDataMessage = false; + } + else + { + NegVerticalDistance += Utils.CalculateDelta(dataPage[5], ref prevNeg) / -10.0; + PosVerticalDistance += Utils.CalculateDelta(dataPage[6], ref prevPos) / 10.0; + } + Cadence = dataPage[4]; + Capabilities = (CapabilityFlags)(dataPage[7] & 0x03); } else { - NegVerticalDistance += Utils.CalculateDelta(dataPage[5], ref prevNeg) / -10.0; - PosVerticalDistance += Utils.CalculateDelta(dataPage[6], ref prevPos) / 10.0; - } - Cadence = dataPage[4]; - Capabilities = (CapabilityFlags)(dataPage[7] & 0x03); - } - else - { - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } } } } diff --git a/AntPlus/DeviceProfiles/Geocache.cs b/AntPlus/DeviceProfiles/Geocache.cs index b43f41ac..37b0ecca 100644 --- a/AntPlus/DeviceProfiles/Geocache.cs +++ b/AntPlus/DeviceProfiles/Geocache.cs @@ -119,78 +119,81 @@ public Geocache(ChannelId channelId, IAntChannel antChannel, ILogger l /// public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - - // don't parse if programming geocache - if (programmingGeocache) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - return; - } + base.Parse(dataPage); - switch ((DataPage)dataPage[0]) - { - case DataPage.TrackableId: - TrackableId = ParseId(dataPage); - break; - case DataPage.PIN: - uint pin = BitConverter.ToUInt32(dataPage, 2); - if (pin != 0xFFFFFFFF) - { - ProgrammingPIN = pin; - } - if (dataPage[6] != 0xFF) - { - TotalPagesProgrammed = dataPage[6]; - } - break; - case DataPage.AuthenticationPage: - if (authRequested) - { - authRequested = false; - AuthenticationToken = dataPage.Skip(1).ToArray(); - } - break; - default: - if (dataPage[0] >= 2 && dataPage[0] <= 31) - { - switch ((DataId)dataPage[1]) + // don't parse if programming geocache + if (programmingGeocache) + { + return; + } + + switch ((DataPage)dataPage[0]) + { + case DataPage.TrackableId: + TrackableId = ParseId(dataPage); + break; + case DataPage.PIN: + uint pin = BitConverter.ToUInt32(dataPage, 2); + if (pin != 0xFFFFFFFF) { - case DataId.Latitude: - NextStageLatitude = Utils.SemicirclesToDegrees(BitConverter.ToInt32(dataPage, 2)); - break; - case DataId.Longitude: - NextStageLongitude = Utils.SemicirclesToDegrees(BitConverter.ToInt32(dataPage, 2)); - break; - case DataId.Hint: - ParseHint(dataPage); - break; - case DataId.LoggedVisits: - loggedVisitsPage = dataPage[0]; - NumberOfVisits = BitConverter.ToUInt16(dataPage, 6); - if (NumberOfVisits > 0) - { - LastVisitTimestamp = new DateTime(1989, 12, 31) + TimeSpan.FromSeconds(BitConverter.ToUInt32(dataPage, 2)); - } - else { LastVisitTimestamp = null; } - break; - default: - OnUnknownDataPageReceived(dataPage[1], dataPage); - break; + ProgrammingPIN = pin; } - } - else - { - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) + if (dataPage[6] != 0xFF) { - OnUnknownDataPageReceived(dataPage); + TotalPagesProgrammed = dataPage[6]; } - } - break; + break; + case DataPage.AuthenticationPage: + if (authRequested) + { + authRequested = false; + AuthenticationToken = dataPage.Skip(1).ToArray(); + } + break; + default: + if (dataPage[0] >= 2 && dataPage[0] <= 31) + { + switch ((DataId)dataPage[1]) + { + case DataId.Latitude: + NextStageLatitude = Utils.SemicirclesToDegrees(BitConverter.ToInt32(dataPage, 2)); + break; + case DataId.Longitude: + NextStageLongitude = Utils.SemicirclesToDegrees(BitConverter.ToInt32(dataPage, 2)); + break; + case DataId.Hint: + ParseHint(dataPage); + break; + case DataId.LoggedVisits: + loggedVisitsPage = dataPage[0]; + NumberOfVisits = BitConverter.ToUInt16(dataPage, 6); + if (NumberOfVisits > 0) + { + LastVisitTimestamp = new DateTime(1989, 12, 31) + TimeSpan.FromSeconds(BitConverter.ToUInt32(dataPage, 2)); + } + else { LastVisitTimestamp = null; } + break; + default: + OnUnknownDataPageReceived(dataPage[1], dataPage); + break; + } + } + else + { + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } + } + break; + } } } - private readonly SortedList hintPages = new SortedList(); + private readonly SortedList hintPages = new(); private void ParseHint(byte[] dataPage) { if (!hintPages.ContainsKey(dataPage[0])) @@ -202,7 +205,7 @@ private void ParseHint(byte[] dataPage) private string ParseId(byte[] dataPage) { - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new(); stringBuilder.Append((char)((dataPage[1] >> 2) + 0x20)); stringBuilder.Append((char)((Utils.RotateLeft(BitConverter.ToUInt16(dataPage, 1), 4) & 0x3F) + 0x20)); @@ -250,7 +253,7 @@ public async Task RequestAuthentication(uint gpsSerialNumbe { _logger.LogMethodEntry(); authRequested = true; - Random random = new Random(); + Random random = new(); byte[] nonce = new byte[2]; random.NextBytes(nonce); byte[] msg = { (byte)DataPage.AuthenticationPage, 0xFF }; @@ -288,7 +291,7 @@ public async Task ProgramGeocache(string id, uint pin, doub { _logger.LogMethodEntry(); programmingGeocache = true; - List messages = new List(); + List messages = new(); // get the previous total number of pages programmed int previousPagesProgrammed = TotalPagesProgrammed ?? 0; diff --git a/AntPlus/DeviceProfiles/HeartRate.cs b/AntPlus/DeviceProfiles/HeartRate.cs index 06582e17..62f1818c 100644 --- a/AntPlus/DeviceProfiles/HeartRate.cs +++ b/AntPlus/DeviceProfiles/HeartRate.cs @@ -306,92 +306,95 @@ public HeartRate(ChannelId channelId, IAntChannel antChannel, ILogger /// public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - if (isFirstDataMessage) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - isFirstDataMessage = false; - observedToggle = dataPage[0] & 0x80; - prevBeatEventTime = BitConverter.ToUInt16(dataPage, 4); - prevBeatCount = dataPage[6]; - lastDataPage = dataPage; - HeartRateData = new CommonHeartRateData(accumulatedHeartBeatEventTime, dataPage[7], rrInterval); - return; - } - - // ignore duplicate/unchanged data pages - if (lastDataPage.SequenceEqual(dataPage)) - { - return; - } - lastDataPage = dataPage; - - // this data is present in all data pages - // fire heart rate event if beat count has changed - int deltaHeartBeatCount = Utils.CalculateDelta(dataPage[6], ref prevBeatCount); - if (deltaHeartBeatCount > 0) - { - // calculate RR interval if delta beat count is 1 - if (deltaHeartBeatCount == 1) + base.Parse(dataPage); + if (isFirstDataMessage) { - rrInterval = CalculateRRInterval(prevBeatEventTime, BitConverter.ToUInt16(dataPage, 4)); + isFirstDataMessage = false; + observedToggle = dataPage[0] & 0x80; + prevBeatEventTime = BitConverter.ToUInt16(dataPage, 4); + prevBeatCount = dataPage[6]; + lastDataPage = dataPage; + HeartRateData = new CommonHeartRateData(accumulatedHeartBeatEventTime, dataPage[7], rrInterval); + return; } - accumulatedHeartBeatEventTime += Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 4), ref prevBeatEventTime); - HeartRateData = new CommonHeartRateData(accumulatedHeartBeatEventTime, dataPage[7], rrInterval); - } - - // handle data page toggle - if (!pageToggle) - { - pageToggle = (dataPage[0] & 0x80) != observedToggle; - if (!pageToggle) return; - } + // ignore duplicate/unchanged data pages + if (lastDataPage.SequenceEqual(dataPage)) + { + return; + } + lastDataPage = dataPage; - switch ((DataPage)(dataPage[0] & 0x7F)) - { - case DataPage.Default: - // this has been handled - break; - case DataPage.CumulativeOperatingTime: - CumulativeOperatingTime = TimeSpan.FromSeconds((BitConverter.ToUInt32(dataPage, 1) & 0x00FFFFFF) * 2.0); - break; - case DataPage.ManufacturerInfo: - ManufacturerInfo = new ManufacturerInfoPage(dataPage, ChannelId.DeviceNumber); - break; - case DataPage.ProductInfo: - ProductInfo = new ProductInfoPage(dataPage); - break; - case DataPage.PreviousHeartBeat: - // fire event if beat count has changed - if (deltaHeartBeatCount > 0) - { - PreviousHeartBeat = new PreviousHeartBeatPage(dataPage); - } - break; - case DataPage.SwimInterval: - SwimInterval = new SwimIntervalPage(dataPage); - break; - case DataPage.Capabilities: - Capabilities = new CapabilitiesPage(dataPage); - break; - case DataPage.BatteryStatus: - BatteryStatus = new BatteryStatusPage(dataPage); - break; - case DataPage.DeviceInformation: - EventType = (HeartbeatEventType)(dataPage[1] & 0x03); - break; - default: - // range check manufacturer specific pages - if ((dataPage[0] & 0x7F) >= 112 && (dataPage[0] & 0x7F) < 128) - { - // let application parse - ManufacturerSpecific = new ManufacturerSpecificPage(dataPage); - } - else + // this data is present in all data pages + // fire heart rate event if beat count has changed + int deltaHeartBeatCount = Utils.CalculateDelta(dataPage[6], ref prevBeatCount); + if (deltaHeartBeatCount > 0) + { + // calculate RR interval if delta beat count is 1 + if (deltaHeartBeatCount == 1) { - OnUnknownDataPageReceived(dataPage); + rrInterval = CalculateRRInterval(prevBeatEventTime, BitConverter.ToUInt16(dataPage, 4)); } - break; + + accumulatedHeartBeatEventTime += Utils.CalculateDelta(BitConverter.ToUInt16(dataPage, 4), ref prevBeatEventTime); + HeartRateData = new CommonHeartRateData(accumulatedHeartBeatEventTime, dataPage[7], rrInterval); + } + + // handle data page toggle + if (!pageToggle) + { + pageToggle = (dataPage[0] & 0x80) != observedToggle; + if (!pageToggle) return; + } + + switch ((DataPage)(dataPage[0] & 0x7F)) + { + case DataPage.Default: + // this has been handled + break; + case DataPage.CumulativeOperatingTime: + CumulativeOperatingTime = TimeSpan.FromSeconds((BitConverter.ToUInt32(dataPage, 1) & 0x00FFFFFF) * 2.0); + break; + case DataPage.ManufacturerInfo: + ManufacturerInfo = new ManufacturerInfoPage(dataPage, ChannelId.DeviceNumber); + break; + case DataPage.ProductInfo: + ProductInfo = new ProductInfoPage(dataPage); + break; + case DataPage.PreviousHeartBeat: + // fire event if beat count has changed + if (deltaHeartBeatCount > 0) + { + PreviousHeartBeat = new PreviousHeartBeatPage(dataPage); + } + break; + case DataPage.SwimInterval: + SwimInterval = new SwimIntervalPage(dataPage); + break; + case DataPage.Capabilities: + Capabilities = new CapabilitiesPage(dataPage); + break; + case DataPage.BatteryStatus: + BatteryStatus = new BatteryStatusPage(dataPage); + break; + case DataPage.DeviceInformation: + EventType = (HeartbeatEventType)(dataPage[1] & 0x03); + break; + default: + // range check manufacturer specific pages + if ((dataPage[0] & 0x7F) >= 112 && (dataPage[0] & 0x7F) < 128) + { + // let application parse + ManufacturerSpecific = new ManufacturerSpecificPage(dataPage); + } + else + { + OnUnknownDataPageReceived(dataPage); + } + break; + } } } diff --git a/AntPlus/DeviceProfiles/MuscleOxygen.cs b/AntPlus/DeviceProfiles/MuscleOxygen.cs index 9ad31d63..c826d4dd 100644 --- a/AntPlus/DeviceProfiles/MuscleOxygen.cs +++ b/AntPlus/DeviceProfiles/MuscleOxygen.cs @@ -138,85 +138,88 @@ public MuscleOxygen(ChannelId channelId, IAntChannel antChannel, ILogger> 1) & 0x07); - - val = BitConverter.ToUInt16(dataPage, 4) & 0x0FFF; - switch (val) - { - case 0xFFE: - thg.Status = MeasurementStatus.AmbientLightTooHigh; - thg.Concentration = double.NaN; - break; - case 0xFFF: - thg.Status = MeasurementStatus.Invalid; - thg.Concentration = double.NaN; - break; - default: - thg.Status = MeasurementStatus.Valid; - thg.Concentration = val * 0.01; - break; - } - TotalHemoglobinConcentration = thg; - - val = (BitConverter.ToUInt16(dataPage, 5) >> 4) & 0x3FF; - switch (val) - { - case 0x3FE: - shg.Status = MeasurementStatus.AmbientLightTooHigh; - shg.PercentSaturated = double.NaN; - break; - case 0x3FF: - shg.Status = MeasurementStatus.Invalid; - shg.PercentSaturated = double.NaN; - break; - default: - shg.Status = MeasurementStatus.Valid; - shg.PercentSaturated = val * 0.1; - break; - } - PreviousSaturatedHemoglobin = shg; - - val = (BitConverter.ToUInt16(dataPage, 6) >> 6) & 0x03FF; - switch (val) - { - case 0x3FE: - shg.Status = MeasurementStatus.AmbientLightTooHigh; - shg.PercentSaturated = double.NaN; - break; - case 0x3FF: - shg.Status = MeasurementStatus.Invalid; - shg.PercentSaturated = double.NaN; - break; - default: - shg.Status = MeasurementStatus.Valid; - shg.PercentSaturated = val * 0.1; - break; - } - CurrentSaturatedHemoglobin = shg; - break; - default: - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); - } - break; + base.Parse(dataPage); + + switch ((DataPage)dataPage[0]) + { + case DataPage.MuscleOxygenData: + // return if the event count has not changed + if (eventCount == dataPage[1]) + { + return; + } + eventCount = dataPage[1]; + UtcTimeRequired = (dataPage[2] & 0x01) == 0x01; + SupportsAntFs = (dataPage[3] & 0x01) == 0x01; + Interval = (MeasurementInterval)((dataPage[3] >> 1) & 0x07); + + val = BitConverter.ToUInt16(dataPage, 4) & 0x0FFF; + switch (val) + { + case 0xFFE: + thg.Status = MeasurementStatus.AmbientLightTooHigh; + thg.Concentration = double.NaN; + break; + case 0xFFF: + thg.Status = MeasurementStatus.Invalid; + thg.Concentration = double.NaN; + break; + default: + thg.Status = MeasurementStatus.Valid; + thg.Concentration = val * 0.01; + break; + } + TotalHemoglobinConcentration = thg; + + val = (BitConverter.ToUInt16(dataPage, 5) >> 4) & 0x3FF; + switch (val) + { + case 0x3FE: + shg.Status = MeasurementStatus.AmbientLightTooHigh; + shg.PercentSaturated = double.NaN; + break; + case 0x3FF: + shg.Status = MeasurementStatus.Invalid; + shg.PercentSaturated = double.NaN; + break; + default: + shg.Status = MeasurementStatus.Valid; + shg.PercentSaturated = val * 0.1; + break; + } + PreviousSaturatedHemoglobin = shg; + + val = (BitConverter.ToUInt16(dataPage, 6) >> 6) & 0x03FF; + switch (val) + { + case 0x3FE: + shg.Status = MeasurementStatus.AmbientLightTooHigh; + shg.PercentSaturated = double.NaN; + break; + case 0x3FF: + shg.Status = MeasurementStatus.Invalid; + shg.PercentSaturated = double.NaN; + break; + default: + shg.Status = MeasurementStatus.Valid; + shg.PercentSaturated = val * 0.1; + break; + } + CurrentSaturatedHemoglobin = shg; + break; + default: + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } + break; + } } } diff --git a/AntPlus/DeviceProfiles/StrideBasedSpeedAndDistance.cs b/AntPlus/DeviceProfiles/StrideBasedSpeedAndDistance.cs index 0e713241..69ffda14 100644 --- a/AntPlus/DeviceProfiles/StrideBasedSpeedAndDistance.cs +++ b/AntPlus/DeviceProfiles/StrideBasedSpeedAndDistance.cs @@ -219,60 +219,63 @@ public StrideBasedSpeedAndDistance(ChannelId channelId, IAntChannel antChannel, /// public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - - switch ((DataPage)dataPage[0]) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - case DataPage.Default: - if (isFirstDefaultPage) - { - isFirstDefaultPage = false; - prevTime = dataPage[2]; - prevDistance = dataPage[3]; - prevStrideCount = dataPage[6]; - } - else - { - AccumulatedTime += Utils.CalculateDelta(dataPage[2], ref prevTime) + (dataPage[1] / 200.0); - AccumulatedDistance += Utils.CalculateDelta(dataPage[3], ref prevDistance) + (dataPage[4] >> 4) / 16.0; - AccumulatedStrideCount += Utils.CalculateDelta(dataPage[6], ref prevStrideCount); - } - InstantaneousSpeed = (dataPage[4] & 0x0F) + (dataPage[5] / 256.0); - UpdateLatency = dataPage[7] / 32.0; - break; - case DataPage.BasePage: - InstantaneousCadence = dataPage[3] + (dataPage[4] >> 4) / 16.0; - InstantaneousSpeed = (dataPage[4] & 0x0F) + (dataPage[5] / 256.0); - Status = new StatusFlags(dataPage[7]); - break; - case DataPage.Calories: - if (isFirstCalPage) - { - isFirstCalPage = false; - prevCals = dataPage[6]; - } - else - { - AccumulatedCalories += Utils.CalculateDelta(dataPage[6], ref prevCals); - } - InstantaneousCadence = dataPage[3] + (dataPage[4] >> 4) / 16.0; - InstantaneousSpeed = (dataPage[4] & 0x0F) + (dataPage[5] / 256.0); - Status = new StatusFlags(dataPage[7]); - break; - case DataPage.DistanceAndStridesSummary: - StrideCountSummary = BitConverter.ToUInt32(dataPage, 1) & 0x00FFFFFF; - DistanceSummary = BitConverter.ToUInt32(dataPage, 4) / 256.0; - break; - case DataPage.Capabilities: - Capabilities = (CapabilitiesFlags)dataPage[1]; - break; - default: - // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. - if (!CommonDataPages.ParseCommonDataPage(dataPage)) - { - OnUnknownDataPageReceived(dataPage); - } - break; + base.Parse(dataPage); + + switch ((DataPage)dataPage[0]) + { + case DataPage.Default: + if (isFirstDefaultPage) + { + isFirstDefaultPage = false; + prevTime = dataPage[2]; + prevDistance = dataPage[3]; + prevStrideCount = dataPage[6]; + } + else + { + AccumulatedTime += Utils.CalculateDelta(dataPage[2], ref prevTime) + (dataPage[1] / 200.0); + AccumulatedDistance += Utils.CalculateDelta(dataPage[3], ref prevDistance) + (dataPage[4] >> 4) / 16.0; + AccumulatedStrideCount += Utils.CalculateDelta(dataPage[6], ref prevStrideCount); + } + InstantaneousSpeed = (dataPage[4] & 0x0F) + (dataPage[5] / 256.0); + UpdateLatency = dataPage[7] / 32.0; + break; + case DataPage.BasePage: + InstantaneousCadence = dataPage[3] + (dataPage[4] >> 4) / 16.0; + InstantaneousSpeed = (dataPage[4] & 0x0F) + (dataPage[5] / 256.0); + Status = new StatusFlags(dataPage[7]); + break; + case DataPage.Calories: + if (isFirstCalPage) + { + isFirstCalPage = false; + prevCals = dataPage[6]; + } + else + { + AccumulatedCalories += Utils.CalculateDelta(dataPage[6], ref prevCals); + } + InstantaneousCadence = dataPage[3] + (dataPage[4] >> 4) / 16.0; + InstantaneousSpeed = (dataPage[4] & 0x0F) + (dataPage[5] / 256.0); + Status = new StatusFlags(dataPage[7]); + break; + case DataPage.DistanceAndStridesSummary: + StrideCountSummary = BitConverter.ToUInt32(dataPage, 1) & 0x00FFFFFF; + DistanceSummary = BitConverter.ToUInt32(dataPage, 4) / 256.0; + break; + case DataPage.Capabilities: + Capabilities = (CapabilitiesFlags)dataPage[1]; + break; + default: + // Attempt to parse the data page as a common data page. If it fails, raise the unknown data page event. + if (!CommonDataPages.ParseCommonDataPage(dataPage)) + { + OnUnknownDataPageReceived(dataPage); + } + break; + } } } diff --git a/AntPlus/DeviceProfiles/UnknownDevice.cs b/AntPlus/DeviceProfiles/UnknownDevice.cs index fbf053d4..75700424 100644 --- a/AntPlus/DeviceProfiles/UnknownDevice.cs +++ b/AntPlus/DeviceProfiles/UnknownDevice.cs @@ -27,7 +27,7 @@ public class UnknownDevice : AntDevice /// /// This ensures changes to the collection are thread safe and marshalled on the UI thread. /// - public object CollectionLock = new object(); + public readonly object CollectionLock = new(); /// /// Gets the collection of data pages received from the unknown device. @@ -60,21 +60,24 @@ public UnknownDevice(ChannelId channelId, IAntChannel antChannel, ILogger public override void Parse(byte[] dataPage) { - base.Parse(dataPage); - - byte[] page = DataPages.FirstOrDefault(p => p[0] == dataPage[0]); - if (page == null) + using (_logger.BeginScope("DeviceNumber={DeviceNumber}", ChannelId.DeviceNumber)) { - lock (CollectionLock) + base.Parse(dataPage); + + byte[] page = DataPages.FirstOrDefault(p => p[0] == dataPage[0]); + if (page == null) { - DataPages.Add(dataPage); + lock (CollectionLock) + { + DataPages.Add(dataPage); + } } - } - else - { - if (!page.SequenceEqual(dataPage)) + else { - DataPages[DataPages.IndexOf(page)] = dataPage; + if (!page.SequenceEqual(dataPage)) + { + DataPages[DataPages.IndexOf(page)] = dataPage; + } } } } diff --git a/AntPlus/Extensions/Logging/LoggerExtensions.cs b/AntPlus/Extensions/Logging/LoggerExtensions.cs index cac784f3..e68e165f 100644 --- a/AntPlus/Extensions/Logging/LoggerExtensions.cs +++ b/AntPlus/Extensions/Logging/LoggerExtensions.cs @@ -26,14 +26,13 @@ public static partial class LoggerExtensions /// /// The logger instance. /// The data page as a byte array. - /// Optional: The caller member name. - public static void LogUnknownDataPage(this ILogger logger, byte[] dataPage, [CallerMemberName] string methodName = "") + public static void LogUnknownDataPage(this ILogger logger, byte[] dataPage) { - s_unknownDataPage(logger, methodName, dataPage[0], BitConverter.ToString(dataPage)); + s_unknownDataPage(logger, dataPage[0], BitConverter.ToString(dataPage)); } - [LoggerMessage(EventId = 1000, Level = LogLevel.Warning, Message = "{MethodName}: Unknown data page# 0x{Page:X2}, Data page = {DataPage}")] - private static partial void s_unknownDataPage(ILogger logger, string methodName, byte page, string dataPage); + [LoggerMessage(EventId = 1000, Level = LogLevel.Warning, Message = "Unknown data page# 0x{Page:X2}, Data page = {DataPage}")] + private static partial void s_unknownDataPage(ILogger logger, byte page, string dataPage); /// /// Logs a warning message indicating an unknown data page deeper in the device parsing hierarchy. @@ -172,14 +171,13 @@ public static void LogIgnoredDataPage(this ILogger logger, byte[] dataPag /// The logger instance. /// Log level. /// The data page as a byte array. - /// The caller member name. - public static void LogDataPage(this ILogger logger, LogLevel level, byte[] dataPage, [CallerMemberName] string methodName = "") + public static void LogDataPage(this ILogger logger, LogLevel level, byte[] dataPage) { - s_logDataPage(logger, level, methodName, BitConverter.ToString(dataPage)); + s_logDataPage(logger, level, BitConverter.ToString(dataPage)); } - [LoggerMessage(EventId = 1009, Message = "{MethodName}: Data page = {DataPage}")] - private static partial void s_logDataPage(ILogger logger, LogLevel level, string methodName, string dataPage); + [LoggerMessage(EventId = 1009, Message = "Data page = {DataPage}")] + private static partial void s_logDataPage(ILogger logger, LogLevel level, string dataPage); /// /// Logs a data page. diff --git a/AntPlus/SendMessageChannel.cs b/AntPlus/SendMessageChannel.cs index 1f77bb76..88a5f51f 100644 --- a/AntPlus/SendMessageChannel.cs +++ b/AntPlus/SendMessageChannel.cs @@ -14,7 +14,7 @@ private class SendMessageChannel : IAntChannel private readonly IAntChannel[] _channels; private readonly ILogger _logger; private readonly bool[] _busyFlags; - private readonly object _channelLock = new object(); + private readonly object _channelLock = new(); public byte ChannelNumber => throw new NotImplementedException(); diff --git a/Documentation/Content/AntPlusOverview.aml b/Documentation/Content/AntPlusOverview.aml index 06d3dff8..3ce7670f 100644 --- a/Documentation/Content/AntPlusOverview.aml +++ b/Documentation/Content/AntPlusOverview.aml @@ -141,7 +141,7 @@ The UnknownDataPageReceived event is raised when a data page is received that is not defined in the - library. This allows applications to receive custom data pages or data pages that supported by the library. + library. This allows applications to receive data pages defined by the ANT device manufacturer. diff --git a/Documentation/Content/VersionHistory/AntPlus/AntPllusVersionHistory.aml b/Documentation/Content/VersionHistory/AntPlus/AntPllusVersionHistory.aml index 9c6f0850..64afa5e0 100644 --- a/Documentation/Content/VersionHistory/AntPlus/AntPllusVersionHistory.aml +++ b/Documentation/Content/VersionHistory/AntPlus/AntPllusVersionHistory.aml @@ -121,6 +121,11 @@ + + + + + + + + + + + Release Date: June 11, 2026 + + + +
+ Changes + + + + + Improved logging with ANT device number scopes. + + + Updated NuGet dependencies to latest version. + + + + +
+ + + + + + + diff --git a/Documentation/Content/VersionHistory/AntUsbStick/AntUsbStickVersionHistory.aml b/Documentation/Content/VersionHistory/AntUsbStick/AntUsbStickVersionHistory.aml index 03e86895..73653d51 100644 --- a/Documentation/Content/VersionHistory/AntUsbStick/AntUsbStickVersionHistory.aml +++ b/Documentation/Content/VersionHistory/AntUsbStick/AntUsbStickVersionHistory.aml @@ -91,6 +91,11 @@ + + + + + + + + + + + Release Date: June 11, 2026 + + + +
+ Changes + + + + + + Updated NuGet dependencies to latest version. + + + + + +
+ + + + + + + diff --git a/Documentation/Content/VersionHistory/HostingExtensions/HostingExtensionsVersionHistory.aml b/Documentation/Content/VersionHistory/HostingExtensions/HostingExtensionsVersionHistory.aml index ecfe20a8..77fdbde4 100644 --- a/Documentation/Content/VersionHistory/HostingExtensions/HostingExtensionsVersionHistory.aml +++ b/Documentation/Content/VersionHistory/HostingExtensions/HostingExtensionsVersionHistory.aml @@ -91,6 +91,11 @@ + + + + + diff --git a/Documentation/Content/VersionHistory/HostingExtensions/v2.0.7.0.aml b/Documentation/Content/VersionHistory/HostingExtensions/v2.0.7.0.aml new file mode 100644 index 00000000..78008d64 --- /dev/null +++ b/Documentation/Content/VersionHistory/HostingExtensions/v2.0.7.0.aml @@ -0,0 +1,101 @@ + + + + + + + + + + + Release Date: June 11, 2026 + + + +
+ Changes + + + + + + Removed unnecessary logging. + + + + + Updated NuGet dependencies to latest version. + + + + + +
+ + + + + +
+
diff --git a/Documentation/ContentLayout.content b/Documentation/ContentLayout.content index 790decce..df7fdf1a 100644 --- a/Documentation/ContentLayout.content +++ b/Documentation/ContentLayout.content @@ -68,6 +68,7 @@ + @@ -83,6 +84,7 @@ + @@ -97,7 +99,8 @@ - + + \ No newline at end of file diff --git a/Documentation/Documentation.shfbproj b/Documentation/Documentation.shfbproj index f4563e58..4a94cb33 100644 --- a/Documentation/Documentation.shfbproj +++ b/Documentation/Documentation.shfbproj @@ -139,12 +139,14 @@ + + @@ -211,6 +213,7 @@ + diff --git a/Examples/AntMulticastServer/AntMulticastServer.csproj b/Examples/AntMulticastServer/AntMulticastServer.csproj index 190b668b..d5437723 100644 --- a/Examples/AntMulticastServer/AntMulticastServer.csproj +++ b/Examples/AntMulticastServer/AntMulticastServer.csproj @@ -18,7 +18,7 @@ - + diff --git a/Examples/AntUsbStick/AntUsbStick.csproj b/Examples/AntUsbStick/AntUsbStick.csproj index e0b294a2..09caee7b 100644 --- a/Examples/AntUsbStick/AntUsbStick.csproj +++ b/Examples/AntUsbStick/AntUsbStick.csproj @@ -6,7 +6,7 @@ True SmallEarthTech.$(MSBuildProjectName) Garmin/Dynastream ANT+ USB Stick Class Library - 4.1.3 + 4.1.4 True Stephen Hidem Provides an interface to ANT+ USB sticks available from Garmin/Dynastream. This class is used in the example projects located at the project URL. @@ -38,7 +38,7 @@ true - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Examples/MAUI-gRPC/AntGrpcService/AntGrpcService.csproj b/Examples/MAUI-gRPC/AntGrpcService/AntGrpcService.csproj index 2ebb9065..7998643f 100644 --- a/Examples/MAUI-gRPC/AntGrpcService/AntGrpcService.csproj +++ b/Examples/MAUI-gRPC/AntGrpcService/AntGrpcService.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/Examples/MAUI-gRPC/AntGrpcShared/AntGrpcShared.csproj b/Examples/MAUI-gRPC/AntGrpcShared/AntGrpcShared.csproj index 593f590a..a45646f1 100644 --- a/Examples/MAUI-gRPC/AntGrpcShared/AntGrpcShared.csproj +++ b/Examples/MAUI-gRPC/AntGrpcShared/AntGrpcShared.csproj @@ -25,13 +25,13 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Examples/MAUI-gRPC/MauiAntGrpcClient/MauiAntGrpcClient.csproj b/Examples/MAUI-gRPC/MauiAntGrpcClient/MauiAntGrpcClient.csproj index 00d66c4e..b845f123 100644 --- a/Examples/MAUI-gRPC/MauiAntGrpcClient/MauiAntGrpcClient.csproj +++ b/Examples/MAUI-gRPC/MauiAntGrpcClient/MauiAntGrpcClient.csproj @@ -89,15 +89,15 @@ - + - + - - - - - + + + + + diff --git a/Examples/WpfUsbStickApp/MainWindowViewModel.cs b/Examples/WpfUsbStickApp/MainWindowViewModel.cs index 0dc20a3a..acac880d 100644 --- a/Examples/WpfUsbStickApp/MainWindowViewModel.cs +++ b/Examples/WpfUsbStickApp/MainWindowViewModel.cs @@ -27,19 +27,14 @@ internal class MainWindowViewModel public MainWindowViewModel() { - // Initialize Serilog early, without access to configuration or services - Log.Logger = new LoggerConfiguration() - .WriteTo.Debug(outputTemplate: - "[{Timestamp:HH:mm:ss.fff} {Level:u3}] ({SourceContext}) {Message:lj}{NewLine}{Exception}") // + file or centralized logging - .CreateLogger(); - // dependency services _host = Host.CreateDefaultBuilder(Environment.GetCommandLineArgs()). UseSerilog((context, loggerConfiguration) => { - loggerConfiguration.WriteTo.Debug(outputTemplate: - "[{Timestamp:HH:mm:ss.fff} {Level:u3}] ({SourceContext}) {Message:lj}{NewLine}{Exception}"); - loggerConfiguration.ReadFrom.Configuration(context.Configuration); + loggerConfiguration.Enrich.FromLogContext() + .WriteTo.Debug(outputTemplate: + "[{Timestamp:HH:mm:ss.fff} {Level:u3}] ({SourceContext}){Scope} {Message:lj}{NewLine}{Exception}") + .ReadFrom.Configuration(context.Configuration); }). UseAntPlus(). // this adds all the required dependencies to use the ANT+ class library ConfigureServices(services => diff --git a/Examples/WpfUsbStickApp/appsettings.json b/Examples/WpfUsbStickApp/appsettings.json index 3c41d39e..c1942c47 100644 --- a/Examples/WpfUsbStickApp/appsettings.json +++ b/Examples/WpfUsbStickApp/appsettings.json @@ -3,4 +3,4 @@ "MissedMessages": 8, "Timeout": null } -} +} \ No newline at end of file