Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DuploTrainBaseHub #125

Merged
merged 4 commits into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
71 changes: 71 additions & 0 deletions examples/SharpBrick.PoweredUp.Examples/ExampleDuploTrainBase.cs
Original file line number Diff line number Diff line change
@@ -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<DuploTrainBaseHub>())
{
await train.VerifyDeploymentModelAsync(modelBuilder => modelBuilder
.AddHub<DuploTrainBaseHub>(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();
}
}
}
}
3 changes: 2 additions & 1 deletion examples/SharpBrick.PoweredUp.Examples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand All @@ -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,
};
}
Expand Down
92 changes: 92 additions & 0 deletions src/SharpBrick.PoweredUp/Devices/DuploTrainBaseColorSensor.cs
Original file line number Diff line number Diff line change
@@ -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<sbyte> _colorMode;
protected SingleValueMode<sbyte> _colorTagMode;
protected SingleValueMode<sbyte> _reflectionMode;
protected MultiValueMode<short> _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<sbyte> ColorObservable => _colorMode.Observable.Select(v => v.SI);
public IObservable<DuploColorTag> ColorTagObservable => _colorTagMode.Observable.Select(v => (DuploColorTag)v.SI);
public IObservable<sbyte> 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<sbyte>(ModeIndexColor);
_colorTagMode = SingleValueMode<sbyte>(ModeIndexColorTag);
_reflectionMode = SingleValueMode<sbyte>(ModeIndexReflection);
_rgbMode = MultiValueMode<short>(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<byte[]> 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));
}
}
147 changes: 147 additions & 0 deletions src/SharpBrick.PoweredUp/Devices/DuploTrainBaseMotor.cs
Original file line number Diff line number Diff line change
@@ -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<int> _onSecMode;

public byte ModeIndexMotor { get; protected set; } = 0;
public byte ModeIndexOnSec { get; protected set; } = 1;

public int OnSeconds => _onSecMode.SI;
public IObservable<int> OnSecondsObservable => _onSecMode.Observable.Select(x => x.SI);

public DuploTrainBaseMotor()
{ }

public DuploTrainBaseMotor(ILegoWirelessProtocol protocol, byte hubId, byte portId)
: base(protocol, hubId, portId)
{
_onSecMode = SingleValueMode<int>(ModeIndexOnSec);

ObserveForPropertyChanged(_onSecMode.Observable, nameof(OnSeconds));
}

public void ExtendPortMode(PortModeInfo modeInfo)
{
if (modeInfo.ModeIndex == ModeIndexOnSec)
{
modeInfo.DisablePercentage = true;
}
}

/// <summary>
/// Starts the motor with full speed at the given power level.
/// </summary>
/// <param name="power">
/// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW)
/// - Stop Motor (floating): 0
/// - Stop Motor (breaking): 127
/// </param>
/// <returns>An awaitable Task.</returns>
public async Task<PortFeedback> 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;
}

/// <summary>
/// Stops the motor (brake; no movement afterwards)
/// </summary>
/// <returns></returns>
public Task StopByBrakeAsync()
=> StartPowerAsync((sbyte)SpecialSpeed.Brake);

/// <summary>
/// Stops the motor (float; freely floating by inertia)
/// </summary>
/// <returns></returns>
public Task StopByFloatAsync()
=> StartPowerAsync((sbyte)SpecialSpeed.Float);

/// <summary>
/// Start the pair on motors with full speed on given power values.
/// </summary>
/// <param name="powerOnMotor1">
/// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW)
/// - Stop Motor (floating): 0
/// - Stop Motor (breaking): 127
/// </param>
/// <param name="powerOnMotor2">
/// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW)
/// - Stop Motor (floating): 0
/// - Stop Motor (breaking): 127
/// </param>
/// <returns></returns>
public async Task<PortFeedback> 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<byte[]> 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));
}
}
Loading