Skip to content

Commit

Permalink
Merge pull request #669 from chrimaka/668-return-error-codes
Browse files Browse the repository at this point in the history
return error codes to application
  • Loading branch information
janusw committed May 18, 2023
2 parents 48670d2 + 84bf80e commit 77ee52e
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 42 deletions.
6 changes: 3 additions & 3 deletions Source/Plugin.BLE.Tests/Mocks/CharacteristicMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ protected override Task<IReadOnlyList<IDescriptor>> GetDescriptorsNativeAsync()
throw new NotImplementedException();
}

protected override Task<byte[]> ReadNativeAsync()
protected override Task<(byte[] data, int resultCode)> ReadNativeAsync()
{
throw new NotImplementedException();
}

protected override Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
protected override Task<int> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
{
WriteHistory.Add(new WriteOperation(data, writeType));
return Task.FromResult(true);
return Task.FromResult(0);
}

protected override Task StartUpdatesNativeAsync(CancellationToken cancellationToken = default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ namespace Plugin.BLE.Android.CallbackEventArgs
public class CharacteristicReadCallbackEventArgs
{
public BluetoothGattCharacteristic Characteristic { get; }
public GattStatus Status { get; }

public CharacteristicReadCallbackEventArgs(BluetoothGattCharacteristic characteristic)
public CharacteristicReadCallbackEventArgs(BluetoothGattCharacteristic characteristic, GattStatus status)
{
Characteristic = characteristic;
Status = status;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ public class CharacteristicWriteCallbackEventArgs
{
public BluetoothGattCharacteristic Characteristic { get; }
public Exception Exception { get; }
public GattStatus Status { get; }

public CharacteristicWriteCallbackEventArgs(BluetoothGattCharacteristic characteristic, Exception exception = null)
public CharacteristicWriteCallbackEventArgs(BluetoothGattCharacteristic characteristic, GattStatus status, Exception exception = null)
{
Characteristic = characteristic;
Status = status;
Exception = exception;
}
}
Expand Down
13 changes: 7 additions & 6 deletions Source/Plugin.BLE/Android/Characteristic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@ protected override Task<IReadOnlyList<IDescriptor>> GetDescriptorsNativeAsync()
return Task.FromResult<IReadOnlyList<IDescriptor>>(NativeCharacteristic.Descriptors.Select(item => new Descriptor(item, _gatt, _gattCallback, this)).Cast<IDescriptor>().ToList());
}

protected override async Task<byte[]> ReadNativeAsync()
protected override async Task<(byte[] data, int resultCode)> ReadNativeAsync()
{
return await TaskBuilder.FromEvent<byte[], EventHandler<CharacteristicReadCallbackEventArgs>, EventHandler>(
return await TaskBuilder.FromEvent<(byte[] data, int resultCode), EventHandler<CharacteristicReadCallbackEventArgs>, EventHandler>(
execute: ReadInternal,
getCompleteHandler: (complete, reject) => ((sender, args) =>
{
if (args.Characteristic.Uuid == NativeCharacteristic.Uuid)
{
complete(args.Characteristic.GetValue());
int resultCode = (int)args.Status;
complete((args.Characteristic.GetValue(), resultCode));
}
}),
subscribeComplete: handler => _gattCallback.CharacteristicValueRead += handler,
Expand All @@ -71,17 +72,17 @@ void ReadInternal()
}
}

protected override async Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
protected override async Task<int> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
{
NativeCharacteristic.WriteType = writeType.ToNative();

return await TaskBuilder.FromEvent<bool, EventHandler<CharacteristicWriteCallbackEventArgs>, EventHandler>(
return await TaskBuilder.FromEvent<int, EventHandler<CharacteristicWriteCallbackEventArgs>, EventHandler>(
execute: () => InternalWrite(data),
getCompleteHandler: (complete, reject) => ((sender, args) =>
{
if (args.Characteristic.Uuid == NativeCharacteristic.Uuid)
{
complete(args.Exception == null);
complete((int)args.Status);
}
}),
subscribeComplete: handler => _gattCallback.CharacteristicValueWritten += handler,
Expand Down
11 changes: 8 additions & 3 deletions Source/Plugin.BLE/Android/GattCallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public override void OnCharacteristicRead(BluetoothGatt gatt, BluetoothGattChara

Trace.Message("OnCharacteristicRead: value {0}; status {1}", characteristic.GetValue().ToHexString(), status);

CharacteristicValueRead?.Invoke(this, new CharacteristicReadCallbackEventArgs(characteristic));
CharacteristicValueRead?.Invoke(this, new CharacteristicReadCallbackEventArgs(characteristic, status));
}

public override void OnCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
Expand All @@ -183,7 +183,7 @@ public override void OnCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCh

Trace.Message("OnCharacteristicChanged: value {0}", characteristic.GetValue().ToHexString());

CharacteristicValueUpdated?.Invoke(this, new CharacteristicReadCallbackEventArgs(characteristic));
CharacteristicValueUpdated?.Invoke(this, new CharacteristicReadCallbackEventArgs(characteristic, GattStatus.Success));
}

public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, GattStatus status)
Expand All @@ -192,7 +192,7 @@ public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattChar

Trace.Message("OnCharacteristicWrite: value {0} status {1}", characteristic.GetValue().ToHexString(), status);

CharacteristicValueWritten?.Invoke(this, new CharacteristicWriteCallbackEventArgs(characteristic, GetExceptionFromGattStatus(status)));
CharacteristicValueWritten?.Invoke(this, new CharacteristicWriteCallbackEventArgs(characteristic, status, GetExceptionFromGattStatus(status)));
}

public override void OnReliableWriteCompleted(BluetoothGatt gatt, GattStatus status)
Expand Down Expand Up @@ -255,6 +255,11 @@ private Exception GetExceptionFromGattStatus(GattStatus status)
break;
case GattStatus.Success:
break;
// default path to handle errors that are non standard BLE ones, but device priprietary ones.
default:
exception = new Exception($"GattStatus: {(int)status}");
break;

}

return exception;
Expand Down
43 changes: 29 additions & 14 deletions Source/Plugin.BLE/Apple/Characteristic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ protected override Task<IReadOnlyList<IDescriptor>> GetDescriptorsNativeAsync()
unsubscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral -= handler);
}

protected override Task<byte[]> ReadNativeAsync()
protected override Task<(byte[] data, int resultCode)> ReadNativeAsync()
{
var exception = new Exception($"Device '{Service.Device.Id}' disconnected while reading characteristic with {Id}.");

return TaskBuilder.FromEvent<byte[], EventHandler<CBCharacteristicEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
return TaskBuilder.FromEvent<(byte[] data, int resultCode), EventHandler<CBCharacteristicEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
execute: () =>
{
if (_parentDevice.State != CBPeripheralState.Connected)
Expand All @@ -101,15 +101,18 @@ protected override Task<byte[]> ReadNativeAsync()
{
if (args.Characteristic.UUID != NativeCharacteristic.UUID)
return;
#if false
// don't throw an error on expection, as we want to properly return errors
if (args.Error != null)
{
reject(new CharacteristicReadException($"Read async error: {args.Error.Description}"));
}
else
#endif
{
Trace.Message($"Read characterteristic value: {Value?.ToHexString()}");
complete(Value);
int resultCode = (args.Error == null) ? 0 : NSErrorToGattStatus(args.Error);
complete((Value, resultCode));
}
},
subscribeComplete: handler => _parentDevice.UpdatedCharacterteristicValue += handler,
Expand All @@ -123,15 +126,15 @@ protected override Task<byte[]> ReadNativeAsync()
unsubscribeReject: handler => _bleCentralManagerDelegate.DisconnectedPeripheral -= handler);
}

protected override Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
protected override Task<int> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
{
var exception = new Exception($"Device {Service.Device.Id} disconnected while writing characteristic with {Id}.");

Task<bool> task;
Task<int> task;
if (writeType.ToNative() == CBCharacteristicWriteType.WithResponse)
{
task = TaskBuilder.FromEvent<bool, EventHandler<CBCharacteristicEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
execute: () =>
task = TaskBuilder.FromEvent<int, EventHandler<CBCharacteristicEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
execute: () =>
{
if (_parentDevice.State != CBPeripheralState.Connected)
throw exception;
Expand All @@ -141,7 +144,7 @@ protected override Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteT
if (args.Characteristic.UUID != NativeCharacteristic.UUID)
return;
complete(args.Error == null);
complete((args.Error == null) ? 0 : NSErrorToGattStatus(args.Error));
},
subscribeComplete: handler => _parentDevice.WroteCharacteristicValue += handler,
unsubscribeComplete: handler => _parentDevice.WroteCharacteristicValue -= handler,
Expand All @@ -166,15 +169,15 @@ protected override Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteT
#endif
&& _parentDevice.CanSendWriteWithoutResponse)
{
task = TaskBuilder.FromEvent<bool, EventHandler, EventHandler<CBPeripheralErrorEventArgs>>(
task = TaskBuilder.FromEvent<int, EventHandler, EventHandler<CBPeripheralErrorEventArgs>>(
execute: () =>
{
if (_parentDevice.State != CBPeripheralState.Connected)
throw exception;
},
getCompleteHandler: (complete, reject) => (sender, args) =>
{
complete(true);
complete(0);
},
subscribeComplete: handler => _parentDevice.IsReadyToSendWriteWithoutResponse += handler,
unsubscribeComplete: handler => _parentDevice.IsReadyToSendWriteWithoutResponse -= handler,
Expand All @@ -188,7 +191,7 @@ protected override Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteT

}
else {
task = Task.FromResult(true);
task = Task.FromResult(0);
}
}

Expand All @@ -206,7 +209,7 @@ protected override Task StartUpdatesNativeAsync(CancellationToken cancellationTo
_parentDevice.UpdatedCharacterteristicValue += UpdatedNotify;

//https://developer.apple.com/reference/corebluetooth/cbperipheral/1518949-setnotifyvalue
return TaskBuilder.FromEvent<bool, EventHandler<CBCharacteristicEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
return TaskBuilder.FromEvent<int, EventHandler<CBCharacteristicEventArgs>, EventHandler<CBPeripheralErrorEventArgs>>(
execute: () =>
{
if (_parentDevice.State != CBPeripheralState.Connected)
Expand All @@ -226,7 +229,7 @@ protected override Task StartUpdatesNativeAsync(CancellationToken cancellationTo
else
{
Trace.Message($"StartUpdates IsNotifying: {args.Characteristic.IsNotifying}");
complete(args.Characteristic.IsNotifying);
complete(args.Characteristic.IsNotifying ? 0 : NSErrorToGattStatus(args.Error));
}
},
subscribeComplete: handler => _parentDevice.UpdatedNotificationState += handler,
Expand Down Expand Up @@ -282,6 +285,18 @@ protected override Task StopUpdatesNativeAsync(CancellationToken cancellationTok
token: cancellationToken);
}

protected int NSErrorToGattStatus(NSError error)
{
switch (error.Domain)
{
case "CBATTErrorDomain":
return (int)error.Code;
case "CBErrorDomain":
default:
return 0x101;
}
}

private void UpdatedNotify(object sender, CBCharacteristicEventArgs e)
{
if (e.Characteristic.UUID == NativeCharacteristic.UUID)
Expand Down
8 changes: 4 additions & 4 deletions Source/Plugin.BLE/Shared/CharacteristicBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ protected CharacteristicBase(IService service, TNativeCharacteristic nativeChara
/// <summary>
/// Reads the characteristic value from the device. The result is also stored inisde the Value property.
/// </summary>
public async Task<byte[]> ReadAsync(CancellationToken cancellationToken = default)
public async Task<(byte[] data, int resultCode)> ReadAsync(CancellationToken cancellationToken = default)
{
if (!CanRead)
{
Expand All @@ -131,7 +131,7 @@ public async Task<byte[]> ReadAsync(CancellationToken cancellationToken = defaul
/// </summary>
/// <param name="data">Data that should be written.</param>
/// <param name="cancellationToken"></param>
public async Task<bool> WriteAsync(byte[] data, CancellationToken cancellationToken = default)
public async Task<int> WriteAsync(byte[] data, CancellationToken cancellationToken = default)
{
if (data == null)
{
Expand Down Expand Up @@ -213,11 +213,11 @@ public async Task<IDescriptor> GetDescriptorAsync(Guid id, CancellationToken can
/// <summary>
/// Native implementation of <c>ReadAsync</c>.
/// </summary>
protected abstract Task<byte[]> ReadNativeAsync();
protected abstract Task<(byte[] data, int resultCode)> ReadNativeAsync();
/// <summary>
/// Native implementation of <c>WriteAsync</c>.
/// </summary>
protected abstract Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType);
protected abstract Task<int> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType);
/// <summary>
/// Native implementation of <c>StartUpdatesAsync</c>.
/// </summary>
Expand Down
12 changes: 6 additions & 6 deletions Source/Plugin.BLE/Shared/Contracts/ICharacteristic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,25 +79,25 @@ public interface ICharacteristic
/// Reads the characteristic value from the device. The result is also stored inisde the Value property.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>A task that represents the asynchronous read operation. The Result property will contain the read bytes.</returns>
/// <returns>A task that represents the asynchronous read operation. The Result property will contain a tuple with the read bytes and the ble result code.</returns>
/// <exception cref="InvalidOperationException">Thrown if characteristic doesn't support read. See: <see cref="CanRead"/></exception>
/// <exception cref="CharacteristicReadException">Thrown if the reading of the value failed.</exception>
Task<byte[]> ReadAsync(CancellationToken cancellationToken = default);
Task<(byte[] data, int resultCode)> ReadAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Sends <paramref name="data"/> as characteristic value to the device.
/// </summary>
/// <param name="data">Data that should be written.</param>
/// <param name="cancellationToken"></param>
/// <returns>
/// A task that represents the asynchronous read operation. The Task will finish after the value was written. The Result property will be <c>true</c> if the value
/// was written successful, otherwise <c>false</c>.
/// A task that represents the asynchronous write operation. The Task will finish after the value was written. The Result property will the errror
/// code sent by the BLE device. (0 = successful)
/// If the characteristic is write with response, the Task will finish if the value has been written.
/// If it is write without response, the task will immediately finish with <c>true</c>.
/// If it is write without response, the task will immediately finish with <c>0</c>.
/// </returns>
/// <exception cref="InvalidOperationException">Thrown if characteristic doesn't support write. See: <see cref="CanWrite"/></exception>
/// <exception cref="ArgumentNullException">Thrwon if <paramref name="data"/> is null.</exception>
Task<bool> WriteAsync(byte[] data, CancellationToken cancellationToken = default);
Task<int> WriteAsync(byte[] data, CancellationToken cancellationToken = default);

/// <summary>
/// Starts listening for notify events on this characteristic.
Expand Down
9 changes: 5 additions & 4 deletions Source/Plugin.BLE/Windows/Characteristic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ protected override async Task<IReadOnlyList<IDescriptor>> GetDescriptorsNativeAs
.ToList();
}

protected override async Task<byte[]> ReadNativeAsync()
protected override async Task<(byte[] data, int resultCode)> ReadNativeAsync()
{
var readResult = await NativeCharacteristic.ReadValueAsync(BleImplementation.CacheModeCharacteristicRead);
return _value = readResult.GetValueOrThrowIfError();
var _value = readResult.GetValueOrThrowIfError();
return (_value, (int)readResult.Status);
}

protected override async Task StartUpdatesNativeAsync(CancellationToken cancellationToken = default)
Expand All @@ -70,14 +71,14 @@ protected override async Task StopUpdatesNativeAsync(CancellationToken cancellat
result.ThrowIfError();
}

protected override async Task<bool> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
protected override async Task<int> WriteNativeAsync(byte[] data, CharacteristicWriteType writeType)
{
var result = await NativeCharacteristic.WriteValueWithResultAsync(
CryptographicBuffer.CreateFromByteArray(data),
writeType == CharacteristicWriteType.WithResponse ? GattWriteOption.WriteWithResponse : GattWriteOption.WriteWithoutResponse);

result.ThrowIfError();
return true;
return (int)result.Status;
}

/// <summary>
Expand Down

0 comments on commit 77ee52e

Please sign in to comment.