From ba95de861e5903ab63bfcd592092a5f6091f0041 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sat, 26 Sep 2020 11:20:47 +0200 Subject: [PATCH 01/22] Add gesture sensor w/ incomplete gesture list (#84) * Add gesture sensor w/ incomplete gesture list #18 non-breaking (new api) --- README.md | 6 ++-- .../ExampleTechnicMediumHubGestSensor.cs | 28 +++++++++++++++ .../SharpBrick.PoweredUp.Examples/Program.cs | 3 +- .../Devices/DeviceFactory.cs | 4 +-- .../Devices/TechnicMediumHubGestSensor.cs | 19 ++++++++--- src/SharpBrick.PoweredUp/Enums/DeviceType.cs | 2 +- src/SharpBrick.PoweredUp/Enums/Gesture.cs | 34 +++++++++++++++++++ .../Hubs/TechnicMediumHub.cs | 4 +-- .../Formatter/HubAttachedIOEncoderTest.cs | 2 +- 9 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumHubGestSensor.cs create mode 100644 src/SharpBrick.PoweredUp/Enums/Gesture.cs diff --git a/README.md b/README.md index ea76f9a..d508e57 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,8 @@ Console.WriteLine($"Or directly read the latest value: {aposMode.SI} / {aposMode ## Connect to Hub and Send a Message and retrieving answers (directly on protocol layer) +***Note**: The `ILegoWirelessProtocol` class was renamed in 3.0. Previously it is known as `IPoweredUpProtocol`.* + ````csharp var serviceProvider = new ServiceCollection() @@ -245,7 +247,7 @@ DI Container Elements - [X] .NET Core 3.1 (on Windows 10 using WinRT) - Library uses `Span` / C# 8.0 and is therefore not supported in .NET Framework 1.0 - 4.8 and UWP Apps until arrival of .NET 5 (WinForms and WPF work in .NET Core 3.1) - Library uses WinRT for communication therefore only Windows 10 - - [ ] Xamarin (on iOS / Android using Xamarin.Essentials) + - [ ] Xamarin (on iOS / Android using ?) - [ ] Blazor (on Browser using WebBluetooth) - Hub Model - Hubs @@ -265,7 +267,7 @@ DI Container Elements - [X] Technic Medium Hub - Accelerometer - [X] Technic Medium Hub - Gyro Sensor - [X] Technic Medium Hub - Tilt Sensor - - [ ] Technic Medium Hub - Gesture Sensor + - [X] Technic Medium Hub - Gesture Sensor (⚠ Usable but Gesture mapping is pending) - [X] Technic XLarge Motor - [X] Technic Large Motor - [ ] Technic Angular Motor (depend on availability of hardware / contributions) diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumHubGestSensor.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumHubGestSensor.cs new file mode 100644 index 0000000..9654aa4 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumHubGestSensor.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using System.Reactive.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleTechnicMediumHubGestSensor : BaseExample + { + public override async Task ExecuteAsync() + { + using (var technicMediumHub = Host.FindByType()) + { + var device = technicMediumHub.GestureSensor; + + await device.SetupNotificationAsync(0, true, deltaInterval: 0); + + using var _ = device.GestureObservable.Subscribe(x => Log.LogInformation($"Gesture {x}")); + + await Task.Delay(30_000); + + await technicMediumHub.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/Program.cs b/examples/SharpBrick.PoweredUp.Examples/Program.cs index 5b7fd17..4bc6a21 100644 --- a/examples/SharpBrick.PoweredUp.Examples/Program.cs +++ b/examples/SharpBrick.PoweredUp.Examples/Program.cs @@ -32,7 +32,8 @@ static async Task Main(string[] args) //example = new Example.ExampleSetHubProperty(); //example = new Example.ExampleHubPropertyObserving(); //example = new Example.ExampleDiscoverByType(); - example = new Example.ExampleCalibrationSteering(); + //example = new Example.ExampleCalibrationSteering(); + example = new Example.ExampleTechnicMediumHubGestSensor(); // NOTE: Examples are programmed object oriented style. Base class implements methods Configure, DiscoverAsync and ExecuteAsync to be overwriten on demand. await example.InitHostAndDiscoverAsync(enableTrace); diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index fac8606..ccf4754 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -35,7 +35,7 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.RgbLight => typeof(RgbLight), DeviceType.TechnicLargeLinearMotor => typeof(TechnicLargeLinearMotor), DeviceType.TechnicXLargeLinearMotor => typeof(TechnicXLargeLinearMotor), - DeviceType.TechnicMediumHubGestSensor => typeof(TechnicMediumHubGestSensor), + DeviceType.TechnicMediumHubGestureSensor => typeof(TechnicMediumHubGestureSensor), DeviceType.TechnicMediumHubAccelerometer => typeof(TechnicMediumHubAccelerometer), DeviceType.TechnicMediumHubGyroSensor => typeof(TechnicMediumHubGyroSensor), DeviceType.TechnicMediumHubTiltSensor => typeof(TechnicMediumHubTiltSensor), @@ -51,7 +51,7 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(RgbLight) => DeviceType.RgbLight, nameof(TechnicLargeLinearMotor) => DeviceType.TechnicLargeLinearMotor, nameof(TechnicXLargeLinearMotor) => DeviceType.TechnicXLargeLinearMotor, - nameof(TechnicMediumHubGestSensor) => DeviceType.TechnicMediumHubGestSensor, + nameof(TechnicMediumHubGestureSensor) => DeviceType.TechnicMediumHubGestureSensor, nameof(TechnicMediumHubAccelerometer) => DeviceType.TechnicMediumHubAccelerometer, nameof(TechnicMediumHubGyroSensor) => DeviceType.TechnicMediumHubGyroSensor, nameof(TechnicMediumHubTiltSensor) => DeviceType.TechnicMediumHubTiltSensor, diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs index 9e036cf..fadb84b 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs @@ -3,19 +3,30 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Linq; using SharpBrick.PoweredUp.Protocol; using SharpBrick.PoweredUp.Utils; namespace SharpBrick.PoweredUp { - public class TechnicMediumHubGestSensor : Device, IPoweredUpDevice + public class TechnicMediumHubGestureSensor : Device, IPoweredUpDevice { - public TechnicMediumHubGestSensor() + protected SingleValueMode _gestureMode; + public byte ModeIndexGesture { get; protected set; } = 0; + + public Gesture Gesture => (Gesture)_gestureMode.SI; + public IObservable GestureObservable => _gestureMode.Observable.Select(x => (Gesture)x.SI); + + public TechnicMediumHubGestureSensor() { } - public TechnicMediumHubGestSensor(ILegoWirelessProtocol protocol, byte hubId, byte portId) + public TechnicMediumHubGestureSensor(ILegoWirelessProtocol protocol, byte hubId, byte portId) : base(protocol, hubId, portId) - { } + { + _gestureMode = SingleValueMode(ModeIndexGesture); + + ObserveForPropertyChanged(_gestureMode.Observable, nameof(Gesture)); + } public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) => @" diff --git a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs index b126dd5..04c58c1 100644 --- a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs +++ b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs @@ -27,7 +27,7 @@ public enum DeviceType : ushort TechnicXLargeLinearMotor = 0x002F, // UNSPECED, TECHNIC_XLARGE_LINEAR_MOTOR TechnicMediumAngularMotor = 0x0030, // UNSPECED, TECHNIC_MEDIUM_ANGULAR_MOTOR (Spike Prime) TechnicLargeAngularMotor = 0x0031, // UNSPECED, TECHNIC_LARGE_ANGULAR_MOTOR (Spike Prime) - TechnicMediumHubGestSensor = 0x0036, // UNSPECED, TECHNIC_MEDIUM_HUB_GEST_SENSOR + TechnicMediumHubGestureSensor = 0x0036, // UNSPECED, TECHNIC_MEDIUM_HUB_GEST_SENSOR RemoteControlButton = 0x0037, // UNSPECED, REMOTE_CONTROL_BUTTON RemoteControlRssi = 0x0038, // UNSPECED, REMOTE_CONTROL_RSSI TechnicMediumHubAccelerometer = 0x0039, // UNSPECED, TECHNIC_MEDIUM_HUB_ACCELEROMETER diff --git a/src/SharpBrick.PoweredUp/Enums/Gesture.cs b/src/SharpBrick.PoweredUp/Enums/Gesture.cs new file mode 100644 index 0000000..f2c8424 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Enums/Gesture.cs @@ -0,0 +1,34 @@ +namespace SharpBrick.PoweredUp +{ + public enum Gesture : sbyte + { + None = 0, // UNSPECED + /// + /// May be "Tap" gesture. + /// + /// Mapping is not well understood. Please contribute an issue if you have found an issue, additional insight or documentation. + /// + Gesture1 = 1, // UNSPECED + + /// + /// Some undocumented gesture (maybe bounce). + /// + /// Mapping is not well understood. Please contribute an issue if you have found an issue, additional insight or documentation. + /// + Gesture2 = 2, + + /// + /// May be "Shake" gesture. + /// + /// Mapping is not well understood. Please contribute an issue if you have found an issue, additional insight or documentation. + /// + Gesture3 = 3, // UNSPECED + + /// + /// May be "free fall" gesture. + /// + /// Mapping is not well understood. Please contribute an issue if you have found an issue, additional insight or documentation. + /// + Gesture4 = 4, // UNSPECED + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs b/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs index 471145f..f017823 100644 --- a/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs +++ b/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs @@ -21,7 +21,7 @@ public TechnicMediumHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFac new Port(97, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubAccelerometer), new Port(98, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubGyroSensor), new Port(99, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubTiltSensor), - new Port(100, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubGestSensor), + new Port(100, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubGestureSensor), }) { } @@ -38,6 +38,6 @@ public TechnicMediumHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFac public TechnicMediumHubAccelerometer Accelerometer => Port(97).GetDevice(); public TechnicMediumHubGyroSensor GyroSensor => Port(98).GetDevice(); public TechnicMediumHubTiltSensor TiltSensor => Port(99).GetDevice(); - public TechnicMediumHubGestSensor GestureSensor => Port(100).GetDevice(); + public TechnicMediumHubGestureSensor GestureSensor => Port(100).GetDevice(); } } \ No newline at end of file diff --git a/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/HubAttachedIOEncoderTest.cs b/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/HubAttachedIOEncoderTest.cs index 90bfa72..9b7feb5 100644 --- a/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/HubAttachedIOEncoderTest.cs +++ b/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/HubAttachedIOEncoderTest.cs @@ -18,7 +18,7 @@ public class HubAttachedIOEncoderTest [InlineData("0F-00-04-61-01-39-00-01-00-00-00-01-00-00-00", DeviceType.TechnicMediumHubAccelerometer, 97, "0.0.0.1", "0.0.0.1")] [InlineData("0F-00-04-62-01-3A-00-01-00-00-00-01-00-00-00", DeviceType.TechnicMediumHubGyroSensor, 98, "0.0.0.1", "0.0.0.1")] [InlineData("0F-00-04-63-01-3B-00-01-00-00-00-01-00-00-00", DeviceType.TechnicMediumHubTiltSensor, 99, "0.0.0.1", "0.0.0.1")] - [InlineData("0F-00-04-64-01-36-00-01-00-00-00-01-00-00-00", DeviceType.TechnicMediumHubGestSensor, 100, "0.0.0.1", "0.0.0.1")] + [InlineData("0F-00-04-64-01-36-00-01-00-00-00-01-00-00-00", DeviceType.TechnicMediumHubGestureSensor, 100, "0.0.0.1", "0.0.0.1")] public void HubAttachedIOEncoder_Decode_Attached(string messageAsString, DeviceType expectedType, byte expectedPortId, string expectedHwVersion, string expectedSwVersion) { // arrange From 51ce93a4b695858a4337323fb372ad4fa1d89cc8 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sat, 26 Sep 2020 20:34:11 +0200 Subject: [PATCH 02/22] Update documentation in README to version 3.0 --- README.md | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d508e57..ab38f07 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SharpBrick.PoweredUp -SharpBrick.PoweredUp is a .NET implementation of the Bluetooth Low Energy Protocol for Lego Powered Up products. +SharpBrick.PoweredUp is a .NET implementation of the Bluetooth Low Energy Protocol for Lego Powered UP products. [![Nuget](https://img.shields.io/nuget/v/SharpBrick.PoweredUp?style=flat-square)](https://www.nuget.org/packages/SharpBrick.PoweredUp/) ![license:MIT](https://img.shields.io/github/license/sharpbrick/powered-up?style=flat-square) @@ -37,12 +37,12 @@ using SharpBrick.PoweredUp.WinRT; // for WinRT Bluetooth NuGet var serviceProvider = new ServiceCollection() .AddLogging() .AddPoweredUp() - .AddSingleton() // using WinRT Bluetooth on Windows + .AddWinRTBluetooth() // using WinRT Bluetooth on Windows .BuildServiceProvider(); var host = serviceProvider.GetService(); -var hub = await host.DiscoverAsync(); // starting version 2.1 +var hub = await host.DiscoverAsync(); await hub.ConnectAsync(); ```` @@ -81,9 +81,9 @@ using (var technicMediumHub = hub as TechnicMediumHub) var motor = technicMediumHub.A.GetDevice(); - await motor.GotoAbsolutePositionAsync(45, 10, 100, PortOutputCommandSpecialSpeed.Brake, PortOutputCommandSpeedProfile.None); + await motor.GotoPositionAsync(45, 10, 100, PortOutputCommandSpecialSpeed.Brake); await Task.Delay(2000); - await motor.GotoAbsolutePositionAsync(-45, 10, 100, PortOutputCommandSpecialSpeed.Brake, PortOutputCommandSpeedProfile.None); + await motor.GotoPositionAsync(-45, 10, 100, PortOutputCommandSpecialSpeed.Brake); await technicMediumHub.SwitchOffAsync(); } @@ -105,19 +105,8 @@ disposable.Dispose(); Console.WriteLine(motor.AbsolutePosition); ```` -## Little Helpers - -````csharp -using SharpBrick.PoweredUp; -using static SharpBrick.PoweredUp.Directions; // CW & CCW starting version 2.1 - -await motor.GotoAbsolutePositionAsync(CW * 45, 10, 100, SpecialSpeed.Brake, SpeedProfiles.None); -```` - ## Connecting to an unknown device -***Note:** Starting version 2.0* - ````csharp // deployment model verification with unknown devices await technicMediumHub.VerifyDeploymentModelAsync(mb => mb @@ -165,7 +154,7 @@ Console.WriteLine($"Or directly read the latest value: {aposMode.SI} / {aposMode var serviceProvider = new ServiceCollection() .AddLogging() .AddPoweredUp() - .AddSingleton() // using WinRT Bluetooth on Windows + .AddWinRTBluetooth() // using WinRT Bluetooth on Windows .BuildServiceProvider(); using (var scope = serviceProvider.CreateScope()) // create a scoped DI container per intented active connection/protocol. If disposed, disposes all disposable artifacts. @@ -198,9 +187,9 @@ using (var scope = serviceProvider.CreateScope()) // create a scoped DI containe // fun with motor on hub 0 and port 0 var motor = new TechnicXLargeLinearMotor(protocol, 0, 0); - await motor.GotoAbsolutePositionAsync(45, 10, 100, PortOutputCommandSpecialSpeed.Brake, PortOutputCommandSpeedProfile.None); + await motor.GotoPositionAsync(45, 10, 100, PortOutputCommandSpecialSpeed.Brake); await Task.Delay(2000); - await motor.GotoAbsolutePositionAsync(-45, 10, 100, PortOutputCommandSpecialSpeed.Brake, PortOutputCommandSpeedProfile.None); + await motor.GotoPositionAsync(-45, 10, 100, PortOutputCommandSpecialSpeed.Brake); } ```` From 4c95bd047939da464fa9bb820c05cfd059361789 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sun, 4 Oct 2020 17:49:56 +0200 Subject: [PATCH 03/22] Add System Type and ManufacturerData entries for Mario (#92) - This allows the CLI to actual work #91 non-breaking --- .../Bluetooth/PoweredUpHubManufacturerData.cs | 1 + src/SharpBrick.PoweredUp/Enums/SystemType.cs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/SharpBrick.PoweredUp/Bluetooth/PoweredUpHubManufacturerData.cs b/src/SharpBrick.PoweredUp/Bluetooth/PoweredUpHubManufacturerData.cs index 7407145..8246017 100644 --- a/src/SharpBrick.PoweredUp/Bluetooth/PoweredUpHubManufacturerData.cs +++ b/src/SharpBrick.PoweredUp/Bluetooth/PoweredUpHubManufacturerData.cs @@ -7,6 +7,7 @@ public enum PoweredUpHubManufacturerData : byte MoveHub = 64, // UNSPECED Hub = 65, // UNSPECED RemoteControl = 66, // UNSPECED + Mario = 67, // UNSPECED, https://github.com/bricklife/LEGO-Mario-Reveng TechnicMediumHub = 128 // UNSPECED } } diff --git a/src/SharpBrick.PoweredUp/Enums/SystemType.cs b/src/SharpBrick.PoweredUp/Enums/SystemType.cs index 66d8d25..bcfe2b0 100644 --- a/src/SharpBrick.PoweredUp/Enums/SystemType.cs +++ b/src/SharpBrick.PoweredUp/Enums/SystemType.cs @@ -3,10 +3,13 @@ namespace SharpBrick.PoweredUp public enum SystemType : byte { LegoWeDo20_WeDoHub = 0b000_00000, + LegoDuplo_DuploTrain = 0b001_00000, + LegoSystem_BoostHub = 0b010_00000, LegoSystem_TwoPortHub = 0b010_00001, LegoSystem_TwoPortHandset = 0b010_00010, + LegoSystem_Mario = 0b0100_0011, // UNSPECED, https://github.com/bricklife/LEGO-Mario-Reveng, 0x43, LegoTechnic_MediumHub = 0b100_00000, // UNSPECED } From d6a9987700ca4565fadf8c229917e9c25a991d59 Mon Sep 17 00:00:00 2001 From: Cornelius Munz Date: Mon, 5 Oct 2020 00:01:12 +0200 Subject: [PATCH 04/22] Fix README (dotnet TOOL install) (#94) non-breaking --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab38f07..48fa639 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ DI Container Elements - Features - [X] Dynamic Device - [X] Deployment Verifier -- Command Line (`dotnet install -g SharpBrick.PoweredUp.Cli`) +- Command Line (`dotnet tool install -g SharpBrick.PoweredUp.Cli`) - [X] `poweredup device list` (discover all connected devices and their port (mode) properties) - [X] `poweredup device dump-static-port -p ` (see [adding new devices tutorial](docs/development/adding-new-device.md)) From 290a929155ee86940ef6216dbb0f5e7b8b94a22d Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Mon, 5 Oct 2020 20:05:30 +0200 Subject: [PATCH 05/22] Add known SystemType to LegoWirelessProtocol (#99) - Known SystemType allows the protocol to react to HubAttachedIO w/ the right static port data specified for a given device. - Adjusted existing IPoweredUpDevice #98 non-breaking (internal API, default values) --- src/SharpBrick.PoweredUp/Devices/Current.cs | 2 +- src/SharpBrick.PoweredUp/Devices/DynamicDevice.cs | 2 +- src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs | 2 +- src/SharpBrick.PoweredUp/Devices/RgbLight.cs | 2 +- .../Devices/TechnicLargeLinearMotor.cs | 2 +- .../Devices/TechnicMediumHubAccelerometer.cs | 2 +- .../Devices/TechnicMediumHubGestSensor.cs | 2 +- .../Devices/TechnicMediumHubGyroSensor.cs | 2 +- .../Devices/TechnicMediumHubTemperatureSensor.cs | 2 +- .../Devices/TechnicMediumHubTiltSensor.cs | 2 +- .../Devices/TechnicXLargeLinearMotor.cs | 2 +- src/SharpBrick.PoweredUp/Devices/Voltage.cs | 10 +++++++--- src/SharpBrick.PoweredUp/Hubs/Hub.cs | 6 ++++-- src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs | 2 +- .../Protocol/ILegoWirelessProtocol.cs | 2 +- .../Protocol/Knowledge/KnowledgeManager.cs | 10 ++++++---- .../Protocol/LegoWirelessProtocol.cs | 11 ++++++++++- test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs | 6 +++--- 18 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/SharpBrick.PoweredUp/Devices/Current.cs b/src/SharpBrick.PoweredUp/Devices/Current.cs index 41ee919..bda2a44 100644 --- a/src/SharpBrick.PoweredUp/Devices/Current.cs +++ b/src/SharpBrick.PoweredUp/Devices/Current.cs @@ -34,7 +34,7 @@ public Current(ILegoWirelessProtocol protocol, byte hubId, byte portId) ObserveForPropertyChanged(_currentSMode.Observable, nameof(CurrentS), nameof(CurrentSPct)); } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-3B-01-02-02-03-00-00-00 05-00-43-3B-02 diff --git a/src/SharpBrick.PoweredUp/Devices/DynamicDevice.cs b/src/SharpBrick.PoweredUp/Devices/DynamicDevice.cs index 6bfb33d..c582439 100644 --- a/src/SharpBrick.PoweredUp/Devices/DynamicDevice.cs +++ b/src/SharpBrick.PoweredUp/Devices/DynamicDevice.cs @@ -25,7 +25,7 @@ public async Task DiscoverAsync() BuildModes(); } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => Array.Empty(); } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs b/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs index 7ac83ac..7ed4e91 100644 --- a/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs +++ b/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs @@ -6,6 +6,6 @@ namespace SharpBrick.PoweredUp public interface IPoweredUpDevice { bool IsConnected { get; } - IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion); + IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType); } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs index 770012c..0543fcf 100644 --- a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs +++ b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs @@ -70,7 +70,7 @@ await _protocol.SendMessageAsync(new PortInputFormatSetupSingleMessage() return response; } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-32-01-01-02-00-00-03-00 05-00-43-32-02 diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicLargeLinearMotor.cs b/src/SharpBrick.PoweredUp/Devices/TechnicLargeLinearMotor.cs index 7316e24..9e9d55e 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicLargeLinearMotor.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicLargeLinearMotor.cs @@ -15,7 +15,7 @@ public TechnicLargeLinearMotor(ILegoWirelessProtocol protocol, byte hubId, byte : base(protocol, hubId, portId) { } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-00-01-0F-06-1E-00-1F-00 07-00-43-00-02-0E-00 diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubAccelerometer.cs b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubAccelerometer.cs index c336c43..045ee61 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubAccelerometer.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubAccelerometer.cs @@ -29,7 +29,7 @@ public TechnicMediumHubAccelerometer(ILegoWirelessProtocol protocol, byte hubId, ObserveForPropertyChanged(_gravityMode.Observable, nameof(Gravity)); } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-61-01-02-02-03-00-00-00 05-00-43-61-02 diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs index fadb84b..fcb079b 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGestSensor.cs @@ -28,7 +28,7 @@ public TechnicMediumHubGestureSensor(ILegoWirelessProtocol protocol, byte hubId, ObserveForPropertyChanged(_gestureMode.Observable, nameof(Gesture)); } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-64-01-02-01-01-00-00-00 05-00-43-64-02 diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGyroSensor.cs b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGyroSensor.cs index 4e8614e..04fd0b1 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGyroSensor.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubGyroSensor.cs @@ -27,7 +27,7 @@ public TechnicMediumHubGyroSensor(ILegoWirelessProtocol protocol, byte hubId, by ObserveForPropertyChanged(_rotationMode.Observable, nameof(Rotation)); } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-62-01-02-01-01-00-00-00 05-00-43-62-02 diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTemperatureSensor.cs b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTemperatureSensor.cs index ca94a0f..7b46990 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTemperatureSensor.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTemperatureSensor.cs @@ -26,7 +26,7 @@ public TechnicMediumHubTemperatureSensor(ILegoWirelessProtocol protocol, byte hu ObserveForPropertyChanged(_temperatureMode.Observable, nameof(Temperature), nameof(TemperaturePct)); } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-60-01-02-01-01-00-00-00 05-00-43-60-02 diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTiltSensor.cs b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTiltSensor.cs index 3a5a021..932d7d4 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTiltSensor.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicMediumHubTiltSensor.cs @@ -120,7 +120,7 @@ public async Task TiltConfigOrientationAsync(TiltConfigOrientation return response; } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => (softwareVersion.ToString(), hardwareVersion.ToString()) switch { ("0.0.0.1", "0.0.0.1") => diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicXLargeLinearMotor.cs b/src/SharpBrick.PoweredUp/Devices/TechnicXLargeLinearMotor.cs index 960dca4..ed7da04 100644 --- a/src/SharpBrick.PoweredUp/Devices/TechnicXLargeLinearMotor.cs +++ b/src/SharpBrick.PoweredUp/Devices/TechnicXLargeLinearMotor.cs @@ -16,7 +16,7 @@ public TechnicXLargeLinearMotor(ILegoWirelessProtocol protocol, byte hubId, byte : base(protocol, hubId, portId) { } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) => @" 0B-00-43-02-01-0F-06-1E-00-1F-00 07-00-43-02-02-0E-00 diff --git a/src/SharpBrick.PoweredUp/Devices/Voltage.cs b/src/SharpBrick.PoweredUp/Devices/Voltage.cs index b546bdb..53ea53f 100644 --- a/src/SharpBrick.PoweredUp/Devices/Voltage.cs +++ b/src/SharpBrick.PoweredUp/Devices/Voltage.cs @@ -34,8 +34,10 @@ public Voltage(ILegoWirelessProtocol protocol, byte hubId, byte portId) ObserveForPropertyChanged(_voltageSMode.Observable, nameof(VoltageS), nameof(VoltageSPct)); } - public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion) - => @" + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => ((softwareVersion, hardwareVersion, systemType) switch + { + (_, _, SystemType.LegoTechnic_MediumHub) => @" 0B-00-43-3C-01-02-02-03-00-00-00 05-00-43-3C-02 11-00-44-3C-00-00-56-4C-54-20-4C-00-00-00-00-00-00 @@ -52,6 +54,8 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 0A-00-44-3C-01-04-6D-56-00-00 08-00-44-3C-01-05-10-00 0A-00-44-3C-01-80-01-01-04-00 -".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); +", + _ => throw new NotImplementedException(), + }).Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Hubs/Hub.cs b/src/SharpBrick.PoweredUp/Hubs/Hub.cs index ed7622b..0992f26 100644 --- a/src/SharpBrick.PoweredUp/Hubs/Hub.cs +++ b/src/SharpBrick.PoweredUp/Hubs/Hub.cs @@ -14,17 +14,19 @@ public abstract partial class Hub : IDisposable private CompositeDisposable _compositeDisposable = new CompositeDisposable(); private readonly ILogger _logger; private readonly IDeviceFactory _deviceFactory; + private readonly SystemType _knownSystemType; public ILegoWirelessProtocol Protocol { get; private set; } public byte HubId { get; private set; } public IServiceProvider ServiceProvider { get; } public bool IsConnected => Protocol != null; - public Hub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider, Port[] knownPorts) + public Hub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider, SystemType knownSystemType, Port[] knownPorts) { Protocol = protocol ?? throw new ArgumentNullException(nameof(protocol)); _deviceFactory = deviceFactory ?? throw new ArgumentNullException(nameof(deviceFactory)); ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _knownSystemType = knownSystemType; AddKnownPorts(knownPorts ?? throw new ArgumentNullException(nameof(knownPorts))); _logger = logger; @@ -59,7 +61,7 @@ public async Task ConnectAsync() var expectedDevicesCompletedTask = ExpectedDevicesCompletedAsync(); _logger?.LogDebug("Connecting BluetoothKernel"); - await Protocol.ConnectAsync(); + await Protocol.ConnectAsync(_knownSystemType); await expectedDevicesCompletedTask; _logger?.LogDebug("Hub Attached IO expected devices completed"); diff --git a/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs b/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs index f017823..e8d8779 100644 --- a/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs +++ b/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs @@ -8,7 +8,7 @@ namespace SharpBrick.PoweredUp public class TechnicMediumHub : Hub { public TechnicMediumHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider = default) - : base(protocol, deviceFactory, logger, serviceProvider, new Port[] { + : base(protocol, deviceFactory, logger, serviceProvider, SystemType.LegoTechnic_MediumHub, new Port[] { new Port(0, nameof(A), true), new Port(1, nameof(B), true), new Port(2, nameof(C), true), diff --git a/src/SharpBrick.PoweredUp/Protocol/ILegoWirelessProtocol.cs b/src/SharpBrick.PoweredUp/Protocol/ILegoWirelessProtocol.cs index 57ad43d..995ea2c 100644 --- a/src/SharpBrick.PoweredUp/Protocol/ILegoWirelessProtocol.cs +++ b/src/SharpBrick.PoweredUp/Protocol/ILegoWirelessProtocol.cs @@ -7,7 +7,7 @@ namespace SharpBrick.PoweredUp.Protocol { public interface ILegoWirelessProtocol : IDisposable { - Task ConnectAsync(); + Task ConnectAsync(SystemType initialSystemType = default); Task DisconnectAsync(); Task SendMessageAsync(LegoWirelessMessage message); diff --git a/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs b/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs index 8f82369..adead23 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs @@ -117,6 +117,7 @@ public static Task ApplyDynamicProtocolKnowledge(LegoWirelessMessage message, Pr hub.SystemType = msg.Payload; break; case HubAttachedIOForAttachedDeviceMessage msg: + hub = knowledge.Hub(msg.HubId); port = knowledge.Port(msg.HubId, msg.PortId); ResetProtocolKnowledgeForPort(msg.HubId, port.PortId, knowledge); @@ -125,7 +126,7 @@ public static Task ApplyDynamicProtocolKnowledge(LegoWirelessMessage message, Pr port.HardwareRevision = msg.HardwareRevision; port.SoftwareRevision = msg.SoftwareRevision; - AddCachePortAndPortModeInformation(msg.IOTypeId, msg.HardwareRevision, msg.SoftwareRevision, port, knowledge, deviceFactory); + AddCachePortAndPortModeInformation(msg.IOTypeId, msg.HardwareRevision, msg.SoftwareRevision, hub, port, knowledge, deviceFactory); break; case HubAttachedIOForDetachedDeviceMessage msg: port = knowledge.Port(msg.HubId, msg.PortId); @@ -134,6 +135,7 @@ public static Task ApplyDynamicProtocolKnowledge(LegoWirelessMessage message, Pr port.IsDeviceConnected = false; break; case HubAttachedIOForAttachedVirtualDeviceMessage msg: + hub = knowledge.Hub(msg.HubId); port = knowledge.Port(msg.HubId, msg.PortId); var partOfVirtual = knowledge.Port(msg.HubId, msg.PortAId); @@ -143,7 +145,7 @@ public static Task ApplyDynamicProtocolKnowledge(LegoWirelessMessage message, Pr port.HardwareRevision = partOfVirtual.HardwareRevision; port.SoftwareRevision = partOfVirtual.SoftwareRevision; - AddCachePortAndPortModeInformation(msg.IOTypeId, partOfVirtual.HardwareRevision, partOfVirtual.SoftwareRevision, port, knowledge, deviceFactory); + AddCachePortAndPortModeInformation(msg.IOTypeId, partOfVirtual.HardwareRevision, partOfVirtual.SoftwareRevision, hub, port, knowledge, deviceFactory); port.IsVirtual = true; break; @@ -176,13 +178,13 @@ public static Task ApplyDynamicProtocolKnowledge(LegoWirelessMessage message, Pr return Task.CompletedTask; } - private static void AddCachePortAndPortModeInformation(DeviceType type, Version hardwareRevision, Version softwareRevision, PortInfo port, ProtocolKnowledge knowledge, IDeviceFactory deviceFactory) + private static void AddCachePortAndPortModeInformation(DeviceType type, Version hardwareRevision, Version softwareRevision, HubInfo hub, PortInfo port, ProtocolKnowledge knowledge, IDeviceFactory deviceFactory) { var device = deviceFactory.Create(type); if (device != null) { - foreach (var message in device.GetStaticPortInfoMessages(hardwareRevision, softwareRevision).Select(b => MessageEncoder.Decode(b, null))) + foreach (var message in device.GetStaticPortInfoMessages(hardwareRevision, softwareRevision, hub.SystemType).Select(b => MessageEncoder.Decode(b, null))) { switch (message) { diff --git a/src/SharpBrick.PoweredUp/Protocol/LegoWirelessProtocol.cs b/src/SharpBrick.PoweredUp/Protocol/LegoWirelessProtocol.cs index 44fb6df..08ac36c 100644 --- a/src/SharpBrick.PoweredUp/Protocol/LegoWirelessProtocol.cs +++ b/src/SharpBrick.PoweredUp/Protocol/LegoWirelessProtocol.cs @@ -33,8 +33,17 @@ public LegoWirelessProtocol(BluetoothKernel kernel, ILogger(); } - public async Task ConnectAsync() + public async Task ConnectAsync(SystemType knownSystemType = default) { + // sets initial system type to provided value. This alllows sensitive IPoweredUpDevice to provide the right GetStaticPortInfo (even on initial HubAttachedIO before a HubProperty can be queried). + await KnowledgeManager.ApplyDynamicProtocolKnowledge(new HubPropertyMessage() + { + HubId = 0x00, + Operation = HubPropertyOperation.Update, + Property = HubProperty.SystemTypeId, + Payload = knownSystemType, + }, Knowledge, _deviceFactory); + await _kernel.ConnectAsync(); await _kernel.ReceiveBytesAsync(async data => diff --git a/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs b/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs index a529dfd..3df7d78 100644 --- a/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs +++ b/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs @@ -15,7 +15,7 @@ public class VoltageTest public async Task Voltage_VoltageLObservable_Receive() { // arrange - var (protocol, mock) = CreateProtocolAndMock(); + var (protocol, mock) = CreateProtocolAndMock(SystemType.LegoTechnic_MediumHub); // announce voltage device in protocol await mock.WriteUpstreamAsync(new HubAttachedIOForAttachedDeviceMessage() @@ -69,7 +69,7 @@ await mock.WriteUpstreamAsync(new PortInputFormatSingleMessage() } - internal (ILegoWirelessProtocol protocol, PoweredUpBluetoothCharacteristicMock mock) CreateProtocolAndMock() + internal (ILegoWirelessProtocol protocol, PoweredUpBluetoothCharacteristicMock mock) CreateProtocolAndMock(SystemType knownSystemType) { var serviceProvider = new ServiceCollection() .AddLogging() @@ -84,7 +84,7 @@ await mock.WriteUpstreamAsync(new PortInputFormatSingleMessage() var protocol = serviceProvider.GetService(); - protocol.ConnectAsync().Wait(); + protocol.ConnectAsync(knownSystemType).Wait(); return (protocol, poweredUpBluetoothAdapterMock.MockCharacteristic); } From 0c7b07bd27674adc65cbb84c1f73d24936bde5a7 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Mon, 5 Oct 2020 21:02:32 +0200 Subject: [PATCH 06/22] Fix initialization of protocol in CLI (#100) #98 non-breaking --- .../Commands/DevicesList.cs | 4 ++-- .../Commands/DumpStaticPortInfo.cs | 4 ++-- src/SharpBrick.PoweredUp.Cli/Program.cs | 20 ++++++++++--------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs b/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs index 4bf7dea..1241c93 100644 --- a/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs +++ b/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs @@ -20,11 +20,11 @@ public DevicesList(ILegoWirelessProtocol protocol, DiscoverPorts discoverPorts) this.protocol = protocol ?? throw new ArgumentNullException(nameof(protocol)); this.discoverPorts = discoverPorts ?? throw new ArgumentNullException(nameof(discoverPorts)); } - public async Task ExecuteAsync() + public async Task ExecuteAsync(SystemType knownSystemType) { Console.WriteLine("Discover Ports. Receiving Messages ..."); - await protocol.ConnectAsync(); // registering to bluetooth notification + await protocol.ConnectAsync(knownSystemType); // registering to bluetooth notification await Task.Delay(2000); // await ports to be announced initially by device. diff --git a/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs b/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs index c81859a..6baa905 100644 --- a/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs +++ b/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs @@ -18,11 +18,11 @@ public DumpStaticPortInfo(ILegoWirelessProtocol protocol, DiscoverPorts discover this.protocol = protocol ?? throw new ArgumentNullException(nameof(protocol)); this.discoverPorts = discoverPorts ?? throw new ArgumentNullException(nameof(discoverPorts)); } - public async Task ExecuteAsync(byte portId) + public async Task ExecuteAsync(SystemType knownSystemType, byte portId) { Console.WriteLine($"Discover Port {portId}. Receiving Messages ..."); - await protocol.ConnectAsync(); // registering to bluetooth notification + await protocol.ConnectAsync(knownSystemType); // registering to bluetooth notification await Task.Delay(2000); // await ports to be announced initially by device. diff --git a/src/SharpBrick.PoweredUp.Cli/Program.cs b/src/SharpBrick.PoweredUp.Cli/Program.cs index a73583c..0641356 100644 --- a/src/SharpBrick.PoweredUp.Cli/Program.cs +++ b/src/SharpBrick.PoweredUp.Cli/Program.cs @@ -34,7 +34,7 @@ static async Task Main(string[] args) var enableTrace = bool.TryParse(traceOption.Value(), out var x) ? x : false; var serviceProvider = CreateServiceProvider(enableTrace); - ulong bluetoothAddress = FindAndSelectHub(serviceProvider.GetService()); + (ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService()); if (bluetoothAddress == 0) return; @@ -50,7 +50,7 @@ static async Task Main(string[] args) var deviceListCli = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory - await deviceListCli.ExecuteAsync(); + await deviceListCli.ExecuteAsync(systemType); } }); }); @@ -67,7 +67,7 @@ static async Task Main(string[] args) var enableTrace = bool.TryParse(traceOption.Value(), out var x) ? x : false; var serviceProvider = CreateServiceProvider(enableTrace); - ulong bluetoothAddress = FindAndSelectHub(serviceProvider.GetService()); + (ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService()); if (bluetoothAddress == 0) return; @@ -85,7 +85,7 @@ static async Task Main(string[] args) var port = byte.Parse(portOption.Value()); - await dumpStaticPortInfoCommand.ExecuteAsync(port); + await dumpStaticPortInfoCommand.ExecuteAsync(systemType, port); } }); }); @@ -125,9 +125,10 @@ public static async Task AddTraceWriterAsync(IServiceProvider serviceProvider, b } } - private static ulong FindAndSelectHub(IPoweredUpBluetoothAdapter poweredUpBluetoothAdapter) + private static (ulong bluetoothAddress, SystemType systemType) FindAndSelectHub(IPoweredUpBluetoothAdapter poweredUpBluetoothAdapter) { - ulong result = 0; + ulong resultBluetooth = 0; + SystemType resultSystemType = default; var devices = new ConcurrentBag<(int key, ulong bluetoothAddresss, PoweredUpHubManufacturerData deviceType)>(); var cts = new CancellationTokenSource(); int idx = 1; @@ -155,15 +156,16 @@ private static ulong FindAndSelectHub(IPoweredUpBluetoothAdapter poweredUpBlueto { var selected = devices.FirstOrDefault(kv => kv.key == key); - result = selected.bluetoothAddresss; // default is 0 + resultBluetooth = selected.bluetoothAddresss; // default is 0 + resultSystemType = (SystemType)selected.deviceType; - if (result != default) + if (resultBluetooth != default) { Console.WriteLine($"Selected {selected.deviceType} with key {selected.key}"); } } - return result; + return (resultBluetooth, resultSystemType); } } } From 9c5e3d5c3ab118c3d217733a1b652369d7f05fcc Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sun, 11 Oct 2020 22:54:44 +0200 Subject: [PATCH 07/22] Add TwoPortHub (#97) - Add TwoPortHub - Add Static Port Information for TwoPortHub - Add TwoPortHub to README and Thanks #39 non-breaking --- README.md | 12 ++++++--- src/SharpBrick.PoweredUp/Devices/Current.cs | 26 ++++++++++++++++-- src/SharpBrick.PoweredUp/Devices/RgbLight.cs | 27 ++++++++++++++++--- src/SharpBrick.PoweredUp/Devices/Voltage.cs | 20 +++++++++++++- src/SharpBrick.PoweredUp/Hubs/HubFactory.cs | 2 ++ src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs | 27 +++++++++++++++++++ .../IServiceCollectionExtensions.cs | 1 + 7 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs diff --git a/README.md b/README.md index 48fa639..3fc2e9a 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ DI Container Elements - [X] Actions - [X] Create Virtual Ports - [X] Technic Medium Hub - - [ ] Hub (88009) + - [X] Two Port Hub (88009) - .. other hubs depend on availability of hardware / contributions - Devices - [X] Technic Medium Hub - Rgb Light @@ -260,9 +260,9 @@ DI Container Elements - [X] Technic XLarge Motor - [X] Technic Large Motor - [ ] Technic Angular Motor (depend on availability of hardware / contributions) - - [ ] Hub (88009) - Rgb Light - - [ ] Hub (88009) - Current - - [ ] Hub (88009) - Voltage + - [X] Hub (88009) - Rgb Light + - [X] Hub (88009) - Current + - [X] Hub (88009) - Voltage - .. other devices depend on availability of hardware / contributions - Protocol - [X] Message Encoding (98% [spec coverage](docs/specification/coverage.md)) @@ -298,3 +298,7 @@ DI Container Elements SharpBrick is an organization intended to host volunteers willing to contribute to the SharpBrick.PoweredUp and related projects. Everyone is welcome (private and commercial entities). Please read our **[Code of Conduct](CODE_OF_CONDUCT.md)** before participating in our project. The product is licensed under **MIT License** to allow a easy and wide adoption into prviate and commercial products. + +## Thanks ... + +Thanks to @nathankellenicki and @corneliusmunz for their code, answers, testing and other important contributions. \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/Current.cs b/src/SharpBrick.PoweredUp/Devices/Current.cs index bda2a44..ee2ad6c 100644 --- a/src/SharpBrick.PoweredUp/Devices/Current.cs +++ b/src/SharpBrick.PoweredUp/Devices/Current.cs @@ -35,7 +35,9 @@ public Current(ILegoWirelessProtocol protocol, byte hubId, byte portId) } public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) - => @" + => ((softwareVersion, hardwareVersion, systemType) switch + { + (_, _, SystemType.LegoTechnic_MediumHub) => @" 0B-00-43-3B-01-02-02-03-00-00-00 05-00-43-3B-02 11-00-44-3B-00-00-43-55-52-20-4C-00-00-00-00-00-00 @@ -52,6 +54,26 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 0A-00-44-3B-01-04-6D-41-00-00 08-00-44-3B-01-05-10-00 0A-00-44-3B-01-80-01-01-04-00 -".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); +", + (_, _, SystemType.LegoSystem_TwoPortHub) => @" +0B-00-43-3B-01-02-02-03-00-00-00 +05-00-43-3B-02 +12-00-44-3B-00-00-43-55-52-20-4C-00-00-00-00-00-00-00 +0E-00-44-3B-00-01-00-00-00-00-00-F0-7F-45 +0E-00-44-3B-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-3B-00-03-00-00-00-00-00-C0-18-45 +0B-00-44-3B-00-04-6D-41-00-00-00 +08-00-44-3B-00-05-10-00 +0A-00-44-3B-00-80-01-01-04-00 +12-00-44-3B-01-00-43-55-52-20-53-00-00-00-00-00-00-00 +0E-00-44-3B-01-01-00-00-00-00-00-F0-7F-45 +0E-00-44-3B-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-3B-01-03-00-00-00-00-00-C0-18-45 +0B-00-44-3B-01-04-6D-41-00-00-00 +08-00-44-3B-01-05-10-00 +0A-00-44-3B-01-80-01-01-04-00 +", + _ => throw new NotSupportedException(), + }).Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs index 0543fcf..cbb7c92 100644 --- a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs +++ b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs @@ -71,7 +71,9 @@ await _protocol.SendMessageAsync(new PortInputFormatSetupSingleMessage() } public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) - => @" + => ((softwareVersion, hardwareVersion, systemType) switch + { + (_, _, SystemType.LegoTechnic_MediumHub) => @" 0B-00-43-32-01-01-02-00-00-03-00 05-00-43-32-02 11-00-44-32-00-00-43-4F-4C-20-4F-00-00-00-00-00-00 @@ -88,7 +90,26 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 0A-00-44-32-01-04-00-00-00-00 08-00-44-32-01-05-00-10 0A-00-44-32-01-80-03-00-03-00 -".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); - +", + (_, _, SystemType.LegoSystem_TwoPortHub) => @" +0B-00-43-32-01-01-02-00-00-03-00 +05-00-43-32-02 +12-00-44-32-00-00-43-4F-4C-20-4F-00-00-00-00-00-00-00 +0E-00-44-32-00-01-00-00-00-00-00-00-20-41 +0E-00-44-32-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-32-00-03-00-00-00-00-00-00-20-41 +0B-00-44-32-00-04-00-00-00-00-00 +08-00-44-32-00-05-00-44 +0A-00-44-32-00-80-01-00-01-00 +12-00-44-32-01-00-52-47-42-20-4F-00-00-00-00-00-00-00 +0E-00-44-32-01-01-00-00-00-00-00-00-7F-43 +0E-00-44-32-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-32-01-03-00-00-00-00-00-00-7F-43 +0B-00-44-32-01-04-00-00-00-00-00 +08-00-44-32-01-05-00-10 +0A-00-44-32-01-80-03-00-03-00 +", + _ => throw new NotSupportedException(), + }).Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/Voltage.cs b/src/SharpBrick.PoweredUp/Devices/Voltage.cs index 53ea53f..e631f23 100644 --- a/src/SharpBrick.PoweredUp/Devices/Voltage.cs +++ b/src/SharpBrick.PoweredUp/Devices/Voltage.cs @@ -55,7 +55,25 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 08-00-44-3C-01-05-10-00 0A-00-44-3C-01-80-01-01-04-00 ", - _ => throw new NotImplementedException(), + (_, _, SystemType.LegoSystem_TwoPortHub) => @" +0B-00-43-3C-01-02-02-03-00-00-00 +05-00-43-3C-02 +12-00-44-3C-00-00-56-4C-54-20-4C-00-00-00-00-00-00-00 +0E-00-44-3C-00-01-00-00-00-00-00-50-73-45 +0E-00-44-3C-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-3C-00-03-00-00-00-00-00-00-16-46 +0B-00-44-3C-00-04-6D-56-00-00-00 +08-00-44-3C-00-05-10-00 +0A-00-44-3C-00-80-01-01-04-00 +12-00-44-3C-01-00-56-4C-54-20-53-00-00-00-00-00-00-00 +0E-00-44-3C-01-01-00-00-00-00-00-50-73-45 +0E-00-44-3C-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-3C-01-03-00-00-00-00-00-00-16-46 +0B-00-44-3C-01-04-6D-56-00-00-00 +08-00-44-3C-01-05-10-00 +0A-00-44-3C-01-80-01-01-04-00 +", + _ => throw new NotSupportedException(), }).Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs index 796a9dc..d7c26fc 100644 --- a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs +++ b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs @@ -37,6 +37,7 @@ private SystemType GetSystemTypeFromManufacturerData(PoweredUpHubManufacturerDat public static Type GetTypeFromSystemType(SystemType systemType) => systemType switch { + SystemType.LegoSystem_TwoPortHub => typeof(TwoPortHub), SystemType.LegoTechnic_MediumHub => typeof(TechnicMediumHub), _ => throw new NotSupportedException(), }; @@ -44,6 +45,7 @@ public static Type GetTypeFromSystemType(SystemType systemType) public static SystemType GetSystemTypeFromType(Type type) => type.Name switch { + nameof(TwoPortHub) => SystemType.LegoSystem_TwoPortHub, nameof(TechnicMediumHub) => SystemType.LegoTechnic_MediumHub, _ => throw new NotSupportedException(), }; diff --git a/src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs b/src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs new file mode 100644 index 0000000..93e0bd1 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp.Devices; +using SharpBrick.PoweredUp.Protocol; + +namespace SharpBrick.PoweredUp +{ + public class TwoPortHub : Hub + { + public TwoPortHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider = default) + : base(protocol, deviceFactory, logger, serviceProvider, SystemType.LegoSystem_TwoPortHub, new Port[] { + new Port(0, nameof(A), true), + new Port(1, nameof(B), true), + new Port(50, string.Empty, false, expectedDevice: DeviceType.RgbLight), + new Port(59, string.Empty, false, expectedDevice: DeviceType.Current), + new Port(60, string.Empty, false, expectedDevice: DeviceType.Voltage), + }) + { } + + public Port A => Port(0); + public Port B => Port(1); + + public RgbLight RgbLight => Port(50).GetDevice(); + public Current Current => Port(59).GetDevice(); + public Voltage Voltage => Port(60).GetDevice(); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs index a95094f..3caf780 100644 --- a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs +++ b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs @@ -21,6 +21,7 @@ public static IServiceCollection AddPoweredUp(this IServiceCollection self) .AddScoped() // hubs + .AddTransient() .AddTransient() // functions From bcc2065aecf5008d5a9f2ece988189394f3c28f8 Mon Sep 17 00:00:00 2001 From: Cornelius Munz Date: Mon, 12 Oct 2020 07:58:42 +0200 Subject: [PATCH 08/22] Add MediumLinearMotor (#105) #102 non-breaking (API extension, enum rename fine w/o usage) --- .../ExampleTwoPortHubMediumLinearMotor.cs | 43 ++++++++++++++++++ .../Devices/DeviceFactory.cs | 2 + .../Devices/MediumLinearMotor.cs | 45 +++++++++++++++++++ src/SharpBrick.PoweredUp/Enums/DeviceType.cs | 2 +- 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleTwoPortHubMediumLinearMotor.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/MediumLinearMotor.cs diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleTwoPortHubMediumLinearMotor.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleTwoPortHubMediumLinearMotor.cs new file mode 100644 index 0000000..34d94b2 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleTwoPortHubMediumLinearMotor.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleTwoPortHubMediumLinearMotor : BaseExample + { + public override async Task ExecuteAsync() + { + using (var twoPortHub = Host.FindByType()) + { + await twoPortHub.VerifyDeploymentModelAsync(modelBuilder => modelBuilder + .AddHub(hubBuilder => hubBuilder + .AddDevice(twoPortHub.A) + ) + ); + + var motor = twoPortHub.A.GetDevice(); + + await motor.SetAccelerationTimeAsync(3000); + await motor.SetDecelerationTimeAsync(1000); + await motor.StartSpeedForTimeAsync(6000, 90, 100, SpecialSpeed.Hold, SpeedProfiles.AccelerationProfile | SpeedProfiles.DecelerationProfile); + + await Task.Delay(10_000); + + await motor.StartSpeedForDegreesAsync(180, -10, 100, SpecialSpeed.Brake, SpeedProfiles.None); + + await Task.Delay(10_000); + + await motor.StartSpeedAsync(100, 90, SpeedProfiles.None); + await Task.Delay(2000); + await motor.StartSpeedAsync(-100, 90, SpeedProfiles.None); + await Task.Delay(2000); + await motor.StartSpeedAsync(0, 90, SpeedProfiles.None); + + await Task.Delay(10_000); + + await twoPortHub.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index ccf4754..5e6ff82 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -40,6 +40,7 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.TechnicMediumHubGyroSensor => typeof(TechnicMediumHubGyroSensor), DeviceType.TechnicMediumHubTiltSensor => typeof(TechnicMediumHubTiltSensor), DeviceType.TechnicMediumHubTemperatureSensor => typeof(TechnicMediumHubTemperatureSensor), + DeviceType.MediumLinearMotor => typeof(MediumLinearMotor), _ => null, }; @@ -56,6 +57,7 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(TechnicMediumHubGyroSensor) => DeviceType.TechnicMediumHubGyroSensor, nameof(TechnicMediumHubTiltSensor) => DeviceType.TechnicMediumHubTiltSensor, nameof(TechnicMediumHubTemperatureSensor) => DeviceType.TechnicMediumHubTemperatureSensor, + nameof(MediumLinearMotor) => DeviceType.MediumLinearMotor, _ => DeviceType.Unknown, }; } diff --git a/src/SharpBrick.PoweredUp/Devices/MediumLinearMotor.cs b/src/SharpBrick.PoweredUp/Devices/MediumLinearMotor.cs new file mode 100644 index 0000000..bceeb71 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/MediumLinearMotor.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class MediumLinearMotor : TachoMotor, IPoweredUpDevice + { + public MediumLinearMotor() + : base() + { } + public MediumLinearMotor(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-00-01-07-03-06-00-07-00 +07-00-43-00-02-06-00 +12-00-44-00-00-00-50-4F-57-45-52-00-00-00-00-00-00-00 +0E-00-44-00-00-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-03-00-00-C8-C2-00-00-C8-42 +0B-00-44-00-00-04-50-43-54-00-00 +08-00-44-00-00-05-00-10 +0A-00-44-00-00-80-01-00-01-00 +12-00-44-00-01-00-53-50-45-45-44-00-00-00-00-00-00-00 +0E-00-44-00-01-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-01-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-01-03-00-00-C8-C2-00-00-C8-42 +0B-00-44-00-01-04-50-43-54-00-00 +08-00-44-00-01-05-10-10 +0A-00-44-00-01-80-01-00-04-00 +12-00-44-00-02-00-50-4F-53-00-00-00-00-00-00-00-00-00 +0E-00-44-00-02-01-00-00-B4-C3-00-00-B4-43 +0E-00-44-00-02-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-02-03-00-00-B4-C3-00-00-B4-43 +0B-00-44-00-02-04-44-45-47-00-00 +08-00-44-00-02-05-08-08 +0A-00-44-00-02-80-01-02-04-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs index 04c58c1..0620040 100644 --- a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs +++ b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs @@ -15,7 +15,7 @@ public enum DeviceType : ushort ExternalTiltSensor = 0x0022, // TILT_SENSOR MotionSensor = 0x0023, // MOTION_SENSOR VisionSensor = 0x0025, // COLOR_DISTANCE_SENSOR - ExternalMotorWithTacho = 0x0026, // MEDIUM_LINEAR_MOTOR + MediumLinearMotor = 0x0026, // MEDIUM_LINEAR_MOTOR InternalMotorWithTacho = 0x0027, // MOVE_HUB_MEDIUM_LINEAR_MOTOR InternalTilt = 0x0028, // MOVE_HUB_TILT_SENSOR From 96d1f75fd97663e6873d8891e5b4fc32358c38b5 Mon Sep 17 00:00:00 2001 From: Rick Jansen Date: Mon, 12 Oct 2020 19:22:48 +0200 Subject: [PATCH 09/22] Add SystemTrainMotor (#107) #104 non-breaking --- .../Devices/DeviceFactory.cs | 2 ++ .../Devices/SystemTrainMotor.cs | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/SharpBrick.PoweredUp/Devices/SystemTrainMotor.cs diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index 5e6ff82..ec1ef42 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -33,6 +33,7 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.Voltage => typeof(Voltage), DeviceType.Current => typeof(Current), DeviceType.RgbLight => typeof(RgbLight), + DeviceType.SystemTrainMotor => typeof(SystemTrainMotor), DeviceType.TechnicLargeLinearMotor => typeof(TechnicLargeLinearMotor), DeviceType.TechnicXLargeLinearMotor => typeof(TechnicXLargeLinearMotor), DeviceType.TechnicMediumHubGestureSensor => typeof(TechnicMediumHubGestureSensor), @@ -50,6 +51,7 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(Voltage) => DeviceType.Voltage, nameof(Current) => DeviceType.Current, nameof(RgbLight) => DeviceType.RgbLight, + nameof(SystemTrainMotor) => DeviceType.SystemTrainMotor, nameof(TechnicLargeLinearMotor) => DeviceType.TechnicLargeLinearMotor, nameof(TechnicXLargeLinearMotor) => DeviceType.TechnicXLargeLinearMotor, nameof(TechnicMediumHubGestureSensor) => DeviceType.TechnicMediumHubGestureSensor, diff --git a/src/SharpBrick.PoweredUp/Devices/SystemTrainMotor.cs b/src/SharpBrick.PoweredUp/Devices/SystemTrainMotor.cs new file mode 100644 index 0000000..dca521a --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/SystemTrainMotor.cs @@ -0,0 +1,32 @@ +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SharpBrick.PoweredUp +{ + public class SystemTrainMotor : BasicMotor, IPoweredUpDevice + { + public SystemTrainMotor() + : base() + { } + public SystemTrainMotor(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-00-01-01-01-00-00-01-00 +05-00-43-00-02 +12-00-44-00-00-00-4C-50-46-32-2D-54-52-41-49-4E-00-00 +0E-00-44-00-00-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-03-00-00-C8-C2-00-00-C8-42 +0B-00-44-00-00-04-00-00-00-00-00 +08-00-44-00-00-05-00-18 +0A-00-44-00-00-80-01-00-04-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} From e26467c78adf18b95eced94dfed8cafe750662ed Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Mon, 12 Oct 2020 19:30:30 +0200 Subject: [PATCH 10/22] Update README w/ community contributions --- README.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3fc2e9a..592b666 100644 --- a/README.md +++ b/README.md @@ -245,24 +245,26 @@ DI Container Elements - [X] Alerts - [X] Actions - [X] Create Virtual Ports - - [X] Technic Medium Hub - [X] Two Port Hub (88009) + - [X] Technic Medium Hub (88012) - .. other hubs depend on availability of hardware / contributions - Devices - - [X] Technic Medium Hub - Rgb Light - - [X] Technic Medium Hub - Current - - [X] Technic Medium Hub - Voltage - - [X] Technic Medium Hub - Temperature Sensor 1 + 2 - - [X] Technic Medium Hub - Accelerometer - - [X] Technic Medium Hub - Gyro Sensor - - [X] Technic Medium Hub - Tilt Sensor - - [X] Technic Medium Hub - Gesture Sensor (⚠ Usable but Gesture mapping is pending) - - [X] Technic XLarge Motor - - [X] Technic Large Motor - - [ ] Technic Angular Motor (depend on availability of hardware / contributions) + - [X] Technic Medium Hub (88012) - Rgb Light + - [X] Technic Medium Hub (88012) - Current + - [X] Technic Medium Hub (88012) - Voltage + - [X] Technic Medium Hub (88012) - Temperature Sensor 1 + 2 + - [X] Technic Medium Hub (88012) - Accelerometer + - [X] Technic Medium Hub (88012) - Gyro Sensor + - [X] Technic Medium Hub (88012) - Tilt Sensor + - [X] Technic Medium Hub (88012) - Gesture Sensor (⚠ Usable but Gesture mapping is pending) - [X] Hub (88009) - Rgb Light - [X] Hub (88009) - Current - [X] Hub (88009) - Voltage + - [X] Medium Linear Motor (88008) + - [X] Train Motor (88011) + - [X] Technic Large Motor (88013) + - [X] Technic XLarge Motor (88014) + - [ ] Technic Angular Motor (depend on availability of hardware / contributions) - .. other devices depend on availability of hardware / contributions - Protocol - [X] Message Encoding (98% [spec coverage](docs/specification/coverage.md)) @@ -301,4 +303,4 @@ The product is licensed under **MIT License** to allow a easy and wide adoption ## Thanks ... -Thanks to @nathankellenicki and @corneliusmunz for their code, answers, testing and other important contributions. \ No newline at end of file +Thanks to [@nathankellenicki](https://github.com/nathankellenicki), [@corneliusmunz](https://github.com/corneliusmunz) and [@vuurbeving](https://github.com/vuurbeving) for their code, answers, testing and other important contributions. \ No newline at end of file From 9bb47143a1b58e85405e7f6381f11c1435aafde7 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Thu, 15 Oct 2020 22:43:05 +0200 Subject: [PATCH 11/22] Add TechnicLargeAngularMotor (Grey) (#108) - Add TechnicLargeAngularMotor - Updated README #75 non-breaking --- README.md | 5 +- .../Devices/DeviceFactory.cs | 2 + .../Devices/TechnicLargeAngularMotorGrey.cs | 66 +++++++++++++++++++ src/SharpBrick.PoweredUp/Enums/DeviceType.cs | 2 + 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/SharpBrick.PoweredUp/Devices/TechnicLargeAngularMotorGrey.cs diff --git a/README.md b/README.md index 592b666..5ffb158 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,10 @@ DI Container Elements - [X] Train Motor (88011) - [X] Technic Large Motor (88013) - [X] Technic XLarge Motor (88014) - - [ ] Technic Angular Motor (depend on availability of hardware / contributions) + - [ ] Technic Medium Angular Motor (Spike) + - [ ] Technic Medium Angular Motor (Grey) + - [ ] Technic Large Angular Motor (Spike) + - [X] Technic Large Angular Motor (Grey) - .. other devices depend on availability of hardware / contributions - Protocol - [X] Message Encoding (98% [spec coverage](docs/specification/coverage.md)) diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index ec1ef42..9192e9b 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -36,6 +36,7 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.SystemTrainMotor => typeof(SystemTrainMotor), DeviceType.TechnicLargeLinearMotor => typeof(TechnicLargeLinearMotor), DeviceType.TechnicXLargeLinearMotor => typeof(TechnicXLargeLinearMotor), + DeviceType.TechnicLargeAngularMotorGrey => typeof(TechnicLargeAngularMotorGrey), DeviceType.TechnicMediumHubGestureSensor => typeof(TechnicMediumHubGestureSensor), DeviceType.TechnicMediumHubAccelerometer => typeof(TechnicMediumHubAccelerometer), DeviceType.TechnicMediumHubGyroSensor => typeof(TechnicMediumHubGyroSensor), @@ -54,6 +55,7 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(SystemTrainMotor) => DeviceType.SystemTrainMotor, nameof(TechnicLargeLinearMotor) => DeviceType.TechnicLargeLinearMotor, nameof(TechnicXLargeLinearMotor) => DeviceType.TechnicXLargeLinearMotor, + nameof(TechnicLargeAngularMotorGrey) => DeviceType.TechnicLargeAngularMotorGrey, nameof(TechnicMediumHubGestureSensor) => DeviceType.TechnicMediumHubGestureSensor, nameof(TechnicMediumHubAccelerometer) => DeviceType.TechnicMediumHubAccelerometer, nameof(TechnicMediumHubGyroSensor) => DeviceType.TechnicMediumHubGyroSensor, diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicLargeAngularMotorGrey.cs b/src/SharpBrick.PoweredUp/Devices/TechnicLargeAngularMotorGrey.cs new file mode 100644 index 0000000..de36541 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/TechnicLargeAngularMotorGrey.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class TechnicLargeAngularMotorGrey : AbsoluteMotor, IPoweredUpDevice + { + public TechnicLargeAngularMotorGrey() + : base() + { } + public TechnicLargeAngularMotorGrey(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-00-01-0F-06-0E-00-0F-00 +07-00-43-00-02-0E-00 +11-00-44-00-00-00-50-4F-57-45-52-00-00-00-00-00-00 +0E-00-44-00-00-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-03-00-00-C8-C2-00-00-C8-42 +0A-00-44-00-00-04-50-43-54-00 +08-00-44-00-00-05-00-50 +0A-00-44-00-00-80-01-00-04-00 +11-00-44-00-01-00-53-50-45-45-44-00-00-00-00-00-00 +0E-00-44-00-01-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-01-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-01-03-00-00-C8-C2-00-00-C8-42 +0A-00-44-00-01-04-50-43-54-00 +08-00-44-00-01-05-30-70 +0A-00-44-00-01-80-01-00-04-00 +11-00-44-00-02-00-50-4F-53-00-00-00-00-00-00-00-00 +0E-00-44-00-02-01-00-00-B4-C3-00-00-B4-43 +0E-00-44-00-02-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-02-03-00-00-B4-C3-00-00-B4-43 +0A-00-44-00-02-04-44-45-47-00 +08-00-44-00-02-05-28-68 +0A-00-44-00-02-80-01-02-0B-00 +11-00-44-00-03-00-41-50-4F-53-00-00-00-00-00-00-00 +0E-00-44-00-03-01-00-00-34-C3-00-00-33-43 +0E-00-44-00-03-02-00-00-48-C3-00-00-48-43 +0E-00-44-00-03-03-00-00-34-C3-00-00-33-43 +0A-00-44-00-03-04-44-45-47-00 +08-00-44-00-03-05-32-72 +0A-00-44-00-03-80-01-01-03-00 +11-00-44-00-04-00-43-41-4C-49-42-00-00-00-00-00-00 +0E-00-44-00-04-01-00-00-00-00-00-00-61-45 +0E-00-44-00-04-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-04-03-00-00-00-00-00-00-61-45 +0A-00-44-00-04-04-43-41-4C-00 +08-00-44-00-04-05-00-00 +0A-00-44-00-04-80-02-01-05-00 +11-00-44-00-05-00-53-54-41-54-53-00-00-00-00-00-00 +0E-00-44-00-05-01-00-00-00-00-00-FF-7F-47 +0E-00-44-00-05-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-05-03-00-00-00-00-00-FF-7F-47 +0A-00-44-00-05-04-4D-49-4E-00 +08-00-44-00-05-05-00-00 +0A-00-44-00-05-80-0E-01-05-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs index 0620040..3f88bfa 100644 --- a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs +++ b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs @@ -37,5 +37,7 @@ public enum DeviceType : ushort TechnicColorSensor = 0x003D, // UNSPECED, TECHNIC_COLOR_SENSOR (Spike Prime) TechnicDistanceSensor = 0x003E, // UNSPECED, TECHNIC_DISTANCE_SENSOR (Spike Prime) TechnicForceSensor = 0x003F, // UNSPECED, TECHNIC_FORCE_SENSOR (Spike Prime) + TechnicMediumAngularMotorGrey = 0x004B, // UNSPECED, TECHNIC_MEDIUM_ANGULAR_MOTOR_GREY (Control+) + TechnicLargeAngularMotorGrey = 0x004C, // UNSPECED, TECHNIC_LARGE_ANGULAR_MOTOR_GREY (Control+) } } \ No newline at end of file From 9a3831ecb083e4dd7573b3f3228ff37633e42982 Mon Sep 17 00:00:00 2001 From: Rick Jansen Date: Tue, 20 Oct 2020 21:51:20 +0200 Subject: [PATCH 12/22] Change discover handler to async #117 non-breaking --- src/SharpBrick.PoweredUp.Cli/Program.cs | 3 +++ .../WinRTPoweredUpBluetoothAdapter.cs | 6 +++--- .../Bluetooth/IPoweredUpBluetoothAdapter.cs | 2 +- src/SharpBrick.PoweredUp/PoweredUpHost.cs | 4 ++-- .../Bluetooth/PoweredUpBluetoothAdapterMock.cs | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/SharpBrick.PoweredUp.Cli/Program.cs b/src/SharpBrick.PoweredUp.Cli/Program.cs index 0641356..fa8ec0a 100644 --- a/src/SharpBrick.PoweredUp.Cli/Program.cs +++ b/src/SharpBrick.PoweredUp.Cli/Program.cs @@ -146,6 +146,9 @@ private static (ulong bluetoothAddress, SystemType systemType) FindAndSelectHub( idx++; } + + return Task.CompletedTask; + }, cts.Token); var input = Console.ReadLine(); diff --git a/src/SharpBrick.PoweredUp.WinRT/WinRTPoweredUpBluetoothAdapter.cs b/src/SharpBrick.PoweredUp.WinRT/WinRTPoweredUpBluetoothAdapter.cs index 51156d8..12f0806 100644 --- a/src/SharpBrick.PoweredUp.WinRT/WinRTPoweredUpBluetoothAdapter.cs +++ b/src/SharpBrick.PoweredUp.WinRT/WinRTPoweredUpBluetoothAdapter.cs @@ -10,7 +10,7 @@ namespace SharpBrick.PoweredUp.WinRT { public class WinRTPoweredUpBluetoothAdapter : IPoweredUpBluetoothAdapter { - public void Discover(Action discoveryHandler, CancellationToken cancellationToken = default) + public void Discover(Func discoveryHandler, CancellationToken cancellationToken = default) { BluetoothLEAdvertisementWatcher watcher = new BluetoothLEAdvertisementWatcher(); watcher.ScanningMode = BluetoothLEScanningMode.Active; @@ -26,7 +26,7 @@ public void Discover(Action discoveryHandler, Canc watcher.Start(); - void ReceivedHandler(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs) + async void ReceivedHandler(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs) { var info = new PoweredUpBluetoothDeviceInfo(); @@ -47,7 +47,7 @@ void ReceivedHandler(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdverti info.BluetoothAddress = eventArgs.BluetoothAddress; - discoveryHandler(info); + await discoveryHandler(info); } } diff --git a/src/SharpBrick.PoweredUp/Bluetooth/IPoweredUpBluetoothAdapter.cs b/src/SharpBrick.PoweredUp/Bluetooth/IPoweredUpBluetoothAdapter.cs index 4bc3f90..4b75641 100644 --- a/src/SharpBrick.PoweredUp/Bluetooth/IPoweredUpBluetoothAdapter.cs +++ b/src/SharpBrick.PoweredUp/Bluetooth/IPoweredUpBluetoothAdapter.cs @@ -6,7 +6,7 @@ namespace SharpBrick.PoweredUp.Bluetooth { public interface IPoweredUpBluetoothAdapter { - void Discover(Action discoveryHandler, CancellationToken cancellationToken = default); + void Discover(Func discoveryHandler, CancellationToken cancellationToken = default); Task GetDeviceAsync(ulong bluetoothAddress); } diff --git a/src/SharpBrick.PoweredUp/PoweredUpHost.cs b/src/SharpBrick.PoweredUp/PoweredUpHost.cs index c9cbbc7..7eb9ca7 100644 --- a/src/SharpBrick.PoweredUp/PoweredUpHost.cs +++ b/src/SharpBrick.PoweredUp/PoweredUpHost.cs @@ -43,7 +43,7 @@ public THub FindByName(string name) where THub : Hub, IDisposable public void Discover(Func onDiscovery, CancellationToken token = default) { - _bluetoothAdapter.Discover(deviceInfo => + _bluetoothAdapter.Discover(async deviceInfo => { try { @@ -55,7 +55,7 @@ public void Discover(Func onDiscovery, CancellationToken token = defa _logger.LogInformation($"Discovered log of type {hub.GetType().Name} with name '{deviceInfo.Name}' on Bluetooth Address '{deviceInfo.BluetoothAddress}'"); - onDiscovery(hub).Wait(); + await onDiscovery(hub); } } catch (Exception e) diff --git a/test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs b/test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs index c5ab69b..3a0039a 100644 --- a/test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs +++ b/test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs @@ -23,7 +23,7 @@ public PoweredUpBluetoothAdapterMock() public PoweredUpBluetoothServiceMock MockService { get; } public PoweredUpBluetoothCharacteristicMock MockCharacteristic { get; } - public void Discover(Action discoveryHandler, CancellationToken cancellationToken = default) + public void Discover(Func discoveryHandler, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } From fe887ee72ab78da9fd627b692e6e17688af13dce Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sat, 24 Oct 2020 09:12:26 +0200 Subject: [PATCH 13/22] Add known HubProperties (#121) - Add Hub constructor argument (is filled automatically if not set) - Query hub properties by known properties instead of static array - Adjusted TechnicMediumHub and TwoPortHub #120 non-breaking --- src/SharpBrick.PoweredUp/Hubs/Hub.cs | 4 +- .../Hubs/Hub_Properties.cs | 43 +++++++++++-------- .../Hubs/TechnicMediumHub.cs | 17 ++++++++ src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs | 17 ++++++++ 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/SharpBrick.PoweredUp/Hubs/Hub.cs b/src/SharpBrick.PoweredUp/Hubs/Hub.cs index 0992f26..d89c456 100644 --- a/src/SharpBrick.PoweredUp/Hubs/Hub.cs +++ b/src/SharpBrick.PoweredUp/Hubs/Hub.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; @@ -21,7 +22,7 @@ public abstract partial class Hub : IDisposable public IServiceProvider ServiceProvider { get; } public bool IsConnected => Protocol != null; - public Hub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider, SystemType knownSystemType, Port[] knownPorts) + public Hub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider, SystemType knownSystemType, Port[] knownPorts, IEnumerable knownProperties = default) { Protocol = protocol ?? throw new ArgumentNullException(nameof(protocol)); _deviceFactory = deviceFactory ?? throw new ArgumentNullException(nameof(deviceFactory)); @@ -30,6 +31,7 @@ public Hub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger AddKnownPorts(knownPorts ?? throw new ArgumentNullException(nameof(knownPorts))); _logger = logger; + SetupHubProperties(knownProperties); SetupOnHubChange(); SetupOnPortChangeObservable(Protocol.UpstreamMessages); SetupHubAlertObservable(Protocol.UpstreamMessages); diff --git a/src/SharpBrick.PoweredUp/Hubs/Hub_Properties.cs b/src/SharpBrick.PoweredUp/Hubs/Hub_Properties.cs index 8da168e..8a401c4 100644 --- a/src/SharpBrick.PoweredUp/Hubs/Hub_Properties.cs +++ b/src/SharpBrick.PoweredUp/Hubs/Hub_Properties.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using SharpBrick.PoweredUp.Protocol; @@ -42,6 +44,29 @@ public Task ResetHardwareNetworkIdAsync() public IObservable RssiObservable { get; private set; } public IObservable BatteryVoltageInPercentObservable { get; private set; } + private IEnumerable _knownProperties; + + private void SetupHubProperties(IEnumerable knownProperties) + { + _knownProperties = knownProperties ?? new HubProperty[] { + HubProperty.AdvertisingName, + HubProperty.Button, + HubProperty.FwVersion, + HubProperty.HwVersion, + HubProperty.Rssi, + HubProperty.BatteryVoltage, + HubProperty.BatteryType, + HubProperty.ManufacturerName, + HubProperty.RadioFirmwareVersion, + HubProperty.LegoWirelessProtocolVersion, + HubProperty.SystemTypeId, + HubProperty.HardwareNetworkId, + HubProperty.PrimaryMacAddress, + HubProperty.SecondaryMacAddress, + HubProperty.HardwareNetworkFamily, + }; + } + private void SetupHubPropertyObservable(IObservable upstreamMessages) { PropertyChangedObservable = upstreamMessages @@ -64,23 +89,7 @@ private IObservable BuildObservableForProperty(IObservable RequestHubPropertySingleUpdate(property))); } public Task RequestHubPropertySingleUpdate(HubProperty property) diff --git a/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs b/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs index e8d8779..08631a1 100644 --- a/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs +++ b/src/SharpBrick.PoweredUp/Hubs/TechnicMediumHub.cs @@ -22,6 +22,23 @@ public TechnicMediumHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFac new Port(98, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubGyroSensor), new Port(99, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubTiltSensor), new Port(100, string.Empty, false, expectedDevice: DeviceType.TechnicMediumHubGestureSensor), + }, + knownProperties: new HubProperty[] { + HubProperty.AdvertisingName, + HubProperty.Button, + HubProperty.FwVersion, + HubProperty.HwVersion, + HubProperty.Rssi, + HubProperty.BatteryVoltage, + HubProperty.BatteryType, + HubProperty.ManufacturerName, + HubProperty.RadioFirmwareVersion, + HubProperty.LegoWirelessProtocolVersion, + HubProperty.SystemTypeId, + HubProperty.HardwareNetworkId, + HubProperty.PrimaryMacAddress, + HubProperty.SecondaryMacAddress, + //HubProperty.HardwareNetworkFamily, // Throws command not recognized error for TechnicMediumHub }) { } diff --git a/src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs b/src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs index 93e0bd1..6d3e5f9 100644 --- a/src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs +++ b/src/SharpBrick.PoweredUp/Hubs/TwoPortHub.cs @@ -14,6 +14,23 @@ public TwoPortHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, new Port(50, string.Empty, false, expectedDevice: DeviceType.RgbLight), new Port(59, string.Empty, false, expectedDevice: DeviceType.Current), new Port(60, string.Empty, false, expectedDevice: DeviceType.Voltage), + }, + knownProperties: new HubProperty[] { + HubProperty.AdvertisingName, + HubProperty.Button, + HubProperty.FwVersion, + HubProperty.HwVersion, + HubProperty.Rssi, + HubProperty.BatteryVoltage, + HubProperty.BatteryType, + HubProperty.ManufacturerName, + HubProperty.RadioFirmwareVersion, + HubProperty.LegoWirelessProtocolVersion, + HubProperty.SystemTypeId, + HubProperty.HardwareNetworkId, + HubProperty.PrimaryMacAddress, + HubProperty.SecondaryMacAddress, + //HubProperty.HardwareNetworkFamily, // support status unknown for TwoPortHub }) { } From af4b510235e4f5a0f9959f86ca1fc1ddbeab1064 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sat, 24 Oct 2020 16:59:40 +0200 Subject: [PATCH 14/22] Add CLI utility to convert a hex-dump to a pretty-printed version (#115) - Add CLI utility to convert a hex-dump to a pretty-printed version - Moved Bluetooth Mock from Testing Infrastructure to main lib Used for device simulation - Extend CLI port dump with flag to dump system and io type #114 non-breaking --- .vscode/launch.json | 28 +++++ .../Commands/DevicesList.cs | 2 +- .../Commands/DumpStaticPortInfo.cs | 49 +++++++- .../Commands/PrettyPrint.cs | 68 +++++++++++ src/SharpBrick.PoweredUp.Cli/Program.cs | 63 +++++++++- .../Mock/PoweredUpBluetoothAdapterMock.cs | 30 +++++ .../PoweredUpBluetoothCharacteristicMock.cs | 51 ++++++++ .../Mock/PoweredUpBluetoothDeviceMock.cs | 23 ++++ .../Mock/PoweredUpBluetoothServiceMock.cs | 24 ++++ .../ServiceCollectionExtensionsForMock.cs | 16 +++ .../Formatter/HubAttachedIOEncoder.cs | 8 +- .../Formatter/HubPropertiesEncoder.cs | 17 ++- .../Protocol/Knowledge/KnowledgeManager.cs | 2 + .../Protocol/Knowledge/PortInfo.cs | 4 +- .../PoweredUpBluetoothAdapterMock.cs | 115 ------------------ .../Devices/VoltageTest.cs | 5 +- 16 files changed, 379 insertions(+), 126 deletions(-) create mode 100644 src/SharpBrick.PoweredUp.Cli/Commands/PrettyPrint.cs create mode 100644 src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothAdapterMock.cs create mode 100644 src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothCharacteristicMock.cs create mode 100644 src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothDeviceMock.cs create mode 100644 src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothServiceMock.cs create mode 100644 src/SharpBrick.PoweredUp/Bluetooth/Mock/ServiceCollectionExtensionsForMock.cs delete mode 100644 test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index 8905a12..7e2d1e2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -47,6 +47,34 @@ "console": "integratedTerminal", "stopAtEntry": false }, + { + "name": "poweredup device pretty-print --t 128 --h 0 --p 100 --io 0036 --hw 1.0.0.2 --sw 1.0.0.3 --file test.txt", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/SharpBrick.PoweredUp.Cli/bin/Debug/netcoreapp3.1/SharpBrick.PoweredUp.Cli.dll", + "args": [ + "device", + "pretty-print", + "--t", + "128", + "--h", + "0", + "--io", + "0036", + "--p", + "100", + "--hw", + "1.0.0.2", + "--sw", + "1.0.0.3", + "--file", + "test.txt" + ], + "cwd": "${workspaceFolder}/src/SharpBrick.PoweredUp.Cli", + "console": "integratedTerminal", + "stopAtEntry": false + }, { "name": ".NET Core Attach", "type": "coreclr", diff --git a/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs b/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs index 1241c93..d91868d 100644 --- a/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs +++ b/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs @@ -41,7 +41,7 @@ public async Task ExecuteAsync(SystemType knownSystemType) PrettyPrintKnowledge(System.Console.Out, protocol.Knowledge); } - private static void PrettyPrintKnowledge(TextWriter writer, ProtocolKnowledge portKnowledge) + public static void PrettyPrintKnowledge(TextWriter writer, ProtocolKnowledge portKnowledge) { string Intent(int depth) => " ".Substring(0, depth * 2); diff --git a/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs b/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs index 6baa905..e1bd1de 100644 --- a/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs +++ b/src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using SharpBrick.PoweredUp.Functions; using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Protocol.Formatter; using SharpBrick.PoweredUp.Protocol.Messages; using SharpBrick.PoweredUp.Utils; @@ -18,7 +19,7 @@ public DumpStaticPortInfo(ILegoWirelessProtocol protocol, DiscoverPorts discover this.protocol = protocol ?? throw new ArgumentNullException(nameof(protocol)); this.discoverPorts = discoverPorts ?? throw new ArgumentNullException(nameof(discoverPorts)); } - public async Task ExecuteAsync(SystemType knownSystemType, byte portId) + public async Task ExecuteAsync(SystemType knownSystemType, byte portId, bool headerEnabled) { Console.WriteLine($"Discover Port {portId}. Receiving Messages ..."); @@ -36,12 +37,58 @@ public async Task ExecuteAsync(SystemType knownSystemType, byte portId) Console.WriteLine($"Discover Ports Function: {discoverPorts.ReceivedMessages} / {discoverPorts.SentMessages}"); + Console.WriteLine(knownSystemType); + + var systemTypeMessage = CreateSystemTypeHeader(knownSystemType); + var attachedIOMessage = CreateAttachedIOHeader(portId); Console.WriteLine("##################################################"); + + if (headerEnabled) + { + Console.WriteLine(BytesStringUtil.DataToString(systemTypeMessage)); + Console.WriteLine(BytesStringUtil.DataToString(attachedIOMessage)); + } + foreach (var data in discoverPorts.ReceivedMessagesData.OrderBy(x => x[2]).ThenBy(x => x[4]).ThenBy(x => (x.Length <= 5) ? -1 : x[5])) { Console.WriteLine(BytesStringUtil.DataToString(data)); } + Console.WriteLine("##################################################"); } + + private byte[] CreateAttachedIOHeader(byte portId) + { + var portInfo = protocol.Knowledge.Port(0, portId); + + return MessageEncoder.Encode(portInfo switch + { + { IsVirtual: false } => new HubAttachedIOForAttachedDeviceMessage() + { + HubId = 0, + PortId = portId, + IOTypeId = portInfo.IOTypeId, + HardwareRevision = portInfo.HardwareRevision, + SoftwareRevision = portInfo.SoftwareRevision, + }, + { IsVirtual: true } => new HubAttachedIOForAttachedVirtualDeviceMessage() + { + HubId = 0, + PortId = portId, + IOTypeId = portInfo.IOTypeId, + PortAId = portInfo.PortAId, + PortBId = portInfo.PortBId, + } + }, protocol.Knowledge); + } + + private byte[] CreateSystemTypeHeader(SystemType knownSystemType) + => MessageEncoder.Encode(new HubPropertyMessage() + { + HubId = 0, + Property = HubProperty.SystemTypeId, + Operation = HubPropertyOperation.Update, + Payload = knownSystemType, + }, protocol.Knowledge); } } diff --git a/src/SharpBrick.PoweredUp.Cli/Commands/PrettyPrint.cs b/src/SharpBrick.PoweredUp.Cli/Commands/PrettyPrint.cs new file mode 100644 index 0000000..9da22cd --- /dev/null +++ b/src/SharpBrick.PoweredUp.Cli/Commands/PrettyPrint.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using SharpBrick.PoweredUp.Functions; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Bluetooth.Mock; +using SharpBrick.PoweredUp.Protocol.Messages; +using SharpBrick.PoweredUp.Protocol.Knowledge; + +namespace SharpBrick.PoweredUp.Cli +{ + public class PrettyPrint + { + private readonly ILegoWirelessProtocol _protocol; + private readonly PoweredUpBluetoothAdapterMock _mock; + + public PrettyPrint(ILegoWirelessProtocol protocol, IServiceProvider serviceProvider) + { + this._protocol = protocol ?? throw new ArgumentNullException(nameof(protocol)); + this._mock = serviceProvider.GetMockBluetoothAdapter(); + } + + public async Task ExecuteAsync(TextReader reader, byte systemType, ushort deviceType, byte hubId, byte portId, Version hw, Version sw) + { + var knowledge = _protocol.Knowledge; + + // override cached data + using var disposable = _protocol.UpstreamRawMessages + .Subscribe(tuple => KnowledgeManager.ApplyStaticProtocolKnowledge(tuple.message, knowledge)); + + await _protocol.ConnectAsync((SystemType)systemType); // registering to bluetooth notification + + if (systemType != 0) + { + Console.Error.WriteLine("Command Line provided Device Type, hubId, portId and versions hw/sw"); + await _mock.MockCharacteristic.AttachIO((DeviceType)deviceType, hubId, portId, hw, sw); + } + + var foundSystemType = false; + var foundAttachedIO = false; + + var line = await reader.ReadLineAsync(); + while (line != null) + { + if (line.Substring(6, 8) == "01-0B-06") // property msg - systemtype - update + { + foundSystemType = true; + } + if (line.Substring(6, 2) == "04" && line.Substring(12, 2) == "01") // attached io msg (04) - port - attach event + { + foundAttachedIO = true; + } + await _mock.MockCharacteristic.WriteUpstreamAsync(line); + + line = await reader.ReadLineAsync(); + } + + if (systemType == 0 && (foundSystemType == false || foundAttachedIO == false)) + { + Console.Error.WriteLine("#############################"); + Console.Error.WriteLine("SystemType and/or attached IO message not found in data or command line arguments"); + Console.Error.WriteLine("#############################"); + } + + DevicesList.PrettyPrintKnowledge(System.Console.Out, _protocol.Knowledge); + } + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp.Cli/Program.cs b/src/SharpBrick.PoweredUp.Cli/Program.cs index fa8ec0a..71152a1 100644 --- a/src/SharpBrick.PoweredUp.Cli/Program.cs +++ b/src/SharpBrick.PoweredUp.Cli/Program.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Concurrent; +using System.Globalization; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -61,10 +63,12 @@ static async Task Main(string[] args) var traceOption = deviceDumpStaticPortApp.Option("--trace", "Enable Tracing", CommandOptionType.SingleValue); var portOption = deviceDumpStaticPortApp.Option("-p", "Port to Dump", CommandOptionType.SingleValue); + var headerOption = deviceDumpStaticPortApp.Option("-f", "Add Hub and IOType Header", CommandOptionType.NoValue); deviceDumpStaticPortApp.OnExecuteAsync(async cts => { var enableTrace = bool.TryParse(traceOption.Value(), out var x) ? x : false; + var headerEnabled = headerOption.Values.Count > 0; var serviceProvider = CreateServiceProvider(enableTrace); (ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService()); @@ -85,16 +89,62 @@ static async Task Main(string[] args) var port = byte.Parse(portOption.Value()); - await dumpStaticPortInfoCommand.ExecuteAsync(systemType, port); + await dumpStaticPortInfoCommand.ExecuteAsync(systemType, port, headerEnabled); } }); }); + + deviceApp.Command("pretty-print", prettyPrintApp => + { + prettyPrintApp.HelpOption(); + var traceOption = prettyPrintApp.Option("--trace", "Enable Tracing", CommandOptionType.SingleValue); + var systemTypeOption = prettyPrintApp.Option("--t", "System Type (parsable number)", CommandOptionType.SingleValue); + var hubOption = prettyPrintApp.Option("--h", "Hub Id (decimal number)", CommandOptionType.SingleValue); + var portOption = prettyPrintApp.Option("--p", "Port Id (decimal number)", CommandOptionType.SingleValue); + var ioTypeOption = prettyPrintApp.Option("--io", "IO Type (hex number, e.g. 003C)", CommandOptionType.SingleValue); + var hwVersionOption = prettyPrintApp.Option("--hw", "Hardware Version", CommandOptionType.SingleValue); + var swVersionOption = prettyPrintApp.Option("--sw", "Software Version", CommandOptionType.SingleValue); + var fileOption = prettyPrintApp.Option("--file", "File to read from (otherwise stdin", CommandOptionType.SingleValue); + + prettyPrintApp.OnExecuteAsync(async cts => + { + var enableTrace = bool.TryParse(traceOption.Value(), out var x0) ? x0 : false; + var systemType = byte.TryParse(systemTypeOption.Value(), out var x1) ? x1 : (byte)0; + var hubId = byte.TryParse(hubOption.Value(), out var x2) ? x2 : (byte)0; + var portId = byte.TryParse(portOption.Value(), out var x3) ? x3 : (byte)0; + var ioType = ushort.TryParse(ioTypeOption.Value(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var x4) ? x4 : (ushort)0; + var hwVersion = Version.TryParse(hwVersionOption.Value(), out var x5) ? x5 : new Version("0.0.0.0"); + var swVersion = Version.TryParse(swVersionOption.Value(), out var x6) ? x6 : new Version("0.0.0.0"); + var file = fileOption.Value(); + + var serviceProvider = CreateServiceProviderWithMock(enableTrace); + + using (var scope = serviceProvider.CreateScope()) + { + var scopedServiceProvider = scope.ServiceProvider; + + await AddTraceWriterAsync(scopedServiceProvider, enableTrace); + + var prettyPrintCommand = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory + + TextReader reader = Console.In; + + if (!string.IsNullOrWhiteSpace(file)) + { + reader = new StringReader(File.ReadAllText(file)); + } + + await prettyPrintCommand.ExecuteAsync(reader, systemType, ioType, hubId, portId, hwVersion, swVersion); + } + }); + }); + }); await app.ExecuteAsync(args); } - private static IServiceProvider CreateServiceProvider(bool enableTrace) + private static IServiceCollection CreateServiceProviderInternal(bool enableTrace) => new ServiceCollection() .AddLogging(builder => { @@ -107,12 +157,19 @@ private static IServiceProvider CreateServiceProvider(bool enableTrace) builder.AddFilter("SharpBrick.PoweredUp.Bluetooth.BluetoothKernel", LogLevel.Debug); } }) - .AddWinRTBluetooth() .AddPoweredUp() // Add CLI Commands .AddTransient() .AddTransient() + .AddTransient(); + private static IServiceProvider CreateServiceProviderWithMock(bool enableTrace) + => CreateServiceProviderInternal(enableTrace) + .AddMockBluetooth() + .BuildServiceProvider(); + private static IServiceProvider CreateServiceProvider(bool enableTrace) + => CreateServiceProviderInternal(enableTrace) + .AddWinRTBluetooth() .BuildServiceProvider(); public static async Task AddTraceWriterAsync(IServiceProvider serviceProvider, bool enableTrace) diff --git a/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothAdapterMock.cs b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothAdapterMock.cs new file mode 100644 index 0000000..76addef --- /dev/null +++ b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothAdapterMock.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace SharpBrick.PoweredUp.Bluetooth.Mock +{ + public class PoweredUpBluetoothAdapterMock : IPoweredUpBluetoothAdapter + { + public PoweredUpBluetoothAdapterMock() + { + MockCharacteristic = new PoweredUpBluetoothCharacteristicMock(); + MockService = new PoweredUpBluetoothServiceMock(MockCharacteristic); + MockDevice = new PoweredUpBluetoothDeviceMock(MockService); + } + + public PoweredUpBluetoothDeviceMock MockDevice { get; } + public PoweredUpBluetoothServiceMock MockService { get; } + public PoweredUpBluetoothCharacteristicMock MockCharacteristic { get; } + + public void Discover(Func discoveryHandler, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task GetDeviceAsync(ulong bluetoothAddress) + { + return Task.FromResult(MockDevice); + } + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothCharacteristicMock.cs b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothCharacteristicMock.cs new file mode 100644 index 0000000..b53945d --- /dev/null +++ b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothCharacteristicMock.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SharpBrick.PoweredUp.Protocol.Formatter; +using SharpBrick.PoweredUp.Protocol.Messages; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp.Bluetooth.Mock +{ + public class PoweredUpBluetoothCharacteristicMock : IPoweredUpBluetoothCharacteristic + { + private Func _next; + + public Guid Uuid => Guid.Empty; + + public Task NotifyValueChangeAsync(Func notificationHandler) + { + _next = notificationHandler; + + return Task.FromResult(true); + } + + public Task WriteValueAsync(byte[] data) + { + DownstreamMessages.Add(data); + + return Task.FromResult(true); + } + + public Task WriteUpstreamAsync(LegoWirelessMessage message) + => WriteUpstreamAsync(MessageEncoder.Encode(message, null)); + + public Task WriteUpstreamAsync(string message) + => WriteUpstreamAsync(BytesStringUtil.StringToData(message)); + + public Task WriteUpstreamAsync(params byte[] message) + => _next(message); + + public List DownstreamMessages { get; } = new List(); + + public Task AttachIO(DeviceType deviceType, byte hubId, byte portId, Version hw, Version sw) + => WriteUpstreamAsync(MessageEncoder.Encode(new HubAttachedIOForAttachedDeviceMessage() + { + HubId = hubId, + PortId = portId, + IOTypeId = deviceType, + HardwareRevision = hw, + SoftwareRevision = sw, + }, null)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothDeviceMock.cs b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothDeviceMock.cs new file mode 100644 index 0000000..a89ba87 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothDeviceMock.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; + +namespace SharpBrick.PoweredUp.Bluetooth.Mock +{ + public class PoweredUpBluetoothDeviceMock : IPoweredUpBluetoothDevice + { + private PoweredUpBluetoothServiceMock _mockService; + + public PoweredUpBluetoothDeviceMock(PoweredUpBluetoothServiceMock mockService) + { + this._mockService = mockService; + } + + public string Name => "Mock Device"; + + public void Dispose() + { } + + public Task GetServiceAsync(Guid serviceId) + => Task.FromResult(_mockService); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothServiceMock.cs b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothServiceMock.cs new file mode 100644 index 0000000..14c6652 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Bluetooth/Mock/PoweredUpBluetoothServiceMock.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; + +namespace SharpBrick.PoweredUp.Bluetooth.Mock +{ + public class PoweredUpBluetoothServiceMock : IPoweredUpBluetoothService + { + private PoweredUpBluetoothCharacteristicMock _mockCharacteristic; + + public PoweredUpBluetoothServiceMock(PoweredUpBluetoothCharacteristicMock mockCharacteristic) + { + this._mockCharacteristic = mockCharacteristic; + } + + public Guid Uuid => Guid.Empty; + + public void Dispose() + { + } + + public Task GetCharacteristicAsync(Guid guid) + => Task.FromResult(_mockCharacteristic); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Bluetooth/Mock/ServiceCollectionExtensionsForMock.cs b/src/SharpBrick.PoweredUp/Bluetooth/Mock/ServiceCollectionExtensionsForMock.cs new file mode 100644 index 0000000..d9b55d7 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Bluetooth/Mock/ServiceCollectionExtensionsForMock.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using SharpBrick.PoweredUp.Bluetooth; +using SharpBrick.PoweredUp.Bluetooth.Mock; + +namespace SharpBrick.PoweredUp +{ + public static class ServiceCollectionExtensionsForMock + { + public static IServiceCollection AddMockBluetooth(this IServiceCollection self) + => self.AddSingleton(); + + public static PoweredUpBluetoothAdapterMock GetMockBluetoothAdapter(this IServiceProvider self) + => self.GetService() as PoweredUpBluetoothAdapterMock; + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Protocol/Formatter/HubAttachedIOEncoder.cs b/src/SharpBrick.PoweredUp/Protocol/Formatter/HubAttachedIOEncoder.cs index 44d5908..b4635f6 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Formatter/HubAttachedIOEncoder.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Formatter/HubAttachedIOEncoder.cs @@ -44,7 +44,13 @@ public void Encode(HubAttachedIOForDetachedDeviceMessage message, in Span => throw new NotImplementedException(); public void Encode(HubAttachedIOForAttachedVirtualDeviceMessage message, in Span data) - => throw new NotImplementedException(); + { + data[0] = message.PortId; + data[1] = (byte)message.Event; + BitConverter.TryWriteBytes(data.Slice(2, 2), (ushort)message.IOTypeId); + data[4] = message.PortAId; + data[5] = message.PortBId; + } public LegoWirelessMessage Decode(byte hubId, in Span data) { diff --git a/src/SharpBrick.PoweredUp/Protocol/Formatter/HubPropertiesEncoder.cs b/src/SharpBrick.PoweredUp/Protocol/Formatter/HubPropertiesEncoder.cs index 5b481a9..642912d 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Formatter/HubPropertiesEncoder.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Formatter/HubPropertiesEncoder.cs @@ -27,7 +27,17 @@ public ushort CalculateMessageLength(HubPropertyMessage message) HubPropertyOperation.DisableUpdates => 0, HubPropertyOperation.Reset => 0, HubPropertyOperation.RequestUpdate => 0, - HubPropertyOperation.Update => throw new NotImplementedException(), + HubPropertyOperation.Update => message switch + { + HubPropertyMessage msg => msg.Payload.Length, + HubPropertyMessage msg => msg.Payload.Length, + HubPropertyMessage msg => msg.Property switch + { + HubProperty.LegoWirelessProtocolVersion => 2, // special + _ => 4, // default version in int + }, + _ => 1, + }, _ => 0, }; @@ -43,7 +53,7 @@ public void Encode(HubPropertyMessage message, Span data) data[0] = (byte)message.Property; data[1] = (byte)message.Operation; - if (message.Operation == HubPropertyOperation.Set) + if (message.Operation == HubPropertyOperation.Set || message.Operation == HubPropertyOperation.Update) { switch (message) { @@ -57,6 +67,9 @@ public void Encode(HubPropertyMessage message, Span data) throw new NotImplementedException(); case HubPropertyMessage msg: throw new NotImplementedException(); //data[2] = (byte)msg.Payload; + case HubPropertyMessage msg: + data[2] = (byte)msg.Payload; + break; case HubPropertyMessage msg: data[2] = msg.Payload; break; diff --git a/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs b/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs index adead23..a209d6e 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs @@ -148,6 +148,8 @@ public static Task ApplyDynamicProtocolKnowledge(LegoWirelessMessage message, Pr AddCachePortAndPortModeInformation(msg.IOTypeId, partOfVirtual.HardwareRevision, partOfVirtual.SoftwareRevision, hub, port, knowledge, deviceFactory); port.IsVirtual = true; + port.PortAId = msg.PortAId; + port.PortBId = msg.PortBId; break; case PortInputFormatSingleMessage msg: diff --git a/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortInfo.cs b/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortInfo.cs index 1b9a77b..a336fed 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortInfo.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortInfo.cs @@ -17,7 +17,9 @@ public class PortInfo public Version SoftwareRevision { get; set; } // HubAttachedIOForAttachedVirtualDeviceMessage - public bool IsVirtual { get; internal set; } + public bool IsVirtual { get; set; } + public byte PortAId { get; set; } + public byte PortBId { get; set; } // PortInformationForModeInfoMessage public bool OutputCapability { get; set; } // seen from hub diff --git a/test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs b/test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs deleted file mode 100644 index 3a0039a..0000000 --- a/test/SharpBrick.PoweredUp.Test/Bluetooth/PoweredUpBluetoothAdapterMock.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using SharpBrick.PoweredUp.Bluetooth; -using SharpBrick.PoweredUp.Protocol; -using SharpBrick.PoweredUp.Protocol.Formatter; -using SharpBrick.PoweredUp.Protocol.Messages; -using SharpBrick.PoweredUp.Utils; - -namespace SharpBrick.PoweredUp -{ - public class PoweredUpBluetoothAdapterMock : IPoweredUpBluetoothAdapter - { - public PoweredUpBluetoothAdapterMock() - { - MockCharacteristic = new PoweredUpBluetoothCharacteristicMock(); - MockService = new PoweredUpBluetoothServiceMock(MockCharacteristic); - MockDevice = new PoweredUpBluetoothDeviceMock(MockService); - } - - public PoweredUpBluetoothDeviceMock MockDevice { get; } - public PoweredUpBluetoothServiceMock MockService { get; } - public PoweredUpBluetoothCharacteristicMock MockCharacteristic { get; } - - public void Discover(Func discoveryHandler, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task GetDeviceAsync(ulong bluetoothAddress) - { - return Task.FromResult(MockDevice); - } - } - - public class PoweredUpBluetoothDeviceMock : IPoweredUpBluetoothDevice - { - private PoweredUpBluetoothServiceMock _mockService; - - public PoweredUpBluetoothDeviceMock(PoweredUpBluetoothServiceMock mockService) - { - this._mockService = mockService; - } - - public string Name => "Mock Device"; - - public void Dispose() - { } - - public Task GetServiceAsync(Guid serviceId) - => Task.FromResult(_mockService); - } - - public class PoweredUpBluetoothServiceMock : IPoweredUpBluetoothService - { - private PoweredUpBluetoothCharacteristicMock _mockCharacteristic; - - public PoweredUpBluetoothServiceMock(PoweredUpBluetoothCharacteristicMock mockCharacteristic) - { - this._mockCharacteristic = mockCharacteristic; - } - - public Guid Uuid => Guid.Empty; - - public void Dispose() - { - } - - public Task GetCharacteristicAsync(Guid guid) - => Task.FromResult(_mockCharacteristic); - } - - public class PoweredUpBluetoothCharacteristicMock : IPoweredUpBluetoothCharacteristic - { - private Func _next; - - public Guid Uuid => Guid.Empty; - - public Task NotifyValueChangeAsync(Func notificationHandler) - { - _next = notificationHandler; - - return Task.FromResult(true); - } - - public Task WriteValueAsync(byte[] data) - { - DownstreamMessages.Add(data); - - return Task.FromResult(true); - } - - public Task WriteUpstreamAsync(LegoWirelessMessage message) - => WriteUpstreamAsync(MessageEncoder.Encode(message, null)); - - public Task WriteUpstreamAsync(string message) - => WriteUpstreamAsync(BytesStringUtil.StringToData(message)); - - public Task WriteUpstreamAsync(params byte[] message) - => _next(message); - - public List DownstreamMessages { get; } = new List(); - - public Task AttachIO(DeviceType deviceType, byte hubId, byte portId, Version hw, Version sw) - => WriteUpstreamAsync(MessageEncoder.Encode(new HubAttachedIOForAttachedDeviceMessage() - { - HubId = hubId, - PortId = portId, - IOTypeId = deviceType, - HardwareRevision = hw, - SoftwareRevision = sw, - }, null)); - } -} \ No newline at end of file diff --git a/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs b/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs index 3df7d78..8b31cfe 100644 --- a/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs +++ b/test/SharpBrick.PoweredUp.Test/Devices/VoltageTest.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using SharpBrick.PoweredUp.Bluetooth; +using SharpBrick.PoweredUp.Bluetooth.Mock; using SharpBrick.PoweredUp.Devices; using SharpBrick.PoweredUp.Protocol; using SharpBrick.PoweredUp.Protocol.Messages; @@ -73,14 +74,14 @@ await mock.WriteUpstreamAsync(new PortInputFormatSingleMessage() { var serviceProvider = new ServiceCollection() .AddLogging() - .AddSingleton() + .AddMockBluetooth() .AddSingleton() .AddSingleton() .AddSingleton() // for protocol knowledge init .BuildServiceProvider(); - var poweredUpBluetoothAdapterMock = serviceProvider.GetService() as PoweredUpBluetoothAdapterMock; + var poweredUpBluetoothAdapterMock = serviceProvider.GetMockBluetoothAdapter(); var protocol = serviceProvider.GetService(); From 261f44b5246df1eb4120bc07a3e172d8634288b1 Mon Sep 17 00:00:00 2001 From: Rick Jansen Date: Mon, 26 Oct 2020 22:40:05 +0100 Subject: [PATCH 15/22] Add Technic Medium Angular Motor Grey (#110) #75 non-breaking --- README.md | 2 +- .../ExampleTechnicMediumAngularMotorGrey.cs | 51 ++++++++++++++ .../SharpBrick.PoweredUp.Examples/Program.cs | 3 +- .../Devices/AbsoluteMotor.cs | 2 +- .../Devices/DeviceFactory.cs | 2 + .../Devices/TechnicMediumAngularMotorGrey.cs | 66 +++++++++++++++++++ 6 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumAngularMotorGrey.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/TechnicMediumAngularMotorGrey.cs diff --git a/README.md b/README.md index 5ffb158..c46411d 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ DI Container Elements - [X] Technic Large Motor (88013) - [X] Technic XLarge Motor (88014) - [ ] Technic Medium Angular Motor (Spike) - - [ ] Technic Medium Angular Motor (Grey) + - [X] Technic Medium Angular Motor (Grey) - [ ] Technic Large Angular Motor (Spike) - [X] Technic Large Angular Motor (Grey) - .. other devices depend on availability of hardware / contributions diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumAngularMotorGrey.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumAngularMotorGrey.cs new file mode 100644 index 0000000..4f77eb4 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleTechnicMediumAngularMotorGrey.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; +using System.Reactive.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using SharpBrick.PoweredUp; +using SharpBrick.PoweredUp.Protocol.Messages; + +namespace Example +{ + public class ExampleTechnicMediumAngularMotorGrey : BaseExample + { + public override async Task ExecuteAsync() + { + using (var technicMediumHub = Host.FindByType()) + { + await technicMediumHub.VerifyDeploymentModelAsync(modelBuilder => modelBuilder + .AddHub(hubBuilder => hubBuilder + .AddDevice(technicMediumHub.A) + ) + ); + + var motor = technicMediumHub.A.GetDevice(); + + await motor.GotoPositionAsync(45, 50, 100); + + await Task.Delay(2000); + + await motor.SetZeroAsync(); + + await Task.Delay(2000); + + await motor.StartSpeedForDegreesAsync(90, 50, 100); + + await Task.Delay(2000); + + // align physical and reset position to it. + await motor.GotoPositionAsync(0, 50, 100); + + await Task.Delay(2000); + + // does not reset back to marked position on device + await motor.GotoRealZeroAsync(); + + await Task.Delay(2000); + + await technicMediumHub.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/Program.cs b/examples/SharpBrick.PoweredUp.Examples/Program.cs index 4bc6a21..ce54ec3 100644 --- a/examples/SharpBrick.PoweredUp.Examples/Program.cs +++ b/examples/SharpBrick.PoweredUp.Examples/Program.cs @@ -33,7 +33,8 @@ static async Task Main(string[] args) //example = new Example.ExampleHubPropertyObserving(); //example = new Example.ExampleDiscoverByType(); //example = new Example.ExampleCalibrationSteering(); - example = new Example.ExampleTechnicMediumHubGestSensor(); + //example = new Example.ExampleTechnicMediumHubGestSensor(); + example = new Example.ExampleTechnicMediumAngularMotorGrey(); // NOTE: Examples are programmed object oriented style. Base class implements methods Configure, DiscoverAsync and ExecuteAsync to be overwriten on demand. await example.InitHostAndDiscoverAsync(enableTrace); diff --git a/src/SharpBrick.PoweredUp/Devices/AbsoluteMotor.cs b/src/SharpBrick.PoweredUp/Devices/AbsoluteMotor.cs index 6a9251e..516da4d 100644 --- a/src/SharpBrick.PoweredUp/Devices/AbsoluteMotor.cs +++ b/src/SharpBrick.PoweredUp/Devices/AbsoluteMotor.cs @@ -127,7 +127,7 @@ public async Task GotoRealZeroAsync() var currentPosition = await GetPositionAsync(); - sbyte speed = 5; + sbyte speed = 10; uint degrees; diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index 9192e9b..789930e 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -36,6 +36,7 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.SystemTrainMotor => typeof(SystemTrainMotor), DeviceType.TechnicLargeLinearMotor => typeof(TechnicLargeLinearMotor), DeviceType.TechnicXLargeLinearMotor => typeof(TechnicXLargeLinearMotor), + DeviceType.TechnicMediumAngularMotorGrey => typeof(TechnicMediumAngularMotorGrey), DeviceType.TechnicLargeAngularMotorGrey => typeof(TechnicLargeAngularMotorGrey), DeviceType.TechnicMediumHubGestureSensor => typeof(TechnicMediumHubGestureSensor), DeviceType.TechnicMediumHubAccelerometer => typeof(TechnicMediumHubAccelerometer), @@ -55,6 +56,7 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(SystemTrainMotor) => DeviceType.SystemTrainMotor, nameof(TechnicLargeLinearMotor) => DeviceType.TechnicLargeLinearMotor, nameof(TechnicXLargeLinearMotor) => DeviceType.TechnicXLargeLinearMotor, + nameof(TechnicMediumAngularMotorGrey) => DeviceType.TechnicMediumAngularMotorGrey, nameof(TechnicLargeAngularMotorGrey) => DeviceType.TechnicLargeAngularMotorGrey, nameof(TechnicMediumHubGestureSensor) => DeviceType.TechnicMediumHubGestureSensor, nameof(TechnicMediumHubAccelerometer) => DeviceType.TechnicMediumHubAccelerometer, diff --git a/src/SharpBrick.PoweredUp/Devices/TechnicMediumAngularMotorGrey.cs b/src/SharpBrick.PoweredUp/Devices/TechnicMediumAngularMotorGrey.cs new file mode 100644 index 0000000..8128bf4 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/TechnicMediumAngularMotorGrey.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class TechnicMediumAngularMotorGrey : AbsoluteMotor, IPoweredUpDevice + { + public TechnicMediumAngularMotorGrey() + : base() + { } + public TechnicMediumAngularMotorGrey(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-00-01-0F-06-0E-00-0F-00 +07-00-43-00-02-0E-00 +11-00-44-00-00-00-50-4F-57-45-52-00-00-00-00-00-00 +0E-00-44-00-00-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-03-00-00-C8-C2-00-00-C8-42 +0A-00-44-00-00-04-50-43-54-00 +08-00-44-00-00-05-00-50 +0A-00-44-00-00-80-01-00-04-00 +11-00-44-00-01-00-53-50-45-45-44-00-00-00-00-00-00 +0E-00-44-00-01-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-01-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-01-03-00-00-C8-C2-00-00-C8-42 +0A-00-44-00-01-04-50-43-54-00 +08-00-44-00-01-05-30-70 +0A-00-44-00-01-80-01-00-04-00 +11-00-44-00-02-00-50-4F-53-00-00-00-00-00-00-00-00 +0E-00-44-00-02-01-00-00-B4-C3-00-00-B4-43 +0E-00-44-00-02-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-02-03-00-00-B4-C3-00-00-B4-43 +0A-00-44-00-02-04-44-45-47-00 +08-00-44-00-02-05-28-68 +0A-00-44-00-02-80-01-02-0B-00 +11-00-44-00-03-00-41-50-4F-53-00-00-00-00-00-00-00 +0E-00-44-00-03-01-00-00-34-C3-00-00-33-43 +0E-00-44-00-03-02-00-00-48-C3-00-00-48-43 +0E-00-44-00-03-03-00-00-34-C3-00-00-33-43 +0A-00-44-00-03-04-44-45-47-00 +08-00-44-00-03-05-32-72 +0A-00-44-00-03-80-01-01-03-00 +11-00-44-00-04-00-43-41-4C-49-42-00-00-00-00-00-00 +0E-00-44-00-04-01-00-00-00-00-00-00-61-45 +0E-00-44-00-04-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-04-03-00-00-00-00-00-00-61-45 +0A-00-44-00-04-04-43-41-4C-00 +08-00-44-00-04-05-00-00 +0A-00-44-00-04-80-02-01-05-00 +11-00-44-00-05-00-53-54-41-54-53-00-00-00-00-00-00 +0E-00-44-00-05-01-00-00-00-00-00-FF-7F-47 +0E-00-44-00-05-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-05-03-00-00-00-00-00-FF-7F-47 +0A-00-44-00-05-04-4D-49-4E-00 +08-00-44-00-05-05-00-00 +0A-00-44-00-05-80-0E-01-05-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file From 0c1cd4c69807f54d095692426e47182968e5e2b5 Mon Sep 17 00:00:00 2001 From: Rick Jansen Date: Tue, 27 Oct 2020 22:40:12 +0100 Subject: [PATCH 16/22] Add TwoPortHandset (#119) #96 non-breaking --- README.md | 3 + .../ExampleRemoteControlButton.cs | 56 ++++++++++++ .../ExampleRemoteControlRssi.cs | 34 ++++++++ .../SharpBrick.PoweredUp.Examples/Program.cs | 3 + .../Devices/DeviceFactory.cs | 4 + .../Devices/RemoteControlButton.cs | 85 +++++++++++++++++++ .../Devices/RemoteControlRssi.cs | 42 +++++++++ src/SharpBrick.PoweredUp/Devices/RgbLight.cs | 18 ++++ src/SharpBrick.PoweredUp/Devices/Voltage.cs | 18 ++++ src/SharpBrick.PoweredUp/Hubs/HubFactory.cs | 2 + .../Hubs/TwoPortHandset.cs | 46 ++++++++++ .../IServiceCollectionExtensions.cs | 1 + 12 files changed, 312 insertions(+) create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlButton.cs create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlRssi.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/RemoteControlButton.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/RemoteControlRssi.cs create mode 100644 src/SharpBrick.PoweredUp/Hubs/TwoPortHandset.cs diff --git a/README.md b/README.md index c46411d..2c354a5 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,7 @@ DI Container Elements - [X] Actions - [X] Create Virtual Ports - [X] Two Port Hub (88009) + - [X] Two Port Handset (88010) - [X] Technic Medium Hub (88012) - .. other hubs depend on availability of hardware / contributions - Devices @@ -261,6 +262,8 @@ DI Container Elements - [X] Hub (88009) - Current - [X] Hub (88009) - Voltage - [X] Medium Linear Motor (88008) + - [X] Remote Control Button (88010) + - [X] Remote Control RSSI (88010) - [X] Train Motor (88011) - [X] Technic Large Motor (88013) - [X] Technic XLarge Motor (88014) diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlButton.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlButton.cs new file mode 100644 index 0000000..9247ed4 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlButton.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading.Tasks; +using System.Reactive.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleRemoteControlButton : BaseExample + { + public override async Task ExecuteAsync() + { + using (var twoPortHandset = Host.FindByType()) + { + var device = twoPortHandset.A; + + await device.SetupNotificationAsync(device.ModeIndexBitField, true, deltaInterval: 1); + + await twoPortHandset.RgbLight.SetRgbColorsAsync(0xFF, 0x00, 0x00); + + Log.LogInformation("Press the '+' button on port A (left)"); + + await device.PlusObservable.Any(x => x); + + Log.LogInformation("Thanks! Now press the 'red' button on port A (left)"); + + await device.RedObservable.Any(x => x); + + Log.LogInformation("Thanks! Now press the '-' button on port A (left)"); + + await device.MinusObservable.Any(x => x); + + Log.LogInformation("Thanks! Now press the '+' and '-' buttons on port A (left) at the same time"); + + await device.ButtonsObservable.Any(x => x.Minus && x.Plus); + + var disposable = device.ButtonsObservable.Subscribe(x => Log.LogWarning($"Buttons: {x.Plus} - {x.Stop} - {x.Minus}")); + var disposable2 = device.PlusObservable.Subscribe(x => Log.LogWarning($"Plus: {x}")); + var disposable3 = device.RedObservable.Subscribe(x => Log.LogWarning($"Red: {x}")); + var disposable4 = device.MinusObservable.Subscribe(x => Log.LogWarning($"Minus: {x}")); + + Log.LogInformation("Thanks! You now have 20 seconds to press any button combinations"); + + await Task.Delay(20_000); + + disposable.Dispose(); + disposable2.Dispose(); + disposable3.Dispose(); + disposable4.Dispose(); + + await twoPortHandset.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlRssi.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlRssi.cs new file mode 100644 index 0000000..915c453 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleRemoteControlRssi.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using System.Reactive.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleRemoteControlRssi : BaseExample + { + public override async Task ExecuteAsync() + { + using (var twoPortHandset = Host.FindByType()) + { + var device = twoPortHandset.RemoteControlRssi; + + await device.SetupNotificationAsync(device.ModeIndexRssi, true, deltaInterval: 1); + + var disposable = device.RssiObservable.Subscribe(x => Log.LogWarning($"RSSI: {x}")); + + await twoPortHandset.RgbLight.SetRgbColorsAsync(0x00, 0xFF, 0x00); + + Log.LogInformation("Watching RSSI changes for the next 20 seconds"); + + await Task.Delay(20_000); + + disposable.Dispose(); + + await twoPortHandset.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/Program.cs b/examples/SharpBrick.PoweredUp.Examples/Program.cs index ce54ec3..ea0319a 100644 --- a/examples/SharpBrick.PoweredUp.Examples/Program.cs +++ b/examples/SharpBrick.PoweredUp.Examples/Program.cs @@ -34,6 +34,9 @@ static async Task Main(string[] args) //example = new Example.ExampleDiscoverByType(); //example = new Example.ExampleCalibrationSteering(); //example = new Example.ExampleTechnicMediumHubGestSensor(); + //example = new Example.ExampleRemoteControlButton(); + //example = new Example.ExampleRemoteControlRssi(); + example = new Example.ExampleTechnicMediumAngularMotorGrey(); // NOTE: Examples are programmed object oriented style. Base class implements methods Configure, DiscoverAsync and ExecuteAsync to be overwriten on demand. diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index 789930e..7f3eb80 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -39,6 +39,8 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.TechnicMediumAngularMotorGrey => typeof(TechnicMediumAngularMotorGrey), DeviceType.TechnicLargeAngularMotorGrey => typeof(TechnicLargeAngularMotorGrey), DeviceType.TechnicMediumHubGestureSensor => typeof(TechnicMediumHubGestureSensor), + DeviceType.RemoteControlButton => typeof(RemoteControlButton), + DeviceType.RemoteControlRssi => typeof(RemoteControlRssi), DeviceType.TechnicMediumHubAccelerometer => typeof(TechnicMediumHubAccelerometer), DeviceType.TechnicMediumHubGyroSensor => typeof(TechnicMediumHubGyroSensor), DeviceType.TechnicMediumHubTiltSensor => typeof(TechnicMediumHubTiltSensor), @@ -59,6 +61,8 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(TechnicMediumAngularMotorGrey) => DeviceType.TechnicMediumAngularMotorGrey, nameof(TechnicLargeAngularMotorGrey) => DeviceType.TechnicLargeAngularMotorGrey, nameof(TechnicMediumHubGestureSensor) => DeviceType.TechnicMediumHubGestureSensor, + nameof(RemoteControlButton) => DeviceType.RemoteControlButton, + nameof(RemoteControlRssi) => DeviceType.RemoteControlRssi, nameof(TechnicMediumHubAccelerometer) => DeviceType.TechnicMediumHubAccelerometer, nameof(TechnicMediumHubGyroSensor) => DeviceType.TechnicMediumHubGyroSensor, nameof(TechnicMediumHubTiltSensor) => DeviceType.TechnicMediumHubTiltSensor, diff --git a/src/SharpBrick.PoweredUp/Devices/RemoteControlButton.cs b/src/SharpBrick.PoweredUp/Devices/RemoteControlButton.cs new file mode 100644 index 0000000..c5c211a --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/RemoteControlButton.cs @@ -0,0 +1,85 @@ +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; + +namespace SharpBrick.PoweredUp.Devices +{ + public class RemoteControlButton : Device, IPoweredUpDevice + { + protected sbyte PlusBitMask = 0b0000_0001; + protected sbyte RedBitMask = 0b0000_0010; + protected sbyte MinusBitMask = 0b0000_0100; + + protected SingleValueMode _keyBitFieldMode; + public byte ModeIndexBitField { get; protected set; } = 3; + + public bool Plus => IsBitMaskSet(_keyBitFieldMode.SI, PlusBitMask); + public bool Red => IsBitMaskSet(_keyBitFieldMode.SI, RedBitMask); + public bool Minus => IsBitMaskSet(_keyBitFieldMode.SI, MinusBitMask); + + public IObservable<(bool Plus, bool Stop, bool Minus)> ButtonsObservable => _keyBitFieldMode.Observable.Select(v => (IsBitMaskSet(_keyBitFieldMode.SI, PlusBitMask), IsBitMaskSet(_keyBitFieldMode.SI, RedBitMask), IsBitMaskSet(_keyBitFieldMode.SI, MinusBitMask))); + public IObservable PlusObservable => _keyBitFieldMode.Observable.Select(v => IsBitMaskSet(_keyBitFieldMode.SI, PlusBitMask)).DistinctUntilChanged(); + public IObservable RedObservable => _keyBitFieldMode.Observable.Select(v => IsBitMaskSet(_keyBitFieldMode.SI, RedBitMask)).DistinctUntilChanged(); + public IObservable MinusObservable => _keyBitFieldMode.Observable.Select(v => IsBitMaskSet(_keyBitFieldMode.SI, MinusBitMask)).DistinctUntilChanged(); + + public RemoteControlButton() + { } + + public RemoteControlButton(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _keyBitFieldMode = SingleValueMode(ModeIndexBitField); + + ObserveForPropertyChanged(PlusObservable, nameof(Plus)); + ObserveForPropertyChanged(RedObservable, nameof(Red)); + ObserveForPropertyChanged(MinusObservable, nameof(Minus)); + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-00-01-02-05-1F-00-00-00 +0C-00-44-00-00-00-52-43-4B-45-59-00 +0E-00-44-00-00-01-00-00-80-BF-00-00-80-3F +0E-00-44-00-00-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-03-00-00-80-BF-00-00-80-3F +0A-00-44-00-00-04-62-74-6E-00 +08-00-44-00-00-05-18-00 +0A-00-44-00-00-80-01-00-02-00 +0C-00-44-00-01-00-4B-45-59-41-20-00 +0E-00-44-00-01-01-00-00-80-BF-00-00-80-3F +0E-00-44-00-01-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-01-03-00-00-80-BF-00-00-80-3F +0A-00-44-00-01-04-62-74-6E-00 +08-00-44-00-01-05-70-00 +0A-00-44-00-01-80-01-00-02-00 +0C-00-44-00-02-00-4B-45-59-52-20-00 +0E-00-44-00-02-01-00-00-80-BF-00-00-80-3F +0E-00-44-00-02-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-02-03-00-00-80-BF-00-00-80-3F +0A-00-44-00-02-04-62-74-6E-00 +08-00-44-00-02-05-68-00 +0A-00-44-00-02-80-01-00-02-00 +0C-00-44-00-03-00-4B-45-59-44-20-00 +0E-00-44-00-03-01-00-00-00-00-00-00-E0-40 +0E-00-44-00-03-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-03-03-00-00-00-00-00-00-E0-40 +0A-00-44-00-03-04-62-74-6E-00 +08-00-44-00-03-05-04-00 +0A-00-44-00-03-80-01-00-01-00 +0C-00-44-00-04-00-4B-45-59-53-44-00 +0E-00-44-00-04-01-00-00-00-00-00-00-80-3F +0E-00-44-00-04-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-04-03-00-00-00-00-00-00-80-3F +0A-00-44-00-04-04-62-74-6E-00 +08-00-44-00-04-05-04-00 +0A-00-44-00-04-80-03-00-01-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + + protected bool IsBitMaskSet(sbyte bitField, sbyte bitMask) + => (bitField & bitMask) != 0; + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/RemoteControlRssi.cs b/src/SharpBrick.PoweredUp/Devices/RemoteControlRssi.cs new file mode 100644 index 0000000..86551d9 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/RemoteControlRssi.cs @@ -0,0 +1,42 @@ +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; + +namespace SharpBrick.PoweredUp.Devices +{ + public class RemoteControlRssi : Device, IPoweredUpDevice + { + protected SingleValueMode _rssiMode; + public byte ModeIndexRssi { get; protected set; } = 0; + + public sbyte Rssi => _rssiMode.SI; + public IObservable RssiObservable => _rssiMode.Observable.Select(x => x.SI); + + public RemoteControlRssi() + { } + + public RemoteControlRssi(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _rssiMode = SingleValueMode(ModeIndexRssi); + + ObserveForPropertyChanged(_rssiMode.Observable, nameof(Rssi)); + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-3C-01-02-01-01-00-00-00 +0C-00-44-3C-00-00-52-53-53-49-20-00 +0E-00-44-3C-00-01-00-00-A0-C2-00-00-F0-C1 +0E-00-44-3C-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-3C-00-03-00-00-A0-C2-00-00-F0-C1 +0A-00-44-3C-00-04-64-62-6D-00 +08-00-44-3C-00-05-50-00 +0A-00-44-3C-00-80-01-00-03-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} diff --git a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs index cbb7c92..ef5b593 100644 --- a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs +++ b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs @@ -108,6 +108,24 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 0B-00-44-32-01-04-00-00-00-00-00 08-00-44-32-01-05-00-10 0A-00-44-32-01-80-03-00-03-00 +", + + (_, _, SystemType.LegoSystem_TwoPortHandset) => @" +0B-00-43-34-01-01-02-00-00-03-00 +0C-00-44-34-00-00-43-4F-4C-20-30-00 +0E-00-44-34-00-01-00-00-00-00-00-00-20-41 +0E-00-44-34-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-34-00-03-00-00-00-00-00-00-20-41 +0A-00-44-34-00-04-69-64-78-00 +08-00-44-34-00-05-00-64 +0A-00-44-34-00-80-01-00-02-00 +0C-00-44-34-01-00-52-47-42-20-30-00 +0E-00-44-34-01-01-00-00-00-00-00-00-7F-43 +0E-00-44-34-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-34-01-03-00-00-00-00-00-00-7F-43 +0A-00-44-34-01-04-72-67-62-00 +08-00-44-34-01-05-00-50 +0A-00-44-34-01-80-03-00-03-00 ", _ => throw new NotSupportedException(), }).Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); diff --git a/src/SharpBrick.PoweredUp/Devices/Voltage.cs b/src/SharpBrick.PoweredUp/Devices/Voltage.cs index e631f23..28e1d0d 100644 --- a/src/SharpBrick.PoweredUp/Devices/Voltage.cs +++ b/src/SharpBrick.PoweredUp/Devices/Voltage.cs @@ -73,6 +73,24 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 08-00-44-3C-01-05-10-00 0A-00-44-3C-01-80-01-01-04-00 ", + (_, _, SystemType.LegoSystem_TwoPortHandset) => @" +0B-00-43-3B-01-02-02-03-00-00-00 +0C-00-44-3B-00-00-56-4C-54-20-4C-00 +0E-00-44-3B-00-01-00-00-00-00-00-00-48-45 +0E-00-44-3B-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-3B-00-03-00-00-00-00-00-00-C8-45 +0A-00-44-3B-00-04-6D-76-00-00 +08-00-44-3B-00-05-10-00 +0A-00-44-3B-00-80-01-01-04-00 +0C-00-44-3B-01-00-56-4C-54-20-53-00 +0E-00-44-3B-01-01-00-00-00-00-00-00-48-45 +0E-00-44-3B-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-3B-01-03-00-00-00-00-00-00-C8-45 +0A-00-44-3B-01-04-6D-76-00-00 +08-00-44-3B-01-05-10-00 +0A-00-44-3B-01-80-01-01-04-00 +", + _ => throw new NotSupportedException(), }).Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); } diff --git a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs index d7c26fc..ce25ccf 100644 --- a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs +++ b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs @@ -38,6 +38,7 @@ public static Type GetTypeFromSystemType(SystemType systemType) => systemType switch { SystemType.LegoSystem_TwoPortHub => typeof(TwoPortHub), + SystemType.LegoSystem_TwoPortHandset => typeof(TwoPortHandset), SystemType.LegoTechnic_MediumHub => typeof(TechnicMediumHub), _ => throw new NotSupportedException(), }; @@ -46,6 +47,7 @@ public static SystemType GetSystemTypeFromType(Type type) => type.Name switch { nameof(TwoPortHub) => SystemType.LegoSystem_TwoPortHub, + nameof(TwoPortHandset) => SystemType.LegoSystem_TwoPortHandset, nameof(TechnicMediumHub) => SystemType.LegoTechnic_MediumHub, _ => throw new NotSupportedException(), }; diff --git a/src/SharpBrick.PoweredUp/Hubs/TwoPortHandset.cs b/src/SharpBrick.PoweredUp/Hubs/TwoPortHandset.cs new file mode 100644 index 0000000..a05f553 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Hubs/TwoPortHandset.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.Logging; + +using SharpBrick.PoweredUp.Devices; +using SharpBrick.PoweredUp.Protocol; + +using System; + +namespace SharpBrick.PoweredUp +{ + public class TwoPortHandset : Hub + { + public TwoPortHandset(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider = default) + : base(protocol, deviceFactory, logger, serviceProvider, SystemType.LegoSystem_TwoPortHandset, new Port[] { + new Port(0, string.Empty, false, expectedDevice: DeviceType.RemoteControlButton), + new Port(1, string.Empty, false, expectedDevice: DeviceType.RemoteControlButton), + new Port(52, string.Empty, false, expectedDevice: DeviceType.RgbLight), + new Port(59, string.Empty, false, expectedDevice: DeviceType.Voltage), + new Port(60, string.Empty, false, expectedDevice: DeviceType.RemoteControlRssi), + }, + knownProperties: new HubProperty[] { + HubProperty.AdvertisingName, + HubProperty.Button, + HubProperty.FwVersion, + HubProperty.HwVersion, + HubProperty.Rssi, + HubProperty.BatteryVoltage, + HubProperty.BatteryType, + HubProperty.ManufacturerName, + HubProperty.RadioFirmwareVersion, + HubProperty.LegoWirelessProtocolVersion, + HubProperty.SystemTypeId, + HubProperty.HardwareNetworkId, + HubProperty.PrimaryMacAddress, + //HubProperty.SecondaryMacAddress, // unsupported on the two port handset + HubProperty.HardwareNetworkFamily, + }) + { } + + public RemoteControlButton A => Port(0).GetDevice(); + public RemoteControlButton B => Port(1).GetDevice(); + + public RgbLight RgbLight => Port(52).GetDevice(); + public Voltage Voltage => Port(59).GetDevice(); + public RemoteControlRssi RemoteControlRssi => Port(60).GetDevice(); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs index 3caf780..65e0c41 100644 --- a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs +++ b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs @@ -22,6 +22,7 @@ public static IServiceCollection AddPoweredUp(this IServiceCollection self) // hubs .AddTransient() + .AddTransient() .AddTransient() // functions From 85e8991912abea34a86fffd51469e2f6da701cec Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Tue, 27 Oct 2020 22:41:52 +0100 Subject: [PATCH 17/22] Improve human readable formatting of port info (#122) - Improve human readable formatting of port info - make sure port mode mapping is reported despite capability mismatch - annotate min/max scaling cases - add hex codes for io type, ports and hub ids #113 non-breaking --- .../Commands/DevicesList.cs | 172 ++++++++++++------ 1 file changed, 116 insertions(+), 56 deletions(-) diff --git a/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs b/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs index d91868d..53ce9b4 100644 --- a/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs +++ b/src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs @@ -38,78 +38,138 @@ public async Task ExecuteAsync(SystemType knownSystemType) Console.WriteLine($"Discover Ports Function: {discoverPorts.ReceivedMessages} / {discoverPorts.SentMessages}"); - PrettyPrintKnowledge(System.Console.Out, protocol.Knowledge); + PrettyPrintKnowledge(System.Console.Out, protocol.Knowledge, false); } - public static void PrettyPrintKnowledge(TextWriter writer, ProtocolKnowledge portKnowledge) + public static void PrettyPrintKnowledge(TextWriter writer, ProtocolKnowledge portKnowledge, bool showConfiguration = true) { string Intent(int depth) => " ".Substring(0, depth * 2); + string MinMaxDescription(float rawMin, float rawMax, float min, float max) + { + bool isPassThrough = (rawMax == max) && (rawMin == min); + bool isTranslation = (rawMax - rawMin) == (max - min); + bool isScaling = !((rawMax == max) && (rawMin == min)); + + return (isPassThrough, isTranslation, isScaling) switch + { + (true, _, _) => " (pass-through)", + (false, false, false) => string.Empty, + (false, false, true) => " (scaling)", + (false, true, false) => " (translation)", + (false, true, true) => " (scaling, translation)", + }; + } + foreach (var hub in portKnowledge.Hubs.OrderBy(h => h.HubId)) { - Console.WriteLine($"{Intent(0)}Hub: {hub.HubId}"); + Console.WriteLine($"{Intent(0)}- Hub: 0x{hub.HubId:X2} / {hub.HubId}"); foreach (var port in hub.Ports.Values.OrderBy(p => p.PortId)) { - writer.WriteLine($"{Intent(1)}Port: {port.PortId}"); - writer.WriteLine($"{Intent(2)}IOTypeId: {port.IOTypeId}"); - writer.WriteLine($"{Intent(2)}HardwareRevision: {port.HardwareRevision}"); - writer.WriteLine($"{Intent(2)}SoftwareRevision: {port.SoftwareRevision}"); + writer.WriteLine($"{Intent(1)}- Port: 0x{port.PortId:X2} / {port.PortId}"); - writer.WriteLine($"{Intent(2)}OutputCapability: {port.OutputCapability}"); - writer.WriteLine($"{Intent(2)}InputCapability: {port.InputCapability}"); - writer.WriteLine($"{Intent(2)}LogicalCombinableCapability: {port.LogicalCombinableCapability}"); - writer.WriteLine($"{Intent(2)}LogicalSynchronizableCapability: {port.LogicalSynchronizableCapability}"); + writer.WriteLine($"{Intent(2)}- IOTypeId: {port.IOTypeId} / 0x{(ushort)port.IOTypeId:X4} / {(ushort)port.IOTypeId}"); + writer.WriteLine($"{Intent(3)}Revision: SW: {port.SoftwareRevision}, HW: {port.HardwareRevision}"); + writer.Write($"{Intent(3)}Capabilities:"); + if (port.OutputCapability) + { + writer.Write(" Output"); + } + if (port.InputCapability) + { + writer.Write(" Input"); + } + if (port.LogicalCombinableCapability) + { + writer.Write(" LogicalCombinable"); + } + if (port.LogicalSynchronizableCapability) + { + writer.Write(" LogicalSynchronizable"); + } + writer.WriteLine(string.Empty); // PortInformationForPossibleModeCombinationsMessage - writer.WriteLine($"{Intent(2)}ModeCombinations: [{string.Join(", ", port.ModeCombinations.Select(x => BytesStringUtil.ToBitString(x)))}]"); + writer.WriteLine($"{Intent(3)}ModeCombinations: [{string.Join(", ", port.ModeCombinations.Select(x => BytesStringUtil.ToBitString(x)))}]"); - writer.WriteLine($"{Intent(2)}UsedCombinationIndex: {port.UsedCombinationIndex}"); - writer.WriteLine($"{Intent(2)}MultiUpdateEnabled: {port.MultiUpdateEnabled}"); - writer.WriteLine($"{Intent(2)}ConfiguredModeDataSetIndex: [{string.Join(",", port.ConfiguredModeDataSetIndex)}]"); + if (showConfiguration) + { + writer.WriteLine($"{Intent(2)}- Configuration"); + writer.WriteLine($"{Intent(3)}UsedCombinationIndex: {port.UsedCombinationIndex}"); + writer.WriteLine($"{Intent(3)}MultiUpdateEnabled: {port.MultiUpdateEnabled}"); + writer.WriteLine($"{Intent(3)}ConfiguredModeDataSetIndex: [{string.Join(",", port.ConfiguredModeDataSetIndex)}]"); + } foreach (var mode in port.Modes.Values.OrderBy(m => m.ModeIndex)) { - writer.WriteLine($"{Intent(2)}Mode: {mode.ModeIndex}"); - writer.WriteLine($"{Intent(3)}Name: {mode.Name}"); - writer.WriteLine($"{Intent(3)}IsInput: {mode.IsInput}"); - writer.WriteLine($"{Intent(3)}IsOutput: {mode.IsOutput}"); - - writer.WriteLine($"{Intent(3)}RawMin: {mode.RawMin}"); - writer.WriteLine($"{Intent(3)}RawMax: {mode.RawMax}"); - - // PortModeInformationForPctMessage - writer.WriteLine($"{Intent(3)}PctMin: {mode.PctMin}"); - writer.WriteLine($"{Intent(3)}PctMax: {mode.PctMax}"); - - // PortModeInformationForSIMessage - writer.WriteLine($"{Intent(3)}SIMin: {mode.SIMin}"); - writer.WriteLine($"{Intent(3)}SIMax: {mode.SIMax}"); - - // PortModeInformationForSymbolMessage - writer.WriteLine($"{Intent(3)}Symbol: {mode.Symbol}"); - - // PortModeInformationForMappingMessage - writer.WriteLine($"{Intent(3)}InputSupportsNull: {mode.InputSupportsNull}"); - writer.WriteLine($"{Intent(3)}InputSupportFunctionalMapping20: {mode.InputSupportFunctionalMapping20}"); - writer.WriteLine($"{Intent(3)}InputAbsolute: {mode.InputAbsolute}"); - writer.WriteLine($"{Intent(3)}InputRelative: {mode.InputRelative}"); - writer.WriteLine($"{Intent(3)}InputDiscrete: {mode.InputDiscrete}"); - - writer.WriteLine($"{Intent(3)}OutputSupportsNull: {mode.OutputSupportsNull}"); - writer.WriteLine($"{Intent(3)}OutputSupportFunctionalMapping20: {mode.OutputSupportFunctionalMapping20}"); - writer.WriteLine($"{Intent(3)}OutputAbsolute: {mode.OutputAbsolute}"); - writer.WriteLine($"{Intent(3)}OutputRelative: {mode.OutputRelative}"); - writer.WriteLine($"{Intent(3)}OutputDiscrete: {mode.OutputDiscrete}"); - - // PortModeInformationForValueFormatMessage - writer.WriteLine($"{Intent(3)}NumberOfDatasets: {mode.NumberOfDatasets}"); - writer.WriteLine($"{Intent(3)}DatasetType: {mode.DatasetType}"); - writer.WriteLine($"{Intent(3)}TotalFigures: {mode.TotalFigures}"); - writer.WriteLine($"{Intent(3)}Decimals: {mode.Decimals}"); - - // PortInputFormatSingleMessage - writer.WriteLine($"{Intent(3)}DeltaInterval: {mode.DeltaInterval}"); - writer.WriteLine($"{Intent(3)}NotificationEnabled: {mode.NotificationEnabled}"); + writer.Write($"{Intent(2)}- Mode {mode.ModeIndex}: Name: {mode.Name}, Symbol: {mode.Symbol}, Capability:"); + writer.Write(mode.IsInput ? " Input" : string.Empty); + writer.WriteLine(mode.IsOutput ? " Output" : string.Empty); + + writer.WriteLine($"{Intent(3)}- DataSet: {mode.NumberOfDatasets}x {mode.DatasetType}, TotalFigures: {mode.TotalFigures}, Decimals: {mode.Decimals}"); + + if (mode.IsInput || (!mode.IsInput && (mode.InputSupportsNull || mode.InputSupportFunctionalMapping20 || mode.InputAbsolute || mode.InputDiscrete || mode.InputRelative))) + { + writer.Write($"{Intent(4)}Input Mapping:"); + if (mode.InputSupportsNull) + { + writer.Write(" SupportsNull"); + } + if (mode.InputSupportFunctionalMapping20) + { + writer.Write(" SupportFunctionalMapping20"); + } + if (mode.InputAbsolute) + { + writer.Write(" Absolute"); + } + if (mode.InputRelative) + { + writer.Write(" Relative"); + } + if (mode.InputDiscrete) + { + writer.Write(" Discrete"); + } + writer.WriteLine(); + } + + if (mode.IsOutput || (!mode.IsOutput && (mode.OutputSupportsNull || mode.OutputSupportFunctionalMapping20 || mode.OutputAbsolute || mode.OutputDiscrete || mode.OutputRelative))) + { + writer.Write($"{Intent(4)}Output Mapping:"); + if (mode.OutputSupportsNull) + { + writer.Write(" SupportsNull"); + } + if (mode.OutputSupportFunctionalMapping20) + { + writer.Write(" SupportFunctionalMapping20"); + } + if (mode.OutputAbsolute) + { + writer.Write(" Absolute"); + } + if (mode.OutputRelative) + { + writer.Write(" Relative"); + } + if (mode.OutputDiscrete) + { + writer.Write(" Discrete"); + } + writer.WriteLine(); + } + + writer.WriteLine($"{Intent(4)}Raw Min: {mode.RawMin,7}, Max: {mode.RawMax,7}"); + writer.WriteLine($"{Intent(4)}Pct Min: {mode.PctMin,7}, Max: {mode.PctMax,7}{MinMaxDescription(mode.RawMin, mode.RawMax, mode.PctMin, mode.PctMax)}"); + writer.WriteLine($"{Intent(4)}SI Min: {mode.SIMin,7}, Max: {mode.SIMax,7}{MinMaxDescription(mode.RawMin, mode.RawMax, mode.SIMin, mode.SIMax)}"); + + if (showConfiguration) + { + writer.WriteLine($"{Intent(3)}- Configuration"); + writer.WriteLine($"{Intent(4)}NotificationEnabled: {mode.NotificationEnabled}"); + writer.WriteLine($"{Intent(4)}DeltaInterval: {mode.DeltaInterval}"); + } } } } From 252d998e4cbeaee665e6c8eb249f94586cb21057 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sun, 15 Nov 2020 18:09:01 +0100 Subject: [PATCH 18/22] Add MarioHub and related devices (#93) - Add MarioHub and related devices (Pants, TagSensor, Accelerometer/Gestures) - Add Mario Examples - Enable CombinedPortValue decoding with multiple data sets - Add capability to override default deltaInterval for notifications - Add capability to suppress percentage calculation (if e.g. data type overflow) #91 non-breaking --- .../ExampleMarioAccelerometer.cs | 30 ++++++++ .../ExampleMarioBarcode.cs | 34 +++++++++ .../ExampleMarioPants.cs | 32 ++++++++ .../SharpBrick.PoweredUp.Examples/Program.cs | 6 +- src/SharpBrick.PoweredUp/Devices/Device.cs | 21 +++++- .../Devices/DeviceFactory.cs | 8 ++ .../Devices/IPoweredUpDevice.cs | 3 + .../Devices/MarioHubAccelerometer.cs | 62 +++++++++++++++ .../Devices/MarioHubDebug.cs | 54 +++++++++++++ .../Devices/MarioHubPants.cs | 44 +++++++++++ .../Devices/MarioHubTagSensor.cs | 75 +++++++++++++++++++ src/SharpBrick.PoweredUp/Devices/Voltage.cs | 18 +++++ src/SharpBrick.PoweredUp/Enums/DeviceType.cs | 4 + .../Enums/MarioBarcode.cs | 17 +++++ src/SharpBrick.PoweredUp/Enums/MarioColor.cs | 17 +++++ .../Enums/MarioGestures.cs | 24 ++++++ src/SharpBrick.PoweredUp/Enums/MarioPants.cs | 13 ++++ src/SharpBrick.PoweredUp/Hubs/HubFactory.cs | 2 + src/SharpBrick.PoweredUp/Hubs/MarioHub.cs | 43 +++++++++++ .../IServiceCollectionExtensions.cs | 1 + .../Formatter/PortValueCombinedModeEncoder.cs | 58 ++++++++------ .../Formatter/PortValueSingleEncoder.cs | 24 +++++- .../Protocol/Knowledge/KnowledgeManager.cs | 5 ++ .../Protocol/Knowledge/PortModeInfo.cs | 4 + .../PortInputFormatCombinedModeMessage.cs | 2 +- .../PortValueCombinedModeEncoderTest.cs | 5 ++ 26 files changed, 576 insertions(+), 30 deletions(-) create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleMarioAccelerometer.cs create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleMarioBarcode.cs create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleMarioPants.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/MarioHubAccelerometer.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/MarioHubDebug.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/MarioHubPants.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/MarioHubTagSensor.cs create mode 100644 src/SharpBrick.PoweredUp/Enums/MarioBarcode.cs create mode 100644 src/SharpBrick.PoweredUp/Enums/MarioColor.cs create mode 100644 src/SharpBrick.PoweredUp/Enums/MarioGestures.cs create mode 100644 src/SharpBrick.PoweredUp/Enums/MarioPants.cs create mode 100644 src/SharpBrick.PoweredUp/Hubs/MarioHub.cs diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleMarioAccelerometer.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleMarioAccelerometer.cs new file mode 100644 index 0000000..37a0238 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleMarioAccelerometer.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleMarioAccelerometer : BaseExample + { + public override async Task ExecuteAsync() + { + using (var mario = Host.FindByType()) + { + await mario.VerifyDeploymentModelAsync(modelBuilder => modelBuilder + .AddHub(hubBuilder => { }) + ); + + var d1 = mario.Accelerometer.GestureObservable.Subscribe(x => Log.LogWarning($"Gesture: {x[0]}/{x[1]}")); + + await mario.Accelerometer.SetupNotificationAsync(mario.Accelerometer.ModeIndexGesture, true); + + await Task.Delay(20_000); + + d1.Dispose(); + + await mario.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleMarioBarcode.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleMarioBarcode.cs new file mode 100644 index 0000000..49a2281 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleMarioBarcode.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleMarioBarcode : BaseExample + { + public override async Task ExecuteAsync() + { + using (var mario = Host.FindByType()) + { + await mario.VerifyDeploymentModelAsync(modelBuilder => modelBuilder + .AddHub(hubBuilder => { }) + ); + + // for a normal scenario, a combined mode would not be needed. The tag sensor is able to detect brick colors AND the barcode on its own. The RGB scanner is neither very accurate and is scaled strangely. + var tagSensor = mario.TagSensor; + await tagSensor.SetupNotificationAsync(tagSensor.ModeIndexTag, true); + + var d1 = tagSensor.BarcodeObservable.Subscribe(x => Log.LogWarning($"Barcode: {x}")); + var d2 = tagSensor.ColorNoObservable.Subscribe(x => Log.LogWarning($"Color No: {x}")); + + await Task.Delay(20_000); + + d1.Dispose(); + d2.Dispose(); + + await mario.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleMarioPants.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleMarioPants.cs new file mode 100644 index 0000000..78f08aa --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleMarioPants.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleMarioPants : BaseExample + { + public override async Task ExecuteAsync() + { + using (var mario = Host.FindByType()) + { + await mario.VerifyDeploymentModelAsync(modelBuilder => modelBuilder + .AddHub(hubBuilder => { }) + ); + + var pants = mario.Pants; + + var d1 = pants.PantsObservable.Subscribe(x => Log.LogWarning($"Pants: {x}")); + + await pants.SetupNotificationAsync(pants.ModeIndexPants, true); + + await Task.Delay(20_000); + + d1.Dispose(); + + await mario.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/Program.cs b/examples/SharpBrick.PoweredUp.Examples/Program.cs index ea0319a..ee43bee 100644 --- a/examples/SharpBrick.PoweredUp.Examples/Program.cs +++ b/examples/SharpBrick.PoweredUp.Examples/Program.cs @@ -36,8 +36,10 @@ static async Task Main(string[] args) //example = new Example.ExampleTechnicMediumHubGestSensor(); //example = new Example.ExampleRemoteControlButton(); //example = new Example.ExampleRemoteControlRssi(); - - example = new Example.ExampleTechnicMediumAngularMotorGrey(); + //example = new Example.ExampleTechnicMediumAngularMotorGrey(); + //example = new Example.ExampleMarioBarcode(); + //example = new Example.ExampleMarioPants(); + example = new Example.ExampleMarioAccelerometer(); // NOTE: Examples are programmed object oriented style. Base class implements methods Configure, DiscoverAsync and ExecuteAsync to be overwriten on demand. await example.InitHostAndDiscoverAsync(enableTrace); diff --git a/src/SharpBrick.PoweredUp/Devices/Device.cs b/src/SharpBrick.PoweredUp/Devices/Device.cs index ddb22ca..bc9bd3a 100644 --- a/src/SharpBrick.PoweredUp/Devices/Device.cs +++ b/src/SharpBrick.PoweredUp/Devices/Device.cs @@ -69,10 +69,15 @@ protected void BuildModes() } } - public async Task SetupNotificationAsync(byte modeIndex, bool enabled, uint deltaInterval = 5) + public async Task SetupNotificationAsync(byte modeIndex, bool enabled, uint deltaInterval = uint.MaxValue) { AssertIsConnected(); + if (deltaInterval == uint.MaxValue) + { + deltaInterval = GetDefaultDeltaInterval(modeIndex); + } + await _protocol.SendMessageAsync(new PortInputFormatSetupSingleMessage() { HubId = _hubId, @@ -83,6 +88,9 @@ await _protocol.SendMessageAsync(new PortInputFormatSetupSingleMessage() }); } + protected virtual uint GetDefaultDeltaInterval(byte modeIndex) + => 5; + private byte IndexOfSupportedCombinedMode(byte[] modeIndices) { var portInfo = _protocol.Knowledge.Port(_hubId, _portId); @@ -130,7 +138,16 @@ await _protocol.SendMessageAsync(new PortInputFormatSetupCombinedModeForSetModeD SubCommand = PortInputFormatSetupCombinedSubCommand.SetModeAndDataSetCombination, CombinationIndex = combinationModeIndex, - ModeDataSets = modeIndices.Select(mode => new PortInputFormatSetupCombinedModeModeDataSet() { Mode = mode, DataSet = 0, }).ToArray(), //TODO: manage DataSet for device which has (A) multiple modes and (B) returns for a mode more than one data set (e.g. R, G, B for color). + ModeDataSets = modeIndices + .Select(m => _protocol.Knowledge.PortMode(_hubId, _portId, m)) + .SelectMany(mode => Enumerable + .Range(0, mode.NumberOfDatasets) + .Select(dataSetPosition => new PortInputFormatSetupCombinedModeModeDataSet() + { + Mode = mode.ModeIndex, + DataSet = (byte)dataSetPosition, + })) + .ToArray(), // manage DataSet for device which has (A) multiple modes and (B) returns for a mode more than one data set (e.g. R, G, B for color). }); result = true; diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index 7f3eb80..4460f9e 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -45,6 +45,10 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.TechnicMediumHubGyroSensor => typeof(TechnicMediumHubGyroSensor), DeviceType.TechnicMediumHubTiltSensor => typeof(TechnicMediumHubTiltSensor), DeviceType.TechnicMediumHubTemperatureSensor => typeof(TechnicMediumHubTemperatureSensor), + DeviceType.MarioHubAccelerometer => typeof(MarioHubAccelerometer), + DeviceType.MarioHubTagSensor => typeof(MarioHubTagSensor), + DeviceType.MarioHubPants => typeof(MarioHubPants), + DeviceType.MarioHubDebug => typeof(MarioHubDebug), DeviceType.MediumLinearMotor => typeof(MediumLinearMotor), _ => null, }; @@ -67,6 +71,10 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(TechnicMediumHubGyroSensor) => DeviceType.TechnicMediumHubGyroSensor, nameof(TechnicMediumHubTiltSensor) => DeviceType.TechnicMediumHubTiltSensor, nameof(TechnicMediumHubTemperatureSensor) => DeviceType.TechnicMediumHubTemperatureSensor, + nameof(MarioHubAccelerometer) => DeviceType.MarioHubAccelerometer, + nameof(MarioHubTagSensor) => DeviceType.MarioHubTagSensor, + nameof(MarioHubPants) => DeviceType.MarioHubPants, + nameof(MarioHubDebug) => DeviceType.MarioHubDebug, nameof(MediumLinearMotor) => DeviceType.MediumLinearMotor, _ => DeviceType.Unknown, }; diff --git a/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs b/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs index 7ed4e91..34b501c 100644 --- a/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs +++ b/src/SharpBrick.PoweredUp/Devices/IPoweredUpDevice.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using SharpBrick.PoweredUp.Protocol.Knowledge; namespace SharpBrick.PoweredUp { @@ -7,5 +8,7 @@ public interface IPoweredUpDevice { bool IsConnected { get; } IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType); + void ExtendPortMode(PortModeInfo portModeInfo) + { } } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/MarioHubAccelerometer.cs b/src/SharpBrick.PoweredUp/Devices/MarioHubAccelerometer.cs new file mode 100644 index 0000000..fd34014 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/MarioHubAccelerometer.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class MarioHubAccelerometer : Device, IPoweredUpDevice + { + protected MultiValueMode _rawMode; + protected MultiValueMode _gestMode; + + public byte ModeIndexRaw { get; protected set; } = 0; + public byte ModeIndexGesture { get; protected set; } = 1; + + public IObservable RawObservable => _rawMode.Observable.Select(x => x.SI); + public IObservable GestureObservable => _gestMode.Observable + .Where(x => x.SI[0] != 0 && x.SI[1] != 0) // filter out none values + .Select(x => x.SI.Cast().ToArray()); + + public MarioHubAccelerometer() + { } + + public MarioHubAccelerometer(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _rawMode = MultiValueMode(ModeIndexRaw); + _gestMode = MultiValueMode(ModeIndexGesture); + + //ObserveForPropertyChanged(_rawMode.Observable, nameof(Coins)); + } + + protected override uint GetDefaultDeltaInterval(byte modeIndex) + => modeIndex switch + { + 1 => 1, + _ => 5, + }; + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-00-01-06-02-03-00-00-00 +07-00-43-00-02-03-00 +11-00-44-00-00-00-52-41-57-00-00-00-00-00-00-00-00 +0E-00-44-00-00-01-00-00-00-00-00-00-C8-42 +0E-00-44-00-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-00-03-00-00-00-00-00-00-C8-42 +0A-00-44-00-00-04-63-6E-74-00 +08-00-44-00-00-05-84-00 +0A-00-44-00-00-80-03-00-03-00 +11-00-44-00-01-00-47-45-53-54-00-00-00-00-00-00-00 +0E-00-44-00-01-01-00-00-00-00-00-00-C8-42 +0E-00-44-00-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-01-03-00-00-00-00-00-00-C8-42 +0A-00-44-00-01-04-63-6E-74-00 +08-00-44-00-01-05-84-00 +0A-00-44-00-01-80-02-01-03-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/MarioHubDebug.cs b/src/SharpBrick.PoweredUp/Devices/MarioHubDebug.cs new file mode 100644 index 0000000..49c31c9 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/MarioHubDebug.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class MarioHubDebug : Device, IPoweredUpDevice + { + public MarioHubDebug() + { } + + public MarioHubDebug(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-03-01-02-04-0F-00-00-00 +05-00-43-03-02 +11-00-44-03-00-00-43-48-41-4C-00-00-00-00-00-00-00 +0E-00-44-03-00-01-00-00-00-00-00-FF-7F-47 +0E-00-44-03-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-03-00-03-00-00-00-00-00-FF-7F-47 +0A-00-44-03-00-04-6E-61-00-00 +08-00-44-03-00-05-84-00 +0A-00-44-03-00-80-02-01-03-00 +11-00-44-03-01-00-56-45-52-53-00-00-00-00-00-00-00 +0E-00-44-03-01-01-00-00-00-00-00-00-7F-43 +0E-00-44-03-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-03-01-03-00-00-00-00-00-00-7F-43 +0A-00-44-03-01-04-6E-61-00-00 +08-00-44-03-01-05-84-00 +0A-00-44-03-01-80-04-02-0A-00 +11-00-44-03-02-00-45-56-45-4E-54-53-00-00-00-00-00 +0E-00-44-03-02-01-00-00-00-00-00-FF-7F-47 +0E-00-44-03-02-02-00-00-00-00-00-00-C8-42 +0E-00-44-03-02-03-00-00-00-00-00-FF-7F-47 +0A-00-44-03-02-04-6E-61-00-00 +08-00-44-03-02-05-84-00 +0A-00-44-03-02-80-02-01-0A-00 +11-00-44-03-03-00-44-45-42-55-47-00-00-00-00-00-00 +0E-00-44-03-03-01-00-00-00-00-00-FF-7F-47 +0E-00-44-03-03-02-00-00-00-00-00-00-C8-42 +0E-00-44-03-03-03-00-00-00-00-00-FF-7F-47 +0A-00-44-03-03-04-6E-61-00-00 +08-00-44-03-03-05-84-00 +0A-00-44-03-03-80-04-02-0A-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/MarioHubPants.cs b/src/SharpBrick.PoweredUp/Devices/MarioHubPants.cs new file mode 100644 index 0000000..dcb11e8 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/MarioHubPants.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + // https://github.com/bricklife/LEGO-Mario-Reveng/blob/master/IOType-0x4a.md + public class MarioHubPants : Device, IPoweredUpDevice + { + protected SingleValueMode _pantsMode; + + public byte ModeIndexPants { get; protected set; } = 0; + + public MarioPants Pants => (MarioPants)_pantsMode.SI; + public IObservable PantsObservable => _pantsMode.Observable.Select(x => (MarioPants)x.SI); + + public MarioHubPants() + { } + + public MarioHubPants(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _pantsMode = SingleValueMode(ModeIndexPants); + + ObserveForPropertyChanged(_pantsMode.Observable, nameof(PantsObservable)); + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-02-01-02-01-01-00-00-00 +05-00-43-02-02 +11-00-44-02-00-00-50-41-4E-54-00-00-00-00-00-00-00 +0E-00-44-02-00-01-00-00-00-00-00-00-7C-42 +0E-00-44-02-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-02-00-03-00-00-00-00-00-00-7C-42 +0A-00-44-02-00-04-69-64-78-00 +08-00-44-02-00-05-84-00 +0A-00-44-02-00-80-01-00-03-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/MarioHubTagSensor.cs b/src/SharpBrick.PoweredUp/Devices/MarioHubTagSensor.cs new file mode 100644 index 0000000..81a15c0 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/MarioHubTagSensor.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Protocol.Knowledge; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class MarioHubTagSensor : Device, IPoweredUpDevice + { + protected MultiValueMode _tagMode; + protected MultiValueMode _rgbMode; + + public byte ModeIndexTag { get; protected set; } = 0; + public byte ModeIndexRgb { get; protected set; } = 1; + + + public MarioBarcode Barcode => (MarioBarcode)_tagMode.SI[0]; + public MarioColor ColorNo => (MarioColor)_tagMode.SI[1]; + public IObservable BarcodeObservable => _tagMode.Observable.Where(x => x.SI[0] != 0).Select(x => (MarioBarcode)x.SI[0]); + public IObservable ColorNoObservable => _tagMode.Observable.Where(x => x.SI[1] != 0).Select(x => (MarioColor)x.SI[1]); + + public (byte red, byte green, byte blue) RgbColor => ((byte)_rgbMode.SI[0], (byte)_rgbMode.SI[1], (byte)_rgbMode.SI[2]); + public IObservable<(byte red, byte green, byte blue)> RgbColorObservable => _rgbMode.Observable.Select(x => ((byte)x.SI[0], (byte)x.SI[1], (byte)x.SI[2])); + + public MarioHubTagSensor() + { } + + public MarioHubTagSensor(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _tagMode = MultiValueMode(ModeIndexTag); + _rgbMode = MultiValueMode(ModeIndexRgb); + + ObserveForPropertyChanged(_tagMode.Observable, nameof(Barcode)); + ObserveForPropertyChanged(_tagMode.Observable, nameof(ColorNo)); + ObserveForPropertyChanged(_rgbMode.Observable, nameof(RgbColor)); + } + + protected override uint GetDefaultDeltaInterval(byte modeIndex) + => modeIndex switch + { + 0x00 => 3, + 0x01 => 5, + _ => 5, + }; + + public void ExtendPortMode(PortModeInfo modeInfo) + { + modeInfo.DisablePercentage = true; + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-01-01-06-02-03-00-00-00 +07-00-43-01-02-03-00 +11-00-44-01-00-00-54-41-47-00-00-00-00-00-00-00-00 +0E-00-44-01-00-01-00-00-00-00-00-00-20-41 +0E-00-44-01-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-01-00-03-00-00-00-00-00-00-20-41 +0A-00-44-01-00-04-69-64-78-00 +08-00-44-01-00-05-84-00 +0A-00-44-01-00-80-02-01-03-00 +11-00-44-01-01-00-52-47-42-00-00-00-00-00-00-00-00 +0E-00-44-01-01-01-00-00-00-00-00-00-20-41 +0E-00-44-01-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-01-01-03-00-00-00-00-00-00-20-41 +0A-00-44-01-01-04-72-61-77-00 +08-00-44-01-01-05-84-00 +0A-00-44-01-01-80-03-00-03-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/Voltage.cs b/src/SharpBrick.PoweredUp/Devices/Voltage.cs index 28e1d0d..9d00d57 100644 --- a/src/SharpBrick.PoweredUp/Devices/Voltage.cs +++ b/src/SharpBrick.PoweredUp/Devices/Voltage.cs @@ -54,6 +54,24 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 0A-00-44-3C-01-04-6D-56-00-00 08-00-44-3C-01-05-10-00 0A-00-44-3C-01-80-01-01-04-00 +", + (_, _, SystemType.LegoSystem_Mario) => @" +0B-00-43-06-01-02-02-03-00-00-00 +05-00-43-06-02 +11-00-44-06-00-00-56-4C-54-20-4C-00-00-00-00-00-00 +0E-00-44-06-00-01-00-00-00-00-00-10-29-45 +0E-00-44-06-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-06-00-03-00-00-00-00-00-40-4E-45 +0A-00-44-06-00-04-6D-56-00-00 +08-00-44-06-00-05-10-00 +0A-00-44-06-00-80-01-01-04-00 +11-00-44-06-01-00-56-4C-54-20-53-00-00-00-00-00-00 +0E-00-44-06-01-01-00-00-00-00-00-10-29-45 +0E-00-44-06-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-06-01-03-00-00-00-00-00-40-4E-45 +0A-00-44-06-01-04-6D-56-00-00 +08-00-44-06-01-05-10-00 +0A-00-44-06-01-80-01-01-04-00 ", (_, _, SystemType.LegoSystem_TwoPortHub) => @" 0B-00-43-3C-01-02-02-03-00-00-00 diff --git a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs index 3f88bfa..8b6c994 100644 --- a/src/SharpBrick.PoweredUp/Enums/DeviceType.cs +++ b/src/SharpBrick.PoweredUp/Enums/DeviceType.cs @@ -37,6 +37,10 @@ public enum DeviceType : ushort TechnicColorSensor = 0x003D, // UNSPECED, TECHNIC_COLOR_SENSOR (Spike Prime) TechnicDistanceSensor = 0x003E, // UNSPECED, TECHNIC_DISTANCE_SENSOR (Spike Prime) TechnicForceSensor = 0x003F, // UNSPECED, TECHNIC_FORCE_SENSOR (Spike Prime) + MarioHubAccelerometer = 0x0047, // UNSPCED, https://github.com/bricklife/LEGO-Mario-Reveng (Lego Mario) + MarioHubTagSensor = 0x049, // UNSPCED, https://github.com/bricklife/LEGO-Mario-Reveng (Lego Mario) + MarioHubPants = 0x004A, // UNSPCED, https://github.com/bricklife/LEGO-Mario-Reveng (Lego Mario) + MarioHubDebug = 0x0046, // UNSPCED, https://github.com/bricklife/LEGO-Mario-Reveng (Lego Mario) TechnicMediumAngularMotorGrey = 0x004B, // UNSPECED, TECHNIC_MEDIUM_ANGULAR_MOTOR_GREY (Control+) TechnicLargeAngularMotorGrey = 0x004C, // UNSPECED, TECHNIC_LARGE_ANGULAR_MOTOR_GREY (Control+) } diff --git a/src/SharpBrick.PoweredUp/Enums/MarioBarcode.cs b/src/SharpBrick.PoweredUp/Enums/MarioBarcode.cs new file mode 100644 index 0000000..3725dfa --- /dev/null +++ b/src/SharpBrick.PoweredUp/Enums/MarioBarcode.cs @@ -0,0 +1,17 @@ +namespace SharpBrick.PoweredUp +{ + public enum MarioBarcode : ushort + { + None = 0xFFFF, + Goomba = 0x0002, + Refresh = 0x0014, + Question = 0x0029, + Cloud = 0x002E, + Bat = 0x0079, + Star = 0x007B, + KingBoo = 0x0088, + BowserJr = 0x0099, + BowserGoal = 0x00B7, + Pipe = 0x00B8, + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Enums/MarioColor.cs b/src/SharpBrick.PoweredUp/Enums/MarioColor.cs new file mode 100644 index 0000000..fe3b5dd --- /dev/null +++ b/src/SharpBrick.PoweredUp/Enums/MarioColor.cs @@ -0,0 +1,17 @@ +namespace SharpBrick.PoweredUp +{ + public enum MarioColor : ushort + { + None = 0xFFFF, + White = 0x0013, + Red = 0x0015, + Blue = 0x0017, + Yellow = 0x0018, + Black = 0x001A, + Green = 0x0025, + Brown = 0x006A, + Unknown1 = 0x010C, + Unknown2 = 0x0138, + Cyan = 0x0142, + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Enums/MarioGestures.cs b/src/SharpBrick.PoweredUp/Enums/MarioGestures.cs new file mode 100644 index 0000000..4b23498 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Enums/MarioGestures.cs @@ -0,0 +1,24 @@ +using System; + +namespace SharpBrick.PoweredUp +{ + [Flags] + public enum MarioGestures : ushort + { + None = 0b0000_0000_0000_0000, + Bump = 0b0000_0000_0000_0001, + Gesture2 = 0b0000_0000_0000_0010, + Gesture4 = 0b0000_0000_0000_0100, + Shake = 0b0000_0000_0001_0000, + Gesture64 = 0b0000_0000_0100_0000, + Gesture128 = 0b0000_0000_1000_0000, + Turning = 0b0000_0001_0000_0000, + FastMove = 0b0000_0010_0000_0000, + Translation = 0b0000_0100_0000_0000, + HighFallCrash = 0b0000_1000_0000_0000, + DirectionChange = 0b0001_0000_0000_0000, + Reverse = 0b0010_0000_0000_0000, + Gesture16384 = 0b0100_0000_0000_0000, + Jump = 0b1000_0000_0000_0000, + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Enums/MarioPants.cs b/src/SharpBrick.PoweredUp/Enums/MarioPants.cs new file mode 100644 index 0000000..9bb3549 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Enums/MarioPants.cs @@ -0,0 +1,13 @@ +namespace SharpBrick.PoweredUp +{ + // UNSPECED: https://github.com/bricklife/LEGO-Mario-Reveng/blob/master/IOType-0x4a.md + public enum MarioPants : sbyte + { + None = 0b00_0000, + Propeller = 0b00_1100, + Cat = 0b01_0001, + Fire = 0b01_0010, + Normal = 0b10_0001, + Builder = 0b10_0010, + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs index ce25ccf..45bcc02 100644 --- a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs +++ b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs @@ -40,6 +40,7 @@ public static Type GetTypeFromSystemType(SystemType systemType) SystemType.LegoSystem_TwoPortHub => typeof(TwoPortHub), SystemType.LegoSystem_TwoPortHandset => typeof(TwoPortHandset), SystemType.LegoTechnic_MediumHub => typeof(TechnicMediumHub), + SystemType.LegoSystem_Mario => typeof(MarioHub), _ => throw new NotSupportedException(), }; @@ -49,6 +50,7 @@ public static SystemType GetSystemTypeFromType(Type type) nameof(TwoPortHub) => SystemType.LegoSystem_TwoPortHub, nameof(TwoPortHandset) => SystemType.LegoSystem_TwoPortHandset, nameof(TechnicMediumHub) => SystemType.LegoTechnic_MediumHub, + nameof(MarioHub) => SystemType.LegoSystem_Mario, _ => throw new NotSupportedException(), }; } diff --git a/src/SharpBrick.PoweredUp/Hubs/MarioHub.cs b/src/SharpBrick.PoweredUp/Hubs/MarioHub.cs new file mode 100644 index 0000000..2bd3ab7 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Hubs/MarioHub.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp.Devices; +using SharpBrick.PoweredUp.Protocol; + +namespace SharpBrick.PoweredUp +{ + public class MarioHub : Hub + { + public MarioHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider = default) + : base(protocol, deviceFactory, logger, serviceProvider, SystemType.LegoSystem_Mario, new Port[] { + new Port(0, string.Empty, false, expectedDevice: DeviceType.MarioHubAccelerometer), + new Port(1, string.Empty, false, expectedDevice: DeviceType.MarioHubTagSensor), + new Port(2, string.Empty, false, expectedDevice: DeviceType.MarioHubPants), + new Port(3, string.Empty, false, expectedDevice: DeviceType.MarioHubDebug), + new Port(6, string.Empty, false, expectedDevice: DeviceType.Voltage), + }, + knownProperties: new HubProperty[] { + HubProperty.AdvertisingName, + HubProperty.Button, + HubProperty.FwVersion, + HubProperty.HwVersion, + HubProperty.Rssi, + HubProperty.BatteryVoltage, + HubProperty.BatteryType, + HubProperty.ManufacturerName, + HubProperty.RadioFirmwareVersion, + HubProperty.LegoWirelessProtocolVersion, + //HubProperty.SystemTypeId, // reports "2" which does not match 0x43 + //HubProperty.HardwareNetworkId, // Throws command not recognized error for MarioHub + HubProperty.PrimaryMacAddress, + HubProperty.SecondaryMacAddress, + //HubProperty.HardwareNetworkFamily, // may throw command not recognized error for MarioHub + }) + { } + + public MarioHubAccelerometer Accelerometer => Port(0).GetDevice(); + public MarioHubTagSensor TagSensor => Port(1).GetDevice(); + public MarioHubPants Pants => Port(2).GetDevice(); + public MarioHubDebug Debug => Port(3).GetDevice(); + public Voltage Voltage => Port(6).GetDevice(); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs index 65e0c41..21a24e6 100644 --- a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs +++ b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs @@ -24,6 +24,7 @@ public static IServiceCollection AddPoweredUp(this IServiceCollection self) .AddTransient() .AddTransient() .AddTransient() + .AddTransient() // functions .AddTransient() diff --git a/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueCombinedModeEncoder.cs b/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueCombinedModeEncoder.cs index be2b4e5..408df40 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueCombinedModeEncoder.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueCombinedModeEncoder.cs @@ -20,46 +20,62 @@ public ushort CalculateContentLength(LegoWirelessMessage message) public LegoWirelessMessage Decode(byte hubId, in Span data) { - var result = new List(); - var port = data[0]; var modePointers = BitConverter.ToUInt16(new byte[] { data[2], data[1] }); - var modeDataSetIndices = Enumerable.Range(0, 16).Select(pos => (modePointers & (0x01 << pos)) > 0 ? pos : -1).Where(x => x >= 0).ToArray(); + var portInfo = _knowledge.Port(hubId, port); + + // each combined value mode message specifies which mode/dataset is transferred (a data set is a part of a sensor input, e.g. green of RGB) + // the modePointers reference to the requested mode/dataset + var modeDataSetOfMessage = Enumerable.Range(0, 16) // iterate each position in ushort bitmask + .Select(pos => (modePointers & (0x01 << pos)) > 0 ? pos : -1) // detect which mode/dataset is pointed + .Where(x => x >= 0) // sort out unaffected ones + .Select(i => portInfo.RequestedCombinedModeDataSets[i]) // get modate/dataset defintions requested + .ToArray(); - var currentIndexIndex = 0; + var influencedModes = modeDataSetOfMessage.Select(md => md.Mode).Distinct().Select(m => _knowledge.PortMode(hubId, port, m)); + // create a buffer for each requested mode + var dataBuffers = influencedModes + .ToDictionary(pmi => pmi.ModeIndex, pmi => new byte[pmi.NumberOfDatasets * PortValueSingleEncoder.GetLengthOfDataType(pmi)]); + + var currentDataSetInMessageIndex = 0; var remainingSlice = data.Slice(3); while (remainingSlice.Length > 0) { - var portInfo = _knowledge.Port(hubId, port); - - var mode = portInfo.RequestedCombinedModeDataSets[modeDataSetIndices[currentIndexIndex]].Mode; - - //var mode = (byte)modeDataSetIndices[currentIndexIndex]; - - var modeInfo = _knowledge.PortMode(hubId, port, mode); + // retrieve which mode and data set is currently sliced + var modeDataSet = modeDataSetOfMessage[currentDataSetInMessageIndex]; + currentDataSetInMessageIndex++; + // retrieve mode for data size length + var modeInfo = _knowledge.PortMode(hubId, port, modeDataSet.Mode); int lengthOfDataType = PortValueSingleEncoder.GetLengthOfDataType(modeInfo); - var dataSlice = remainingSlice.Slice(0, modeInfo.NumberOfDatasets * lengthOfDataType); - - var value = PortValueSingleEncoder.CreatPortValueData(modeInfo, dataSlice); + // cut data based on data type + var dataSlice = remainingSlice.Slice(0, lengthOfDataType); - value.PortId = port; - value.ModeIndex = mode; - value.DataType = modeInfo.DatasetType; + // copy data into buffer position + dataSlice.CopyTo(dataBuffers[modeInfo.ModeIndex].AsSpan().Slice(modeDataSet.DataSet * lengthOfDataType)); - result.Add(value); - remainingSlice = remainingSlice.Slice(lengthOfDataType * modeInfo.NumberOfDatasets); - currentIndexIndex++; + remainingSlice = remainingSlice.Slice(lengthOfDataType); } return new PortValueCombinedModeMessage() { PortId = port, - Data = result.ToArray(), + Data = influencedModes + .Select(mode => + { + var value = PortValueSingleEncoder.CreatPortValueData(mode, dataBuffers[mode.ModeIndex]); + + value.PortId = mode.PortId; + value.ModeIndex = mode.ModeIndex; + value.DataType = mode.DatasetType; + + return value; + }) + .ToArray(), }; } diff --git a/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs b/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs index 601701b..8ebd627 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs @@ -79,7 +79,11 @@ internal static PortValueData CreatePortValueDataSByte(PortModeInfo modeI var rawValues = MemoryMarshal.Cast(dataSlice).ToArray(); var siValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.SIMin, modeInfo.SIMax)).Select(f => Convert.ToSByte(f)).ToArray(); - var pctValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToSByte(f)).ToArray(); + var pctValues = modeInfo.DisablePercentage switch + { + false => rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToSByte(f)).ToArray(), + true => new sbyte[rawValues.Length], + }; return new PortValueData() { @@ -94,7 +98,11 @@ internal static PortValueData CreatePortValueDataInt16(PortModeInfo modeI var rawValues = MemoryMarshal.Cast(dataSlice).ToArray(); var siValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.SIMin, modeInfo.SIMax)).Select(f => Convert.ToInt16(f)).ToArray(); - var pctValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToInt16(f)).ToArray(); + var pctValues = modeInfo.DisablePercentage switch + { + false => rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToInt16(f)).ToArray(), + true => new short[rawValues.Length], + }; return new PortValueData() { @@ -109,7 +117,11 @@ internal static PortValueData CreatePortValueDataInt32(PortModeInfo modeInf var rawValues = MemoryMarshal.Cast(dataSlice).ToArray(); var siValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.SIMin, modeInfo.SIMax)).Select(f => Convert.ToInt32(f)).ToArray(); - var pctValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToInt32(f)).ToArray(); + var pctValues = modeInfo.DisablePercentage switch + { + false => rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToInt32(f)).ToArray(), + true => new int[rawValues.Length], + }; return new PortValueData() { @@ -124,7 +136,11 @@ internal static PortValueData CreatePortValueDataSingle(PortModeInfo mode var rawValues = MemoryMarshal.Cast(dataSlice).ToArray(); var siValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.SIMin, modeInfo.SIMax)).ToArray(); - var pctValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).ToArray(); + var pctValues = modeInfo.DisablePercentage switch + { + false => rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).ToArray(), + true => new float[rawValues.Length], + }; return new PortValueData() { diff --git a/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs b/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs index a209d6e..5303c1f 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Knowledge/KnowledgeManager.cs @@ -201,6 +201,11 @@ private static void AddCachePortAndPortModeInformation(DeviceType type, Version } ApplyStaticProtocolKnowledge(message, knowledge); + + if (message is PortModeInformationMessage pmim2 && pmim2.InformationType == PortModeInformationType.Name) + { + device.ExtendPortMode(knowledge.PortMode(pmim2.HubId, pmim2.PortId, pmim2.Mode)); + } } } } diff --git a/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs b/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs index 2696e81..75cc2de 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs @@ -52,5 +52,9 @@ public class PortModeInfo // PortInputFormatSingleMessage public uint DeltaInterval { get; set; } public bool NotificationEnabled { get; set; } + + + // Additional Settings + public bool DisablePercentage { get; set; } = false; } } \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Protocol/Messages/PortInputFormatCombinedModeMessage.cs b/src/SharpBrick.PoweredUp/Protocol/Messages/PortInputFormatCombinedModeMessage.cs index b2d145c..aa13560 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Messages/PortInputFormatCombinedModeMessage.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Messages/PortInputFormatCombinedModeMessage.cs @@ -8,6 +8,6 @@ public class PortInputFormatCombinedModeMessage : LegoWirelessMessage public int[] ConfiguredModeDataSetIndex { get; set; } public override string ToString() - => $"Port Input Format (Combined Mode) - Port {HubId}/{PortId} UsedCombinationIndex {UsedCombinationIndex} Enabled {MultiUpdateEnabled} Configured Modes {string.Join(",", ConfiguredModeDataSetIndex)}"; + => $"Port Input Format (Combined Mode) - Port {HubId}/{PortId} UsedCombinationIndex {UsedCombinationIndex} Enabled {MultiUpdateEnabled} Confirmed Modes/DataSet Indices {string.Join(",", ConfiguredModeDataSetIndex)}"; } } \ No newline at end of file diff --git a/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/PortValueCombinedModeEncoderTest.cs b/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/PortValueCombinedModeEncoderTest.cs index a5945ca..729298a 100644 --- a/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/PortValueCombinedModeEncoderTest.cs +++ b/test/SharpBrick.PoweredUp.Test/Protocol/Formatter/PortValueCombinedModeEncoderTest.cs @@ -63,5 +63,10 @@ public void PortValueCombinedModeEncoder_Decode(string dataAsString, byte expect }).ToArray()); } + + // TODO: Mario Port 1 (Scanner), Mode 1 & 0 + // 07-00-46-01-00-02-0B + // 08-00-46-01-00-06-04-02 + // 0D-00-46-01-00-1F-09-07-06-00-00-00-00 } } \ No newline at end of file From 3c1e0cfe6b431ebbd9e6002aa3e746a835f5628f Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sun, 15 Nov 2020 18:18:14 +0100 Subject: [PATCH 19/22] Extendend README with MarioHub (#123) #91 --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2c354a5..10771f0 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ DI Container Elements - [X] Two Port Hub (88009) - [X] Two Port Handset (88010) - [X] Technic Medium Hub (88012) + - [X] MarioHub (71360) - .. other hubs depend on availability of hardware / contributions - Devices - [X] Technic Medium Hub (88012) - Rgb Light @@ -261,6 +262,10 @@ DI Container Elements - [X] Hub (88009) - Rgb Light - [X] Hub (88009) - Current - [X] Hub (88009) - Voltage + - [X] Mario Hub (71360) - Accelerometer (Raw & Gesture) (⚠ Usable but Gesture mapping is a rough draft) + - [X] Mario Hub (71360) - TagSensor (Barcode & RGB) + - [X] Mario Hub (71360) - Pants + - [ ] Mario Hub (71360) - Debug - [X] Medium Linear Motor (88008) - [X] Remote Control Button (88010) - [X] Remote Control RSSI (88010) From 14e16880cebaf2fef97f8bb31de5579a0fff9ad9 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Wed, 18 Nov 2020 23:10:47 +0100 Subject: [PATCH 20/22] Add DuploTrainBaseHub (#125) * Add DuploTrainBaseHub - incl. Speaker, Motor, Voltage, RgbLight - incl. Speedometer, ColorSensor - Disable Scaling Support (used for Speedometer Count) #124 non-breaking --- README.md | 16 +- .../ExampleDuploTrainBase.cs | 71 +++++++++ .../SharpBrick.PoweredUp.Examples/Program.cs | 3 +- .../Devices/DeviceFactory.cs | 8 + .../Devices/DuploTrainBaseColorSensor.cs | 92 +++++++++++ .../Devices/DuploTrainBaseMotor.cs | 147 ++++++++++++++++++ .../Devices/DuploTrainBaseSpeaker.cs | 64 ++++++++ .../Devices/DuploTrainBaseSpeedometer.cs | 75 +++++++++ src/SharpBrick.PoweredUp/Devices/RgbLight.cs | 18 +++ src/SharpBrick.PoweredUp/Devices/Voltage.cs | 18 +++ .../Enums/DuploColorTag.cs | 12 ++ .../Enums/DuploTrainBaseSound.cs | 11 ++ .../Hubs/DuploTrainBaseHub.cs | 45 ++++++ src/SharpBrick.PoweredUp/Hubs/HubFactory.cs | 2 + .../IServiceCollectionExtensions.cs | 1 + .../Formatter/PortValueSingleEncoder.cs | 14 +- .../Protocol/Knowledge/PortModeInfo.cs | 1 + 17 files changed, 588 insertions(+), 10 deletions(-) create mode 100644 examples/SharpBrick.PoweredUp.Examples/ExampleDuploTrainBase.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/DuploTrainBaseColorSensor.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/DuploTrainBaseMotor.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeaker.cs create mode 100644 src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeedometer.cs create mode 100644 src/SharpBrick.PoweredUp/Enums/DuploColorTag.cs create mode 100644 src/SharpBrick.PoweredUp/Enums/DuploTrainBaseSound.cs create mode 100644 src/SharpBrick.PoweredUp/Hubs/DuploTrainBaseHub.cs diff --git a/README.md b/README.md index 10771f0..af7b08a 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,8 @@ DI Container Elements - [X] Two Port Hub (88009) - [X] Two Port Handset (88010) - [X] Technic Medium Hub (88012) - - [X] MarioHub (71360) + - [X] MarioHub (set 71360) + - [X] Duplo Train Base (set 10874) - .. other hubs depend on availability of hardware / contributions - Devices - [X] Technic Medium Hub (88012) - Rgb Light @@ -262,10 +263,15 @@ DI Container Elements - [X] Hub (88009) - Rgb Light - [X] Hub (88009) - Current - [X] Hub (88009) - Voltage - - [X] Mario Hub (71360) - Accelerometer (Raw & Gesture) (⚠ Usable but Gesture mapping is a rough draft) - - [X] Mario Hub (71360) - TagSensor (Barcode & RGB) - - [X] Mario Hub (71360) - Pants - - [ ] Mario Hub (71360) - Debug + - [X] Mario Hub (set 71360) - Accelerometer (Raw & Gesture) (⚠ Usable but Gesture mapping is a rough draft) + - [X] Mario Hub (set 71360) - TagSensor (Barcode & RGB) + - [X] Mario Hub (set 71360) - Pants + - [ ] Mario Hub (set 71360) - Debug + - [X] Duplo Train Base (set 10874) - Motor + - [X] Duplo Train Base (set 10874) - Speaker + - [X] Duplo Train Base (set 10874) - Rgb Light + - [X] Duplo Train Base (set 10874) - ColorSensor + - [X] Duplo Train Base (set 10874) - Speedometer - [X] Medium Linear Motor (88008) - [X] Remote Control Button (88010) - [X] Remote Control RSSI (88010) diff --git a/examples/SharpBrick.PoweredUp.Examples/ExampleDuploTrainBase.cs b/examples/SharpBrick.PoweredUp.Examples/ExampleDuploTrainBase.cs new file mode 100644 index 0000000..bf8a762 --- /dev/null +++ b/examples/SharpBrick.PoweredUp.Examples/ExampleDuploTrainBase.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp; + +namespace Example +{ + public class ExampleDuploTrainBase : BaseExample + { + public override async Task ExecuteAsync() + { + using (var train = Host.FindByType()) + { + await train.VerifyDeploymentModelAsync(modelBuilder => modelBuilder + .AddHub(hubBuilder => { }) + ); + + using var d1 = train.Voltage.VoltageSObservable.Subscribe(x => Log.LogWarning($"Voltage: {x.Pct}% / {x.SI}")); + using var d2 = train.Motor.OnSecondsObservable.Subscribe(x => Log.LogWarning($"Seconds: {x}")); + using var d3 = train.Speedometer.SpeedObservable.Subscribe(x => Log.LogWarning($"Speed: {x.Pct}% / {x.SI}")); + using var d4 = train.Speedometer.CountObservable.Subscribe(x => Log.LogWarning($"Count: {x}")); + using var d5 = train.ColorSensor.ColorObservable.Subscribe(x => Log.LogWarning($"Color: {x}")); + using var d6 = train.ColorSensor.ColorTagObservable.Subscribe(x => Log.LogWarning($"Color Tag: {x}")); // does not work + using var d7 = train.ColorSensor.ReflectionObservable.Subscribe(x => Log.LogWarning($"Reflection: {x}")); + using var d8 = train.ColorSensor.RgbObservable.Subscribe(x => Log.LogWarning($"RGB {x.red}/{x.green}/{x.blue}")); + + await train.Voltage.SetupNotificationAsync(train.Voltage.ModeIndexVoltageS, true); + + // Motor: motor can either be queried for seconds active OR be instructed to run + //await train.Motor.SetupNotificationAsync(train.Motor.ModeIndexOnSec, false); + + await train.Speedometer.TryLockDeviceForCombinedModeNotificationSetupAsync(train.Speedometer.ModeIndexSpeed, train.Speedometer.ModeIndexCount); + await train.Speedometer.SetupNotificationAsync(train.Speedometer.ModeIndexSpeed, true, deltaInterval: 1); + await train.Speedometer.SetupNotificationAsync(train.Speedometer.ModeIndexCount, true, deltaInterval: 1); + await train.Speedometer.UnlockFromCombinedModeNotificationSetupAsync(true); + + // ColorSensor: either this combination + // await train.ColorSensor.TryLockDeviceForCombinedModeNotificationSetupAsync(train.ColorSensor.ModeIndexColor, train.ColorSensor.ModeIndexReflection, train.ColorSensor.ModeIndexRgb); + // await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexColor, true, deltaInterval: 1); + // await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexReflection, true, deltaInterval: 1); + // await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexRgb, true, deltaInterval: 1); + // await train.ColorSensor.UnlockFromCombinedModeNotificationSetupAsync(true); + + // ColorSensor: or standalone + await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexColorTag, true, deltaInterval: 1); + + await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.Horn); + await train.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Red); + await Task.Delay(1_000); + await train.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Yellow); + await Task.Delay(1_000); + await train.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Green); + await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.StationDeparture); + + await train.Motor.StartPowerAsync(40); + await Task.Delay(3_000); + await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.Steam); + await train.Motor.StartPowerAsync(-40); + await Task.Delay(3_000); + await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.Brake); + await train.Motor.StopByFloatAsync(); + await Task.Delay(1_000); + await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.WaterRefill); + + await Task.Delay(1_000); + + await train.SwitchOffAsync(); + } + } + } +} \ No newline at end of file diff --git a/examples/SharpBrick.PoweredUp.Examples/Program.cs b/examples/SharpBrick.PoweredUp.Examples/Program.cs index ee43bee..92c0efe 100644 --- a/examples/SharpBrick.PoweredUp.Examples/Program.cs +++ b/examples/SharpBrick.PoweredUp.Examples/Program.cs @@ -39,7 +39,8 @@ static async Task Main(string[] args) //example = new Example.ExampleTechnicMediumAngularMotorGrey(); //example = new Example.ExampleMarioBarcode(); //example = new Example.ExampleMarioPants(); - example = new Example.ExampleMarioAccelerometer(); + //example = new Example.ExampleMarioAccelerometer(); + example = new Example.ExampleDuploTrainBase(); // NOTE: Examples are programmed object oriented style. Base class implements methods Configure, DiscoverAsync and ExecuteAsync to be overwriten on demand. await example.InitHostAndDiscoverAsync(enableTrace); diff --git a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs index 4460f9e..36052c3 100644 --- a/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs +++ b/src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs @@ -50,6 +50,10 @@ public Type GetTypeFromDeviceType(DeviceType deviceType) DeviceType.MarioHubPants => typeof(MarioHubPants), DeviceType.MarioHubDebug => typeof(MarioHubDebug), DeviceType.MediumLinearMotor => typeof(MediumLinearMotor), + DeviceType.DuploTrainBaseMotor => typeof(DuploTrainBaseMotor), + DeviceType.DuploTrainBaseSpeaker => typeof(DuploTrainBaseSpeaker), + DeviceType.DuploTrainBaseColorSensor => typeof(DuploTrainBaseColorSensor), + DeviceType.DuploTrainBaseSpeedometer => typeof(DuploTrainBaseSpeedometer), _ => null, }; @@ -76,6 +80,10 @@ public static DeviceType GetDeviceTypeFromType(Type type) nameof(MarioHubPants) => DeviceType.MarioHubPants, nameof(MarioHubDebug) => DeviceType.MarioHubDebug, nameof(MediumLinearMotor) => DeviceType.MediumLinearMotor, + nameof(DuploTrainBaseMotor) => DeviceType.DuploTrainBaseMotor, + nameof(DuploTrainBaseSpeaker) => DeviceType.DuploTrainBaseSpeaker, + nameof(DuploTrainBaseColorSensor) => DeviceType.DuploTrainBaseColorSensor, + nameof(DuploTrainBaseSpeedometer) => DeviceType.DuploTrainBaseSpeedometer, _ => DeviceType.Unknown, }; } diff --git a/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseColorSensor.cs b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseColorSensor.cs new file mode 100644 index 0000000..cd6a278 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseColorSensor.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class DuploTrainBaseColorSensor : Device, IPoweredUpDevice + { + protected SingleValueMode _colorMode; + protected SingleValueMode _colorTagMode; + protected SingleValueMode _reflectionMode; + protected MultiValueMode _rgbMode; + + public byte ModeIndexColor { get; protected set; } = 0; + public byte ModeIndexColorTag { get; protected set; } = 1; + public byte ModeIndexReflection { get; protected set; } = 2; + public byte ModeIndexRgb { get; protected set; } = 3; + + public sbyte Color => _colorMode.SI; + public DuploColorTag ColorTag => (DuploColorTag)_colorTagMode.SI; + public sbyte Reflection => _reflectionMode.SI; + public (short red, short green, short blue) Rgb => (_rgbMode.SI[0], _rgbMode.SI[1], _rgbMode.SI[2]); + public (short red, short green, short blue) RgbPct => (_rgbMode.Pct[0], _rgbMode.Pct[1], _rgbMode.Pct[2]); + + public IObservable ColorObservable => _colorMode.Observable.Select(v => v.SI); + public IObservable ColorTagObservable => _colorTagMode.Observable.Select(v => (DuploColorTag)v.SI); + public IObservable ReflectionObservable => _reflectionMode.Observable.Select(v => v.SI); + public IObservable<(short red, short green, short blue)> RgbObservable => _rgbMode.Observable.Select(v => (v.SI[0], v.SI[1], v.SI[2])); + public IObservable<(short red, short green, short blue)> RgbPctObservable => _rgbMode.Observable.Select(v => (v.Pct[0], v.Pct[1], v.Pct[2])); + + public DuploTrainBaseColorSensor() + { } + + public DuploTrainBaseColorSensor(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _colorMode = SingleValueMode(ModeIndexColor); + _colorTagMode = SingleValueMode(ModeIndexColorTag); + _reflectionMode = SingleValueMode(ModeIndexReflection); + _rgbMode = MultiValueMode(ModeIndexRgb); + + ObserveForPropertyChanged(_colorMode.Observable, nameof(Color)); + ObserveForPropertyChanged(_colorTagMode.Observable, nameof(ColorTag)); + ObserveForPropertyChanged(_reflectionMode.Observable, nameof(Reflection)); + ObserveForPropertyChanged(_rgbMode.Observable, nameof(Rgb), nameof(RgbPct)); + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-12-01-06-05-1F-00-00-00 +07-00-43-12-02-0F-00 +11-00-44-12-00-00-43-4F-4C-4F-52-00-00-00-00-00-00 +0E-00-44-12-00-01-00-00-00-00-00-00-20-41 +0E-00-44-12-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-12-00-03-00-00-00-00-00-00-20-41 +0A-00-44-12-00-04-69-64-78-00 +08-00-44-12-00-05-84-00 +0A-00-44-12-00-80-01-00-03-00 +11-00-44-12-01-00-43-20-54-41-47-00-00-00-00-00-00 +0E-00-44-12-01-01-00-00-00-00-00-00-20-41 +0E-00-44-12-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-12-01-03-00-00-00-00-00-00-20-41 +0A-00-44-12-01-04-69-64-78-00 +08-00-44-12-01-05-C4-00 +0A-00-44-12-01-80-01-00-03-00 +11-00-44-12-02-00-52-45-46-4C-54-00-00-00-00-00-00 +0E-00-44-12-02-01-00-00-00-00-00-00-C8-42 +0E-00-44-12-02-02-00-00-00-00-00-00-C8-42 +0E-00-44-12-02-03-00-00-00-00-00-00-C8-42 +0A-00-44-12-02-04-72-61-77-00 +08-00-44-12-02-05-10-00 +0A-00-44-12-02-80-01-00-03-00 +11-00-44-12-03-00-52-47-42-20-49-00-00-00-00-00-00 +0E-00-44-12-03-01-00-00-00-00-00-C0-7F-44 +0E-00-44-12-03-02-00-00-00-00-00-00-C8-42 +0E-00-44-12-03-03-00-00-00-00-00-C0-7F-44 +0A-00-44-12-03-04-72-61-77-00 +08-00-44-12-03-05-10-00 +0A-00-44-12-03-80-03-01-05-00 +11-00-44-12-04-00-43-41-4C-49-42-00-00-00-00-00-00 +0E-00-44-12-04-01-00-00-00-00-00-00-FA-45 +0E-00-44-12-04-02-00-00-00-00-00-00-C8-42 +0E-00-44-12-04-03-00-00-00-00-00-00-FA-45 +0A-00-44-12-04-04-00-00-00-00 +08-00-44-12-04-05-10-00 +0A-00-44-12-04-80-03-01-05-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseMotor.cs b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseMotor.cs new file mode 100644 index 0000000..f95e588 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseMotor.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Threading.Tasks; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Protocol.Knowledge; +using SharpBrick.PoweredUp.Protocol.Messages; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class DuploTrainBaseMotor : Device, IPoweredUpDevice + { + public SingleValueMode _onSecMode; + + public byte ModeIndexMotor { get; protected set; } = 0; + public byte ModeIndexOnSec { get; protected set; } = 1; + + public int OnSeconds => _onSecMode.SI; + public IObservable OnSecondsObservable => _onSecMode.Observable.Select(x => x.SI); + + public DuploTrainBaseMotor() + { } + + public DuploTrainBaseMotor(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _onSecMode = SingleValueMode(ModeIndexOnSec); + + ObserveForPropertyChanged(_onSecMode.Observable, nameof(OnSeconds)); + } + + public void ExtendPortMode(PortModeInfo modeInfo) + { + if (modeInfo.ModeIndex == ModeIndexOnSec) + { + modeInfo.DisablePercentage = true; + } + } + + /// + /// Starts the motor with full speed at the given power level. + /// + /// + /// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW) + /// - Stop Motor (floating): 0 + /// - Stop Motor (breaking): 127 + /// + /// An awaitable Task. + public async Task StartPowerAsync(sbyte power) + { + AssertValidPower(power, nameof(power)); + AssertIsConnected(); + + var response = await _protocol.SendPortOutputCommandAsync(new PortOutputCommandStartPowerMessage() + { + HubId = _hubId, + PortId = _portId, + StartupInformation = PortOutputCommandStartupInformation.ExecuteImmediately, + CompletionInformation = PortOutputCommandCompletionInformation.CommandFeedback, + Power = power, + }); + + return response; + } + + /// + /// Stops the motor (brake; no movement afterwards) + /// + /// + public Task StopByBrakeAsync() + => StartPowerAsync((sbyte)SpecialSpeed.Brake); + + /// + /// Stops the motor (float; freely floating by inertia) + /// + /// + public Task StopByFloatAsync() + => StartPowerAsync((sbyte)SpecialSpeed.Float); + + /// + /// Start the pair on motors with full speed on given power values. + /// + /// + /// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW) + /// - Stop Motor (floating): 0 + /// - Stop Motor (breaking): 127 + /// + /// + /// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW) + /// - Stop Motor (floating): 0 + /// - Stop Motor (breaking): 127 + /// + /// + public async Task StartPowerAsync(sbyte powerOnMotor1, sbyte powerOnMotor2) + { + AssertValidPower(powerOnMotor1, nameof(powerOnMotor1)); + AssertValidPower(powerOnMotor2, nameof(powerOnMotor2)); + AssertIsConnected(); + AssertIsVirtualPort(); + + var response = await _protocol.SendPortOutputCommandAsync(new PortOutputCommandStartPower2Message() + { + HubId = _hubId, + PortId = _portId, + StartupInformation = PortOutputCommandStartupInformation.ExecuteImmediately, + CompletionInformation = PortOutputCommandCompletionInformation.CommandFeedback, + Power1 = powerOnMotor1, + Power2 = powerOnMotor2, + }); + + return response; + } + + protected void AssertValidPower(sbyte power, string argumentName) + { + if ( + power < -100 || + (power > 100 && power != 127) + ) + { + throw new ArgumentOutOfRangeException(argumentName); + } + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-00-01-03-02-02-00-01-00 +05-00-43-00-02 +11-00-44-00-00-00-54-20-4D-4F-54-00-00-00-00-00-00 +0E-00-44-00-00-01-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-00-00-03-00-00-C8-C2-00-00-C8-42 +0A-00-44-00-00-04-70-77-72-00 +08-00-44-00-00-05-00-50 +0A-00-44-00-00-80-01-00-03-00 +11-00-44-00-01-00-4F-4E-53-45-43-00-00-00-00-00-00 +0E-00-44-00-01-01-00-00-00-00-00-00-80-3F +0E-00-44-00-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-00-01-03-00-00-00-00-00-00-80-3F +0A-00-44-00-01-04-73-65-63-00 +08-00-44-00-01-05-08-00 +0A-00-44-00-01-80-01-02-04-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeaker.cs b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeaker.cs new file mode 100644 index 0000000..0492877 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeaker.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Threading.Tasks; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + + public class DuploTrainBaseSpeaker : Device, IPoweredUpDevice + { + protected SingleValueMode _soundMode; + + public byte ModeIndexTone { get; protected set; } = 0; + public byte ModeIndexSound { get; protected set; } = 1; + public byte ModeIndexUiSound { get; protected set; } = 2; + + public DuploTrainBaseSpeaker() + { } + + public DuploTrainBaseSpeaker(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _soundMode = SingleValueMode(ModeIndexSound); + } + + public async Task PlaySoundAsync(DuploTrainBaseSound sound) + { + await this.SetupNotificationAsync(ModeIndexSound, false); + var response = await _soundMode.WriteDirectModeDataAsync((byte)sound); + + return response; + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-01-01-01-03-00-00-07-00 +05-00-43-01-02 +11-00-44-01-00-00-54-4F-4E-45-00-00-00-00-00-00-00 +0E-00-44-01-00-01-00-00-00-00-00-00-20-41 +0E-00-44-01-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-01-00-03-00-00-00-00-00-00-20-41 +0A-00-44-01-00-04-69-64-78-00 +08-00-44-01-00-05-00-04 +0A-00-44-01-00-80-01-00-03-00 +11-00-44-01-01-00-53-4F-55-4E-44-00-00-00-00-00-00 +0E-00-44-01-01-01-00-00-00-00-00-00-20-41 +0E-00-44-01-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-01-01-03-00-00-00-00-00-00-20-41 +0A-00-44-01-01-04-69-64-78-00 +08-00-44-01-01-05-00-44 +0A-00-44-01-01-80-01-00-03-00 +11-00-44-01-02-00-55-49-20-53-4E-44-00-00-00-00-00 +0E-00-44-01-02-01-00-00-00-00-00-00-20-41 +0E-00-44-01-02-02-00-00-00-00-00-00-C8-42 +0E-00-44-01-02-03-00-00-00-00-00-00-20-41 +0A-00-44-01-02-04-69-64-78-00 +08-00-44-01-02-05-00-04 +0A-00-44-01-02-80-01-00-03-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeedometer.cs b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeedometer.cs new file mode 100644 index 0000000..6238817 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Devices/DuploTrainBaseSpeedometer.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using SharpBrick.PoweredUp.Protocol; +using SharpBrick.PoweredUp.Protocol.Knowledge; +using SharpBrick.PoweredUp.Utils; + +namespace SharpBrick.PoweredUp +{ + public class DuploTrainBaseSpeedometer : Device, IPoweredUpDevice + { + protected SingleValueMode _speedMode; + protected SingleValueMode _countMode; + + public byte ModeIndexSpeed { get; protected set; } = 0; + public byte ModeIndexCount { get; protected set; } = 1; + + public short Speed => _speedMode.SI; + public short SpeedPct => _speedMode.Pct; + public IObservable> SpeedObservable => _speedMode.Observable; + + public int Count => _countMode.SI; + public IObservable CountObservable => _countMode.Observable.Select(v => v.SI); + + public DuploTrainBaseSpeedometer() + { } + + public DuploTrainBaseSpeedometer(ILegoWirelessProtocol protocol, byte hubId, byte portId) + : base(protocol, hubId, portId) + { + _speedMode = SingleValueMode(ModeIndexSpeed); + _countMode = SingleValueMode(ModeIndexCount); + + ObserveForPropertyChanged(_speedMode.Observable, nameof(Speed), nameof(SpeedPct)); + ObserveForPropertyChanged(_countMode.Observable, nameof(Count)); + } + + public void ExtendPortMode(PortModeInfo portModeInfo) + { + if (portModeInfo.ModeIndex == ModeIndexCount) + { + portModeInfo.DisableScaling = true; + portModeInfo.DisablePercentage = true; + } + } + + public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType) + => @" +0B-00-43-13-01-06-03-07-00-00-00 +07-00-43-13-02-03-00 +11-00-44-13-00-00-53-50-45-45-44-00-00-00-00-00-00 +0E-00-44-13-00-01-00-00-96-C3-00-00-96-43 +0E-00-44-13-00-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-13-00-03-00-00-20-C1-00-00-20-41 +0A-00-44-13-00-04-73-70-64-00 +08-00-44-13-00-05-50-00 +0A-00-44-13-00-80-01-01-05-00 +11-00-44-13-01-00-43-4F-55-4E-54-00-00-00-00-00-00 +0E-00-44-13-01-01-00-00-00-41-00-00-00-41 +0E-00-44-13-01-02-00-00-C8-C2-00-00-C8-42 +0E-00-44-13-01-03-00-00-B4-43-00-00-B4-43 +0A-00-44-13-01-04-63-6E-74-00 +08-00-44-13-01-05-08-00 +0A-00-44-13-01-80-01-02-04-00 +11-00-44-13-02-00-43-41-4C-49-42-00-00-00-00-00-00 +0E-00-44-13-02-01-00-00-00-00-00-00-80-45 +0E-00-44-13-02-02-00-00-00-00-00-00-C8-42 +0E-00-44-13-02-03-00-00-00-00-00-00-80-45 +0A-00-44-13-02-04-6E-2F-61-00 +08-00-44-13-02-05-08-00 +0A-00-44-13-02-80-04-01-05-00 +".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs index ef5b593..9d52dff 100644 --- a/src/SharpBrick.PoweredUp/Devices/RgbLight.cs +++ b/src/SharpBrick.PoweredUp/Devices/RgbLight.cs @@ -126,6 +126,24 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 0A-00-44-34-01-04-72-67-62-00 08-00-44-34-01-05-00-50 0A-00-44-34-01-80-03-00-03-00 +", + (_, _, SystemType.LegoDuplo_DuploTrain) => @" +0B-00-43-11-01-01-02-00-00-03-00 +05-00-43-11-02 +11-00-44-11-00-00-43-4F-4C-20-4F-00-00-00-00-00-00 +0E-00-44-11-00-01-00-00-00-00-00-00-20-41 +0E-00-44-11-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-11-00-03-00-00-00-00-00-00-20-41 +0A-00-44-11-00-04-00-00-00-00 +08-00-44-11-00-05-00-44 +0A-00-44-11-00-80-01-00-01-00 +11-00-44-11-01-00-52-47-42-20-4F-00-00-00-00-00-00 +0E-00-44-11-01-01-00-00-00-00-00-00-7F-43 +0E-00-44-11-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-11-01-03-00-00-00-00-00-00-7F-43 +0A-00-44-11-01-04-00-00-00-00 +08-00-44-11-01-05-00-10 +0A-00-44-11-01-80-03-00-03-00 ", _ => throw new NotSupportedException(), }).Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s)); diff --git a/src/SharpBrick.PoweredUp/Devices/Voltage.cs b/src/SharpBrick.PoweredUp/Devices/Voltage.cs index 9d00d57..1fd4fda 100644 --- a/src/SharpBrick.PoweredUp/Devices/Voltage.cs +++ b/src/SharpBrick.PoweredUp/Devices/Voltage.cs @@ -107,6 +107,24 @@ public IEnumerable GetStaticPortInfoMessages(Version softwareVersion, Ve 0A-00-44-3B-01-04-6D-76-00-00 08-00-44-3B-01-05-10-00 0A-00-44-3B-01-80-01-01-04-00 +", + (_, _, SystemType.LegoDuplo_DuploTrain) => @" +0B-00-43-14-01-02-02-03-00-00-00 +05-00-43-14-02 +11-00-44-14-00-00-56-4C-54-20-4C-00-00-00-00-00-00 +0E-00-44-14-00-01-00-00-00-00-00-70-3E-45 +0E-00-44-14-00-02-00-00-00-00-00-00-C8-42 +0E-00-44-14-00-03-00-00-00-00-00-00-C8-45 +0A-00-44-14-00-04-6D-56-00-00 +08-00-44-14-00-05-10-00 +0A-00-44-14-00-80-01-01-04-00 +11-00-44-14-01-00-56-4C-54-20-53-00-00-00-00-00-00 +0E-00-44-14-01-01-00-00-00-00-00-70-3E-45 +0E-00-44-14-01-02-00-00-00-00-00-00-C8-42 +0E-00-44-14-01-03-00-00-00-00-00-00-C8-45 +0A-00-44-14-01-04-6D-56-00-00 +08-00-44-14-01-05-10-00 +0A-00-44-14-01-80-01-01-04-00 ", _ => throw new NotSupportedException(), diff --git a/src/SharpBrick.PoweredUp/Enums/DuploColorTag.cs b/src/SharpBrick.PoweredUp/Enums/DuploColorTag.cs new file mode 100644 index 0000000..d5edd2f --- /dev/null +++ b/src/SharpBrick.PoweredUp/Enums/DuploColorTag.cs @@ -0,0 +1,12 @@ +namespace SharpBrick.PoweredUp +{ + public enum DuploColorTag : sbyte + { + None = -1, + Water = 3, // blue + Redirect = 5, // green + Sound = 7, // yellow + Stop = 9, // red + Light = 10, // white + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Enums/DuploTrainBaseSound.cs b/src/SharpBrick.PoweredUp/Enums/DuploTrainBaseSound.cs new file mode 100644 index 0000000..9f6f824 --- /dev/null +++ b/src/SharpBrick.PoweredUp/Enums/DuploTrainBaseSound.cs @@ -0,0 +1,11 @@ +namespace SharpBrick.PoweredUp +{ + public enum DuploTrainBaseSound : byte + { + Brake = 3, + StationDeparture = 5, + WaterRefill = 7, + Horn = 9, + Steam = 10 + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Hubs/DuploTrainBaseHub.cs b/src/SharpBrick.PoweredUp/Hubs/DuploTrainBaseHub.cs new file mode 100644 index 0000000..df7347a --- /dev/null +++ b/src/SharpBrick.PoweredUp/Hubs/DuploTrainBaseHub.cs @@ -0,0 +1,45 @@ +using System; +using Microsoft.Extensions.Logging; +using SharpBrick.PoweredUp.Devices; +using SharpBrick.PoweredUp.Protocol; + +namespace SharpBrick.PoweredUp +{ + public class DuploTrainBaseHub : Hub + { + public DuploTrainBaseHub(ILegoWirelessProtocol protocol, IDeviceFactory deviceFactory, ILogger logger, IServiceProvider serviceProvider = default) + : base(protocol, deviceFactory, logger, serviceProvider, SystemType.LegoDuplo_DuploTrain, new Port[] { + new Port(0, string.Empty, false, expectedDevice: DeviceType.DuploTrainBaseMotor), + new Port(1, string.Empty, false, expectedDevice: DeviceType.DuploTrainBaseSpeaker), + new Port(17, string.Empty, false, expectedDevice: DeviceType.RgbLight), + new Port(18, string.Empty, false, expectedDevice: DeviceType.DuploTrainBaseColorSensor), + new Port(19, string.Empty, false, expectedDevice: DeviceType.DuploTrainBaseSpeedometer ), + new Port(20, string.Empty, false, expectedDevice: DeviceType.Voltage), + }, + knownProperties: new HubProperty[] { + HubProperty.AdvertisingName, + HubProperty.Button, + HubProperty.FwVersion, + HubProperty.HwVersion, + HubProperty.Rssi, + HubProperty.BatteryVoltage, + HubProperty.BatteryType, + HubProperty.ManufacturerName, + HubProperty.RadioFirmwareVersion, + HubProperty.LegoWirelessProtocolVersion, + HubProperty.SystemTypeId, + HubProperty.HardwareNetworkId, + HubProperty.PrimaryMacAddress, + // HubProperty.SecondaryMacAddress, // throws + // HubProperty.HardwareNetworkFamily, // throws + }) + { } + + public DuploTrainBaseMotor Motor => Port(0).GetDevice(); + public DuploTrainBaseSpeaker Speaker => Port(1).GetDevice(); + public RgbLight RgbLight => Port(17).GetDevice(); + public DuploTrainBaseColorSensor ColorSensor => Port(18).GetDevice(); + public DuploTrainBaseSpeedometer Speedometer => Port(19).GetDevice(); + public Voltage Voltage => Port(20).GetDevice(); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs index 45bcc02..a4ea941 100644 --- a/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs +++ b/src/SharpBrick.PoweredUp/Hubs/HubFactory.cs @@ -41,6 +41,7 @@ public static Type GetTypeFromSystemType(SystemType systemType) SystemType.LegoSystem_TwoPortHandset => typeof(TwoPortHandset), SystemType.LegoTechnic_MediumHub => typeof(TechnicMediumHub), SystemType.LegoSystem_Mario => typeof(MarioHub), + SystemType.LegoDuplo_DuploTrain => typeof(DuploTrainBaseHub), _ => throw new NotSupportedException(), }; @@ -51,6 +52,7 @@ public static SystemType GetSystemTypeFromType(Type type) nameof(TwoPortHandset) => SystemType.LegoSystem_TwoPortHandset, nameof(TechnicMediumHub) => SystemType.LegoTechnic_MediumHub, nameof(MarioHub) => SystemType.LegoSystem_Mario, + nameof(DuploTrainBaseHub) => SystemType.LegoDuplo_DuploTrain, _ => throw new NotSupportedException(), }; } diff --git a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs index 21a24e6..f96bff4 100644 --- a/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs +++ b/src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs @@ -25,6 +25,7 @@ public static IServiceCollection AddPoweredUp(this IServiceCollection self) .AddTransient() .AddTransient() .AddTransient() + .AddTransient() // functions .AddTransient() diff --git a/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs b/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs index 8ebd627..7e1f8c3 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Formatter/PortValueSingleEncoder.cs @@ -116,11 +116,17 @@ internal static PortValueData CreatePortValueDataInt32(PortModeInfo modeInf { var rawValues = MemoryMarshal.Cast(dataSlice).ToArray(); - var siValues = rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.SIMin, modeInfo.SIMax)).Select(f => Convert.ToInt32(f)).ToArray(); - var pctValues = modeInfo.DisablePercentage switch + var siValues = modeInfo switch + { + { DisableScaling: true } => rawValues, + _ => rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.SIMin, modeInfo.SIMax)).Select(f => Convert.ToInt32(f)).ToArray(), + }; + + var pctValues = modeInfo switch { - false => rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToInt32(f)).ToArray(), - true => new int[rawValues.Length], + { DisablePercentage: true } => new int[rawValues.Length], + { DisableScaling: true } => rawValues, + _ => rawValues.Select(rv => Scale(rv, modeInfo.RawMin, modeInfo.RawMax, modeInfo.PctMin, modeInfo.PctMax)).Select(f => Convert.ToInt32(f)).ToArray(), }; return new PortValueData() diff --git a/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs b/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs index 75cc2de..115c056 100644 --- a/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs +++ b/src/SharpBrick.PoweredUp/Protocol/Knowledge/PortModeInfo.cs @@ -56,5 +56,6 @@ public class PortModeInfo // Additional Settings public bool DisablePercentage { get; set; } = false; + public bool DisableScaling { get; set; } = false; } } \ No newline at end of file From 799fc18d4e9384ec118161f1ce06104216d33937 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Sun, 22 Nov 2020 09:54:29 +0100 Subject: [PATCH 21/22] Update to Release Build --- .github/workflows/build-release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 9ca4cf2..e13451e 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -17,7 +17,8 @@ jobs: with: dotnet-version: 3.1.100 - name: Build Version - run: echo ::set-env name=RELEASE_VERSION::$(echo ${GITHUB_REF:11}) + # run: echo ::set-env name=RELEASE_VERSION::$(echo ${GITHUB_REF:11}) // deprecated + run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV - name: Build Project run: dotnet build --configuration Release -p:Version=$RELEASE_VERSION - name: Test Project From 6f7980b83499de1ed618c9b4f02854da0a51de69 Mon Sep 17 00:00:00 2001 From: "T. Thiery" Date: Wed, 23 Dec 2020 13:18:31 +0100 Subject: [PATCH 22/22] Improve CLI experience (#129) * Add description to CLI help options * Add exit codes and exception handling * Add more advertisement in the README for the CLI #22 non-breaking --- README.md | 37 +++++- src/SharpBrick.PoweredUp.Cli/Program.cs | 154 ++++++++++++++++-------- 2 files changed, 137 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index af7b08a..332a3ad 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ Additional to code fragments below, look into the `examples/SharpBrick.PoweredUp ````csharp using SharpBrick.PoweredUp; -using SharpBrick.PoweredUp.WinRT; // for WinRT Bluetooth NuGet ```` ## Discovering Hubs @@ -37,7 +36,7 @@ using SharpBrick.PoweredUp.WinRT; // for WinRT Bluetooth NuGet var serviceProvider = new ServiceCollection() .AddLogging() .AddPoweredUp() - .AddWinRTBluetooth() // using WinRT Bluetooth on Windows + .AddWinRTBluetooth() // using WinRT Bluetooth on Windows (separate NuGet SharpBrick.PoweredUp.WinRT) .BuildServiceProvider(); var host = serviceProvider.GetService(); @@ -70,7 +69,7 @@ See source code in `examples/SharpBrick.PoweredUp.Examples` for more examples. using (var technicMediumHub = hub as TechnicMediumHub) { - // optionally verify if everything is wired up correctly (v2.0 onwards) + // optionally verify if everything is wired up correctly await technicMediumHub.VerifyDeploymentModelAsync(modelBuilder => modelBuilder .AddHub(hubBuilder => hubBuilder .AddDevice(technicMediumHub.A) @@ -154,7 +153,7 @@ Console.WriteLine($"Or directly read the latest value: {aposMode.SI} / {aposMode var serviceProvider = new ServiceCollection() .AddLogging() .AddPoweredUp() - .AddWinRTBluetooth() // using WinRT Bluetooth on Windows + .AddWinRTBluetooth() // using WinRT Bluetooth on Windows (separate NuGet SharpBrick.PoweredUp.WinRT) .BuildServiceProvider(); using (var scope = serviceProvider.CreateScope()) // create a scoped DI container per intented active connection/protocol. If disposed, disposes all disposable artifacts. @@ -193,6 +192,34 @@ using (var scope = serviceProvider.CreateScope()) // create a scoped DI containe } ```` +# Command Line Experience + +The `poweredup` command line utility intends to allow the inspection of LEGO Wireless Protocol / Powered UP hubs and devices for their properties. It has utilities for ... + +- **Enumerating all connected Devices** including hub internal devices and emit their static self-description as they expose using the LEGO Wireless Protocol. + ```` + poweredup device list + ```` +- **Binary dumping the self-description** helps protocol implementors with a lack of devices to understand and try to implement the devices without having the physical device. Also the output is needed when programming the library to enable a fast bootup of the SDK. + ```` + poweredup device dump-static-port -p 0 + ```` +- **Pretty Print Binary Dumps**: Help to convert a binary dump in a nice representation. + +***Note**: Currently only Windows based WinRT Bluetooth drivers are available. Work is on the way to support bluez to run the utility also on Linux.* + +## Installation Instruction + +1. Install the [latest .NET (Core)](https://dotnet.microsoft.com/download) on your machine (e.g. .NET Core 3.1). +2. Install the `poweredup` dotnet utility using the following instruction + ```` + dotnet tools install -g SharpBrick.PoweredUp.Cli + ```` +3. Start using the tool + ```` + poweredup device list + ```` + # SDK Status, Hardware Support, Contributions, .. Basic Architecture within the SDK @@ -282,6 +309,8 @@ DI Container Elements - [X] Technic Medium Angular Motor (Grey) - [ ] Technic Large Angular Motor (Spike) - [X] Technic Large Angular Motor (Grey) + - [ ] Technic Color Sensor + - [ ] Technic Distance Sensor - .. other devices depend on availability of hardware / contributions - Protocol - [X] Message Encoding (98% [spec coverage](docs/specification/coverage.md)) diff --git a/src/SharpBrick.PoweredUp.Cli/Program.cs b/src/SharpBrick.PoweredUp.Cli/Program.cs index 71152a1..0a88ec0 100644 --- a/src/SharpBrick.PoweredUp.Cli/Program.cs +++ b/src/SharpBrick.PoweredUp.Cli/Program.cs @@ -15,50 +15,81 @@ namespace SharpBrick.PoweredUp.Cli { class Program { - static async Task Main(string[] args) + public const int SuccessExitCode = 0; + public const int UnknownOperationExitCode = 1; + public const int BluetoothNoSelectedDeviceExitCode = 2; + public const int ExceptionExitCode = 3; + + static async Task Main(string[] args) { var app = new CommandLineApplication(); + app.Name = "poweredup"; + app.Description = "A command line interface to investigate LEGO Powered UP hubs and devices."; app.HelpOption(); app.UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.CollectAndContinue; + app.OnExecute(() => + { + Console.WriteLine($"See {app.Name} --help for Options"); + return Program.UnknownOperationExitCode; + }); app.Command("device", deviceApp => { + deviceApp.Description = "Options to enumerate devices on a hub"; deviceApp.HelpOption(); + deviceApp.OnExecute(() => + { + Console.WriteLine($"See {app.Name} {deviceApp.Name} --help for Options"); + return Program.UnknownOperationExitCode; + }); + deviceApp.Command("list", deviceListApp => { + deviceListApp.Description = "Inspect all devices declared on the Hub by using information gathered using the LEGO Wireless Protocol"; deviceListApp.HelpOption(); var traceOption = deviceListApp.Option("--trace", "Enable Tracing", CommandOptionType.SingleValue); deviceListApp.OnExecuteAsync(async cts => { - var enableTrace = bool.TryParse(traceOption.Value(), out var x) ? x : false; + try + { + var enableTrace = bool.TryParse(traceOption.Value(), out var x) ? x : false; - var serviceProvider = CreateServiceProvider(enableTrace); - (ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService()); + var serviceProvider = CreateServiceProvider(enableTrace); + (ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService()); - if (bluetoothAddress == 0) - return; + if (bluetoothAddress == 0) + return Program.BluetoothNoSelectedDeviceExitCode; - // initialize a DI scope per bluetooth connection / protocol (e.g. protocol is a per-bluetooth connection service) - using (var scope = serviceProvider.CreateScope()) - { - var scopedServiceProvider = scope.ServiceProvider; + // initialize a DI scope per bluetooth connection / protocol (e.g. protocol is a per-bluetooth connection service) + using (var scope = serviceProvider.CreateScope()) + { + var scopedServiceProvider = scope.ServiceProvider; - await AddTraceWriterAsync(scopedServiceProvider, enableTrace); + await AddTraceWriterAsync(scopedServiceProvider, enableTrace); - scopedServiceProvider.GetService().BluetoothAddress = bluetoothAddress; + scopedServiceProvider.GetService().BluetoothAddress = bluetoothAddress; - var deviceListCli = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory + var deviceListCli = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory + + await deviceListCli.ExecuteAsync(systemType); + } - await deviceListCli.ExecuteAsync(systemType); + return Program.SuccessExitCode; + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + return Program.ExceptionExitCode; } }); }); deviceApp.Command("dump-static-port", deviceDumpStaticPortApp => { + deviceDumpStaticPortApp.Description = "Inspect a specific device on a Hub Port by using (non-dynamic) information gathered using the LEGO Wireless Protocol. Emits a binary dump (use list for human readable output)."; deviceDumpStaticPortApp.HelpOption(); var traceOption = deviceDumpStaticPortApp.Option("--trace", "Enable Tracing", CommandOptionType.SingleValue); @@ -67,35 +98,47 @@ static async Task Main(string[] args) deviceDumpStaticPortApp.OnExecuteAsync(async cts => { - var enableTrace = bool.TryParse(traceOption.Value(), out var x) ? x : false; - var headerEnabled = headerOption.Values.Count > 0; + try + { + var enableTrace = bool.TryParse(traceOption.Value(), out var x) ? x : false; + var headerEnabled = headerOption.Values.Count > 0; - var serviceProvider = CreateServiceProvider(enableTrace); - (ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService()); + var serviceProvider = CreateServiceProvider(enableTrace); + (ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService()); - if (bluetoothAddress == 0) - return; + if (bluetoothAddress == 0) + return Program.BluetoothNoSelectedDeviceExitCode; - // initialize a DI scope per bluetooth connection / protocol (e.g. protocol is a per-bluetooth connection service) - using (var scope = serviceProvider.CreateScope()) - { - var scopedServiceProvider = scope.ServiceProvider; + // initialize a DI scope per bluetooth connection / protocol (e.g. protocol is a per-bluetooth connection service) + using (var scope = serviceProvider.CreateScope()) + { + var scopedServiceProvider = scope.ServiceProvider; - await AddTraceWriterAsync(scopedServiceProvider, enableTrace); + await AddTraceWriterAsync(scopedServiceProvider, enableTrace); - scopedServiceProvider.GetService().BluetoothAddress = bluetoothAddress; + scopedServiceProvider.GetService().BluetoothAddress = bluetoothAddress; - var dumpStaticPortInfoCommand = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory + var dumpStaticPortInfoCommand = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory - var port = byte.Parse(portOption.Value()); + var port = byte.Parse(portOption.Value()); + + await dumpStaticPortInfoCommand.ExecuteAsync(systemType, port, headerEnabled); + } - await dumpStaticPortInfoCommand.ExecuteAsync(systemType, port, headerEnabled); + return Program.SuccessExitCode; + + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + return Program.ExceptionExitCode; } }); }); deviceApp.Command("pretty-print", prettyPrintApp => { + prettyPrintApp.Description = "Pretty prints a previously recorded binary dump collected using dump-static-ports"; prettyPrintApp.HelpOption(); var traceOption = prettyPrintApp.Option("--trace", "Enable Tracing", CommandOptionType.SingleValue); var systemTypeOption = prettyPrintApp.Option("--t", "System Type (parsable number)", CommandOptionType.SingleValue); @@ -108,40 +151,51 @@ static async Task Main(string[] args) prettyPrintApp.OnExecuteAsync(async cts => { - var enableTrace = bool.TryParse(traceOption.Value(), out var x0) ? x0 : false; - var systemType = byte.TryParse(systemTypeOption.Value(), out var x1) ? x1 : (byte)0; - var hubId = byte.TryParse(hubOption.Value(), out var x2) ? x2 : (byte)0; - var portId = byte.TryParse(portOption.Value(), out var x3) ? x3 : (byte)0; - var ioType = ushort.TryParse(ioTypeOption.Value(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var x4) ? x4 : (ushort)0; - var hwVersion = Version.TryParse(hwVersionOption.Value(), out var x5) ? x5 : new Version("0.0.0.0"); - var swVersion = Version.TryParse(swVersionOption.Value(), out var x6) ? x6 : new Version("0.0.0.0"); - var file = fileOption.Value(); - - var serviceProvider = CreateServiceProviderWithMock(enableTrace); - - using (var scope = serviceProvider.CreateScope()) + try { - var scopedServiceProvider = scope.ServiceProvider; + var enableTrace = bool.TryParse(traceOption.Value(), out var x0) ? x0 : false; + var systemType = byte.TryParse(systemTypeOption.Value(), out var x1) ? x1 : (byte)0; + var hubId = byte.TryParse(hubOption.Value(), out var x2) ? x2 : (byte)0; + var portId = byte.TryParse(portOption.Value(), out var x3) ? x3 : (byte)0; + var ioType = ushort.TryParse(ioTypeOption.Value(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var x4) ? x4 : (ushort)0; + var hwVersion = Version.TryParse(hwVersionOption.Value(), out var x5) ? x5 : new Version("0.0.0.0"); + var swVersion = Version.TryParse(swVersionOption.Value(), out var x6) ? x6 : new Version("0.0.0.0"); + var file = fileOption.Value(); + + var serviceProvider = CreateServiceProviderWithMock(enableTrace); + + using (var scope = serviceProvider.CreateScope()) + { + var scopedServiceProvider = scope.ServiceProvider; - await AddTraceWriterAsync(scopedServiceProvider, enableTrace); + await AddTraceWriterAsync(scopedServiceProvider, enableTrace); - var prettyPrintCommand = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory + var prettyPrintCommand = scopedServiceProvider.GetService(); // ServiceLocator ok: transient factory - TextReader reader = Console.In; + TextReader reader = Console.In; - if (!string.IsNullOrWhiteSpace(file)) - { - reader = new StringReader(File.ReadAllText(file)); + if (!string.IsNullOrWhiteSpace(file)) + { + reader = new StringReader(File.ReadAllText(file)); + } + + await prettyPrintCommand.ExecuteAsync(reader, systemType, ioType, hubId, portId, hwVersion, swVersion); } - await prettyPrintCommand.ExecuteAsync(reader, systemType, ioType, hubId, portId, hwVersion, swVersion); + return Program.SuccessExitCode; + + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + return Program.ExceptionExitCode; } }); }); }); - await app.ExecuteAsync(args); + return await app.ExecuteAsync(args); } private static IServiceCollection CreateServiceProviderInternal(bool enableTrace)