Skip to content
Merged
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
Expand Up @@ -2,10 +2,12 @@
using BrickController2.DeviceManagement.CaDA;
using BrickController2.DeviceManagement.JieStar;
using BrickController2.DeviceManagement.MouldKing;
using BrickController2.DeviceManagement.PowerBox;
using BrickController2.Droid.PlatformServices.BluetoothLE;
using BrickController2.Droid.PlatformServices.DeviceManagement.CaDA;
using BrickController2.Droid.PlatformServices.DeviceManagement.JieStar;
using BrickController2.Droid.PlatformServices.DeviceManagement.MouldKing;
using BrickController2.Droid.PlatformServices.DeviceManagement.PowerBox;
using BrickController2.Droid.PlatformServices.GameController;
using BrickController2.Droid.PlatformServices.Infrared;
using BrickController2.Droid.PlatformServices.Localization;
Expand Down Expand Up @@ -35,6 +37,7 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType<MKPlatformService>().As<IMKPlatformService>().SingleInstance();
builder.RegisterType<CaDAPlatformService>().As<ICaDAPlatformService>().SingleInstance();
builder.RegisterType<JieStarPlatformService>().As<IJieStarPlatformService>().SingleInstance();
builder.RegisterType<PowerBoxPlatformService>().As<IPowerBoxPlatformService>().SingleInstance();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using BrickController2.DeviceManagement.PowerBox;
using BrickController2.Protocols;

namespace BrickController2.Droid.PlatformServices.DeviceManagement.PowerBox;

public class PowerBoxPlatformService : IPowerBoxPlatformService
{
private const int HeaderOffset = 15;
private const int PayloadLength = 18;

public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)
{
rfPayload = new byte[PayloadLength];
int payloadLength = CryptTools.GetRfPayload(PowerBoxProtocol.SeedArray, PowerBoxProtocol.HeaderArray, rawData, HeaderOffset, PowerBoxProtocol.CTXValue1, PowerBoxProtocol.CTXValue2, rfPayload);

// fill rest of array
for (int index = payloadLength; index < PayloadLength; index++)
{
rfPayload[index] = (byte)(index + 1);
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Autofac;
using BrickController2.DeviceManagement;
using BrickController2.DeviceManagement.PowerBox;
using BrickController2.PlatformServices.BluetoothLE;
using BrickController2.UI.Images;
using FluentAssertions;
using Moq;
using Xunit;
using PowerBoxVendor = BrickController2.DeviceManagement.PowerBox.PowerBox;

namespace BrickController2.Tests.DeviceManagement.DI;

public class PowerBoxVendorTests : VendorTestsBase
{
private readonly DeviceFactory _deviceFactory;

public PowerBoxVendorTests()
{
// Act
var container = InitializeContainer().Build();

_deviceFactory = container.Resolve<DeviceFactory>();
}

protected override ContainerBuilder InitializeContainer()
{
var builder = base.InitializeContainer();

// Arrange
builder.RegisterInstance(Mock.Of<IBluetoothLEService>());
builder.RegisterInstance(Mock.Of<IPowerBoxPlatformService>());
builder.RegisterInstance(Mock.Of<IDeviceImageRegistry>()); // needed because RegisterDevice<PowerFunctionsDevice>().WithImages("powerfunctions_image.png", "powerfunctions_image_small.png")

// execute registration of vendor PowerBox
builder.RegisterModule<PowerBoxVendor>();

return builder;
}

[Theory]
[InlineData("Device")]
public void RegisterDevice_PowerBox_MBattery_ReturnedDevice(string address)
{
DeviceType deviceType = DeviceType.PowerBoxMBattery;
string name = "TestDevice";
byte[] deviceData = [1, 2, 3];

var device = _deviceFactory(deviceType, name, address, deviceData, []);

device.Should().NotBeNull();
device.Should().BeOfType<PowerBoxMBattery>();
}

[Theory]
[InlineData("Device")]
public void RegisterDevice_PowerBox_ASeries_ReturnedDevice(string address)
{
DeviceType deviceType = DeviceType.PowerBoxASeries;
string name = "TestDevice";
byte[] deviceData = [1, 2, 3];

var device = _deviceFactory(deviceType, name, address, deviceData, []);

device.Should().NotBeNull();
device.Should().BeOfType<PowerBoxASeries>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using BrickController2.DeviceManagement.PowerBox;
using FluentAssertions;
using System;
using Xunit;

namespace BrickController2.Tests.DeviceManagement.PowerBox;

public sealed class PowerBoxAseriesDatagramTests : PowerBoxDatagramTestsBase
{
private const byte PayloadIdentifierConnect1 = 0xa4;
private const byte PayloadIdentifierConnect2 = 0x5b;
private const byte PayloadIdentifierCommand1 = 0x40;
private const byte PayloadIdentifierCommand2 = 0xbf;

/// <summary>
/// This test checks that the payload identifiers are correctly set in the connect datagram for each device address.
/// </summary>
[Fact]
public void TryGetTelegram_ConnectDatagram_PayloadIdentifier()
{
PowerBoxASeries device = new PowerBoxASeries("PowerBoxASeries", PowerBoxASeries.Device, [], _deviceRepository.Object, _bluetoothLEService.Object, _powerBoxPlatformService, _manager.Object);

device.TryGetTelegram(true, out byte[] payload).Should().BeTrue();
payload[0].Should().Be(PayloadIdentifierConnect1);
payload[7].Should().Be(PayloadIdentifierConnect2);
}

/// <summary>
/// This test checks that the AppIdentifier is correctly included in the connect datagram payload for each device address.
/// </summary>
[Fact]
public void TryGetTelegram_ConnectDatagram_AppIdentifier()
{
PowerBoxASeries device = new PowerBoxASeries("PowerBoxASeries", PowerBoxASeries.Device, [], _deviceRepository.Object, _bluetoothLEService.Object, _powerBoxPlatformService, _manager.Object);

device.TryGetTelegram(true, out byte[] payload).Should().BeTrue();
payload[1].Should().Be(AppIdentifier1);
payload[2].Should().Be(AppIdentifier2);
}

/// <summary>
/// This test checks that the payload identifiers are correctly set in the command datagram for each device address.
/// </summary>
[Fact]
public void TryGetTelegram_CommandDatagram_PayloadIdentifier()
{
PowerBoxASeries device = new PowerBoxASeries("PowerBoxASeries", PowerBoxASeries.Device, [], _deviceRepository.Object, _bluetoothLEService.Object, _powerBoxPlatformService, _manager.Object);

device.TryGetTelegram(false, out byte[] payload).Should().BeTrue();
payload[0].Should().Be(PayloadIdentifierCommand1);
payload[7].Should().Be(PayloadIdentifierCommand2);
}

/// <summary>
/// This test checks that the AppIdentifier is correctly included in the command datagram payload for each device address.
/// </summary>
[Fact]
public void TryGetTelegram_CommandDatagram_AppIdentifier()
{
PowerBoxASeries device = new PowerBoxASeries("PowerBoxASeries", PowerBoxASeries.Device, [], _deviceRepository.Object, _bluetoothLEService.Object, _powerBoxPlatformService, _manager.Object);

device.TryGetTelegram(false, out byte[] payload).Should().BeTrue();
payload[1].Should().Be(AppIdentifier1);
payload[2].Should().Be(AppIdentifier2);
}

/// <summary>
/// This test checks that the command datagram payload is correctly constructed based on the set output values for each device address.
/// </summary>
/// <param name="setValues">The set values for each channel.</param>
/// <param name="expectedPayload">The expected payload for the command datagram.</param>
[Theory]
[InlineData(new float[] { 0.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x00, 0x00, 0x00, PayloadIdentifierCommand2 })] // all channels neutral
[InlineData(new float[] { 1.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0xf0, 0x00, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 1 maximum, others neutral
[InlineData(new float[] { 0.0f, 1.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x0f, 0x00, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 2 maximum, others neutral
[InlineData(new float[] { 0.0f, 0.0f, 1.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0xf0, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 3 maximum, others neutral
[InlineData(new float[] { 0.0f, 0.0f, 0.0f, 1.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x0f, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 4 maximum, others neutral
[InlineData(new float[] { -1.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x70, 0x00, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 1 minimum, others neutral
[InlineData(new float[] { 0.0f, -1.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x07, 0x00, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 2 minimum, others neutral
[InlineData(new float[] { 0.0f, 0.0f, -1.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x70, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 3 minimum, others neutral
[InlineData(new float[] { 0.0f, 0.0f, 0.0f, -1.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x07, 0x00, 0x00, PayloadIdentifierCommand2 })] // channel 4 minimum, others neutral
[InlineData(new float[] { 9.0f, 9.0f, 9.0f, 9.0f, }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0xff, 0xff, 0x00, 0x00, PayloadIdentifierCommand2 })] // all channels above maximum, should be clamped to maximum
[InlineData(new float[] { -9.0f, -9.0f, -9.0f, -9.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x77, 0x77, 0x00, 0x00, PayloadIdentifierCommand2 })] // all channels below minimum, should be clamped to minimum
public void TryGetTelegram_CommandDatagram_Payload(float[] setValues, byte[] expectedPayload)
{
PowerBoxASeries device = new PowerBoxASeries("PowerBoxASeries", PowerBoxASeries.Device, [], _deviceRepository.Object, _bluetoothLEService.Object, _powerBoxPlatformService, _manager.Object);

// Set the output values for the device
for (int i = 0; i < setValues.Length; i++)
{
device.SetOutput(i, setValues[i]);
}

// Get the command datagram payload
device.TryGetTelegram(false, out byte[] payload).Should().BeTrue();

// Check that the payload matches the expected values
payload.Should().Equal(expectedPayload);
}

/// <summary>
/// Tests that setting an illegal channel index throws an ArgumentOutOfRangeException.
/// </summary>
[Fact]
public void TryGetTelegram_CommandDatagram_SetIllegalChannel()
{
PowerBoxASeries device = new PowerBoxASeries("PowerBoxASeries", PowerBoxASeries.Device, [], _deviceRepository.Object, _bluetoothLEService.Object, _powerBoxPlatformService, _manager.Object);

Action action = () => device.SetOutput(device.NumberOfChannels, 0);

action.Should().Throw<ArgumentOutOfRangeException>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BrickController2.DeviceManagement;
using BrickController2.DeviceManagement.PowerBox;
using BrickController2.PlatformServices.BluetoothLE;
using Moq;

namespace BrickController2.Tests.DeviceManagement.PowerBox;

public abstract class PowerBoxDatagramTestsBase
{
protected const byte AppIdentifier1 = 0x61; // This is the first byte of an randomly choosen AppIdentifier for UnitTesting
protected const byte AppIdentifier2 = 0x62; // This is the second byte of an randomly choosen AppIdentifier for UnitTesting

protected static readonly byte[] AppIdentifier = [AppIdentifier1, AppIdentifier2];

/// <summary>
/// This class is a test implementation of the IPowerBoxPlatformService interface that simulates the behavior of the TryGetRfPayload method for testing purposes.
/// It always returns true and sets the rfPayload to the rawData provided.
/// </summary>
protected class TestPowerBoxPlatformService : IPowerBoxPlatformService
{
public bool TryGetRfPayload(byte[] rawData, out byte[] rfPayload)
{
rfPayload = rawData;
return true; // Simulate success
}
}

protected readonly Mock<IBluetoothLEService> _bluetoothLEService = new(MockBehavior.Strict);
protected readonly Mock<IPowerBoxDeviceManager> _manager = new(MockBehavior.Strict);
protected readonly TestPowerBoxPlatformService _powerBoxPlatformService = new();
internal readonly Mock<IDeviceRepository> _deviceRepository = new(MockBehavior.Strict);

protected PowerBoxDatagramTestsBase()
{
_manager.Setup(x => x.GetAppId()).Returns(AppIdentifier);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using BrickController2.DeviceManagement.PowerBox;
using BrickController2.UI.Services.AppIdentifier;
using BrickController2.UI.Services.Preferences;
using FluentAssertions;
using Moq;
using Xunit;

namespace BrickController2.Tests.DeviceManagement.PowerBox;

public class PowerBoxDeviceManagerTests
{
private const byte AppIdentifier1 = 0x61; // 'a' = 0x61
private const byte AppIdentifier2 = 0x62; // 'b' = 0x62

private readonly IPowerBoxDeviceManager _manager;
private readonly Mock<IAppIdentifierService> _appIdentifierService = new(MockBehavior.Strict);

public PowerBoxDeviceManagerTests()
{
_appIdentifierService.Setup(x => x.GetAppId(2)).Returns(new byte[] { AppIdentifier1, AppIdentifier2 });
_manager = new PowerBoxDeviceManager(_appIdentifierService.Object);
}

[Fact]
public void AppId_TwoBytesInPreferences_AllBytes()
{
var appId = _manager.GetAppId();
appId.Length.Should().Be(2);
appId.Span[0].Should().Be(AppIdentifier1);
appId.Span[1].Should().Be(AppIdentifier2);
}
}
Loading
Loading