Skip to content

Commit

Permalink
Add DynamicDevice
Browse files Browse the repository at this point in the history
- Add example (requires cleanup in samples
- Add extension mode for IServiceCollection to AddPoweredUp services
  (like IDeviceFactory)
- Made DeviceFactory a singleton instead of static (to allow testing
  of DynamicDevice on a known device)
- Fix console output in DiscoverPorts function
- Add WriteDirectModeData to Mode
- Add GenericWriteDirectModeDataMessage for byte[] provided by user
- Breaking Change due to ServiceCollection need to contain IDeviceFactory

#37 breaking
  • Loading branch information
tthiery committed Jul 8, 2020
1 parent 5dcc181 commit 2c56fe6
Show file tree
Hide file tree
Showing 21 changed files with 293 additions and 28 deletions.
125 changes: 125 additions & 0 deletions examples/SharpBrick.PoweredUp.Examples/ExampleDynamicDevice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Threading.Tasks;
using System.Reactive.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using SharpBrick.PoweredUp;
using SharpBrick.PoweredUp.Deployment;
using SharpBrick.PoweredUp.WinRT;
using System.Threading;
using SharpBrick.PoweredUp.Functions;
using SharpBrick.PoweredUp.Devices;
using SharpBrick.PoweredUp.Protocol;

namespace Example
{
public class EmptyDeviceFactory : IDeviceFactory
{
public IPoweredUpDevice Create(DeviceType deviceType)
=> null;

public IPoweredUpDevice CreateConnected(DeviceType deviceType, IPoweredUpProtocol protocol, byte hubId, byte portId)
=> null;
}
public class ExampleDynamicDevice
{
public static (PoweredUpHost host, IServiceProvider serviceProvider, Hub selectedHub) CreateHostAndDiscover(bool enableTrace)
{
var serviceProvider = new ServiceCollection()
// configure your favourite level of logging.
.AddLogging(builder =>
{
builder
.AddConsole();
if (enableTrace)
{
builder.AddFilter("SharpBrick.PoweredUp.Bluetooth.BluetoothKernel", LogLevel.Debug);
}
})
.AddSingleton<IDeviceFactory, EmptyDeviceFactory>()
//.AddPoweredUp()
.BuildServiceProvider();


var logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger("Main");

var poweredUpBluetoothAdapter = new WinRTPoweredUpBluetoothAdapter();

var host = new PoweredUpHost(poweredUpBluetoothAdapter, serviceProvider);

Hub result = null;

logger.LogInformation("Finding Service");
var cts = new CancellationTokenSource();
host.Discover(async hub =>
{
// add this when you are interested in a tracing of the message ("human readable")
if (enableTrace)
{
var tracer = new TraceMessages(hub.Protocol, serviceProvider.GetService<ILoggerFactory>().CreateLogger<TraceMessages>());
await tracer.ExecuteAsync();
}
logger.LogInformation("Connecting to Hub");
await hub.ConnectAsync();
result = hub;
logger.LogInformation(hub.AdvertisingName);
logger.LogInformation(hub.SystemType.ToString());
cts.Cancel();
logger.LogInformation("Press RETURN to continue to the action");
}, cts.Token);

logger.LogInformation("Press RETURN to cancel Scanning");
Console.ReadLine();

cts.Cancel();
return (host, serviceProvider, result);
}

public static async Task ExecuteAsync(bool enableTrace)
{
var (host, serviceProvider, selectedHub) = ExampleDynamicDevice.CreateHostAndDiscover(enableTrace);

var logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger<ExampleMotorInputAbsolutePosition>();

using (var technicMediumHub = host.FindByType<TechnicMediumHub>())
{
var model = new DeploymentModelBuilder()
.AddAnyHub(hubBuilder => hubBuilder
.AddAnyDevice(0))
.Build();

model.Verify(technicMediumHub.Protocol);

var dynamicDeviceWhichIsAMotor = new DynamicDevice(technicMediumHub.Protocol, technicMediumHub.HubId, 0);
await dynamicDeviceWhichIsAMotor.DiscoverAsync();

logger.LogInformation("Discovery completed");

await dynamicDeviceWhichIsAMotor.TryLockDeviceForCombinedModeNotificationSetupAsync(2, 3);
await dynamicDeviceWhichIsAMotor.SetupNotificationAsync(2, true);
await dynamicDeviceWhichIsAMotor.SetupNotificationAsync(3, true);
await dynamicDeviceWhichIsAMotor.UnlockFromCombinedModeNotificationSetupAsync(true);

using var disposable = dynamicDeviceWhichIsAMotor.SingleValueMode<int>(2).Observable.Subscribe(x => logger.LogWarning($"Position: {x.SI} / {x.Pct}"));
using var disposable2 = dynamicDeviceWhichIsAMotor.SingleValueMode<short>(3).Observable.Subscribe(x => logger.LogWarning($"Absolute Position: {x.SI} / {x.Pct}"));

await dynamicDeviceWhichIsAMotor.SingleValueMode<sbyte>(0).WriteDirectModeDataAsync(0x64); // That is StartPower on a motor

await Task.Delay(2_000);

await dynamicDeviceWhichIsAMotor.SingleValueMode<sbyte>(0).WriteDirectModeDataAsync(0x00); // That is Stop on a motor

await Task.Delay(10_000);

await technicMediumHub.SwitchOffAsync();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static (PoweredUpHost host, IServiceProvider serviceProvider, Hub selecte
builder.AddFilter("SharpBrick.PoweredUp.Bluetooth.BluetoothKernel", LogLevel.Debug);
}
})
.AddPoweredUp()
.BuildServiceProvider();


Expand Down
5 changes: 4 additions & 1 deletion examples/SharpBrick.PoweredUp.Examples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ static async Task Main(string[] args)
{
var enableTrace = (args.Length > 0 && args[0] == "--trace");

await Example.ExampleDynamicDevice.ExecuteAsync(enableTrace); return;

var (host, serviceProvider, selectedHub) = Example.ExampleHubDiscover.CreateHostAndDiscover(enableTrace);

if (selectedHub != null)
{
//await Example.ExampleColors.ExecuteAsync(host, serviceProvider, selectedHub);
//await Example.ExampleMotorControl.ExecuteAsync(host, serviceProvider, selectedHub);
await Example.ExampleMotorInputAbsolutePosition.ExecuteAsync(host, serviceProvider, selectedHub);
//await Example.ExampleMotorInputAbsolutePosition.ExecuteAsync(host, serviceProvider, selectedHub);
//await Example.ExampleMotorVirtualPort.ExecuteAsync(host, serviceProvider, selectedHub);
//await Example.ExampleHubActions.ExecuteAsync(host, serviceProvider, selectedHub);
//await Example.ExampleTechnicMediumHubAccelerometer.ExecuteAsync(host, serviceProvider, selectedHub);
Expand All @@ -27,6 +29,7 @@ static async Task Main(string[] args)
//await Example.ExampleHubAlert.ExecuteAsync(host, serviceProvider, selectedHub);
//await Example.ExampleTechnicMediumHubTiltSensor.ExecuteAsync(host, serviceProvider, selectedHub);
//await Example.ExampleTechnicMediumHubTiltSensorImpacts.ExecuteAsync(host, serviceProvider, selectedHub);

}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/SharpBrick.PoweredUp.Cli/Commands/DevicesList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@
using SharpBrick.PoweredUp.Protocol.Messages;
using SharpBrick.PoweredUp.WinRT;
using SharpBrick.PoweredUp.Utils;
using Microsoft.Extensions.DependencyInjection;

namespace SharpBrick.PoweredUp.Cli
{
public static class DevicesList
{
public static async Task ExecuteAsync(ILoggerFactory loggerFactory, WinRTPoweredUpBluetoothAdapter poweredUpBluetoothAdapter, ulong bluetoothAddress, bool enableTrace)
{
var serviceProvider = new ServiceCollection()
.AddSingleton<ILoggerFactory>(loggerFactory)
.BuildServiceProvider();

using (var protocol = new PoweredUpProtocol(
new BluetoothKernel(poweredUpBluetoothAdapter, bluetoothAddress, loggerFactory.CreateLogger<BluetoothKernel>()),
loggerFactory.CreateLogger<PoweredUpProtocol>()))
serviceProvider))
{
var discoverPorts = new DiscoverPorts(protocol, logger: loggerFactory.CreateLogger<DiscoverPorts>()); // register to upstream

Expand Down
9 changes: 6 additions & 3 deletions src/SharpBrick.PoweredUp.Cli/Commands/DumpStaticPortInfo.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SharpBrick.PoweredUp.Bluetooth;
using SharpBrick.PoweredUp.Functions;
using SharpBrick.PoweredUp.Protocol.Knowledge;
using SharpBrick.PoweredUp.Protocol;
using SharpBrick.PoweredUp.Protocol.Messages;
using SharpBrick.PoweredUp.Utils;
Expand All @@ -17,9 +16,13 @@ public static class DumpStaticPortInfo
{
public static async Task ExecuteAsync(ILoggerFactory loggerFactory, WinRTPoweredUpBluetoothAdapter poweredUpBluetoothAdapter, ulong bluetoothAddress, byte portId, bool enableTrace)
{
var serviceProvider = new ServiceCollection()
.AddSingleton<ILoggerFactory>(loggerFactory)
.BuildServiceProvider();

using (var protocol = new PoweredUpProtocol(
new BluetoothKernel(poweredUpBluetoothAdapter, bluetoothAddress, loggerFactory.CreateLogger<BluetoothKernel>()),
loggerFactory.CreateLogger<PoweredUpProtocol>()))
serviceProvider))
{
var discoverPorts = new DiscoverPorts(protocol, logger: loggerFactory.CreateLogger<DiscoverPorts>());

Expand Down
7 changes: 6 additions & 1 deletion src/SharpBrick.PoweredUp/Devices/Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ public Device(IPoweredUpProtocol protocol, byte hubId, byte portId)
BuildModes();
}

private void BuildModes()
public SingleValueMode<TPayload> SingleValueMode<TPayload>(byte modeIndex)
=> _modes.TryGetValue(modeIndex, out var mode) ? mode as SingleValueMode<TPayload> : default;
public MultiValueMode<TPayload> MultiValueMode<TPayload>(byte modeIndex)
=> _modes.TryGetValue(modeIndex, out var mode) ? mode as MultiValueMode<TPayload> : default;

protected void BuildModes()
{
foreach (var mode in _modes.Values)
{
Expand Down
20 changes: 14 additions & 6 deletions src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@

namespace SharpBrick.PoweredUp.Devices
{
public class DeviceFactory
public class DeviceFactory : IDeviceFactory
{
public static IPoweredUpDevice Create(DeviceType type)
=> (IPoweredUpDevice)Activator.CreateInstance(GetTypeFromDeviceType(type));
public IPoweredUpDevice Create(DeviceType deviceType)
{
var type = GetTypeFromDeviceType(deviceType);

public static IPoweredUpDevice CreateConnected(DeviceType type, IPoweredUpProtocol protocol, byte hubId, byte portId)
=> (IPoweredUpDevice)Activator.CreateInstance(GetTypeFromDeviceType(type), protocol, hubId, portId);
return (type == null) ? null : (IPoweredUpDevice)Activator.CreateInstance(type);
}

public static Type GetTypeFromDeviceType(DeviceType deviceType)
public IPoweredUpDevice CreateConnected(DeviceType deviceType, IPoweredUpProtocol protocol, byte hubId, byte portId)
{
var type = GetTypeFromDeviceType(deviceType);

return (type == null) ? null : (IPoweredUpDevice)Activator.CreateInstance(type, protocol, hubId, portId);
}

public Type GetTypeFromDeviceType(DeviceType deviceType)
=> deviceType switch
{
DeviceType.Voltage => typeof(Voltage),
Expand Down
31 changes: 31 additions & 0 deletions src/SharpBrick.PoweredUp/Devices/DynamicDevice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SharpBrick.PoweredUp.Functions;
using SharpBrick.PoweredUp.Protocol;
using SharpBrick.PoweredUp.Protocol.Messages;

namespace SharpBrick.PoweredUp
{
public class DynamicDevice : Device, IPoweredUpDevice
{
public DynamicDevice()
{ }

public DynamicDevice(IPoweredUpProtocol protocol, byte hubId, byte portId)
: base(protocol, hubId, portId)
{ }

public async Task DiscoverAsync()
{
var discoverPortsFunction = new DiscoverPorts(_protocol);

await discoverPortsFunction.ExecuteAsync(_portId);

BuildModes();
}

public IEnumerable<byte[]> GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion)
=> Array.Empty<byte[]>();
}
}
10 changes: 10 additions & 0 deletions src/SharpBrick.PoweredUp/Devices/IDeviceFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using SharpBrick.PoweredUp.Protocol;

namespace SharpBrick.PoweredUp.Devices
{
public interface IDeviceFactory
{
IPoweredUpDevice Create(DeviceType deviceType);
IPoweredUpDevice CreateConnected(DeviceType deviceType, IPoweredUpProtocol protocol, byte hubId, byte portId);
}
}
16 changes: 16 additions & 0 deletions src/SharpBrick.PoweredUp/Devices/Mode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ protected Mode(IPoweredUpProtocol protocol, PortModeInfo modeInfo, IObservable<P
this._modeValueObservable = modeValueObservable;
}

public async Task<PortFeedback> WriteDirectModeDataAsync(params byte[] data)
{
AssertIsConnected();

var response = await _protocol.SendPortOutputCommandAsync(new GenericWriteDirectModeDataMessage(_modeInfo.ModeIndex)
{
HubId = _modeInfo.HubId,
PortId = _modeInfo.PortId,
StartupInformation = PortOutputCommandStartupInformation.ExecuteImmediately,
CompletionInformation = PortOutputCommandCompletionInformation.CommandFeedback,
Data = data,
});

return response;
}

protected void ObserveOnLocalProperty<T>(IObservable<T> modeObservable, params Action<T>[] updaters)
{
var disposable = modeObservable.Subscribe(v =>
Expand Down
3 changes: 1 addition & 2 deletions src/SharpBrick.PoweredUp/Functions/DiscoverPorts.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reactive;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using SharpBrick.PoweredUp.Protocol;
Expand Down Expand Up @@ -47,7 +46,7 @@ public async Task ExecuteAsync(byte portFilter = 0xFF)

_stageTwoExpected = _protocol.Knowledge.Hub(_hubId).Ports.Values.Where(p => portFilter == 0xFF || p.PortId == portFilter).Count();

Console.WriteLine($"Number of Ports: {_stageTwoExpected}");
_logger?.LogInformation($"Number of Ports: {_stageTwoExpected}");

foreach (var port in _protocol.Knowledge.Hub(_hubId).Ports.Values.Where(p => portFilter == 0xFF || p.PortId == portFilter))
{
Expand Down
2 changes: 1 addition & 1 deletion src/SharpBrick.PoweredUp/Hubs/Hub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void ConnectWithBluetoothAdapter(IPoweredUpBluetoothAdapter poweredUpBlue
_logger?.LogDebug("Init Hub with BluetoothKernel");
var kernel = new BluetoothKernel(poweredUpBluetoothAdapter, bluetoothAddress, loggerFactory.CreateLogger<BluetoothKernel>());
_logger?.LogDebug("Init Hub with PoweredUpProtocol");
Protocol = new PoweredUpProtocol(kernel, loggerFactory.CreateLogger<PoweredUpProtocol>());
Protocol = new PoweredUpProtocol(kernel, ServiceProvider);

SetupOnHubChange();
SetupOnPortChangeObservable(Protocol.UpstreamMessages);
Expand Down
9 changes: 7 additions & 2 deletions src/SharpBrick.PoweredUp/Hubs/Hub_Ports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using SharpBrick.PoweredUp.Devices;
using SharpBrick.PoweredUp.Protocol;
using SharpBrick.PoweredUp.Protocol.Messages;
Expand Down Expand Up @@ -100,7 +101,9 @@ private void OnHubAttachedIOMessage(HubAttachedIOMessage hubAttachedIO)
case HubAttachedIOForAttachedDeviceMessage attachedDeviceMessage:
port = Port(attachedDeviceMessage.PortId);

var device = DeviceFactory.CreateConnected(attachedDeviceMessage.IOTypeId, Protocol, attachedDeviceMessage.HubId, attachedDeviceMessage.PortId);
var deviceFactory = ServiceProvider.GetService<IDeviceFactory>();

var device = deviceFactory.CreateConnected(attachedDeviceMessage.IOTypeId, Protocol, attachedDeviceMessage.HubId, attachedDeviceMessage.PortId);

port.AttachDevice(device, attachedDeviceMessage.IOTypeId);
break;
Expand Down Expand Up @@ -128,7 +131,9 @@ private Port OnHubAttachedVirtualIOMessage(HubAttachedIOForAttachedVirtualDevice

_ports[createdVirtualPort.PortId] = createdVirtualPort;

var deviceOnVirtualPort = DeviceFactory.CreateConnected(attachedVirtualDeviceMessage.IOTypeId, Protocol, attachedVirtualDeviceMessage.HubId, attachedVirtualDeviceMessage.PortId);
var deviceFactory = ServiceProvider.GetService<IDeviceFactory>();

var deviceOnVirtualPort = deviceFactory.CreateConnected(attachedVirtualDeviceMessage.IOTypeId, Protocol, attachedVirtualDeviceMessage.HubId, attachedVirtualDeviceMessage.PortId);

createdVirtualPort.AttachDevice(deviceOnVirtualPort, attachedVirtualDeviceMessage.IOTypeId);

Expand Down
12 changes: 12 additions & 0 deletions src/SharpBrick.PoweredUp/IServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.Extensions.DependencyInjection;
using SharpBrick.PoweredUp.Devices;

namespace SharpBrick.PoweredUp
{
public static class IServiceCollectionExtensions
{
public static IServiceCollection AddPoweredUp(this IServiceCollection self)
=> self
.AddSingleton<IDeviceFactory, DeviceFactory>();
}
}
Loading

0 comments on commit 2c56fe6

Please sign in to comment.