ble-io: implement confirmation for writes
and make sure there is only one one IO operation in progress. In theory, we could have parallel IO operations on each characteristic, but I'm not sure yet if we really need to support that. implement reads enable notifications. Use BlockingQueue for write confirmations
This commit is contained in:
parent
1ca69ec414
commit
b4d769844c
15 changed files with 381 additions and 140 deletions
|
@ -1,55 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
|
||||
public class BleCommCallbacks extends BluetoothGattCallback {
|
||||
private final CountDownLatch serviceDiscoveryComplete;
|
||||
private final CountDownLatch connected;
|
||||
private final AAPSLogger aapsLogger;
|
||||
private final Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets;
|
||||
|
||||
public BleCommCallbacks(AAPSLogger aapsLogger, Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets) {
|
||||
this.serviceDiscoveryComplete = new CountDownLatch(1);
|
||||
this.connected = new CountDownLatch(1);
|
||||
this.aapsLogger = aapsLogger;
|
||||
this.incomingPackets = incomingPackets;
|
||||
}
|
||||
|
||||
|
||||
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||
super.onConnectionStateChange(gatt, status, newState);
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM,"OnConnectionStateChange discovered with status/state"+status+"/"+newState);
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
|
||||
this.connected.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
super.onServicesDiscovered(gatt, status);
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM,"OnServicesDiscovered with status"+status);
|
||||
if (status == gatt.GATT_SUCCESS) {
|
||||
this.serviceDiscoveryComplete.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void waitForConnection(int timeout_ms)
|
||||
throws InterruptedException {
|
||||
this.connected.await(timeout_ms, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void waitForServiceDiscovery(int timeout_ms)
|
||||
throws InterruptedException {
|
||||
this.serviceDiscoveryComplete.await(timeout_ms, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotSendBleException;
|
||||
|
||||
|
||||
public class BleIO {
|
||||
private static final int DEFAULT_IO_TIMEOUT_MS = 1000;
|
||||
|
||||
private final AAPSLogger aapsLogger;
|
||||
private final Map<CharacteristicType, BluetoothGattCharacteristic> chars;
|
||||
private final Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets;
|
||||
private final BluetoothGatt gatt;
|
||||
|
||||
|
||||
public BleIO(AAPSLogger aapsLogger, Map<CharacteristicType, BluetoothGattCharacteristic> chars, Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets, BluetoothGatt gatt) {
|
||||
this.aapsLogger = aapsLogger;
|
||||
this.chars = chars;
|
||||
this.incomingPackets = incomingPackets;
|
||||
this.gatt = gatt;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @param characteristic where to read from(CMD or DATA)
|
||||
* @return a byte array with the received data
|
||||
*/
|
||||
public byte[] receiveData(CharacteristicType characteristic) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @param characteristic where to write to(CMD or DATA)
|
||||
* @param packet the data to send
|
||||
* @throws CouldNotSendBleException
|
||||
*/
|
||||
public void sendAndConfirmData(CharacteristicType characteristic, byte[] packet)
|
||||
throws CouldNotSendBleException {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "BleIO: Sending data on " + characteristic.name() + " :: " +packet.toString());
|
||||
BluetoothGattCharacteristic ch = chars.get(characteristic);
|
||||
boolean set = ch.setValue(packet);
|
||||
if (!set) {
|
||||
throw new CouldNotSendBleException("setValue");
|
||||
}
|
||||
boolean sent = this.gatt.writeCharacteristic(ch);
|
||||
if (!sent) {
|
||||
throw new CouldNotSendBleException("writeCharacteristic");
|
||||
}
|
||||
// TODO: wait for confirmation callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before sending a new message.
|
||||
* The incoming queues should be empty, so we log when they are not.
|
||||
*/
|
||||
public void flushIncomingQueues() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable intentions on the characteristics.
|
||||
* This will signal the pod it can start sending back data
|
||||
*/
|
||||
public void readyToRead()
|
||||
throws CouldNotSendBleException {
|
||||
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import java.util.EnumMap;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
@ -20,7 +21,14 @@ import javax.inject.Singleton;
|
|||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommandHello;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.BleIOBusyException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmDescriptorWriteException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmWrite;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotEnableNotifications;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.DescriptorNotFoundException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleIO;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotSendBleException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.FailedToConnectException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailException;
|
||||
|
@ -62,18 +70,22 @@ public class BleManager implements OmnipodDashCommunicationManager {
|
|||
throws InterruptedException,
|
||||
ScanFailException,
|
||||
FailedToConnectException,
|
||||
CouldNotSendBleException {
|
||||
CouldNotSendBleException,
|
||||
BleIOBusyException,
|
||||
TimeoutException,
|
||||
CouldNotConfirmWrite, CouldNotEnableNotifications, DescriptorNotFoundException, CouldNotConfirmDescriptorWriteException {
|
||||
this.aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation");
|
||||
PodScanner podScanner = new PodScanner(this.aapsLogger, this.bluetoothAdapter);
|
||||
this.podAddress = podScanner.scanForPod(PodScanner.SCAN_FOR_SERVICE_UUID, PodScanner.POD_ID_NOT_ACTIVATED).getScanResult().getDevice().getAddress();
|
||||
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
|
||||
this.connect_();
|
||||
this.connect();
|
||||
}
|
||||
|
||||
public void connect_()
|
||||
public void connect()
|
||||
throws FailedToConnectException,
|
||||
CouldNotSendBleException,
|
||||
InterruptedException {
|
||||
InterruptedException,
|
||||
BleIOBusyException, TimeoutException, CouldNotConfirmWrite, CouldNotEnableNotifications, DescriptorNotFoundException, CouldNotConfirmDescriptorWriteException {
|
||||
// TODO: locking?
|
||||
|
||||
BluetoothDevice podDevice = this.bluetoothAdapter.getRemoteDevice(this.podAddress);
|
||||
|
@ -105,8 +117,9 @@ public class BleManager implements OmnipodDashCommunicationManager {
|
|||
ServiceDiscoverer discoverer = new ServiceDiscoverer(this.aapsLogger, gatt, bleCommCallbacks);
|
||||
Map<CharacteristicType, BluetoothGattCharacteristic> chars = discoverer.discoverServices();
|
||||
|
||||
this.bleio = new BleIO(aapsLogger, chars, incomingPackets, gatt);
|
||||
this.bleio = new BleIO(aapsLogger, chars, incomingPackets, gatt, bleCommCallbacks);
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM, "Saying hello to the pod");
|
||||
this.bleio.sendAndConfirmData(CharacteristicType.CMD, new BleCommandHello(CONTROLLER_ID).asByteArray());
|
||||
this.bleio.sendAndConfirmPacket(CharacteristicType.CMD, new BleCommandHello(CONTROLLER_ID).asByteArray());
|
||||
this.bleio.readyToRead();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import java.math.BigInteger;
|
|||
import java.util.UUID;
|
||||
|
||||
public enum CharacteristicType {
|
||||
CMD("1a7e-2441-e3ed-4464-8b7e-751e03d0dc5f"),
|
||||
DATA("1a7e-2442-e3ed-4464-8b7e-751e03d0dc5f");
|
||||
CMD("1a7e2441-e3ed-4464-8b7e-751e03d0dc5f"),
|
||||
DATA("1a7e2442-e3ed-4464-8b7e-751e03d0dc5f");
|
||||
|
||||
public final String value;
|
||||
|
||||
|
@ -13,7 +13,7 @@ public enum CharacteristicType {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
public static CharacteristicType byValue(byte value) {
|
||||
public static CharacteristicType byValue(String value) {
|
||||
for (CharacteristicType type : values()) {
|
||||
if (type.value.equals(value)) {
|
||||
return type;
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.UUID;
|
|||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CharacteristicNotFoundException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ServiceNotFoundException;
|
||||
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmDescriptorWriteException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmWrite;
|
||||
|
||||
public class BleCommCallbacks extends BluetoothGattCallback {
|
||||
private final static int WRITE_CONFIRM_TIMEOUT_MS = 10; // the other thread should be waiting for the exchange
|
||||
|
||||
private final CountDownLatch serviceDiscoveryComplete;
|
||||
private final CountDownLatch connected;
|
||||
private final AAPSLogger aapsLogger;
|
||||
private final Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets;
|
||||
private final BlockingQueue<CharacteristicWriteConfirmation> writeQueue;
|
||||
private final BlockingQueue<DescriptorWriteConfirmation> descriptorWriteQueue;
|
||||
|
||||
public BleCommCallbacks(AAPSLogger aapsLogger, Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets) {
|
||||
this.serviceDiscoveryComplete = new CountDownLatch(1);
|
||||
this.connected = new CountDownLatch(1);
|
||||
this.aapsLogger = aapsLogger;
|
||||
this.incomingPackets = incomingPackets;
|
||||
this.writeQueue = new LinkedBlockingQueue<>(1);
|
||||
this.descriptorWriteQueue = new LinkedBlockingQueue<>(1);
|
||||
}
|
||||
|
||||
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||
super.onConnectionStateChange(gatt, status, newState);
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM, "OnConnectionStateChange discovered with status/state" + status + "/" + newState);
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
|
||||
this.connected.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
super.onServicesDiscovered(gatt, status);
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM, "OnServicesDiscovered with status" + status);
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
this.serviceDiscoveryComplete.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void waitForConnection(int timeout_ms)
|
||||
throws InterruptedException {
|
||||
this.connected.await(timeout_ms, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void waitForServiceDiscovery(int timeout_ms)
|
||||
throws InterruptedException {
|
||||
this.serviceDiscoveryComplete.await(timeout_ms, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void confirmWrite(CharacteristicType characteristicType, byte[] expectedPayload, int timeout_ms) throws InterruptedException, TimeoutException, CouldNotConfirmWrite {
|
||||
CharacteristicWriteConfirmation received = this.writeQueue.poll(timeout_ms, TimeUnit.MILLISECONDS);
|
||||
if (received == null ) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
if (!Arrays.equals(expectedPayload, received.payload)) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Could not confirm write. Got " + received.payload + ".Excepted: " + expectedPayload + ". Status: "+received.status);
|
||||
throw new CouldNotConfirmWrite(expectedPayload, received.payload);
|
||||
}
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed write with value: " + received.payload);
|
||||
}
|
||||
|
||||
@Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status);
|
||||
byte[] received = null;
|
||||
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
received = characteristic.getValue();
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicWrite value " + characteristic.getStringValue(0));
|
||||
}
|
||||
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicWrite with status/char/value " +
|
||||
status + "/" +
|
||||
CharacteristicType.byValue(characteristic.getUuid().toString()) + "/" +
|
||||
received);
|
||||
try {
|
||||
if (this.writeQueue.size() > 0) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Write confirm queue should be empty. found: "+ this.writeQueue.size());
|
||||
this.writeQueue.clear();
|
||||
}
|
||||
boolean offered = this.writeQueue.offer(new CharacteristicWriteConfirmation(received, status), WRITE_CONFIRM_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
if (!offered) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed write confirmation");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Interrupted while sending write confirmation");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
super.onCharacteristicChanged(gatt, characteristic);
|
||||
|
||||
byte[] payload = characteristic.getValue();
|
||||
CharacteristicType characteristicType = CharacteristicType.byValue(characteristic.getUuid().toString());
|
||||
this.aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicChanged with char/value " +
|
||||
characteristicType + "/" +
|
||||
payload);
|
||||
this.incomingPackets.get(characteristicType).add(payload);
|
||||
}
|
||||
|
||||
public void confirmWriteDescriptor(String descriptorUUID, int timeout_ms) throws InterruptedException, CouldNotConfirmDescriptorWriteException {
|
||||
DescriptorWriteConfirmation confirmed = this.descriptorWriteQueue.poll(timeout_ms, TimeUnit.MILLISECONDS);
|
||||
if (!descriptorUUID.equals(confirmed.uuid)) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Could not confirm descriptor write. Got " + confirmed.uuid + ".Expected: " + descriptorUUID + ". Status: " + confirmed.status);
|
||||
throw new CouldNotConfirmDescriptorWriteException(confirmed.uuid, descriptorUUID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
super.onDescriptorWrite(gatt, descriptor, status);
|
||||
String uuid = null;
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
uuid = descriptor.getUuid().toString();
|
||||
}
|
||||
DescriptorWriteConfirmation confirmation = new DescriptorWriteConfirmation(status, uuid);
|
||||
try {
|
||||
if (this.descriptorWriteQueue.size() > 0) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Descriptor write queue should be empty, found: "+ this.descriptorWriteQueue.size());
|
||||
this.descriptorWriteQueue.clear();
|
||||
}
|
||||
|
||||
boolean offered = this.descriptorWriteQueue.offer(confirmation, WRITE_CONFIRM_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
if (!offered) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed descriptor write confirmation");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
this.aapsLogger.warn(LTag.PUMPBTCOMM, "Interrupted while sending descriptor write confirmation");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks;
|
||||
|
||||
public class CharacteristicWriteConfirmation {
|
||||
public byte[] payload;
|
||||
public int status;
|
||||
|
||||
public CharacteristicWriteConfirmation(byte[] payload, int status) {
|
||||
this.payload = payload;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks;
|
||||
|
||||
public class DescriptorWriteConfirmation {
|
||||
public int status;
|
||||
public String uuid;
|
||||
|
||||
public DescriptorWriteConfirmation(int status, String uuid) {
|
||||
this.status = status;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class BleIOBusyException extends Exception{
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class CouldNotConfirmDescriptorWriteException extends Exception{
|
||||
private final String received;
|
||||
private final String expected;
|
||||
|
||||
public CouldNotConfirmDescriptorWriteException(String received, String expected) {
|
||||
super();
|
||||
this.received = received;
|
||||
this.expected = expected;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class CouldNotConfirmWrite extends Exception {
|
||||
|
||||
private final byte[] sent;
|
||||
private final Object confirmed;
|
||||
|
||||
public CouldNotConfirmWrite(byte[] sent, byte[] confirmed) {
|
||||
super();
|
||||
this.sent = sent;
|
||||
this.confirmed = confirmed;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType;
|
||||
|
||||
public class CouldNotEnableNotifications extends Exception {
|
||||
public CouldNotEnableNotifications(CharacteristicType cmd) {
|
||||
super(cmd.getValue());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class DescriptorNotFoundException extends Exception {
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.BleIOBusyException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmDescriptorWriteException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmWrite;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotEnableNotifications;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotSendBleException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.DescriptorNotFoundException;
|
||||
|
||||
|
||||
public class BleIO {
|
||||
private static final int DEFAULT_IO_TIMEOUT_MS = 1000;
|
||||
|
||||
private final AAPSLogger aapsLogger;
|
||||
private final Map<CharacteristicType, BluetoothGattCharacteristic> chars;
|
||||
private final Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets;
|
||||
private final BluetoothGatt gatt;
|
||||
private final BleCommCallbacks bleCommCallbacks;
|
||||
|
||||
private IOState state;
|
||||
|
||||
public BleIO(AAPSLogger aapsLogger, Map<CharacteristicType, BluetoothGattCharacteristic> chars, Map<CharacteristicType, BlockingQueue<byte[]>> incomingPackets, BluetoothGatt gatt, BleCommCallbacks bleCommCallbacks) {
|
||||
this.aapsLogger = aapsLogger;
|
||||
this.chars = chars;
|
||||
this.incomingPackets = incomingPackets;
|
||||
this.gatt = gatt;
|
||||
this.bleCommCallbacks = bleCommCallbacks;
|
||||
this.state = IOState.IDLE;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @param characteristic where to read from(CMD or DATA)
|
||||
* @return a byte array with the received data
|
||||
*/
|
||||
public byte[] receivePacket(CharacteristicType characteristic) throws
|
||||
BleIOBusyException,
|
||||
InterruptedException,
|
||||
TimeoutException {
|
||||
synchronized (this.state) {
|
||||
if (this.state != IOState.IDLE) {
|
||||
throw new BleIOBusyException();
|
||||
}
|
||||
this.state = IOState.READING;
|
||||
}
|
||||
byte[] ret = this.incomingPackets.get(characteristic).poll(DEFAULT_IO_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
if (ret == null) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
synchronized (this.state) {
|
||||
this.state = IOState.IDLE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @param characteristic where to write to(CMD or DATA)
|
||||
* @param payload the data to send
|
||||
* @throws CouldNotSendBleException
|
||||
*/
|
||||
public void sendAndConfirmPacket(CharacteristicType characteristic, byte[] payload)
|
||||
throws CouldNotSendBleException,
|
||||
BleIOBusyException,
|
||||
InterruptedException,
|
||||
CouldNotConfirmWrite,
|
||||
TimeoutException {
|
||||
synchronized (this.state) {
|
||||
if (this.state != IOState.IDLE) {
|
||||
throw new BleIOBusyException();
|
||||
}
|
||||
this.state = IOState.WRITING;
|
||||
}
|
||||
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "BleIO: Sending data on" + characteristic.name() + "/" + payload.toString());
|
||||
BluetoothGattCharacteristic ch = chars.get(characteristic);
|
||||
boolean set = ch.setValue(payload);
|
||||
if (!set) {
|
||||
throw new CouldNotSendBleException("setValue");
|
||||
}
|
||||
boolean sent = this.gatt.writeCharacteristic(ch);
|
||||
if (!sent) {
|
||||
throw new CouldNotSendBleException("writeCharacteristic");
|
||||
}
|
||||
this.bleCommCallbacks.confirmWrite(CharacteristicType.CMD, payload, DEFAULT_IO_TIMEOUT_MS);
|
||||
synchronized (this.state) {
|
||||
this.state = IOState.IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before sending a new message.
|
||||
* The incoming queues should be empty, so we log when they are not.
|
||||
*/
|
||||
public void flushIncomingQueues() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable intentions on the characteristics.
|
||||
* This will signal the pod it can start sending back data
|
||||
* @return
|
||||
*/
|
||||
public void readyToRead()
|
||||
throws CouldNotSendBleException,
|
||||
CouldNotEnableNotifications,
|
||||
DescriptorNotFoundException,
|
||||
InterruptedException, CouldNotConfirmDescriptorWriteException {
|
||||
|
||||
for (CharacteristicType type : CharacteristicType.values()) {
|
||||
BluetoothGattCharacteristic ch = this.chars.get(type);
|
||||
boolean notificationSet = this.gatt.setCharacteristicNotification(ch, true);
|
||||
if (!notificationSet) {
|
||||
throw new CouldNotEnableNotifications(type);
|
||||
}
|
||||
List<BluetoothGattDescriptor> descriptors = ch.getDescriptors();
|
||||
if (descriptors.size() != 1) {
|
||||
throw new DescriptorNotFoundException();
|
||||
}
|
||||
BluetoothGattDescriptor descriptor = descriptors.get(0);
|
||||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
|
||||
gatt.writeDescriptor(descriptor);
|
||||
bleCommCallbacks.confirmWriteDescriptor(descriptor.getUuid().toString(), DEFAULT_IO_TIMEOUT_MS);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io;
|
||||
|
||||
public enum IOState {
|
||||
IDLE,
|
||||
WRITING,
|
||||
READING;
|
||||
}
|
Loading…
Reference in a new issue