Skip to content

Commit

Permalink
add functionality to SkStackActiveScanOptions to specify the channel …
Browse files Browse the repository at this point in the history
…mask when performing active scan
  • Loading branch information
smdn committed Sep 23, 2024
1 parent 644a716 commit 3d3437a
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,33 @@ public static SkStackActiveScanOptions Create(
)
=> new UserDefinedActiveScanOptions(
paaSelector: paaSelector,
channelMask: null,
scanDurationGenerator: scanDurationGenerator
);

/// <summary>
/// Creates the <see cref="SkStackActiveScanOptions"/> with the custom selection method and duration factors.
/// </summary>
/// <param name="scanDurationGenerator">A collection or iterator that defines the scan durations.</param>
/// <param name="channelMask">
/// An unsigned 32-bit value that specifies the channels to scan, by bit flags.
/// The least significant bit of this value indicates <see cref="SkStackChannel.Channel33">channel 33</see>.
/// This value will be passed to the parameter <c>CHANNEL_MASK</c> in the <c>SKSCAN</c> command.
/// </param>
/// <param name="paaSelector">
/// A callback to select the target PAA from the PAAs found during the scan.
/// If <see langword="null"/>, selects the PAA which found at first during the scan.
/// </param>
/// <seealso cref="SkStackChannel.CreateMask"/>
[CLSCompliant(false)]
public static SkStackActiveScanOptions Create(
IEnumerable<int> scanDurationGenerator,
uint channelMask,
Predicate<SkStackPanDescription>? paaSelector = null
)
=> new UserDefinedActiveScanOptions(
paaSelector: paaSelector,
channelMask: channelMask,
scanDurationGenerator: scanDurationGenerator
);

Expand All @@ -100,6 +127,33 @@ public static SkStackActiveScanOptions Create(
)
=> new UserDefinedActiveScanOptions(
paaSelector: paaSelector,
channelMask: null,
scanDurationGeneratorFunc: scanDurationGeneratorFunc
);

/// <summary>
/// Creates the <see cref="SkStackActiveScanOptions"/> with the custom selection method and the delegate for generating duration factor.
/// </summary>
/// <param name="scanDurationGeneratorFunc">A delegate to method that iterates the scan durations.</param>
/// <param name="channelMask">
/// An unsigned 32-bit value that specifies the channels to scan, by bit flags.
/// The least significant bit of this value indicates <see cref="SkStackChannel.Channel33">channel 33</see>.
/// This value will be passed to the parameter <c>CHANNEL_MASK</c> in the <c>SKSCAN</c> command.
/// </param>
/// <param name="paaSelector">
/// A callback to select the target PAA from the PAAs found during the scan.
/// If <see langword="null"/>, selects the PAA which found at first during the scan.
/// </param>
/// <seealso cref="SkStackChannel.CreateMask"/>
[CLSCompliant(false)]
public static SkStackActiveScanOptions Create(
Func<IEnumerable<int>> scanDurationGeneratorFunc,
uint channelMask,
Predicate<SkStackPanDescription>? paaSelector = null
)
=> new UserDefinedActiveScanOptions(
paaSelector: paaSelector,
channelMask: channelMask,
scanDurationGeneratorFunc: scanDurationGeneratorFunc
);

Expand Down Expand Up @@ -128,26 +182,33 @@ Func<IEnumerable<int>> scanDurationGeneratorFunc

private sealed class UserDefinedActiveScanOptions : ScanDurationGeneratorFuncActiveScanOptions {
private readonly Predicate<SkStackPanDescription>? paaSelector;
private readonly uint? channelMask;

internal override uint? ChannelMask => channelMask;

public UserDefinedActiveScanOptions(
Predicate<SkStackPanDescription>? paaSelector,
uint? channelMask,
IEnumerable<int> scanDurationGenerator
)
: base(scanDurationGenerator ?? throw new ArgumentNullException(nameof(scanDurationGenerator)))
{
this.paaSelector = paaSelector;
this.channelMask = channelMask;
}

public UserDefinedActiveScanOptions(
Predicate<SkStackPanDescription>? paaSelector,
uint? channelMask,
Func<IEnumerable<int>> scanDurationGeneratorFunc
)
: base(scanDurationGeneratorFunc ?? throw new ArgumentNullException(nameof(scanDurationGeneratorFunc)))
{
this.paaSelector = paaSelector;
this.channelMask = channelMask;
}

public override SkStackActiveScanOptions Clone() => new UserDefinedActiveScanOptions(paaSelector, ScanDurationGeneratorFunc);
public override SkStackActiveScanOptions Clone() => new UserDefinedActiveScanOptions(paaSelector, channelMask, ScanDurationGeneratorFunc);
internal override bool SelectPanaAuthenticationAgent(SkStackPanDescription desc) => paaSelector?.Invoke(desc) ?? true;
}

Expand Down Expand Up @@ -214,6 +275,7 @@ Func<IEnumerable<int>> scanDurationGeneratorFunc
// TODO: public IProgress<int> Progress { get; set; }
public abstract SkStackActiveScanOptions Clone();
object ICloneable.Clone() => Clone();
internal virtual uint? ChannelMask => null;
internal abstract bool SelectPanaAuthenticationAgent(SkStackPanDescription desc);
internal abstract IEnumerable<int> YieldScanDurationFactors();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public ValueTask<IReadOnlyList<SkStackPanDescription>> ActiveScanAsync(
writeRBID: CreateActionForWritingRBID(rbid, nameof(rbid)),
writePassword: CreateActionForWritingPassword(password, nameof(password)),
scanDurationFactorGenerator: (scanOptions ?? SkStackActiveScanOptions.Default).YieldScanDurationFactors(),
channelMask: scanOptions?.ChannelMask,
cancellationToken: cancellationToken
);

Expand All @@ -34,13 +35,15 @@ public ValueTask<IReadOnlyList<SkStackPanDescription>> ActiveScanAsync(
writeRBID: writeRBID ?? throw new ArgumentNullException(nameof(writeRBID)),
writePassword: writePassword ?? throw new ArgumentNullException(nameof(writePassword)),
scanDurationFactorGenerator: (scanOptions ?? SkStackActiveScanOptions.Default).YieldScanDurationFactors(),
channelMask: scanOptions?.ChannelMask,
cancellationToken: cancellationToken
);

private async ValueTask<IReadOnlyList<SkStackPanDescription>> ActiveScanAsyncCore(
Action<IBufferWriter<byte>>? writeRBID,
Action<IBufferWriter<byte>>? writePassword,
IEnumerable<int> scanDurationFactorGenerator,
uint? channelMask,
CancellationToken cancellationToken = default
)
{
Expand All @@ -59,6 +62,7 @@ await SetRouteBCredentialAsync(
foreach (var durationFactor in scanDurationFactorGenerator) {
var (_, result) = await SendSKSCANActiveScanPairAsync(
durationFactor: durationFactor,
channelMask: channelMask ?? SKSCANDefaultChannelMask,
cancellationToken: cancellationToken
).ConfigureAwait(false);

Expand All @@ -82,6 +86,7 @@ CancellationToken cancellationToken
writeRBID: null,
writePassword: null,
scanDurationFactorGenerator: baseScanOptions.YieldScanDurationFactors(),
channelMask: baseScanOptions.ChannelMask,
cancellationToken: cancellationToken
).ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,48 @@ public void ActiveScanAsync_ScanDurations_Found()
Assert.That(commands, Does.Contain("SKSCAN 2 FFFFFFFF 4"));
Assert.That(commands, Does.Not.Contain("SKSCAN 2 FFFFFFFF 6"));
}

[TestCase(0x_FFFF_FFFFu)]
[TestCase(0x_0000_0001u)]
[TestCase(0x_0800_0000u)]
public void ActiveScanAsync_ChannelMask(uint channelMask)
{
using var stream = new PseudoSkStackStream();

// SKSETRBID
stream.ResponseWriter.WriteLine("OK");
// SKSETPWD
stream.ResponseWriter.WriteLine("OK");
// SKSCAN 2
stream.ResponseWriter.WriteLine("OK");
stream.ResponseWriter.WriteLine("EVENT 20 FE80:0000:0000:0000:021D:1290:0003:C890");
stream.ResponseWriter.WriteLine("EPANDESC");
stream.ResponseWriter.WriteLine(" Channel:21");
stream.ResponseWriter.WriteLine(" Channel Page:09");
stream.ResponseWriter.WriteLine(" Pan ID:8888");
stream.ResponseWriter.WriteLine(" Addr:12345678ABCDEF01");
stream.ResponseWriter.WriteLine(" LQI:E1");
stream.ResponseWriter.WriteLine(" PairID:AABBCCDD");
stream.ResponseWriter.WriteLine("EVENT 22 FE80:0000:0000:0000:021D:1290:0003:C890");

using var client = new SkStackClient(stream, logger: CreateLoggerForTestCase());
using var cts = new CancellationTokenSource(DefaultTimeOut);
IReadOnlyList<SkStackPanDescription>? scanResult = null;

Assert.DoesNotThrowAsync(async () => {
scanResult = await client.ActiveScanAsync(
rbid: "00112233445566778899AABBCCDDEEFF".ToByteSequence(),
password: "0123456789AB".ToByteSequence(),
scanOptions: SkStackActiveScanOptions.Create(new[] { 2 }, channelMask: channelMask),
cancellationToken: cts.Token
);
});

Assert.That(scanResult, Is.Not.Null);
Assert.That(scanResult!.Count, Is.EqualTo(1));

var commands = Encoding.ASCII.GetString(stream.ReadSentData());

Assert.That(commands, Does.Contain($"SKSCAN 2 {channelMask:X8} 2"));
}
}

0 comments on commit 3d3437a

Please sign in to comment.