Skip to content

Commit acf519c

Browse files
authored
Prep for Next Release (#37)
* Remove Dispose call in critical log scenario The line `Dispose(); // clear the channels` was removed from the method where a critical log message is generated if `ChannelId` is null. This change ensures that the code will no longer call the `Dispose` method to clear the channels in this scenario. * Refactor AntDeviceCollection and add SendMessageChannel - Made `AntDeviceCollection` a partial class. - Added `_sendMessageChannel` field to `AntDeviceCollection`. - Simplified `AntDeviceCollection` constructor by removing async initialization. - Added `StartScanning` method to initialize ANT radio and channels. - Renamed `Channel_ChannelResponse` to `MessageHandler` and updated it. - Refactored `CreateAntDevice` to use `_sendMessageChannel`. - Updated `AntPlus.csproj` to version `5.0.0.0`. - Added `SendMessageChannel` class to manage multiple ANT channels. - Implemented async data sending and channel management in `SendMessageChannel`. * Refactor tests and add new logging verifications Refactored test classes to use mock loggers and updated test methods to be asynchronous. Added new tests for logging critical messages and warnings. Introduced a new `SendMessageChannelTests` class to verify `NotImplementedException` and concurrent method invocations. * Add release notes and documentation for versions 5.0.0.0 and 1.1.1.0 Updated `AntPlus.csproj` to include new package release notes indicating breaking changes. The `AntDeviceCollection` constructor no longer initiates ANT device scanning; `StartScanning` must be called after instantiation. Updated `AntPllusVersionHistory.aml` and `HostingExtensionsVersionHistory.aml` to include new list items with links to the new version history. Updated `ContentLayout.content` to add new topics for versions 5.0.0.0 and 1.1.1.0, marking version 5.0.0.0 as selected. Updated `Documentation.shfbproj` to re-add namespace summary items and include new entries for the version history files for versions 5.0.0.0 and 1.1.1.0. Added new files `v5.0.0.0.aml` and `v1.1.1.0.aml` detailing the releases, including breaking changes, improvements, and a fix for a potential race condition. * Remove ackWaitTime parameter and standardize timeouts The changes primarily involve the removal of the `ackWaitTime` parameter from various method calls and method signatures. This parameter, which specified the time in milliseconds to wait for device acknowledgment, has been replaced with `It.IsAny<uint>()` in the unit tests and with a default timeout value `_deviceTimeout` in the main code. In the unit tests within `TrainerStationaryBikeTests.cs`, the hardcoded `500` milliseconds wait time has been replaced with `It.IsAny<uint>()` to make the tests more flexible and less dependent on specific timeout values. In the `AntDevice.cs` file, the `ackWaitTime` parameter has been removed from the `RequestDataPage` and `SendExtAcknowledgedMessage` methods. Instead, these methods now use the `_deviceTimeout` field to determine the timeout duration. Similar changes have been made in the `Tracker.cs`, `Parameters.cs`, and `Geocache.cs` files, where the `ackWaitTime` parameter has been removed from method calls and replaced with the default timeout value. These changes aim to standardize the timeout handling across the codebase, making it more maintainable and consistent. * Update tests and refactor Geocache functionality Updated NuGet packages in AntPlus.UnitTests.csproj: - Microsoft.NET.Test.Sdk to 17.13.0 - MSTest.TestAdapter to 3.7.3 - MSTest.TestFramework to 3.7.3 - coverlet.collector to 6.0.4 GeocacheTests.cs changes: - mockAntChannel now uses MockBehavior.Loose - Added tests: EraseGeocache_Success_ReturnsPass and EraseGeocache_Failure_ReturnsError Geocache.cs changes: - Refactored ProgramGeocache method: pin is no longer nullable, uses ClearGeocacheState helper, streamlined message assembly, added logging - Added ClearGeocacheState private method - Added EraseGeocache public method with detailed logging * Examples Update - Add EraseGeocacheCommand and related functionality - Added EraseGeocacheCommand to GeocacheViewModel in MauiAntGrpcClient and WpfUsbStickApp namespaces. - Introduced EraseGeocache method in GeocacheViewModel to handle geocache erasure. - Added CanEraseGeocache method to determine if EraseGeocacheCommand can be executed. - Updated ProgramGeocacheView.xaml and GeocacheWindow.xaml to include buttons for erasing geocaches. - Changed pin property type from uint? to uint in GeocacheViewModel. - Added NotifyCanExecuteChangedFor attribute to isBusy property. * Refactor geocache logic and improve logging - Updated `EraseGeocache` test in `GeocacheTests.cs` to verify specific byte arrays. - Removed retry logic in `SendExtAcknowledgedMessage` in `AntDevice.cs`; now logs a warning on failure. - Refactored `RequestPinPage` in `Geocache.cs` to use new `ClearGeocacheState` method. - Corrected typo in exception message in `UpdateLoggedVisits` in `Geocache.cs`. - Updated logging in `ProgramGeocache` and `EraseGeocache` in `Geocache.cs` to use structured logging. * Refactor methods and update logging in Ant classes Renamed private methods and updated logging statements in the AntChannel and AntRadio classes within the SmallEarthTech.AntUsbStick namespace. Specifically: - In AntChannel.cs: - Renamed Channel_ChannelResponse to OnChannelResponse. - Renamed Channel_DeviceNotification to OnDeviceNotification. - Changed logging level for OnChannelResponse from LogDebug to LogTrace. - Updated logging statements to use structured logging with named placeholders. - In AntRadio.cs: - Renamed AntDevice_deviceResponse to OnDeviceResponse. - Renamed AntDevice_SerialError to OnAntDeviceSerialError. - Updated logging statements to use structured logging with named placeholders. Updated AntUsbStick.csproj to upgrade Microsoft.Extensions.Logging.Abstractions package from version 8.0.2 to 9.0.2. * Clean up logging in various projects. * Clean up logging. * Project file updates for hosting and USB stick. * Updated documentation project content.
1 parent f54ee37 commit acf519c

File tree

27 files changed

+363
-137
lines changed

27 files changed

+363
-137
lines changed

AntPlus.Extensions.Hosting/Hosting.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<Description>Useful ANT+ extensions for hosting in DI containers. Simple and easy to use! Includes support for .NET MAUI and Windows apps.</Description>
1313
<PackageTags>ant; ant+; antplus; smallearthtech</PackageTags>
1414
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
15+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
16+
<EmbedUntrackedSources>true</EmbedUntrackedSources>
1517
<PackageIcon>PackageLogo.png</PackageIcon>
1618
<PackageReadmeFile>readme.md</PackageReadmeFile>
1719
<PackageReleaseNotes>Fixed potential race condition sending multiple messages to multiple ANT devices managed by the AntCollection class.</PackageReleaseNotes>
@@ -20,6 +22,8 @@
2022
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
2123
<AssemblyVersion>1.1.1.0</AssemblyVersion>
2224
<Version>$(VersionPrefix)$(AssemblyVersion)</Version>
25+
<PackageProjectUrl>https://stephenhidem.github.io/AntPlus</PackageProjectUrl>
26+
<RepositoryUrl>https://github.com/StephenHidem/AntPlus</RepositoryUrl>
2327
</PropertyGroup>
2428

2529
<ItemGroup>

AntPlus.Extensions.Hosting/SendMessageChannel.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,10 @@ public Task<MessagingReturnCode> SendExtAcknowledgedDataAsync(ChannelId channelI
8585
.ContinueWith(antecedent =>
8686
{
8787
int index = antecedent.Result;
88-
_logger.LogDebug("SendExtAcknowledgedDataAsync: Invoke. Task ID = {TaskId}, channel index = {ChannelIndex}, channel ID = 0x{ChannelId:X8}", antecedent.Id, index, channelId.Id);
88+
_logger.LogDebug("SendExtAcknowledgedDataAsync: channel index = {ChannelIndex}, channel ID = 0x{ChannelId:X8}, data = {Data}", index, channelId.Id, BitConverter.ToString(data));
8989
_channels[index].SendExtAcknowledgedDataAsync(channelId, data, ackWaitTime)
9090
.ContinueWith(innerAntecedent =>
9191
{
92-
_logger.LogDebug("SendExtAcknowledgedDataAsync: Completed. Task ID = {TaskId}, channel index = {ChannelIndex}, channel ID = 0x{ChannelId:X8}", antecedent.Id, index, channelId.Id);
9392
lock (_channelLock)
9493
{
9594
// release the channel and notify this channel is available

AntPlus.UnitTests/AntPlus.UnitTests.csproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
1212
<PackageReference Include="Moq" Version="4.20.72" />
13-
<PackageReference Include="MSTest.TestAdapter" Version="3.6.1" />
14-
<PackageReference Include="MSTest.TestFramework" Version="3.6.1" />
15-
<PackageReference Include="coverlet.collector" Version="6.0.2">
13+
<PackageReference Include="MSTest.TestAdapter" Version="3.7.3" />
14+
<PackageReference Include="MSTest.TestFramework" Version="3.7.3" />
15+
<PackageReference Include="coverlet.collector" Version="6.0.4">
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1818
</PackageReference>

AntPlus.UnitTests/DeviceProfiles/FitnessEquipment/TrainerStationaryBikeTests.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public async Task Parse_CalibrationResponse_Success(CalibrationRequestResponse r
116116
mockAntChannel.Setup(ac => ac.SendExtAcknowledgedDataAsync(
117117
mockChannelId,
118118
dataPage,
119-
500).Result).Returns(MessagingReturnCode.Pass);
119+
It.IsAny<uint>()).Result).Returns(MessagingReturnCode.Pass);
120120

121121
// Act
122122
var result = await trainer.CalibrationRequest(request);
@@ -262,7 +262,7 @@ public async Task SetBasicResistance_Message_Matches()
262262
mockAntChannel.Setup(ac => ac.SendExtAcknowledgedDataAsync(
263263
mockChannelId,
264264
new byte[8] { 48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, (byte)(resistance / 0.5) },
265-
500).Result).Returns(MessagingReturnCode.Pass);
265+
It.IsAny<uint>()).Result).Returns(MessagingReturnCode.Pass);
266266

267267
// Act
268268
var result = await trainer.SetBasicResistance(
@@ -283,7 +283,7 @@ public async Task SetTargetPower_Message_Matches()
283283
mockAntChannel.Setup(ac => ac.SendExtAcknowledgedDataAsync(
284284
mockChannelId,
285285
new byte[8] { 49, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, expPow[0], expPow[1] },
286-
500).Result).Returns(MessagingReturnCode.Pass);
286+
It.IsAny<uint>()).Result).Returns(MessagingReturnCode.Pass);
287287

288288
// Act
289289
var result = await trainer.SetTargetPower(
@@ -305,7 +305,7 @@ public async Task SetWindResistance_Message_Matches()
305305
mockAntChannel.Setup(ac => ac.SendExtAcknowledgedDataAsync(
306306
mockChannelId,
307307
new byte[8] { 50, 0xFF, 0xFF, 0xFF, 0xFF, (byte)(windResistanceCoefficient / 0.01), (byte)(windSpeed + 127), (byte)(draftingFactor / 0.01) },
308-
500).Result).Returns(MessagingReturnCode.Pass);
308+
It.IsAny<uint>()).Result).Returns(MessagingReturnCode.Pass);
309309

310310
// Act
311311
var result = await trainer.SetWindResistance(
@@ -329,7 +329,7 @@ public async Task SetTrackResistance_Message_Matches()
329329
mockAntChannel.Setup(ac => ac.SendExtAcknowledgedDataAsync(
330330
mockChannelId,
331331
new byte[8] { 51, 0xFF, 0xFF, 0xFF, 0xFF, expGrade[0], expGrade[1], (byte)(rollingResistanceCoefficient / 0.00005) },
332-
500).Result).Returns(MessagingReturnCode.Pass);
332+
It.IsAny<uint>()).Result).Returns(MessagingReturnCode.Pass);
333333

334334
// Act
335335
var result = await trainer.SetTrackResistance(
@@ -357,7 +357,7 @@ public async Task SetUserConfiguration_Message_Matches()
357357
mockAntChannel.Setup(ac => ac.SendExtAcknowledgedDataAsync(
358358
mockChannelId,
359359
new byte[8] { 55, expWeight[0], expWeight[1], 0xFF, (byte)((wheelDiameterOffset & 0x0F) | (expBikeWeight[0] & 0xF0)), expBikeWeight[1], (byte)(wheelDiameter / 0.01), (byte)(gearRatio / 0.03) },
360-
500).Result).Returns(MessagingReturnCode.Pass);
360+
It.IsAny<uint>()).Result).Returns(MessagingReturnCode.Pass);
361361

362362
// Act
363363
var result = await trainer.SetUserConfiguration(

AntPlus.UnitTests/DeviceProfiles/GeocacheTests.cs

+49-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public void TestInitialize()
4545
{
4646
mockRepository = new MockRepository(MockBehavior.Strict);
4747

48-
mockAntChannel = mockRepository.Create<IAntChannel>();
48+
mockAntChannel = mockRepository.Create<IAntChannel>(MockBehavior.Loose);
4949
mockLogger = mockRepository.Create<ILogger<Geocache>>(MockBehavior.Loose);
5050
}
5151

@@ -175,5 +175,53 @@ public async Task ProgramGeocache_StateInitialize_StateCleared()
175175
Assert.AreEqual(default, geocache.NumberOfVisits);
176176
Assert.AreEqual(default, geocache.LastVisitTimestamp);
177177
}
178+
179+
[TestMethod]
180+
public async Task EraseGeocache_Success_ReturnsPass()
181+
{
182+
// Arrange
183+
mockAntChannel.Setup(ac => ac.SendExtAcknowledgedDataAsync(cid, It.IsAny<byte[]>(), It.IsAny<uint>()).Result)
184+
.Returns(MessagingReturnCode.Pass);
185+
Geocache geocache = new(cid, mockAntChannel.Object, mockLogger.Object, null);
186+
187+
// Act
188+
var result = await geocache.EraseGeocache();
189+
190+
// Assert
191+
Assert.AreEqual(MessagingReturnCode.Pass, result);
192+
mockAntChannel.Verify(ac => ac.SendExtAcknowledgedDataAsync(
193+
cid,
194+
It.Is<byte[]>(msg =>
195+
msg.Length == 8 &&
196+
msg[0] == 0 &&
197+
msg.Skip(1).All(el => el == 0)),
198+
It.IsAny<uint>()),
199+
Times.Once);
200+
mockAntChannel.Verify(ac => ac.SendExtAcknowledgedDataAsync(
201+
cid,
202+
It.Is<byte[]>(msg =>
203+
msg.Length == 8 &&
204+
msg[0] >= 1 && msg[0] <= 31 &&
205+
msg.Skip(1).All(el => el == 0xFF)),
206+
It.IsAny<uint>()),
207+
Times.Exactly(31));
208+
}
209+
210+
[TestMethod]
211+
public async Task EraseGeocache_Failure_ReturnsError()
212+
{
213+
// Arrange
214+
mockAntChannel.SetupSequence(ac => ac.SendExtAcknowledgedDataAsync(cid, It.IsAny<byte[]>(), It.IsAny<uint>()).Result)
215+
.Returns(MessagingReturnCode.Pass)
216+
.Returns(MessagingReturnCode.Fail);
217+
Geocache geocache = new(cid, mockAntChannel.Object, mockLogger.Object, null);
218+
219+
// Act
220+
var result = await geocache.EraseGeocache();
221+
222+
// Assert
223+
Assert.AreEqual(MessagingReturnCode.Fail, result);
224+
mockAntChannel.Verify(ac => ac.SendExtAcknowledgedDataAsync(cid, It.IsAny<byte[]>(), It.IsAny<uint>()), Times.AtLeastOnce);
225+
}
178226
}
179227
}

AntPlus/AntDevice.cs

+4-11
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,20 @@ public override string ToString()
127127
/// <summary>Requests the data page.</summary>
128128
/// <typeparam name="T">The data page enumeration of the derived ANT device class.</typeparam>
129129
/// <param name="page">The requested page.</param>
130-
/// <param name="ackWaitTime">Time in milliseconds to wait for the device acknowledgment. The default is 500ms.</param>
131130
/// <param name="descriptor1">The descriptor1. The default is 0xFF.</param>
132131
/// <param name="descriptor2">The descriptor2. The default is 0xFF.</param>
133132
/// <param name="transmissionResponse">The transmission response. The default is to send 4 messages.</param>
134133
/// <param name="commandType">Type of the command. The default is <see cref="CommandType.DataPage"/>.</param>
135134
/// <param name="slaveSerialNumber">The slave serial number. The default is 0xFFFF.</param>
136135
/// <returns>Status of the request.</returns>
137136
/// <exception cref="System.ArgumentException">Invalid data page requested.</exception>
138-
public async Task<MessagingReturnCode> RequestDataPage<T>(T page, uint ackWaitTime = 500, byte descriptor1 = 0xFF, byte descriptor2 = 0xFF, byte transmissionResponse = 4, CommandType commandType = CommandType.DataPage, ushort slaveSerialNumber = 0xFFFF) where T : Enum
137+
public async Task<MessagingReturnCode> RequestDataPage<T>(T page, byte descriptor1 = 0xFF, byte descriptor2 = 0xFF, byte transmissionResponse = 4, CommandType commandType = CommandType.DataPage, ushort slaveSerialNumber = 0xFFFF) where T : Enum
139138
{
140139
if (Enum.IsDefined(typeof(T), page))
141140
{
142141
byte[] msg = new byte[] { (byte)CommonDataPage.RequestDataPage, 0, 0, descriptor1, descriptor2, transmissionResponse, Convert.ToByte(page), (byte)commandType };
143142
BitConverter.GetBytes(slaveSerialNumber).CopyTo(msg, 1);
144-
return await _antChannel.SendExtAcknowledgedDataAsync(ChannelId, msg, ackWaitTime);
143+
return await _antChannel.SendExtAcknowledgedDataAsync(ChannelId, msg, (uint)_deviceTimeout);
145144
}
146145
else
147146
{
@@ -153,16 +152,10 @@ public async Task<MessagingReturnCode> RequestDataPage<T>(T page, uint ackWaitTi
153152

154153
/// <summary>Sends an acknowledged message to the ANT device.</summary>
155154
/// <param name="message">The message.</param>
156-
/// <param name="ackWaitTime">Time in milliseconds to wait for the device acknowledgment. The default is 500ms.</param>
157155
/// <returns>Status of the request.</returns>
158-
public async Task<MessagingReturnCode> SendExtAcknowledgedMessage(byte[] message, uint ackWaitTime = 500)
156+
public async Task<MessagingReturnCode> SendExtAcknowledgedMessage(byte[] message)
159157
{
160-
int retries = 3;
161-
MessagingReturnCode ret;
162-
do
163-
{
164-
ret = await _antChannel.SendExtAcknowledgedDataAsync(ChannelId, message, ackWaitTime);
165-
} while (ret != MessagingReturnCode.Pass && --retries > 0);
158+
MessagingReturnCode ret = await _antChannel.SendExtAcknowledgedDataAsync(ChannelId, message, (uint)_deviceTimeout);
166159

167160
if (ret != MessagingReturnCode.Pass)
168161
{

AntPlus/DeviceProfiles/AssetTracker/Tracker.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ internal Asset GetAsset(byte[] data)
138138
{
139139
Assets.Add(asset);
140140
}
141-
_ = RequestDataPage(DataPage.AssetId1, 500, 255, 255, 4, CommandType.DataPageSet);
141+
_ = RequestDataPage(DataPage.AssetId1, 255, 255, 4, CommandType.DataPageSet);
142142
}
143143
return asset;
144144
}

AntPlus/DeviceProfiles/BicyclePower/Parameters.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ private void ParseParameters(byte[] dataPage)
263263
/// <returns>Status of the request.</returns>
264264
public async Task<MessagingReturnCode> GetParameters(SubPage parameterSubpage)
265265
{
266-
return await RequestDataPage(DataPage.GetSetParameters, 500, (byte)parameterSubpage);
266+
return await RequestDataPage(DataPage.GetSetParameters, (byte)parameterSubpage);
267267
}
268268

269269
/// <summary>

0 commit comments

Comments
 (0)