diff --git a/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/MessageEncoderFactoryTests.cs b/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/MessageEncoderFactoryTests.cs index a53aa72a5..4e9960df4 100644 --- a/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/MessageEncoderFactoryTests.cs +++ b/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/MessageEncoderFactoryTests.cs @@ -59,7 +59,7 @@ public void Create_WithLength16DeviceData_UsesCorrectDeviceIdAndSequence() result.Should().NotBeNull() .And.BeOfType(); - var encoded = result!.Encode([Half.Zero, Half.Zero, Half.Zero], true); + var encoded = result!.Encode([Half.Zero, Half.Zero, Half.Zero, Half.Zero], true); encoded.Length.Should().Be(16); // Verify deviceId is present in output (bytes 3-4) encoded[3].Should().Be(0xC9); diff --git a/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderRev2Tests.cs b/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderRev2Tests.cs index 94ec44d58..1616492a1 100644 --- a/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderRev2Tests.cs +++ b/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderRev2Tests.cs @@ -10,7 +10,7 @@ namespace BrickController2.Tests.DeviceManagement.CaDA; public class RaceCarMessageEncoderRev2Tests { [Theory] - [InlineData(0xB920, 0x4076, 0x32, 0x32, 0xB2)] //AA111120B97640 323200B2A1 CCB892A0 + [InlineData(0xb920, 0x4076, 0x32, 0x32, 0xb2)] // aa 11 11 20 b9 76 40 32 32 00 b2 a1 cc b8 92 a0 public void EncodeValues_Connect_ReturnsProperPayload(ushort deviceId, ushort appId, byte v1, byte v2, byte v4) { @@ -18,7 +18,7 @@ public void EncodeValues_Connect_ReturnsProperPayload(ushort deviceId, ushort ap var encoder = Create(deviceId, appId); // act - var result = encoder.EncodeValues([Zero, Zero, Zero], true); + var result = encoder.EncodeValues([Zero, Zero, Zero, Zero], true); // assert result.Length.Should().Be(16); @@ -27,16 +27,16 @@ public void EncodeValues_Connect_ReturnsProperPayload(ushort deviceId, ushort ap 0xAA, 0x11, 0x11, (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), - v1, v2, 0x00, v4, + v1, v2, 0b00, v4, 0xA1, 0xCC, 0xB8, 0x92, 0xA0 ]); } [Theory] - [InlineData(0xB920, 0x42AD, 0x8C, 0x8C, 0x0C)] //BB111120B9AD42 8C8C000C A1CCB892B0 - [InlineData(0xB920, 0x5188, 0x76, 0x76, 0xF6)] //BB111120B98851 767600F6 A1CCB892B0 - [InlineData(0xB920, 0x4076, 0x53, 0x53, 0xD3)] //BB111120B97640 535300D3 A1CCB892B0 - [InlineData(0xC1C9, 0xA4B7, 0xa9, 0xa9, 0x29)] // bb 11 11 c9 c1 b7 a4 a9 a9 00 29 a1 cc b8 92 b0 + [InlineData(0xb920, 0x42ad, 0x8c, 0x8c, 0x0c)] // bb 11 11 20 b9 ad 42 8c 8c 00 0c a1 cc b8 92 b0 + [InlineData(0xb920, 0x5188, 0x76, 0x76, 0xf6)] // bb 11 11 20 b9 88 51 76 76 00 f6 a1 cc b8 92 b0 + [InlineData(0xb920, 0x4076, 0x53, 0x53, 0xd3)] // bb 11 11 20 b9 76 40 53 53 00 d3 a1 cc b8 92 b0 + [InlineData(0xc1c9, 0xa4b7, 0xa9, 0xa9, 0x29)] // bb 11 11 c9 c1 b7 a4 a9 a9 00 29 a1 cc b8 92 b0 public void EncodeValues_WithZeroValues_ReturnsProperPayload(ushort deviceId, ushort appId, byte v1, byte v2, byte v4) { @@ -44,7 +44,7 @@ public void EncodeValues_WithZeroValues_ReturnsProperPayload(ushort deviceId, us var encoder = Create(deviceId, appId); // act - var result = encoder.EncodeValues([Zero, Zero, Zero], false); + var result = encoder.EncodeValues([Zero, Zero, Zero, Zero], false); // assert result.Length.Should().Be(16); @@ -53,22 +53,22 @@ public void EncodeValues_WithZeroValues_ReturnsProperPayload(ushort deviceId, us 0xBB, 0x11, 0x11, (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), - v1, v2, 0x00, v4, + v1, v2, 0b00, v4, 0xA1, 0xCC, 0xB8, 0x92, 0xB0 ]); } [Theory] - [InlineData(0xB920, 0x4076, 0x54, 0x54, 0xD4)] //BB111120B97640 545401D4A1 CCB892B0 - [InlineData(0xC1C9, 0xA4B7, 0xaa, 0xaa, 0x2a)] // bb 11 11 c9 c1 b7 a4 aa aa 01 2a a1 cc b8 92 b0 - public void EncodeValues_WithZeroValuesAndLightOn_ReturnsProperPayload(ushort deviceId, ushort appId, + [InlineData(0xb920, 0x4076, 0x56, 0x56, 0xd6)] // bb 11 11 20 b9 76 40 56 56 03 d6 a1 cc b8 92 b0 + [InlineData(0xc1c9, 0xa4b7, 0xac, 0xac, 0x2c)] // bb 11 11 c9 c1 b7 a4 ac ac 03 2c a1 cc b8 92 b0 + public void EncodeValues_WithZeroValuesAndFrontLightOnAndRearLightOn_ReturnsProperPayload(ushort deviceId, ushort appId, byte v1, byte v2, byte v4) { // arrange var encoder = Create(deviceId, appId); // act - var result = encoder.EncodeValues([Zero, Zero, One]); + var result = encoder.EncodeValues([Zero, Zero, One, One]); // assert result.Length.Should().Be(16); @@ -77,27 +77,27 @@ public void EncodeValues_WithZeroValuesAndLightOn_ReturnsProperPayload(ushort de 0xBB, 0x11, 0x11, (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), - v1, v2, 0x01, v4, + v1, v2, 0b11, v4, 0xA1, 0xCC, 0xB8, 0x92, 0xB0 ]); } [Theory] - [InlineData(0xB920, 0x4076, -1.00f, 0x3E, 0xBE, 0xBE, 0x0B)] //BB111120B97640 3EBE01BE0B CCB892B0 - [InlineData(0xB920, 0x4076, -0.75f, 0xFE, 0x5E, 0x7E, 0xAB)] //BB111120B97640 FE5E017EAB CCB892B0 - [InlineData(0xC1C9, 0xA4B7, -0.75f, 0x4a, 0xea, 0xca, 0xa1)] //bb 11 11 c9 c1 b7 a4 4a ea 01 ca a1 cc b8 92 b0 - [InlineData(0xC1C9, 0x2979, -0.75f, 0x9b, 0x3b, 0x1b, 0xab)] //bb 11 11 c9 c1 79 29 9b 3b 01 1b ab cc b8 92 b0 - [InlineData(0xB920, 0x4076, 1.000f, 0xAA, 0xD5, 0x2A, 0x78)] //BB111120B97640 AAD5BE0178 CCB892B0 - [InlineData(0xC1C9, 0xA4B7, 0.746f, 0x9d, 0xc2, 0x1d, 0x35)] // bb 11 11 c9 c1 b7 a4 9d c2 01 1d 35 cc b8 92 b0 - [InlineData(0xC1C9, 0x2979, 0.746f, 0x1b, 0x44, 0x9b, 0x6c)] // bb 11 11 c9 c1 79 29 1b 44 01 9b 6c cc b8 92 b0 - public void EncodeValues_WithPartialSteeringAndLightOn_ReturnsProperPayload(ushort deviceId, ushort appId, + [InlineData(0xb920, 0x4076, -1.00f, 0x40, 0xc0, 0xc0, 0x0b)] // bb 11 11 20 b9 76 40 40 c0 03 c0 0b cc b8 92 b0 + [InlineData(0xb920, 0x4076, -0.75f, 0x00, 0xa0, 0x80, 0xab)] // bb 11 11 20 b9 76 40 00 a0 03 80 ab cc b8 92 b0 + [InlineData(0xc1c9, 0xa4b7, -0.75f, 0x4c, 0xec, 0xcc, 0xa1)] // bb 11 11 c9 c1 b7 a4 4c ec 03 cc a1 cc b8 92 b0 + [InlineData(0xc1c9, 0x2979, -0.75f, 0x9d, 0x3d, 0x1d, 0xab)] // bb 11 11 c9 c1 79 29 9d 3d 03 1d ab cc b8 92 b0 + [InlineData(0xb920, 0x4076, 1.000f, 0xac, 0xd3, 0x2c, 0x78)] // bb 11 11 20 b9 76 40 aa ac d3 03 2c cc b8 92 b0 + [InlineData(0xc1c9, 0xa4b7, 0.746f, 0x9f, 0xc0, 0x1f, 0x35)] // bb 11 11 c9 c1 b7 a4 9f c0 03 1f 35 cc b8 92 b0 + [InlineData(0xc1c9, 0x2979, 0.746f, 0x1d, 0x42, 0x9d, 0x6c)] // bb 11 11 c9 c1 79 29 1d 42 03 9d 6c cc b8 92 b0 + public void EncodeValues_WithPartialSteeringAndFrontLightOnAndRearLightOn_ReturnsProperPayload(ushort deviceId, ushort appId, float value, byte v1, byte v2, byte v4, byte sequence) { // arrange var encoder = Create(deviceId, appId, sequence: (byte)(sequence - 1)); // act - var result = encoder.EncodeValues([Zero, (Half)value, One]); + var result = encoder.EncodeValues([Zero, (Half)value, One, One]); // assert result.Length.Should().Be(16); @@ -106,21 +106,21 @@ public void EncodeValues_WithPartialSteeringAndLightOn_ReturnsProperPayload(usho 0xBB, 0x11, 0x11, (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), - v1, v2, 0x01, v4, + v1, v2, 0b11, v4, sequence, 0xCC, 0xB8, 0x92, 0xB0 ]); } [Theory] - [InlineData(0xC1C9, 0x2979, 1.000f, 0x4A, 0xCA, 0x4A, 0xFA)] // bb 11 11 c9 c1 79 29 4a ca 01 4a fa cc b8 92 b0 - public void EncodeValues_WithPartialSpeedAndLightOn_ReturnsProperPayload(ushort deviceId, ushort appId, + [InlineData(0xC1C9, 0x2979, 1.000f, 0x4C, 0xCC, 0x4C, 0xFA)] // bb 11 11 c9 c1 79 29 4a ca 01 4a fa cc b8 92 b0 + public void EncodeValues_WithPartialSpeedAndFrontLightOnAndRearLightOn_ReturnsProperPayload(ushort deviceId, ushort appId, float value, byte v1, byte v2, byte v4, byte sequence) { // arrange var encoder = Create(deviceId, appId, sequence: (byte)(sequence - 1)); // act - var result = encoder.EncodeValues([(Half)value, Zero, One]); + var result = encoder.EncodeValues([(Half)value, Zero, One, One]); // assert result.Length.Should().Be(16); @@ -129,7 +129,7 @@ public void EncodeValues_WithPartialSpeedAndLightOn_ReturnsProperPayload(ushort 0xBB, 0x11, 0x11, (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), - v1, v2, 0x01, v4, + v1, v2, 0b11, v4, sequence, 0xCC, 0xB8, 0x92, 0xB0 ]); } @@ -143,7 +143,7 @@ public void EncodeValues_WithMiddleFirstChannel_ReturnsProperPayload(ushort devi var encoder = Create(deviceId, appId, sequence: (byte)(sequence - 1)); // act - var result = encoder.EncodeValues([(Half)value, Zero, Zero]); + var result = encoder.EncodeValues([(Half)value, Zero, Zero, Zero]); // assert result.Length.Should().Be(16); @@ -152,7 +152,53 @@ public void EncodeValues_WithMiddleFirstChannel_ReturnsProperPayload(ushort devi 0xBB, 0x11, 0x11, (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), - v1, v2, 0x00, v4, + v1, v2, 0b00, v4, + sequence, 0xCC, 0xB8, 0x92, 0xB0 + ]); + } + + [Theory] + [InlineData(0xC1C9, 0x2979, 0x4A, 0x4A, 0xCA, 0xFA)] // bb 11 11 c9 c1 79 29 4a 4a 01 ca fa cc b8 92 b0 + public void EncodeValues_WithZeroValuesAndFrontLightOnAndRearLightOff_ReturnsProperPayload(ushort deviceId, ushort appId, + byte v1, byte v2, byte v4, byte sequence) + { + // arrange + var encoder = Create(deviceId, appId, sequence: sequence); // speed and steering are zero, so the sequence is not incremented + + // act + var result = encoder.EncodeValues([Zero, Zero, One, Zero]); + + // assert + result.Length.Should().Be(16); + result.ToArray().Should().BeEquivalentTo( + [ + 0xBB, 0x11, 0x11, + (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), + (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), + v1, v2, 0b01, v4, + sequence, 0xCC, 0xB8, 0x92, 0xB0 + ]); + } + + [Theory] + [InlineData(0xC1C9, 0x2979, 0x4B, 0x4B, 0xCB, 0xFA)] // bb 11 11 c9 c1 79 29 4b 4b 03 cb fa cc b8 92 b0 + public void EncodeValues_WithZeroValuesAndFrontLightOffAndRearLightOn_ReturnsProperPayload(ushort deviceId, ushort appId, + byte v1, byte v2, byte v4, byte sequence) + { + // arrange + var encoder = Create(deviceId, appId, sequence: sequence); // speed and steering are zero, so the sequence is not incremented + + // act + var result = encoder.EncodeValues([Zero, Zero, Zero, One]); + + // assert + result.Length.Should().Be(16); + result.ToArray().Should().BeEquivalentTo( + [ + 0xBB, 0x11, 0x11, + (byte)(deviceId & 0xFF), (byte)((deviceId >> 8) & 0xFF), + (byte)(appId & 0xFF), (byte)((appId >> 8) & 0xFF), + v1, v2, 0b10, v4, sequence, 0xCC, 0xB8, 0x92, 0xB0 ]); } diff --git a/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderTests.cs b/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderTests.cs index 5c88406f9..99f028d41 100644 --- a/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderTests.cs +++ b/BrickController2/BrickController2.Tests/DeviceManagement/CaDA/RaceCarMessageEncoderTests.cs @@ -19,7 +19,7 @@ public void Encode_WithValidInput_ReturnsCorrectLengthAndStaticStructure() var encoder = Create(0x4032, [0x01, 0x23, 0x40], [0x87, 0x65, 0x43]); // Act - var result = encoder.Encode([Zero, Zero, Zero]); + var result = encoder.Encode([Zero, Zero, Zero, Zero]); // Assert result.Length.Should().Be(16); @@ -43,18 +43,74 @@ public void Encode_WithFullLightValue_EncodesAndEncryptsValues() var encoder = Create(0x4032, [0x01, 0x02, 0x03], [0x04, 0x05, 0x06]); // Act - var result = encoder.Encode([Zero, Zero, One]); + var result = encoder.Encode([Zero, Zero, One, One]); // Assert result.Length.Should().Be(16); - result.Should().EndWith([0xD6, 0xA4, 0x25, 0x89, 0x4E, 0x6D, 0x25, 0x25]); + result.Should().EndWith([ + 0xD6, + 0xA4, + 0x25, // [10] ChannelData verticalValue (min= 0x80 (128)) + 0x89, // [11] ChannelData horizontalValue (min= 0x80 (128)) + 0x26, // [12] ChannelData lightValue + 0x6D, // [13] ChannelData + 0x25, // [14] ChannelData + 0x25 // [15] ChannelData + ]); + } + + [Fact] + public void Encode_WithFrontLightValue_EncodesAndEncryptsValues() + { + // Arrange + var encoder = Create(0x4032, [0x01, 0x02, 0x03], [0x04, 0x05, 0x06]); + + // Act + var result = encoder.Encode([Zero, Zero, One, Zero]); + + // Assert + result.Length.Should().Be(16); + result.Should().EndWith([ + 0xD6, + 0xA4, + 0x25, // [10] ChannelData verticalValue (min= 0x80 (128)) + 0x89, // [11] ChannelData horizontalValue (min= 0x80 (128)) + 0x24, // [12] ChannelData lightValue + 0x6D, // [13] ChannelData + 0x25, // [14] ChannelData + 0x25 // [15] ChannelData + ]); + } + + [Fact] + public void Encode_WithRearLightValue_EncodesAndEncryptsValues() + { + // Arrange + var encoder = Create(0x4032, [0x01, 0x02, 0x03], [0x04, 0x05, 0x06]); + + // Act + var result = encoder.Encode([Zero, Zero, Zero, One]); + + // Assert + result.Length.Should().Be(16); + result.Should().EndWith([ + 0xD6, + 0xA4, + 0x25, // [10] ChannelData verticalValue (min= 0x80 (128)) + 0x89, // [11] ChannelData horizontalValue (min= 0x80 (128)) + 0x27, // [12] ChannelData lightValue + 0x6D, // [13] ChannelData + 0x25, // [14] ChannelData + 0x25 // [15] ChannelData + ]); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] - [InlineData(4)] + [InlineData(3)] + [InlineData(5)] [InlineData(10)] public void Encode_WithInvalidValueCount_ThrowsArgumentException(int count) { @@ -76,7 +132,7 @@ public void EncodeValues_WithZeros_ShouldEncodeCorrectly() // Arrange var encoder = Create(0xABCD, [0x01, 0x02, 0x03], [0x04, 0x05, 0x06]); // Act - var result = encoder.EncodeValues([Zero, Zero, Zero]); + var result = encoder.EncodeValues([Zero, Zero, Zero, Zero]); // Assert result.Length.Should().Be(8); result.ToArray().Should().Equal( @@ -85,7 +141,7 @@ public void EncodeValues_WithZeros_ShouldEncodeCorrectly() 0xAB, // [9] ChannelData random 0x80, // [10] ChannelData verticalValue (min= 0x80 (128)) 0x80, // [11] ChannelData horizontalValue (min= 0x80 (128)) - 0x80, // [12] ChannelData lightValue + 0x00, // [12] ChannelData lightValue 0x00, // [13] ChannelData 0x00, // [14] ChannelData 0x00, // [15] ChannelData @@ -93,21 +149,20 @@ public void EncodeValues_WithZeros_ShouldEncodeCorrectly() } [Theory] - [InlineData(-2.0f, 1.0f, 1.0f)] // Out of range speed - [InlineData(-1.0f, 2.0f, 1.0f)] // Out of range steering - [InlineData(-1.0f, 1.0f, 2.0f)] // Out of range light - public void Encode_WithOutOfRangeValues_ClampsTo0xFF(float speed, float steering, float light) + [InlineData(-2.0f, 1.0f)] // Out of range speed + [InlineData(-1.0f, 2.0f)] // Out of range steering + public void Encode_WithOutOfRangeValues_ClampsTo0xFF(float speed, float steering) { // Arrange var encoder = Create(); // Act - var result = encoder.EncodeValues([(Half)speed, (Half)steering, (Half)light]); + var result = encoder.EncodeValues([(Half)speed, (Half)steering, Zero, Zero]); // Assert result.ToArray().Should().EndWith( [ 0xFF, // [10] ChannelData verticalValue (min= 0x80 (128)) 0xFF, // [11] ChannelData horizontalValue (min= 0x80 (128)) - 0xFF, // [12] ChannelData lightValue + 0x00, // [12] ChannelData lightValue 0x00, // [13] ChannelData 0x00, // [14] ChannelData 0x00, // [15] ChannelData @@ -115,15 +170,14 @@ public void Encode_WithOutOfRangeValues_ClampsTo0xFF(float speed, float steering } [Theory] - [InlineData(2.0f, -1.0f, -1.0f)] // Out of range speed - [InlineData(1.0f, -2.0f, -1.0f)] // Out of range steering - [InlineData(1.0f, -1.0f, -2.0f)] // Out of range light - public void Encode_WithOutOfRangeValues_ClampsTo0x00(float speed, float steering, float light) + [InlineData(2.0f, -1.0f)] // Out of range speed + [InlineData(1.0f, -2.0f)] // Out of range steering + public void Encode_WithOutOfRangeValues_ClampsTo0x00(float speed, float steering) { // Arrange var encoder = Create(); // Act - var result = encoder.EncodeValues([(Half)speed, (Half)steering, (Half)light]); + var result = encoder.EncodeValues([(Half)speed, (Half)steering, Zero, Zero]); // Assert result.ToArray().Should().EndWith( [ diff --git a/BrickController2/BrickController2/DeviceManagement/CaDA/CaDARaceCar.cs b/BrickController2/BrickController2/DeviceManagement/CaDA/CaDARaceCar.cs index 6710a095e..7f1a2548b 100644 --- a/BrickController2/BrickController2/DeviceManagement/CaDA/CaDARaceCar.cs +++ b/BrickController2/BrickController2/DeviceManagement/CaDA/CaDARaceCar.cs @@ -11,7 +11,7 @@ namespace BrickController2.DeviceManagement.CaDA; internal class CaDARaceCar : BluetoothAdvertisingDevice { private readonly IMessageEncoder _messageEncoder; - private readonly OutputValuesGroup _outputValues = new(3); + private readonly OutputValuesGroup _outputValues = new(4); public CaDARaceCar(string name, string address, byte[] deviceData, IDeviceRepository deviceRepository, IBluetoothLEService bleService, IMessageEncoderFactory messageEncoderFactory) : base(name, address, deviceRepository, bleService) @@ -26,7 +26,7 @@ public CaDARaceCar(string name, string address, byte[] deviceData, IDeviceReposi /// protected override ushort ManufacturerId => CaDAProtocol.ManufacturerID; - public override int NumberOfChannels => 3; + public override int NumberOfChannels => 4; public override void SetOutput(int channelNo, float value) { @@ -34,7 +34,7 @@ public override void SetOutput(int channelNo, float value) value = CutOutputValue(value); // check for change - if (_outputValues.SetOutput(channelNo, (Half)value)) + if (SetChannelOutput(channelNo, value)) { // notify data changed _bluetoothAdvertisingDeviceHandler.NotifyDataChanged(); @@ -67,4 +67,13 @@ protected override BluetoothAdvertisingDeviceHandler GetBluetoothAdvertisingDevi { return new BluetoothAdvertisingDeviceHandler(_bleService, ManufacturerId, TryGetTelegram, TimeSpan.MaxValue); } + + private bool SetChannelOutput(int channelNo, float value) + { + return channelNo switch + { + 2 or 3 => _outputValues.SetOutput(channelNo, (Math.Abs(value) > 0.5f) ? Half.One : Half.Zero), // channel 2 front lights, channel 3 rear lights + _ => _outputValues.SetOutput(channelNo, (Half)value) // channel 0 throttle, channel 1 steering + }; + } } diff --git a/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoder.cs b/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoder.cs index 74a6c3c0f..31be7a570 100644 --- a/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoder.cs +++ b/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoder.cs @@ -59,7 +59,7 @@ public void Initialize() public byte[] Encode(ReadOnlySpan values, bool connectDevice = false) { // check params - if (values.Length != 3) + if (values.Length != 4) { throw new ArgumentException("Invalid input data.", nameof(values)); } @@ -78,11 +78,15 @@ internal Span EncodeValues(ReadOnlySpan values) { ushort random = (ushort)_random.Next(ushort.MinValue, ushort.MaxValue); + byte lightsBits = (byte)( + (values[2] != Half.Zero ? (byte)0x01 : (byte)0x00) | + (values[3] != Half.Zero ? (byte)0x02 : (byte)0x00)); + _controlDataArray[8] = (byte)(random & 0xFF); _controlDataArray[9] = (byte)((random >> 8) & 0xFF); _controlDataArray[10] = Clamp(HalfByte - (values[0] * HalfByte)); // speed value - reversed - _controlDataArray[11] = Clamp(HalfByte + (values[1] * HalfByte)); // - _controlDataArray[12] = Clamp(HalfByte + (values[2] * HalfByte)); // light on/off + _controlDataArray[11] = Clamp(HalfByte + (values[1] * HalfByte)); // steering value + _controlDataArray[12] = lightsBits; // bitfield: 0x01 frontlights, 0x02 rearlights _controlDataArray[13] = 0; _controlDataArray[14] = 0; _controlDataArray[15] = 0; diff --git a/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoderRev2.cs b/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoderRev2.cs index 07b0c4839..7fcb67c93 100644 --- a/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoderRev2.cs +++ b/BrickController2/BrickController2/DeviceManagement/CaDA/RaceCarMessageEncoderRev2.cs @@ -66,7 +66,7 @@ public void Initialize() public byte[] Encode(ReadOnlySpan values, bool connectDevice = false) { // check params - if (values.Length != 3) + if (values.Length != 4) { throw new ArgumentException("Invalid input data.", nameof(values)); } @@ -86,6 +86,10 @@ internal ReadOnlySpan EncodeValues(ReadOnlySpan values, bool connect // Map input (-1.0 to 1.0) to Steering (0x00 to 0xFF) byte steering = Clamp(HalfByte + (values[1] * HalfByte)); + byte lightsBits = (byte)( + (values[2] != Half.Zero ? (byte)0x01 : (byte)0x00) | + (values[3] != Half.Zero ? (byte)0x02 : (byte)0x00)); + // header: PAIRING : COMMAND var header = connectDevice ? (byte)0xAA : (byte)0xBB; @@ -94,7 +98,7 @@ internal ReadOnlySpan EncodeValues(ReadOnlySpan values, bool connect _data[7] = throttle; _data[8] = steering; // flags: lights on/off - _data[9] = MapAsFlag(values[2]); + _data[9] = lightsBits; // bitfield: 0x01 frontlights, 0x02 rearlights _data[10] = 0x00; // reset checksum before recalculating _data[11] = (connectDevice || (throttle == 0x80 && steering == 0x80)) ? _defaultSequenceValue diff --git a/BrickController2/BrickController2/DeviceManagement/IO/OutputValuesGroup.cs b/BrickController2/BrickController2/DeviceManagement/IO/OutputValuesGroup.cs index a74486979..8ba40eb47 100644 --- a/BrickController2/BrickController2/DeviceManagement/IO/OutputValuesGroup.cs +++ b/BrickController2/BrickController2/DeviceManagement/IO/OutputValuesGroup.cs @@ -91,7 +91,7 @@ public bool TryGetValues(out ReadOnlySpan values) /// /// Collection of changes /// true there is any reason to apply changes - public bool TryGetChanges(out IReadOnlyCollection> changes) + public bool TryGetChanges(out IReadOnlyCollection> changes) { if (!TryGetValues(out var values) || values.IsEmpty) { diff --git a/BrickController2/BrickController2/Protocols/CaDAProtocol.cs b/BrickController2/BrickController2/Protocols/CaDAProtocol.cs index a43c7414c..266e28216 100644 --- a/BrickController2/BrickController2/Protocols/CaDAProtocol.cs +++ b/BrickController2/BrickController2/Protocols/CaDAProtocol.cs @@ -136,5 +136,4 @@ public static void Encrypt(Span data) } public static byte Clamp(Half value) => (byte)Half.Clamp(value, Half.Zero, MaxByte); - public static byte MapAsFlag(Half value) => (byte)(Half.Abs(value) > OneHalf ? 0x01 : 0x00); } diff --git a/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml b/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml index 3489572ce..112c79a2b 100644 --- a/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml +++ b/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml @@ -7,17 +7,20 @@ x:Class="BrickController2.UI.Controls.Devices.CaDARaceCarChannelSelectorView"> - + + + + + + + + + - - - - - - - - + + + + - diff --git a/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml.cs b/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml.cs index d8b68852d..7373f446a 100644 --- a/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml.cs +++ b/BrickController2/BrickController2/UI/Controls/Devices/CaDARaceCarChannelSelectorView.xaml.cs @@ -12,6 +12,6 @@ public partial class CaDARaceCarChannelSelectorView : DeviceChannelSelectorViewB public CaDARaceCarChannelSelectorView() { InitializeComponent(); - RegisterChannelButtons(CaDARaceCarChannel0, CaDARaceCarChannel1, CaDARaceCarChannel2); + RegisterChannelButtons(CaDARaceCarChannel0, CaDARaceCarChannel1, CaDARaceCarChannel2, CaDARaceCarChannel3); } } diff --git a/BrickController2/BrickController2/UI/Images/rc_base.png b/BrickController2/BrickController2/UI/Images/rc_base.png deleted file mode 100644 index 713259018..000000000 Binary files a/BrickController2/BrickController2/UI/Images/rc_base.png and /dev/null differ diff --git a/BrickController2/BrickController2/UI/Images/rc_left_right_image.png b/BrickController2/BrickController2/UI/Images/rc_left_right_image.png deleted file mode 100644 index 49a20fe0a..000000000 Binary files a/BrickController2/BrickController2/UI/Images/rc_left_right_image.png and /dev/null differ diff --git a/BrickController2/BrickController2/UI/Images/rc_light_on_off.png b/BrickController2/BrickController2/UI/Images/rc_light_on_off.png deleted file mode 100644 index 6f7ac3421..000000000 Binary files a/BrickController2/BrickController2/UI/Images/rc_light_on_off.png and /dev/null differ diff --git a/BrickController2/BrickController2/UI/Images/rc_up_down_image.png b/BrickController2/BrickController2/UI/Images/rc_up_down_image.png deleted file mode 100644 index e6f570f15..000000000 Binary files a/BrickController2/BrickController2/UI/Images/rc_up_down_image.png and /dev/null differ