Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BrickController2.DeviceManagement.CaDA;
using BrickController2.Protocols;
using System;

namespace BrickController2.Droid.PlatformServices.DeviceManagement.CaDA;

Expand All @@ -8,6 +9,8 @@ public class CaDAPlatformService : ICaDAPlatformService
private const int HeaderOffset = 15;
private const int PayloadLength = 24;

private const int PayloadRev2Length = 16;

public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)
{
rfPayload = new byte[PayloadLength];
Expand All @@ -16,4 +19,11 @@ public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)

return true;
}

public bool TryGetRfPayload(ushort manufacturerId, ReadOnlySpan<byte> rawData, out byte[] rfPayload)
{
// copy past data
rfPayload = rawData.ToArray();
return rawData.Length == PayloadRev2Length;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ public CaDADeviceManagerTests()
_manager = new CaDADeviceManager(_preferencesService.Object, _cadaPlatformService.Object);
}

[Fact]
public void CreateScanData_IosPlatform_PatchesAppIdIntoScanData()
{
// arrange
_cadaPlatformService.TryGetRfPayload_ForIosPlatform();

var scanData = _manager.CreateScanData();

scanData.Should().BeEquivalentTo(new[]
{
0xC0, 0x3D, 0xCA, 0x66, 0x6D, 0x32, 0xB2, 0x9D,
0xD2, 0x57, 0xA1, 0x5C, 0xC5, 0x05, 0xB0, 0x75,
0xB1, 0x91, 0x48, 0x96, 0x77, 0xF8, 0x00, 0x8D,
0x18, 0x19
});
}

[Fact]
public void TryGetDevice_CadaCarWithMatchingAppId_ReturnsCaDaRaceCarDevice()
{
Expand Down Expand Up @@ -65,7 +82,7 @@ public void TryGetDevice_CadaCarWithMatchingAppId_ReturnsCaDaRaceCarDevice()
}

[Fact]
public void TryGetDevice_CadaCarWithDifferentAppId_ReturnsCaDaRaceCarDevice()
public void TryGetDevice_CadaCarWithDifferentAppId_ReturnsFalse()
{
byte[] manufacturerData =
{
Expand All @@ -91,4 +108,67 @@ public void TryGetDevice_CadaCarWithDifferentAppId_ReturnsCaDaRaceCarDevice()
result.Should().BeFalse();
device.DeviceType.Should().Be(DeviceType.Unknown);
}

[Fact]
public void TryGetDevice_CadaCarRev2WithZeroAppId_ReturnsCaDaRaceCarRev2Device()
{
byte[] manufacturerData =
[
// manufacturerId
0xAA,0x11,
// CADA RaceCar
0x11,
// 2 bytes AppID
0x00, 0x00,
// Seed
0x20, 0xB9,
// flag
0x85,
0x00, 0x00, 0x00, 0xA1, 0xCC, 0xB8, 0x92, 0xA0
];

var scanResult = new ScanResult("RaceCar-Revision2", "AA-BB-CC-DD", new Dictionary<byte, byte[]>()
{
{ 0xFF, manufacturerData }
});

var result = _manager.TryGetDevice(scanResult, out var device);

result.Should().BeTrue();
device.Should().BeEquivalentTo(new FoundDevice()
{
DeviceAddress = "AA-BB-CC-DD",
DeviceName = "RaceCar-Revision2",
DeviceType = DeviceType.CaDA_RaceCar_Rev2,
ManufacturerData = manufacturerData
});
}

[Fact]
public void TryGetDevice_CadaCarRev2WithSomeAppId_ReturnsFalse()
{
byte[] manufacturerData =
[
// manufacturerId
0xAA,0x11,
// CADA RaceCar
0x11,
// 2 bytes AppID
0x12, 0x34,
// Seed
0x20, 0xB9,
// flag
0x86,
0x00, 0x00, 0x00, 0xA1, 0xCC, 0xB8, 0x92, 0xA0
];

var scanResult = new ScanResult("RaceCar-Revision2", "AA-BB-CC-DD", new Dictionary<byte, byte[]>()
{
{ 0xFF, manufacturerData }
});

var result = _manager.TryGetDevice(scanResult, out var device);

result.Should().BeFalse();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using BrickController2.DeviceManagement;
using BrickController2.DeviceManagement.CaDA;
using BrickController2.PlatformServices.BluetoothLE;
using FluentAssertions;
using Moq;
using Xunit;

namespace BrickController2.Tests.DeviceManagement.CaDA;

public class CaDARaceCarRev2Tests
{
private readonly CaDARaceCarRev2 _device;
private readonly Mock<ICaDAPlatformService> _cadaPlatformService = new(MockBehavior.Strict);

public CaDARaceCarRev2Tests()
{
_device = new CaDARaceCarRev2("RC",
"1-2-3",
[
// manufacturerId
0xAA, 0x11,
// CADA RaceCar?
0x11,
// 2 bytes AppID
0x00, 0x00,
// Device Seed
0x20, 0xB9,
// some flag(s)
0x86, 0x00, 0x00, 0x00,
// hardware id
0xA1, 0xCC, 0xB8, 0x92, 0xA0
],
Mock.Of<IDeviceRepository>(MockBehavior.Strict),
Mock.Of<IBluetoothLEService>(MockBehavior.Strict),
_cadaPlatformService.Object);
}

[Fact]
public void TryGetTelegram_ConnectTelegram_ReturnsProperDatagram()
{
// arrange
_cadaPlatformService.TryGetRfPayload_ForIosPlatform();

var result = _device.TryGetTelegram(true, out var telegram);

result.Should().BeTrue();
telegram.Should().StartWith(new byte[]
{
0xC0, 0x00, 0xAA, 0x11, 0x11,
0x20, 0xB9,
0xAD, 0x42,
0x6B, 0x6B, 0x00, /*0xEB,*/ 0xD6, //TODO checksum
0xA1, 0xCC, 0xB8, 0x92, 0xA0,
0xEF, 0xF2, 0xC5, 0x67, 0x8F, 0x9F, 0xF1, 0xF8
});
}

[Fact]
public void TryGetTelegram_WithZeroValuesTelegram_ReturnsProperDatagram()
{
// arrange
_cadaPlatformService.TryGetRfPayload_ForIosPlatform();

var result = _device.TryGetTelegram(false, out var telegram);

result.Should().BeTrue();
telegram.Should().StartWith(new byte[]
{
0xC0, 0x00, 0xBB, 0x11, 0x11,
0x20, 0xB9,
0xAD, 0x42,
0x80, 0x80, 0x80, 0x80, //TODO checksum
0xA1, 0xCC, 0xB8, 0x92, 0xB0,
0xEF, 0xF2, 0xC5, 0x67, 0x8F, 0x9F, 0xF1, 0xF8
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BrickController2.DeviceManagement.CaDA;
using BrickController2.Protocols;
using Moq;

namespace BrickController2.Tests.DeviceManagement.CaDA;

public static class MockSetups
{
public static Mock<ICaDAPlatformService> TryGetRfPayload_ForIosPlatform(this Mock<ICaDAPlatformService> mock)
{
const int HeaderOffset = 13;
const int PayloadLength = 26;

mock.Setup(m => m.TryGetRfPayload(It.IsAny<byte[]>(), out It.Ref<byte[]>.IsAny))
.Callback((byte[] rawData, out byte[] rfPayload) =>
{
rfPayload = new byte[PayloadLength];
int payloadLength = CryptTools.GetRfPayload(CaDAProtocol.SeedArray,
CaDAProtocol.HeaderArray,
rawData,
HeaderOffset,
CaDAProtocol.CTXValue1,
CaDAProtocol.CTXValue2,
rfPayload);

// fill rest of array
byte bVar = 0x18; // initial value
for (int index = payloadLength; index < PayloadLength; index++)
{
rfPayload[index] = bVar++;
}
})
.Returns(true);

return mock;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

namespace BrickController2.Windows.Extensions;

public static class AdvertismentExtensions
public static class AdvertisementExtensions
{
public static string GetLocalName(this BluetoothLEAdvertisementReceivedEventArgs args) => args.Advertisement.LocalName.TrimEnd();

public static bool IsValidDeviceName(this string deviceName) => !string.IsNullOrEmpty(deviceName);

public static bool CanCarryData(this BluetoothLEAdvertisementReceivedEventArgs args) =>
args.AdvertisementType == BluetoothLEAdvertisementType.ScanResponse ||
args.AdvertisementType == BluetoothLEAdvertisementType.ConnectableUndirected;
args.AdvertisementType == BluetoothLEAdvertisementType.ConnectableUndirected ||
args.AdvertisementType == BluetoothLEAdvertisementType.NonConnectableUndirected; // some per advertisement devices which are not directly connectable
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private void _passiveWatcher_Received(BluetoothLEAdvertisementWatcher sender, Bl
else if (args.Advertisement.DataSections?.Count > 0)
{
// allow processing of advertised data from a device
var advertismentData = args.Advertisement.DataSections
var advertisementData = args.Advertisement.DataSections
.Where(s => AdvertismentDataTypes.Contains(s.DataType))
.ToDictionary(s => s.DataType, s => s.Data.ToByteArray());

Expand All @@ -68,7 +68,7 @@ private void _passiveWatcher_Received(BluetoothLEAdvertisementWatcher sender, Bl
// if no local name is set, try to get it from the cache
_deviceNameCache.TryGetValue(args.BluetoothAddress, out deviceName);

_scanCallback(new ScanResult(deviceName, bluetoothAddress, advertismentData));
_scanCallback(new ScanResult(deviceName, bluetoothAddress, advertisementData));
}
}

Expand All @@ -79,6 +79,7 @@ private void _passiveWatcher_Stopped(BluetoothLEAdvertisementWatcher sender, Blu

private void _activeWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
var bt = args.BluetoothAddress;
if (!args.CanCarryData())
{
return;
Expand All @@ -92,17 +93,17 @@ private void _activeWatcher_Received(BluetoothLEAdvertisementWatcher sender, Blu

var bluetoothAddress = args.BluetoothAddress.ToBluetoothAddressString();

var advertismentData = args.Advertisement.DataSections
var advertisementData = args.Advertisement.DataSections
.Where(s => AdvertismentDataTypes.Contains(s.DataType))
.ToDictionary(s => s.DataType, s => s.Data.ToByteArray());

// enrich data with name manually (SBrick do not like CompleteLocalName, but Buwizz3 requires it)
if (!advertismentData.ContainsKey(BluetoothLEAdvertisementDataTypes.CompleteLocalName))
if (!advertisementData.ContainsKey(BluetoothLEAdvertisementDataTypes.CompleteLocalName))
{
advertismentData[BluetoothLEAdvertisementDataTypes.CompleteLocalName] = Encoding.ASCII.GetBytes(deviceName);
advertisementData[BluetoothLEAdvertisementDataTypes.CompleteLocalName] = Encoding.ASCII.GetBytes(deviceName);
}

_scanCallback(new ScanResult(deviceName, bluetoothAddress, advertismentData));
_scanCallback(new ScanResult(deviceName, bluetoothAddress, advertisementData));
}

public void Stop()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BrickController2.DeviceManagement.CaDA;
using BrickController2.Protocols;
using System;

namespace BrickController2.Windows.PlatformServices.DeviceManagement.CaDA;

Expand All @@ -9,6 +10,8 @@ public class CaDAPlatformService : ICaDAPlatformService
private const int PayloadOffset = 3;
private const int PayloadLength = 24 + PayloadOffset;

private const int PayloadRev2Length = 16;

public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)
{
// JK:
Expand All @@ -28,4 +31,11 @@ public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)

return true;
}

public bool TryGetRfPayload(ushort manufacturerId, ReadOnlySpan<byte> rawData, out byte[] rfPayload)
{
// copy past data
rfPayload = rawData.ToArray();
return rawData.Length == PayloadRev2Length;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BrickController2.DeviceManagement.CaDA;
using BrickController2.Protocols;
using System;

namespace BrickController2.iOS.PlatformServices.DeviceManagement.CaDA;

Expand All @@ -8,6 +9,19 @@ public class CaDAPlatformService : ICaDAPlatformService
private const int HeaderOffset = 13;
private const int PayloadLength = 26;

private const int SessionLength = 8;
private const int PayloadRev2Length = 16;

// Session - 8 bytes per application run
private static readonly Lazy<byte[]> _sessionPostfix = new(() =>
{
var session = new byte[SessionLength];
Random.Shared.NextBytes(session);
return session;
});

public ReadOnlySpan<byte> Session => _sessionPostfix.Value;

public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)
{
rfPayload = new byte[PayloadLength];
Expand All @@ -22,4 +36,16 @@ public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)

return true;
}

public bool TryGetRfPayload(short manufacturerId, ReadOnlySpan<byte> rawData, out byte[] rfPayload)
{
rfPayload = new byte[2 + rawData.Length + PayloadLength];

BitConverter.TryWriteBytes(rfPayload, manufacturerId);

rawData.CopyTo(rfPayload.AsSpan(2));
Session.CopyTo(rfPayload.AsSpan(2 + rawData.Length));

return rawData.Length == PayloadRev2Length;
}
}
Loading
Loading