split BLE to separate class

This commit is contained in:
Milos Kozak 2017-09-23 15:03:07 +02:00
parent 144a227d14
commit c3a0f5ddad
5 changed files with 758 additions and 692 deletions

View file

@ -109,7 +109,7 @@ public class DanaRExecutionService extends Service {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
log.debug("Device has disconnected " + device.getName());//Device has disconnected
log.debug("Device was disconnected " + device.getName());//Device was disconnected
if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(device.getName())) {
if (mSerialIOThread != null) {
mSerialIOThread.disconnect("BT disconnection broadcast");

View file

@ -105,7 +105,7 @@ public class DanaRKoreanExecutionService extends Service {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
log.debug("Device has disconnected " + device.getName());//Device has disconnected
log.debug("Device was disconnected " + device.getName());//Device was disconnected
if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(device.getName())) {
if (mSerialIOThread != null) {
mSerialIOThread.disconnect("BT disconnection broadcast");

View file

@ -0,0 +1,699 @@
package info.nightscout.androidaps.plugins.PumpDanaRS.services;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import com.cozmo.danar.util.BleCommandUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventPumpStatusChanged;
import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump;
import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingHelperActivity;
import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingProgressDialog;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRSMessageHashTable;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet;
import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPacket;
import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPairingSuccess;
import info.nightscout.utils.SP;
/**
* Created by mike on 23.09.2017.
*/
public class BLEComm {
private static Logger log = LoggerFactory.getLogger(BLEComm.class);
private static final long WRITE_DELAY_MILLIS = 50;
public static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb";
public static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb";
private byte PACKET_START_BYTE = (byte) 0xA5;
private byte PACKET_END_BYTE = (byte) 0x5A;
private static BLEComm instance = null;
public static BLEComm getInstance(DanaRSService service) {
if (instance == null)
instance = new BLEComm(service);
return instance;
}
private Object mConfirmConnect = null;
private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> scheduledDisconnection = null;
private DanaRS_Packet processsedMessage = null;
private ArrayList<byte[]> mSendQueue = new ArrayList<>();
// Variables for connection progress (elapsed time)
private Handler sHandler;
private HandlerThread sHandlerThread;
private long connectionStartTime = 0;
private final Runnable updateProgress = new Runnable() {
@Override
public void run() {
long secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000;
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed));
sHandler.postDelayed(updateProgress, 1000);
}
};
private BluetoothManager mBluetoothManager = null;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothDevice mBluetoothDevice = null;
private String mBluetoothDeviceAddress = null;
private String mBluetoothDeviceName = null;
private BluetoothGatt mBluetoothGatt = null;
protected boolean isConnected = false;
protected boolean isConnecting = false;
private BluetoothGattCharacteristic UART_Read;
private BluetoothGattCharacteristic UART_Write;
private DanaRSService service;
BLEComm(DanaRSService service) {
this.service = service;
initialize();
if (sHandlerThread == null) {
sHandlerThread = new HandlerThread(PairingProgressDialog.class.getSimpleName());
sHandlerThread.start();
sHandler = new Handler(sHandlerThread.getLooper());
}
}
private boolean initialize() {
log.debug("Initializing BLEComm.");
if (mBluetoothManager == null) {
mBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE));
if (mBluetoothManager == null) {
log.debug("Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
log.debug("Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
public boolean isConnected() {
return isConnected;
}
public boolean isConnecting() {
return isConnecting;
}
public boolean connect(String from, String address, Object confirmConnect) {
mConfirmConnect = confirmConnect;
BluetoothManager tBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE));
if (tBluetoothManager == null) {
return false;
}
BluetoothAdapter tBluetoothAdapter = tBluetoothManager.getAdapter();
if (tBluetoothAdapter == null) {
return false;
}
if (mBluetoothAdapter == null) {
if (!initialize()) {
return false;
}
}
if (address == null) {
log.debug("unspecified address.");
return false;
}
connectionStartTime = System.currentTimeMillis();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING));
isConnecting = true;
// Following should be removed later because we close Gatt on disconnect and this should never happen
if ((mBluetoothDeviceAddress != null) && (address.equals(mBluetoothDeviceAddress)) && (mBluetoothGatt != null)) {
log.debug("Trying to use an existing mBluetoothGatt for connection.");
sHandler.post(updateProgress);
if (mBluetoothGatt.connect()) {
setCharacteristicNotification(getUARTReadBTGattChar(), true);
return true;
}
sHandler.removeCallbacks(updateProgress);
return false;
}
// end
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
log.debug("Device not found. Unable to connect.");
return false;
}
sHandler.post(updateProgress);
mBluetoothGatt = device.connectGatt(service.getApplicationContext(), false, mGattCallback);
setCharacteristicNotification(getUARTReadBTGattChar(), true);
log.debug("Trying to create a new connection.");
mBluetoothDevice = device;
mBluetoothDeviceAddress = address;
mBluetoothDeviceName = device.getName();
return true;
}
public void disconnect(String from) {
log.debug("disconnect from: " + from);
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
return;
}
setCharacteristicNotification(getUARTReadBTGattChar(), false);
mBluetoothGatt.disconnect();
isConnected = false;
}
public void close() {
log.debug("BluetoothAdapter close");
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
public BluetoothDevice getConnectDevice() {
return mBluetoothDevice;
}
public String getConnectDeviceAddress() {
return mBluetoothDeviceAddress;
}
public String getConnectDeviceName() {
return mBluetoothDeviceName;
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
log.debug("onConnectionStateChange");
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
close();
isConnected = false;
sHandler.removeCallbacks(updateProgress); // just to be sure
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED));
log.debug("Device was disconnected " + gatt.getDevice().getName());//Device was disconnected
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
log.debug("onServicesDiscovered");
isConnecting = false;
if (status == BluetoothGatt.GATT_SUCCESS) {
findCharacteristic();
}
// stop sending connection progress
sHandler.removeCallbacks(updateProgress);
SendPumpCheck();
// 1st message sent to pump after connect
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
log.debug("onCharacteristicRead" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : ""));
addToReadBuffer(characteristic.getValue());
readDataParsing();
}
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
log.debug("onCharacteristicChanged" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : ""));
addToReadBuffer(characteristic.getValue());
new Thread(new Runnable() {
@Override
public void run() {
readDataParsing();
}
}).start();
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
log.debug("onCharacteristicWrite" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : ""));
new Thread(new Runnable() {
@Override
public void run() {
synchronized (mSendQueue) {
// after message sent, check if there is the rest of the message waiting and send it
if (mSendQueue.size() > 0) {
byte[] bytes = mSendQueue.get(0);
mSendQueue.remove(0);
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
}
}
}).start();
}
};
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
log.debug("setCharacteristicNotification");
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
log.debug("readCharacteristic");
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
public void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) {
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return;
}
new Thread(new Runnable() {
public void run() {
SystemClock.sleep(WRITE_DELAY_MILLIS);
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data));
mBluetoothGatt.writeCharacteristic(characteristic);
}
}).start();
}
public BluetoothGattCharacteristic getUARTReadBTGattChar() {
if (UART_Read == null) {
UART_Read = new BluetoothGattCharacteristic(UUID.fromString(UART_READ_UUID), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 0);
}
return UART_Read;
}
public BluetoothGattCharacteristic getUARTWriteBTGattChar() {
if (UART_Write == null) {
UART_Write = new BluetoothGattCharacteristic(UUID.fromString(UART_WRITE_UUID), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 0);
}
return UART_Write;
}
public List<BluetoothGattService> getSupportedGattServices() {
log.debug("getSupportedGattServices");
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return null;
}
return mBluetoothGatt.getServices();
}
private void findCharacteristic() {
List<BluetoothGattService> gattServices = getSupportedGattServices();
if (gattServices == null) {
return;
}
String uuid = null;
for (BluetoothGattService gattService : gattServices) {
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
uuid = gattCharacteristic.getUuid().toString();
if (UART_READ_UUID.equals(uuid)) {
UART_Read = gattCharacteristic;
setCharacteristicNotification(UART_Read, true);
}
if (UART_WRITE_UUID.equals(uuid)) {
UART_Write = gattCharacteristic;
}
}
}
}
private byte[] readBuffer = new byte[1024];
private int bufferLength = 0;
private void addToReadBuffer(byte[] buffer) {
//log.debug("addToReadBuffer " + DanaRS_Packet.toHexString(buffer));
if (buffer == null || buffer.length == 0) {
return;
}
synchronized (readBuffer) {
// Append incomming data to input buffer
System.arraycopy(buffer, 0, readBuffer, bufferLength, buffer.length);
bufferLength += buffer.length;
}
}
private void readDataParsing() {
boolean startSignatureFound = false, packetIsValid = false;
boolean isProcessing;
isProcessing = true;
while (isProcessing) {
int length = 0;
byte[] inputBuffer = null;
synchronized (readBuffer) {
// Find packet start [A5 A5]
if (bufferLength >= 6) {
for (int idxStartByte = 0; idxStartByte < bufferLength - 2; idxStartByte++) {
if ((readBuffer[idxStartByte] == PACKET_START_BYTE) && (readBuffer[idxStartByte + 1] == PACKET_START_BYTE)) {
if (idxStartByte > 0) {
// if buffer doesn't start with signature remove the leading trash
log.debug("Shifting the input buffer by " + idxStartByte + " bytes");
System.arraycopy(readBuffer, idxStartByte, readBuffer, 0, bufferLength - idxStartByte);
bufferLength -= idxStartByte;
}
startSignatureFound = true;
break;
}
}
}
// A5 A5 LEN TYPE CODE PARAMS CHECKSUM1 CHECKSUM2 5A 5A
// ^---- LEN -----^
// total packet length 2 + 1 + readBuffer[2] + 2 + 2
if (startSignatureFound) {
length = readBuffer[2];
// test if there is enough data loaded
if (length + 7 > bufferLength)
return;
// Verify packed end [5A 5A]
if ((readBuffer[length + 5] == PACKET_END_BYTE) && (readBuffer[length + 6] == PACKET_END_BYTE)) {
packetIsValid = true;
}
}
if (packetIsValid) {
inputBuffer = new byte[length + 7];
// copy packet to input buffer
System.arraycopy(readBuffer, 0, inputBuffer, 0, length + 7);
// Cut off the message from readBuffer
try {
System.arraycopy(readBuffer, length + 7, readBuffer, 0, bufferLength - (length + 7));
} catch (Exception e) {
log.debug("length: " + length + "bufferLength: " + bufferLength);
throw e;
}
bufferLength -= (length + 7);
// now we have encrypted packet in inputBuffer
}
}
if (packetIsValid) {
try {
// decrypt the packet
inputBuffer = BleCommandUtil.getInstance().getDecryptedPacket(inputBuffer);
if (inputBuffer == null) {
log.debug("Null decryptedInputBuffer");
return;
}
switch (inputBuffer[0]) {
// initial handshake packet
case (byte) BleCommandUtil.DANAR_PACKET__TYPE_ENCRYPTION_RESPONSE:
switch (inputBuffer[1]) {
// 1st packet
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK:
if (inputBuffer.length == 4 && inputBuffer[2] == 'O' && inputBuffer[3] == 'K') {
log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(inputBuffer));
// Grab pairing key from preferences if exists
String pairingKey = SP.getString(R.string.key_danars_pairingkey, null);
log.debug("Using stored pairing key: " + pairingKey);
if (pairingKey != null) {
byte[] encodedPairingKey = DanaRS_Packet.hexToBytes(pairingKey);
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY, encodedPairingKey, null);
log.debug(">>>>> " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
} else {
// Stored pairing key does not exists, request pairing
SendPairingRequest();
}
} else if (inputBuffer.length == 6 && inputBuffer[2] == 'B' && inputBuffer[3] == 'U' && inputBuffer[4] == 'S' && inputBuffer[5] == 'Y') {
log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(inputBuffer));
mSendQueue.clear();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.pumpbusy)));
} else {
log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(inputBuffer));
mSendQueue.clear();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.connectionerror)));
}
break;
// 2nd packet, pairing key
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY:
log.debug("<<<<< " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(inputBuffer));
if (inputBuffer[2] == (byte) 0x00) {
// Paring is not requested, sending time info
SendTimeInfo();
} else {
// Pairing on pump is requested
SendPairingRequest();
}
break;
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST:
log.debug("<<<<< " + "ENCRYPTION__PASSKEY_REQUEST " + DanaRS_Packet.toHexString(inputBuffer));
if (inputBuffer[2] != (byte) 0x00) {
disconnect("passkey request failed");
}
break;
// Paring response, OK button on pump pressed
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_RETURN:
log.debug("<<<<< " + "ENCRYPTION__PASSKEY_RETURN " + DanaRS_Packet.toHexString(inputBuffer));
// Paring is successfull, sending time info
MainApp.bus().post(new EventDanaRSPairingSuccess());
SendTimeInfo();
byte[] pairingKey = {inputBuffer[2], inputBuffer[3]};
// store pairing key to preferences
SP.putString(R.string.key_danars_pairingkey, DanaRS_Packet.toHexString(pairingKey));
log.debug("Got pairing key: " + DanaRS_Packet.toHexString(pairingKey));
break;
// time and user password information. last packet in handshake
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION:
log.debug("<<<<< " + "ENCRYPTION__TIME_INFORMATION " + /*message.getMessageName() + " " + */ DanaRS_Packet.toHexString(inputBuffer));
int size = inputBuffer.length;
int pass = ((inputBuffer[size - 1] & 0x000000FF) << 8) + ((inputBuffer[size - 2] & 0x000000FF));
pass = pass ^ 3463;
DanaRPump.getInstance().rs_password = Integer.toHexString(pass);
log.debug("Pump user password: " + Integer.toHexString(pass));
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTED));
isConnected = true;
isConnecting = false;
service.getPumpStatus();
scheduleDisconnection();
if (mConfirmConnect != null) {
synchronized (mConfirmConnect) {
mConfirmConnect.notify();
mConfirmConnect = null;
}
}
break;
}
break;
// common data packet
default:
DanaRS_Packet message;
// Retrieve message code from received buffer and last message sent
int originalCommand = processsedMessage != null ? processsedMessage.getCommand() : 0xFFFF;
int receivedCommand = DanaRS_Packet.getCommand(inputBuffer);
if (originalCommand == receivedCommand) {
// it's response to last message
message = processsedMessage;
} else {
// it's not response to last message, create new instance
message = DanaRSMessageHashTable.findMessage(receivedCommand);
}
if (message != null) {
log.debug("<<<<< " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(inputBuffer));
// process received data
message.handleMessage(inputBuffer);
message.setReceived();
synchronized (message) {
// notify to sendMessage
message.notify();
}
MainApp.bus().post(new EventDanaRSPacket(message));
} else {
log.error("Unknown message received " + DanaRS_Packet.toHexString(inputBuffer));
}
scheduleDisconnection();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
startSignatureFound = false;
packetIsValid = false;
if (bufferLength < 6) {
// stop the loop
isProcessing = false;
}
} else {
// stop the loop
isProcessing = false;
}
}
}
public void sendMessage(DanaRS_Packet message) {
processsedMessage = message;
if (message == null)
return;
byte[] command = {(byte) message.getType(), (byte) message.getOpCode()};
byte[] params = message.getRequestParams();
log.debug(">>>>> " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(command) + " " + DanaRS_Packet.toHexString(params));
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(message.getOpCode(), params, null);
// If there is another message not completely sent, add to queue only
if (mSendQueue.size() > 0) {
// Split to parts per 20 bytes max
for (; ; ) {
if (bytes.length > 20) {
byte[] addBytes = new byte[20];
System.arraycopy(bytes, 0, addBytes, 0, addBytes.length);
byte[] reBytes = new byte[bytes.length - addBytes.length];
System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length);
bytes = reBytes;
synchronized (mSendQueue) {
mSendQueue.add(addBytes);
}
} else {
synchronized (mSendQueue) {
mSendQueue.add(bytes);
}
break;
}
}
} else {
if (bytes.length > 20) {
// Cut first 20 bytes
byte[] sendBytes = new byte[20];
System.arraycopy(bytes, 0, sendBytes, 0, sendBytes.length);
byte[] reBytes = new byte[bytes.length - sendBytes.length];
System.arraycopy(bytes, sendBytes.length, reBytes, 0, reBytes.length);
bytes = reBytes;
// and send
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), sendBytes);
// The rest split to parts per 20 bytes max
for (; ; ) {
if (bytes.length > 20) {
byte[] addBytes = new byte[20];
System.arraycopy(bytes, 0, addBytes, 0, addBytes.length);
reBytes = new byte[bytes.length - addBytes.length];
System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length);
bytes = reBytes;
synchronized (mSendQueue) {
mSendQueue.add(addBytes);
}
} else {
synchronized (mSendQueue) {
mSendQueue.add(bytes);
}
break;
}
}
} else {
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
}
// The rest from queue is send from onCharasteristicWrite (after sending 1st part)
synchronized (message) {
try {
message.wait(5000);
} catch (InterruptedException e) {
log.error("sendMessage InterruptedException", e);
e.printStackTrace();
}
}
//SystemClock.sleep(200);
if (!message.isReceived()) {
log.warn("Reply not received " + message.getFriendlyName());
}
scheduleDisconnection();
}
private void SendPairingRequest() {
// Start activity which is waiting 20sec
// On pump pairing request is displayed and is waiting for conformation
Intent i = new Intent();
i.setClass(MainApp.instance(), PairingHelperActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainApp.instance().startActivity(i);
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST, null, null);
log.debug(">>>>> " + "ENCRYPTION__PASSKEY_REQUEST" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
protected void SendPumpCheck() {
// 1st message sent to pump after connect
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK, null, getConnectDeviceName());
log.debug(">>>>> " + "ENCRYPTION__PUMP_CHECK (0x00)" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
private void SendTimeInfo() {
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, null, null);
log.debug(">>>>> " + "ENCRYPTION__TIME_INFORMATION" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
public void scheduleDisconnection() {
class DisconnectRunnable implements Runnable {
public void run() {
disconnect("scheduleDisconnection");
scheduledDisconnection = null;
}
}
// prepare task for execution in 5 sec
// cancel waiting task to prevent sending multiple disconnections
if (scheduledDisconnection != null)
scheduledDisconnection.cancel(false);
Runnable task = new DisconnectRunnable();
final int sec = 5;
scheduledDisconnection = worker.schedule(task, sec, TimeUnit.SECONDS);
}
}

View file

@ -1,37 +1,19 @@
package info.nightscout.androidaps.plugins.PumpDanaRS.services;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.SystemClock;
import com.cozmo.danar.util.BleCommandUtil;
import com.squareup.otto.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants;
@ -48,9 +30,6 @@ import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump;
import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes;
import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus;
import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin;
import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingHelperActivity;
import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingProgressDialog;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRSMessageHashTable;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Basal_Get_Basal_Rate;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Basal_Get_Profile_Number;
@ -86,34 +65,13 @@ import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Notify_D
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Notify_Delivery_Rate_Display;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Get_Pump_Time;
import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Set_Pump_Time;
import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPacket;
import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPairingSuccess;
import info.nightscout.utils.NSUpload;
import info.nightscout.utils.SP;
public class DanaRSService extends Service {
private static Logger log = LoggerFactory.getLogger(DanaRSService.class);
private static final long WRITE_DELAY_MILLIS = 50;
public static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb";
public static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb";
private byte PACKET_START_BYTE = (byte) 0xA5;
private byte PACKET_END_BYTE = (byte) 0x5A;
private BluetoothManager mBluetoothManager = null;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothDevice mBluetoothDevice = null;
private String mBluetoothDeviceAddress = null;
private String mBluetoothDeviceName = null;
private BluetoothGatt mBluetoothGatt = null;
private boolean isConnected = false;
private boolean isConnecting = false;
private BluetoothGattCharacteristic UART_Read;
private BluetoothGattCharacteristic UART_Write;
private BLEComm bleComm = BLEComm.getInstance(this);
private PowerManager.WakeLock mWakeLock;
private IBinder mBinder = new LocalBinder();
@ -121,27 +79,6 @@ public class DanaRSService extends Service {
private DanaRPump danaRPump = DanaRPump.getInstance();
private Treatment bolusingTreatment = null;
private Object mConfirmConnect = null;
private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> scheduledDisconnection = null;
private DanaRS_Packet processsedMessage = null;
private ArrayList<byte[]> mSendQueue = new ArrayList<>();
// Variables pro connection progress (elapsed time)
private Handler sHandler;
private HandlerThread sHandlerThread;
private long connectionStartTime = 0;
private final Runnable updateProgress = new Runnable() {
@Override
public void run() {
long secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000;
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed));
sHandler.postDelayed(updateProgress, 1000);
}
};
private long lastHistoryFetched = 0;
public DanaRSService() {
@ -150,49 +87,63 @@ public class DanaRSService extends Service {
} catch (RuntimeException x) {
// Ignore
}
initialize();
MainApp.bus().register(this);
PowerManager powerManager = (PowerManager) MainApp.instance().getApplicationContext().getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, DanaRSService.class.getSimpleName());
if (sHandlerThread == null) {
sHandlerThread = new HandlerThread(PairingProgressDialog.class.getSimpleName());
sHandlerThread.start();
sHandler = new Handler(sHandlerThread.getLooper());
}
}
private boolean getPumpStatus() {
public boolean isConnected() {
return bleComm.isConnected;
}
public boolean isConnecting() {
return bleComm.isConnecting;
}
public boolean connect(String from, String address, Object confirmConnect) {
return bleComm.connect(from, address, confirmConnect);
}
public void disconnect(String from) {
bleComm.disconnect(from);
}
public void sendMessage(DanaRS_Packet message) {
bleComm.sendMessage(message);
}
protected boolean getPumpStatus() {
try {
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingpumpstatus)));
sendMessage(new DanaRS_Packet_General_Initial_Screen_Information());
bleComm.sendMessage(new DanaRS_Packet_General_Initial_Screen_Information());
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingextendedbolusstatus)));
sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State());
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State());
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingbolusstatus)));
sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // last bolus
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // last bolus
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingtempbasalstatus)));
sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State());
bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State());
Date now = new Date();
if (danaRPump.lastSettingsRead.getTime() + 60 * 60 * 1000L < now.getTime() || !MainApp.getSpecificPlugin(DanaRSPlugin.class).isInitialized()) {
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingpumpsettings)));
sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no
sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware
sendMessage(new DanaRS_Packet_Basal_Get_Profile_Number());
sendMessage(new DanaRS_Packet_Bolus_Get_Bolus_Option()); // isExtendedEnabled
sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // bolusStep, maxBolus
sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal
sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target
sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array());
bleComm.sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no
bleComm.sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware
bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Profile_Number());
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Bolus_Option()); // isExtendedEnabled
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // bolusStep, maxBolus
bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array());
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingpumptime)));
sendMessage(new DanaRS_Packet_Option_Get_Pump_Time());
bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time());
long timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L;
log.debug("Pump time difference: " + timeDiff + " seconds");
if (Math.abs(timeDiff) > 10) {
sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date()));
sendMessage(new DanaRS_Packet_Option_Get_Pump_Time());
bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date()));
bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time());
timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L;
log.debug("Pump time difference: " + timeDiff + " seconds");
}
@ -233,16 +184,16 @@ public class DanaRSService extends Service {
if (carbs > 0) {
// MsgSetCarbsEntry msg = new MsgSetCarbsEntry(carbtime, carbs); ####
// sendMessage(msg);
// bleComm.sendMessage(msg);
// MsgSetHistoryEntry_v2 msgSetHistoryEntry_v2 = new MsgSetHistoryEntry_v2(DanaRPump.CARBS, carbtime, carbs, 0);
// sendMessage(msgSetHistoryEntry_v2);
// bleComm.sendMessage(msgSetHistoryEntry_v2);
// lastHistoryFetched = carbtime - 60000;
}
if (insulin > 0) {
DanaRS_Packet_Notify_Delivery_Rate_Display progress = new DanaRS_Packet_Notify_Delivery_Rate_Display(insulin, t); // initialize static variables
if (!stop.stopped) {
sendMessage(start);
bleComm.sendMessage(start);
} else {
t.insulin = 0d;
return false;
@ -268,9 +219,9 @@ public class DanaRSService extends Service {
DanaRS_Packet_Bolus_Set_Step_Bolus_Stop stop = new DanaRS_Packet_Bolus_Set_Step_Bolus_Stop();
stop.forced = true;
if (isConnected()) {
sendMessage(stop);
bleComm.sendMessage(stop);
while (!stop.stopped) {
sendMessage(stop);
bleComm.sendMessage(stop);
SystemClock.sleep(200);
}
} else {
@ -282,13 +233,13 @@ public class DanaRSService extends Service {
if (!isConnected()) return false;
if (danaRPump.isTempBasalInProgress) {
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal)));
sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal());
bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal());
SystemClock.sleep(500);
}
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingtempbasal)));
sendMessage(new DanaRS_Packet_Basal_Set_Temporary_Basal(percent, durationInHours));
bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Temporary_Basal(percent, durationInHours));
SystemClock.sleep(200);
sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State());
bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State());
loadEvents();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING));
return true;
@ -301,8 +252,8 @@ public class DanaRSService extends Service {
public boolean tempBasalStop() {
if (!isConnected()) return false;
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal)));
sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal());
sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State());
bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal());
bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State());
loadEvents();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING));
return true;
@ -311,9 +262,9 @@ public class DanaRSService extends Service {
public boolean extendedBolus(Double insulin, int durationInHalfHours) {
if (!isConnected()) return false;
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingextendedbolus)));
sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus(insulin, durationInHalfHours));
bleComm.sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus(insulin, durationInHalfHours));
SystemClock.sleep(200);
sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State());
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State());
loadEvents();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING));
return true;
@ -322,8 +273,8 @@ public class DanaRSService extends Service {
public boolean extendedBolusStop() {
if (!isConnected()) return false;
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingextendedbolus)));
sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus_Cancel());
sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State());
bleComm.sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus_Cancel());
bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State());
loadEvents();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING));
return true;
@ -334,9 +285,9 @@ public class DanaRSService extends Service {
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.updatingbasalrates)));
double[] basal = DanaRPump.buildDanaRProfileRecord(profile);
DanaRS_Packet_Basal_Set_Profile_Basal_Rate msgSet = new DanaRS_Packet_Basal_Set_Profile_Basal_Rate(0, basal);
sendMessage(msgSet);
bleComm.sendMessage(msgSet);
DanaRS_Packet_Basal_Set_Profile_Number msgActivate = new DanaRS_Packet_Basal_Set_Profile_Number(0);
sendMessage(msgActivate);
bleComm.sendMessage(msgActivate);
danaRPump.lastSettingsRead = new Date(0); // force read full settings
getPumpStatus();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING));
@ -376,14 +327,14 @@ public class DanaRSService extends Service {
break;
}
if (msg != null) {
sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(1));
bleComm.sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(1));
SystemClock.sleep(200);
sendMessage(msg);
bleComm.sendMessage(msg);
while (!msg.done && isConnected()) {
SystemClock.sleep(100);
}
SystemClock.sleep(200);
sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(0));
bleComm.sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(0));
}
return true;
}
@ -416,588 +367,4 @@ public class DanaRSService extends Service {
log.debug("EventAppExit finished");
}
private boolean initialize() {
log.debug("Initializing service.");
if (mBluetoothManager == null) {
mBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE));
if (mBluetoothManager == null) {
log.debug("Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
log.debug("Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
public boolean isConnected() {
return isConnected;
}
public boolean isConnecting() {
return isConnecting;
}
public boolean connect(String from, String address, Object confirmConnect) {
mConfirmConnect = confirmConnect;
BluetoothManager tBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE));
if (tBluetoothManager == null) {
return false;
}
BluetoothAdapter tBluetoothAdapter = tBluetoothManager.getAdapter();
if (tBluetoothAdapter == null) {
return false;
}
if (mBluetoothAdapter == null) {
if (!initialize()) {
return false;
}
}
if (address == null) {
log.debug("unspecified address.");
return false;
}
connectionStartTime = System.currentTimeMillis();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING));
isConnecting = true;
// Following should be removed later because we close Gatt on disconnect and this should never happen
if ((mBluetoothDeviceAddress != null) && (address.equals(mBluetoothDeviceAddress)) && (mBluetoothGatt != null)) {
log.debug("Trying to use an existing mBluetoothGatt for connection.");
sHandler.post(updateProgress);
if (mBluetoothGatt.connect()) {
setCharacteristicNotification(getUARTReadBTGattChar(), true);
return true;
}
sHandler.removeCallbacks(updateProgress);
return false;
}
// end
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
log.debug("Device not found. Unable to connect.");
return false;
}
sHandler.post(updateProgress);
mBluetoothGatt = device.connectGatt(getApplicationContext(), false, mGattCallback);
setCharacteristicNotification(getUARTReadBTGattChar(), true);
log.debug("Trying to create a new connection.");
mBluetoothDevice = device;
mBluetoothDeviceAddress = address;
mBluetoothDeviceName = device.getName();
return true;
}
public void disconnect(String from) {
log.debug("disconnect from: " + from);
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
return;
}
setCharacteristicNotification(getUARTReadBTGattChar(), false);
mBluetoothGatt.disconnect();
isConnected = false;
}
public void close() {
log.debug("BluetoothAdapter close");
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
public BluetoothDevice getConnectDevice() {
return mBluetoothDevice;
}
public String getConnectDeviceAddress() {
return mBluetoothDeviceAddress;
}
public String getConnectDeviceName() {
return mBluetoothDeviceName;
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
log.debug("onConnectionStateChange");
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
close();
isConnected = false;
sHandler.removeCallbacks(updateProgress); // just to be sure
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED));
log.debug("Device was disconnected " + gatt.getDevice().getName());//Device was disconnected
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
log.debug("onServicesDiscovered");
isConnecting = false;
if (status == BluetoothGatt.GATT_SUCCESS) {
findCharacteristic();
}
// stop sending connection progress
sHandler.removeCallbacks(updateProgress);
// 1st message sent to pump after connect
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK, null, getConnectDeviceName());
log.debug(">>>>> " + "ENCRYPTION__PUMP_CHECK (0x00)" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
log.debug("onCharacteristicRead" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : ""));
addToReadBuffer(characteristic.getValue());
readDataParsing();
}
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
log.debug("onCharacteristicChanged" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : ""));
addToReadBuffer(characteristic.getValue());
new Thread(new Runnable() {
@Override
public void run() {
readDataParsing();
}
}).start();
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
log.debug("onCharacteristicWrite" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : ""));
new Thread(new Runnable() {
@Override
public void run() {
synchronized (mSendQueue) {
// after message sent, check if there is the rest of the message waiting and send it
if (mSendQueue.size() > 0) {
byte[] bytes = mSendQueue.get(0);
mSendQueue.remove(0);
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
}
}
}).start();
}
};
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
log.debug("setCharacteristicNotification");
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
log.debug("readCharacteristic");
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
public void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) {
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return;
}
new Thread(new Runnable() {
public void run() {
SystemClock.sleep(WRITE_DELAY_MILLIS);
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data));
mBluetoothGatt.writeCharacteristic(characteristic);
}
}).start();
}
public BluetoothGattCharacteristic getUARTReadBTGattChar() {
if (UART_Read == null) {
UART_Read = new BluetoothGattCharacteristic(UUID.fromString(UART_READ_UUID), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 0);
}
return UART_Read;
}
public BluetoothGattCharacteristic getUARTWriteBTGattChar() {
if (UART_Write == null) {
UART_Write = new BluetoothGattCharacteristic(UUID.fromString(UART_WRITE_UUID), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 0);
}
return UART_Write;
}
public List<BluetoothGattService> getSupportedGattServices() {
log.debug("getSupportedGattServices");
if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) {
log.debug("BluetoothAdapter not initialized_ERROR");
return null;
}
return mBluetoothGatt.getServices();
}
private void findCharacteristic() {
List<BluetoothGattService> gattServices = getSupportedGattServices();
if (gattServices == null) {
return;
}
String uuid = null;
for (BluetoothGattService gattService : gattServices) {
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
uuid = gattCharacteristic.getUuid().toString();
if (UART_READ_UUID.equals(uuid)) {
UART_Read = gattCharacteristic;
setCharacteristicNotification(UART_Read, true);
}
if (UART_WRITE_UUID.equals(uuid)) {
UART_Write = gattCharacteristic;
}
}
}
}
private byte[] readBuffer = new byte[1024];
private int bufferLength = 0;
private void addToReadBuffer(byte[] buffer) {
//log.debug("addToReadBuffer " + DanaRS_Packet.toHexString(buffer));
if (buffer == null || buffer.length == 0) {
return;
}
synchronized (readBuffer) {
// Append incomming data to input buffer
System.arraycopy(buffer, 0, readBuffer, bufferLength, buffer.length);
bufferLength += buffer.length;
}
}
private void readDataParsing() {
boolean startSignatureFound = false, packetIsValid = false;
boolean isProcessing;
isProcessing = true;
while (isProcessing) {
int length = 0;
byte[] inputBuffer = null;
synchronized (readBuffer) {
// Find packet start [A5 A5]
if (bufferLength >= 6) {
for (int idxStartByte = 0; idxStartByte < bufferLength - 2; idxStartByte++) {
if ((readBuffer[idxStartByte] == PACKET_START_BYTE) && (readBuffer[idxStartByte + 1] == PACKET_START_BYTE)) {
if (idxStartByte > 0) {
// if buffer doesn't start with signature remove the leading trash
log.debug("Shifting the input buffer by " + idxStartByte + " bytes");
System.arraycopy(readBuffer, idxStartByte, readBuffer, 0, bufferLength - idxStartByte);
bufferLength -= idxStartByte;
}
startSignatureFound = true;
break;
}
}
}
// A5 A5 LEN TYPE CODE PARAMS CHECKSUM1 CHECKSUM2 5A 5A
// ^---- LEN -----^
// total packet length 2 + 1 + readBuffer[2] + 2 + 2
if (startSignatureFound) {
length = readBuffer[2];
// test if there is enough data loaded
if (length + 7 > bufferLength)
return;
// Verify packed end [5A 5A]
if ((readBuffer[length + 5] == PACKET_END_BYTE) && (readBuffer[length + 6] == PACKET_END_BYTE)) {
packetIsValid = true;
}
}
if (packetIsValid) {
inputBuffer = new byte[length + 7];
// copy packet to input buffer
System.arraycopy(readBuffer, 0, inputBuffer, 0, length + 7);
// Cut off the message from readBuffer
try {
System.arraycopy(readBuffer, length + 7, readBuffer, 0, bufferLength - (length + 7));
} catch (Exception e) {
log.debug("length: " + length + "bufferLength: " + bufferLength);
throw e;
}
bufferLength -= (length + 7);
// now we have encrypted packet in inputBuffer
}
}
if (packetIsValid) {
try {
// decrypt the packet
inputBuffer = BleCommandUtil.getInstance().getDecryptedPacket(inputBuffer);
if (inputBuffer == null) {
log.debug("Null decryptedInputBuffer");
return;
}
switch (inputBuffer[0]) {
// initial handshake packet
case (byte) BleCommandUtil.DANAR_PACKET__TYPE_ENCRYPTION_RESPONSE:
switch (inputBuffer[1]) {
// 1st packet
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK:
if (inputBuffer.length == 4 && inputBuffer[2] == 'O' && inputBuffer[3] == 'K') {
log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(inputBuffer));
// Grab pairing key from preferences if exists
String pairingKey = SP.getString(R.string.key_danars_pairingkey, null);
log.debug("Using stored pairing key: " + pairingKey);
if (pairingKey != null) {
byte[] encodedPairingKey = DanaRS_Packet.hexToBytes(pairingKey);
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY, encodedPairingKey, null);
log.debug(">>>>> " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
} else {
// Stored pairing key does not exists, request pairing
SendPairingRequest();
}
} else if (inputBuffer.length == 6 && inputBuffer[2] == 'B' && inputBuffer[3] == 'U' && inputBuffer[4] == 'S' && inputBuffer[5] == 'Y') {
log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(inputBuffer));
mSendQueue.clear();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.pumpbusy)));
} else {
log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(inputBuffer));
mSendQueue.clear();
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.connectionerror)));
}
break;
// 2nd packet, pairing key
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY:
log.debug("<<<<< " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(inputBuffer));
if (inputBuffer[2] == (byte) 0x00) {
// Paring is not requested, sending time info
SendTimeInfo();
} else {
// Pairing on pump is requested
SendPairingRequest();
}
break;
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST:
log.debug("<<<<< " + "ENCRYPTION__PASSKEY_REQUEST " + DanaRS_Packet.toHexString(inputBuffer));
if (inputBuffer[2] != (byte) 0x00) {
disconnect("passkey request failed");
}
break;
// Paring response, OK button on pump pressed
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_RETURN:
log.debug("<<<<< " + "ENCRYPTION__PASSKEY_RETURN " + DanaRS_Packet.toHexString(inputBuffer));
// Paring is successfull, sending time info
MainApp.bus().post(new EventDanaRSPairingSuccess());
SendTimeInfo();
byte[] pairingKey = {inputBuffer[2], inputBuffer[3]};
// store pairing key to preferences
SP.putString(R.string.key_danars_pairingkey, DanaRS_Packet.toHexString(pairingKey));
log.debug("Got pairing key: " + DanaRS_Packet.toHexString(pairingKey));
break;
// time and user password information. last packet in handshake
case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION:
log.debug("<<<<< " + "ENCRYPTION__TIME_INFORMATION " + /*message.getMessageName() + " " + */ DanaRS_Packet.toHexString(inputBuffer));
int size = inputBuffer.length;
int pass = ((inputBuffer[size - 1] & 0x000000FF) << 8) + ((inputBuffer[size - 2] & 0x000000FF));
pass = pass ^ 3463;
DanaRPump.getInstance().rs_password = Integer.toHexString(pass);
log.debug("Pump user password: " + Integer.toHexString(pass));
MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTED));
isConnected = true;
isConnecting = false;
getPumpStatus();
scheduleDisconnection();
if (mConfirmConnect != null) {
synchronized (mConfirmConnect) {
mConfirmConnect.notify();
mConfirmConnect = null;
}
}
break;
}
break;
// common data packet
default:
DanaRS_Packet message;
// Retrieve message code from received buffer and last message sent
int originalCommand = processsedMessage != null ? processsedMessage.getCommand() : 0xFFFF;
int receivedCommand = DanaRS_Packet.getCommand(inputBuffer);
if (originalCommand == receivedCommand) {
// it's response to last message
message = processsedMessage;
} else {
// it's not response to last message, create new instance
message = DanaRSMessageHashTable.findMessage(receivedCommand);
}
if (message != null) {
log.debug("<<<<< " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(inputBuffer));
// process received data
message.handleMessage(inputBuffer);
message.setReceived();
synchronized (message) {
// notify to sendMessage
message.notify();
}
MainApp.bus().post(new EventDanaRSPacket(message));
} else {
log.error("Unknown message received " + DanaRS_Packet.toHexString(inputBuffer));
}
scheduleDisconnection();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
startSignatureFound = false;
packetIsValid = false;
if (bufferLength < 6) {
// stop the loop
isProcessing = false;
}
} else {
// stop the loop
isProcessing = false;
}
}
}
public void sendMessage(DanaRS_Packet message) {
processsedMessage = message;
if (message == null)
return;
byte[] command = {(byte) message.getType(), (byte) message.getOpCode()};
byte[] params = message.getRequestParams();
log.debug(">>>>> " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(command) + " " + DanaRS_Packet.toHexString(params));
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(message.getOpCode(), params, null);
// If there is another message not completely sent, add to queue only
if (mSendQueue.size() > 0) {
// Split to parts per 20 bytes max
for (; ; ) {
if (bytes.length > 20) {
byte[] addBytes = new byte[20];
System.arraycopy(bytes, 0, addBytes, 0, addBytes.length);
byte[] reBytes = new byte[bytes.length - addBytes.length];
System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length);
bytes = reBytes;
synchronized (mSendQueue) {
mSendQueue.add(addBytes);
}
} else {
synchronized (mSendQueue) {
mSendQueue.add(bytes);
}
break;
}
}
} else {
if (bytes.length > 20) {
// Cut first 20 bytes
byte[] sendBytes = new byte[20];
System.arraycopy(bytes, 0, sendBytes, 0, sendBytes.length);
byte[] reBytes = new byte[bytes.length - sendBytes.length];
System.arraycopy(bytes, sendBytes.length, reBytes, 0, reBytes.length);
bytes = reBytes;
// and send
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), sendBytes);
// The rest split to parts per 20 bytes max
for (; ; ) {
if (bytes.length > 20) {
byte[] addBytes = new byte[20];
System.arraycopy(bytes, 0, addBytes, 0, addBytes.length);
reBytes = new byte[bytes.length - addBytes.length];
System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length);
bytes = reBytes;
synchronized (mSendQueue) {
mSendQueue.add(addBytes);
}
} else {
synchronized (mSendQueue) {
mSendQueue.add(bytes);
}
break;
}
}
} else {
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
}
// The rest from queue is send from onCharasteristicWrite (after sending 1st part)
synchronized (message) {
try {
message.wait(5000);
} catch (InterruptedException e) {
log.error("sendMessage InterruptedException", e);
e.printStackTrace();
}
}
//SystemClock.sleep(200);
if (!message.isReceived()) {
log.warn("Reply not received " + message.getFriendlyName());
}
scheduleDisconnection();
}
private void SendPairingRequest() {
// Start activity which is waiting 20sec
// On pump pairing request is displayed and is waiting for conformation
Intent i = new Intent();
i.setClass(MainApp.instance(), PairingHelperActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainApp.instance().startActivity(i);
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST, null, null);
log.debug(">>>>> " + "ENCRYPTION__PASSKEY_REQUEST" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
private void SendTimeInfo() {
byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, null, null);
log.debug(">>>>> " + "ENCRYPTION__TIME_INFORMATION" + " " + DanaRS_Packet.toHexString(bytes));
writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes);
}
public void scheduleDisconnection() {
class DisconnectRunnable implements Runnable {
public void run() {
disconnect("scheduleDisconnection");
scheduledDisconnection = null;
}
}
// prepare task for execution in 5 sec
// cancel waiting task to prevent sending multiple disconnections
if (scheduledDisconnection != null)
scheduledDisconnection.cancel(false);
Runnable task = new DisconnectRunnable();
final int sec = 5;
scheduledDisconnection = worker.schedule(task, sec, TimeUnit.SECONDS);
}
}

View file

@ -79,7 +79,7 @@ public class DanaRv2ExecutionService extends Service {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
log.debug("Device has disconnected " + device.getName());//Device has disconnected
log.debug("Device was disconnected " + device.getName());//Device was disconnected
if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(device.getName())) {
if (mSerialIOThread != null) {
mSerialIOThread.disconnect("BT disconnection broadcast");