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

Change how BluetoothAdapter identifies devices #173

Merged
merged 3 commits into from
May 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class ExampleBluetoothByKnownAddress : BaseExample
// device needs to be switched on!
public override async Task DiscoverAsync(bool enableTrace)
{
var hub = Host.Create<TechnicMediumHub>(ChangeMe_BluetoothAddress);
var hub = await Host.CreateByStateAsync<TechnicMediumHub>(ChangeMe_BluetoothAddress);

SelectedHub = DirectlyConnectedHub = hub;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public class ExampleTwoHubsMotorControl : BaseExample
// devices need to be switched on!
public override async Task DiscoverAsync(bool enableTrace)
{
var hub1 = Host.Create<TechnicMediumHub>(BluetoothAddressHub1);
var hub2 = Host.Create<TwoPortHub>(BluetoothAddressHub2);
var hub1 = await Host.CreateByStateAsync<TechnicMediumHub>(BluetoothAddressHub1);
var hub2 = await Host.CreateByStateAsync<TwoPortHub>(BluetoothAddressHub2);
Log.LogInformation($"Press button on first Hub with address {BluetoothAddressHub1}");
await hub1.ConnectAsync();
Log.LogInformation($"Press button on second Hub with address {BluetoothAddressHub2}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class BlueGigaBLEPoweredUpBluetoothAdapater : IPoweredUpBluetoothAdapter,
/// The devices this adapter knows about (by Discovery or direct Connect)
/// </summary>
private ConcurrentDictionary<ulong, BlueGigaBLEPoweredUpBluetoothDevice> Devices { get; }
private ConcurrentDictionary<ulong, PoweredUpBluetoothDeviceInfo> DevicesInfo { get; }
private ConcurrentDictionary<ulong, PoweredUpBluetoothDeviceInfoWithMacAddress> DevicesInfo { get; }
#endregion

#region Constructor
Expand All @@ -48,7 +48,7 @@ public BlueGigaBLEPoweredUpBluetoothAdapater(IOptions<BlueGigaBLEOptions> option
BleModuleConnection = new BleModuleConnection(BgLib);
BleModuleConnection.Start(options.Value.COMPortName, 0);
Devices = new ConcurrentDictionary<ulong, BlueGigaBLEPoweredUpBluetoothDevice>();
DevicesInfo = new ConcurrentDictionary<ulong, PoweredUpBluetoothDeviceInfo>();
DevicesInfo = new ConcurrentDictionary<ulong, PoweredUpBluetoothDeviceInfoWithMacAddress>();
}
#endregion

Expand All @@ -61,7 +61,7 @@ public BlueGigaBLEPoweredUpBluetoothAdapater(IOptions<BlueGigaBLEOptions> option
/// </summary>
/// <param name="discoveryHandler">The handler form the SharpBrick.PoweredUp-core which shall be called when a device has been discovered</param>
/// <param name="cancellationToken">Cancelation-token to handle a cancel of the discovery</param>
public void Discover(Func<PoweredUpBluetoothDeviceInfo, Task> discoveryHandler, CancellationToken cancellationToken = default)
public void Discover(Func<IPoweredUpBluetoothDeviceInfo, Task> discoveryHandler, CancellationToken cancellationToken = default)
{
var bleDeviceDiscovery = new BleDeviceDiscovery(BgLib, BleModuleConnection);
var bleParsedData = new ConcurrentDictionary<ulong, List<BleAdvertisingData>>();
Expand Down Expand Up @@ -99,13 +99,13 @@ async void GAPScanResponseEvent(object sender, BleScanResponseReceivedEventArgs
&& actualListAdvertisingData.Any(y => y.Type == BleAdvertisingDataType.ManufacturerSpecificData)
&& actualListAdvertisingData.Any(z => z.Type == BleAdvertisingDataType.CompleteLocalName))
{
var deviceInfo = new PoweredUpBluetoothDeviceInfo
var deviceInfo = new PoweredUpBluetoothDeviceInfoWithMacAddress
{
BluetoothAddress = BlueGigaBLEHelper.ByteArrayToUlong(e.Address),
MacAddressAsUInt64 = BlueGigaBLEHelper.ByteArrayToUlong(e.Address),
ManufacturerData = actualListAdvertisingData.First(y => y.Type == BleAdvertisingDataType.ManufacturerSpecificData).Data.Skip(2).ToArray(), //PoweredUp only wants to have data starting with the button state (see 2. advertising in [Lego]
Name = actualListAdvertisingData.First(z => z.Type == BleAdvertisingDataType.CompleteLocalName).ToAsciiString()
};
_ = DevicesInfo.AddOrUpdate(deviceInfo.BluetoothAddress, deviceInfo, (key, oldvalue) => oldvalue = deviceInfo);
_ = DevicesInfo.AddOrUpdate(deviceInfo.MacAddressAsUInt64, deviceInfo, (key, oldvalue) => oldvalue = deviceInfo);
actualListAdvertisingData.Clear();
await discoveryHandler(deviceInfo);
}
Expand Down Expand Up @@ -135,8 +135,10 @@ async void GAPScanResponseEvent(object sender, BleScanResponseReceivedEventArgs
/// </summary>
/// <param name="bluetoothAddress"></param>
/// <returns></returns>
public async Task<IPoweredUpBluetoothDevice> GetDeviceAsync(ulong bluetoothAddress)
public async Task<IPoweredUpBluetoothDevice> GetDeviceAsync(IPoweredUpBluetoothDeviceInfo bluetoothDeviceInfo)
{
var bluetoothAddress = (bluetoothDeviceInfo is PoweredUpBluetoothDeviceInfoWithMacAddress local) ? local.MacAddressAsUInt64 : throw new ArgumentException("DeviceInfo not created by adapter", nameof(bluetoothDeviceInfo));

var bleDeviceManager = new BleDeviceManager(BgLib, BleModuleConnection);
var bluetoothAdressBytes = BlueGigaBLEHelper.UlongTo6ByteArray(bluetoothAddress);
var bleDevice = await bleDeviceManager.ConnectAsync(bluetoothAdressBytes, BleAddressType.Public);
Expand All @@ -148,6 +150,16 @@ public async Task<IPoweredUpBluetoothDevice> GetDeviceAsync(ulong bluetoothAddre
await Devices.AddOrUpdate(device.DeviceAdress, device, (key, oldvalue) => oldvalue = device).LogInfosAsync();
return await Task.FromResult(Devices[bluetoothAddress]);
}

public Task<IPoweredUpBluetoothDeviceInfo> CreateDeviceInfoByKnownStateAsync(object state)
=> Task.FromResult<IPoweredUpBluetoothDeviceInfo>(state switch
{
ulong address => new PoweredUpBluetoothDeviceInfoWithMacAddress
{
MacAddressAsUInt64 = address,
},
_ => null,
});
#endregion
#region IDisposable
private bool disposedValue = false;
Expand Down Expand Up @@ -227,7 +239,7 @@ public async Task<string> GetLogInfosAsync(int indent)
var innerindentStr = indentStr + "\t";
foreach (var device in DevicesInfo)
{
_ = stringToLog.Append($"{innerindentStr}Bluetooth-Adress (ulong): {device.Value.BluetoothAddress}" + Environment.NewLine + $"{innerindentStr}Bluetooth-Name: {device.Value.Name}" + Environment.NewLine + $"{innerindentStr}Bluetooth-ManufacturerData (decimal): {BlueGigaBLEHelper.ByteArrayToNumberString(device.Value.ManufacturerData)}" + Environment.NewLine + $"{innerindentStr}Bluetooth-ManufacturerData (hex): {BlueGigaBLEHelper.ByteArrayToHexString(device.Value.ManufacturerData)}" + Environment.NewLine);
_ = stringToLog.Append($"{innerindentStr}Bluetooth-Adress (ulong): {device.Value.MacAddressAsUInt64}" + Environment.NewLine + $"{innerindentStr}Bluetooth-Name: {device.Value.Name}" + Environment.NewLine + $"{innerindentStr}Bluetooth-ManufacturerData (decimal): {BlueGigaBLEHelper.ByteArrayToNumberString(device.Value.ManufacturerData)}" + Environment.NewLine + $"{innerindentStr}Bluetooth-ManufacturerData (hex): {BlueGigaBLEHelper.ByteArrayToHexString(device.Value.ManufacturerData)}" + Environment.NewLine);
}
_ = stringToLog.Append($"{indentStr}End of devices which have been found by Discovery (not neccessarily connected){Environment.NewLine}");
}
Expand Down
36 changes: 21 additions & 15 deletions src/SharpBrick.PoweredUp.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ static async Task<int> Main(string[] args)
try
{
var serviceProvider = CreateServiceProvider(configuration);
(ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService<IPoweredUpBluetoothAdapter>());
var (bluetoothDeviceInfo, systemType) = FindAndSelectHub(serviceProvider.GetService<IPoweredUpBluetoothAdapter>());

if (bluetoothAddress == 0)
if (bluetoothDeviceInfo == null)
{
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())
Expand All @@ -84,7 +86,7 @@ static async Task<int> Main(string[] args)

await AddTraceWriterAsync(scopedServiceProvider, enableTrace);

scopedServiceProvider.GetService<BluetoothKernel>().BluetoothAddress = bluetoothAddress;
scopedServiceProvider.GetService<BluetoothKernel>().BluetoothDeviceInfo = bluetoothDeviceInfo;

var deviceListCli = scopedServiceProvider.GetService<DevicesList>(); // ServiceLocator ok: transient factory

Expand Down Expand Up @@ -122,10 +124,12 @@ static async Task<int> Main(string[] args)
var headerEnabled = headerOption.Values.Count > 0;

var serviceProvider = CreateServiceProvider(configuration);
(ulong bluetoothAddress, SystemType systemType) = FindAndSelectHub(serviceProvider.GetService<IPoweredUpBluetoothAdapter>());
var (bluetoothDeviceInfo, systemType) = FindAndSelectHub(serviceProvider.GetService<IPoweredUpBluetoothAdapter>());

if (bluetoothAddress == 0)
if (bluetoothDeviceInfo == null)
{
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())
Expand All @@ -134,7 +138,7 @@ static async Task<int> Main(string[] args)

await AddTraceWriterAsync(scopedServiceProvider, enableTrace);

scopedServiceProvider.GetService<BluetoothKernel>().BluetoothAddress = bluetoothAddress;
scopedServiceProvider.GetService<BluetoothKernel>().BluetoothDeviceInfo = bluetoothDeviceInfo;

var dumpStaticPortInfoCommand = scopedServiceProvider.GetService<DumpStaticPortInfo>(); // ServiceLocator ok: transient factory

Expand Down Expand Up @@ -275,24 +279,26 @@ public static async Task AddTraceWriterAsync(IServiceProvider serviceProvider, b
}
}

private static (ulong bluetoothAddress, SystemType systemType) FindAndSelectHub(IPoweredUpBluetoothAdapter poweredUpBluetoothAdapter)
private static (IPoweredUpBluetoothDeviceInfo bluetoothDeviceInfo, SystemType systemType) FindAndSelectHub(IPoweredUpBluetoothAdapter poweredUpBluetoothAdapter)
{
ulong resultBluetooth = 0;
IPoweredUpBluetoothDeviceInfo resultDeviceInfo = default;
SystemType resultSystemType = default;
var devices = new ConcurrentBag<(int key, ulong bluetoothAddresss, PoweredUpHubManufacturerData deviceType)>();
var devices = new ConcurrentBag<(int key, IPoweredUpBluetoothDeviceInfo bluetoothDeviceInfo, PoweredUpHubManufacturerData deviceType)>();
var cts = new CancellationTokenSource();
int idx = 1;

Console.WriteLine("Scan Started. Please select the Hub (using a number keys or 'q' to terminate):");

poweredUpBluetoothAdapter.Discover(info =>
{
if (devices.FirstOrDefault(kv => kv.bluetoothAddresss == info.BluetoothAddress) == default)
if (devices.FirstOrDefault(kv => kv.bluetoothDeviceInfo.Equals(info)) == default)
{
var deviceType = (PoweredUpHubManufacturerData)info.ManufacturerData[1];
devices.Add((idx, info.BluetoothAddress, deviceType));
devices.Add((idx, info, deviceType));

var text = (info is IPoweredUpBluetoothDeviceInfoWithMacAddress mac) ? mac.ToIdentificationString() : "not revealed";

Console.WriteLine($"{idx}: {(PoweredUpHubManufacturerData)info.ManufacturerData[1]} (with address {info.BluetoothAddress})");
Console.WriteLine($"{idx}: {(PoweredUpHubManufacturerData)info.ManufacturerData[1]} (with address {text})");

idx++;
}
Expand All @@ -309,16 +315,16 @@ private static (ulong bluetoothAddress, SystemType systemType) FindAndSelectHub(
{
var selected = devices.FirstOrDefault(kv => kv.key == key);

resultBluetooth = selected.bluetoothAddresss; // default is 0
resultDeviceInfo = selected.bluetoothDeviceInfo;
resultSystemType = (SystemType)selected.deviceType;

if (resultBluetooth != default)
if (resultDeviceInfo != default)
{
Console.WriteLine($"Selected {selected.deviceType} with key {selected.key}");
}
}

return (resultBluetooth, resultSystemType);
return (resultDeviceInfo, resultSystemType);
}
}
}
8 changes: 5 additions & 3 deletions src/SharpBrick.PoweredUp.Mobile/NativeDeviceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
{
public class NativeDeviceInfo
{
public string MacAddress { get; set; }

public ulong MacAddressNumeric { get; set; }
/// <summary>
/// In Android, the MacAddressString will be returned
/// In iOS the unique identifier is used
/// </summary>
public string DeviceIdentifier { get; set; }
}
}
69 changes: 69 additions & 0 deletions src/SharpBrick.PoweredUp.Mobile/XamarinBluetoothDeviceInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Runtime.InteropServices;
using SharpBrick.PoweredUp.Bluetooth;
using SharpBrick.PoweredUp.Utils;

namespace SharpBrick.PoweredUp.Mobile
{
public class XamarinBluetoothDeviceInfo : IPoweredUpBluetoothDeviceInfo, IPoweredUpBluetoothDeviceInfoWithMacAddress
{
private string _deviceIdentifier;

public string DeviceIdentifier
{
get => _deviceIdentifier;
set
{
_deviceIdentifier = value;
UpdateMacAddress();
}
}

public string Name { get; set; }

public byte[] ManufacturerData { get; set; }

public byte[] MacAddress { get; private set; } = new byte[0];

public ulong MacAddressAsUInt64 { get; private set; } = 0;

public bool Equals(IPoweredUpBluetoothDeviceInfo other)
{
if (other != null && other is XamarinBluetoothDeviceInfo otherXamarin)
{
return this.DeviceIdentifier == otherXamarin.DeviceIdentifier;
}
else
{
return false;
}
}

private void UpdateMacAddress()
{
if (string.IsNullOrWhiteSpace(_deviceIdentifier)) return;

if (Guid.TryParse(_deviceIdentifier, out Guid result))
{
// we're on an iOS device -> no mac address is revealed
return;
}

if (_deviceIdentifier.Contains(":"))
{
MacAddress = BytesStringUtil.HexStringToByteArray(_deviceIdentifier);
MacAddressAsUInt64 = BytesStringUtil.HexStringToUInt64(_deviceIdentifier);
return;
}

if (ulong.TryParse(_deviceIdentifier, out ulong macAdressAsUlong))
{
MacAddressAsUInt64 = macAdressAsUlong;
MacAddress = BytesStringUtil.UInt64MacAddressToByteArray(macAdressAsUlong);
_deviceIdentifier = BytesStringUtil.ByteArrayToHexString(MacAddress);
}
}

}

}
Loading