mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-22 08:54:21 +01:00
Bugs fixed in CGMS
This commit is contained in:
@@ -34,7 +34,9 @@ import android.util.SparseArray;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointDataCallback;
|
import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointDataCallback;
|
||||||
|
import no.nordicsemi.android.ble.common.callback.cgm.CGMFeatureDataCallback;
|
||||||
import no.nordicsemi.android.ble.common.callback.cgm.CGMSpecificOpsControlPointDataCallback;
|
import no.nordicsemi.android.ble.common.callback.cgm.CGMSpecificOpsControlPointDataCallback;
|
||||||
|
import no.nordicsemi.android.ble.common.callback.cgm.CGMStatusDataCallback;
|
||||||
import no.nordicsemi.android.ble.common.callback.cgm.ContinuousGlucoseMeasurementDataCallback;
|
import no.nordicsemi.android.ble.common.callback.cgm.ContinuousGlucoseMeasurementDataCallback;
|
||||||
import no.nordicsemi.android.ble.common.data.RecordAccessControlPointData;
|
import no.nordicsemi.android.ble.common.data.RecordAccessControlPointData;
|
||||||
import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData;
|
import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData;
|
||||||
@@ -52,6 +54,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
* Cycling Speed and Cadence service UUID
|
* Cycling Speed and Cadence service UUID
|
||||||
*/
|
*/
|
||||||
public static final UUID CGMS_UUID = UUID.fromString("0000181F-0000-1000-8000-00805f9b34fb");
|
public static final UUID CGMS_UUID = UUID.fromString("0000181F-0000-1000-8000-00805f9b34fb");
|
||||||
|
private static final UUID CGM_STATUS_UUID = UUID.fromString("00002AA9-0000-1000-8000-00805f9b34fb");
|
||||||
private static final UUID CGM_FEAURE_UUID = UUID.fromString("00002AA8-0000-1000-8000-00805f9b34fb");
|
private static final UUID CGM_FEAURE_UUID = UUID.fromString("00002AA8-0000-1000-8000-00805f9b34fb");
|
||||||
private static final UUID CGM_MEASUREMENT_UUID = UUID.fromString("00002AA7-0000-1000-8000-00805f9b34fb");
|
private static final UUID CGM_MEASUREMENT_UUID = UUID.fromString("00002AA7-0000-1000-8000-00805f9b34fb");
|
||||||
private static final UUID CGM_OPS_CONTROL_POINT_UUID = UUID.fromString("00002AAC-0000-1000-8000-00805f9b34fb");
|
private static final UUID CGM_OPS_CONTROL_POINT_UUID = UUID.fromString("00002AAC-0000-1000-8000-00805f9b34fb");
|
||||||
@@ -60,15 +63,22 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
*/
|
*/
|
||||||
private static final UUID RACP_UUID = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb");
|
private static final UUID RACP_UUID = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
|
private BluetoothGattCharacteristic mCGMStatusCharacteristic;
|
||||||
private BluetoothGattCharacteristic mCGMFeatureCharacteristic;
|
private BluetoothGattCharacteristic mCGMFeatureCharacteristic;
|
||||||
private BluetoothGattCharacteristic mCGMMeasurementCharacteristic;
|
private BluetoothGattCharacteristic mCGMMeasurementCharacteristic;
|
||||||
private BluetoothGattCharacteristic mCGMSpecificOpsControlPointCharacteristic;
|
private BluetoothGattCharacteristic mCGMSpecificOpsControlPointCharacteristic;
|
||||||
private BluetoothGattCharacteristic mRecordAccessControlPointCharacteristic;
|
private BluetoothGattCharacteristic mRecordAccessControlPointCharacteristic;
|
||||||
|
|
||||||
private SparseArray<CGMSRecord> mRecords = new SparseArray<>();
|
private SparseArray<CGMSRecord> mRecords = new SparseArray<>();
|
||||||
private boolean mAbort;
|
|
||||||
/** A flag set to true if the remote device supports E2E CRC. */
|
/** A flag set to true if the remote device supports E2E CRC. */
|
||||||
private boolean mSecured;
|
private boolean mSecured;
|
||||||
|
/**
|
||||||
|
* A flag set when records has been requested using RACP. This is to distinguish CGM packets received
|
||||||
|
* as continuous measurements or requested.
|
||||||
|
*/
|
||||||
|
private boolean mRecordAccessRequestInProgress;
|
||||||
|
/** The timestamp when the session has started. This is needed to display the user facing times of samples. */
|
||||||
private long mSessionStartTime;
|
private long mSessionStartTime;
|
||||||
|
|
||||||
CGMSManager(final Context context) {
|
CGMSManager(final Context context) {
|
||||||
@@ -90,6 +100,30 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
// Enable Battery service
|
// Enable Battery service
|
||||||
super.initialize(device);
|
super.initialize(device);
|
||||||
|
|
||||||
|
// Read CGM Feature characteristic, mainly to see if the device supports E2E CRC.
|
||||||
|
// This is not supported in the experimental CGMS from the SDK.
|
||||||
|
readCharacteristic(mCGMFeatureCharacteristic)
|
||||||
|
.with(new CGMFeatureDataCallback() {
|
||||||
|
@Override
|
||||||
|
public void onContinuousGlucoseMonitorFeaturesReceived(@NonNull final BluetoothDevice device, @NonNull final CGMFeatures features,
|
||||||
|
final int type, final int sampleLocation, final boolean secured) {
|
||||||
|
mSecured = features.e2eCrcSupported;
|
||||||
|
log(LogContract.Log.Level.APPLICATION, "E2E CRC feature " + (mSecured ? "supported" : "not supported"));
|
||||||
|
}
|
||||||
|
}).fail(status -> log(LogContract.Log.Level.WARNING, "Could not read CGM Feature characteristic"));
|
||||||
|
|
||||||
|
// Check if the session is already started. This is not supported in the experimental CGMS from the SDK.
|
||||||
|
readCharacteristic(mCGMStatusCharacteristic)
|
||||||
|
.with(new CGMStatusDataCallback() {
|
||||||
|
@Override
|
||||||
|
public void onContinuousGlucoseMonitorStatusChanged(@NonNull final BluetoothDevice device, @NonNull final CGMStatus status, final int timeOffset, final boolean secured) {
|
||||||
|
if (!status.sessionStopped) {
|
||||||
|
mSessionStartTime = System.currentTimeMillis() - timeOffset * 60000L;
|
||||||
|
log(LogContract.Log.Level.APPLICATION, "Session already started");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).fail(status -> log(LogContract.Log.Level.WARNING, "Could not read CGM Status characteristic"));
|
||||||
|
|
||||||
// Enable Continuous Glucose Measurement notifications
|
// Enable Continuous Glucose Measurement notifications
|
||||||
enableNotifications(mCGMMeasurementCharacteristic)
|
enableNotifications(mCGMMeasurementCharacteristic)
|
||||||
.with(new ContinuousGlucoseMeasurementDataCallback() {
|
.with(new ContinuousGlucoseMeasurementDataCallback() {
|
||||||
@@ -101,8 +135,17 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContinuousGlucoseMeasurementReceived(@NonNull final BluetoothDevice device, final float glucoseConcentration, @Nullable final Float cgmTrend, @Nullable final Float cgmQuality, final CGMStatus status, final int timeOffset, final boolean secured) {
|
public void onContinuousGlucoseMeasurementReceived(@NonNull final BluetoothDevice device, final float glucoseConcentration, @Nullable final Float cgmTrend, @Nullable final Float cgmQuality, final CGMStatus status, final int timeOffset, final boolean secured) {
|
||||||
|
// If the CGM Status characteristic has not been read and the session was already started before,
|
||||||
|
// estimate the Session Start Time by subtracting timeOffset minutes from the current timestamp.
|
||||||
|
if (mSessionStartTime == 0 && !mRecordAccessRequestInProgress) {
|
||||||
|
mSessionStartTime = System.currentTimeMillis() - timeOffset * 60000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the sample timestamp based on the Session Start Time
|
||||||
final long timestamp = mSessionStartTime + (timeOffset * 60000L); // Sequence number is in minutes since Start Session
|
final long timestamp = mSessionStartTime + (timeOffset * 60000L); // Sequence number is in minutes since Start Session
|
||||||
|
|
||||||
final CGMSRecord record = new CGMSRecord(timeOffset, glucoseConcentration, timestamp);
|
final CGMSRecord record = new CGMSRecord(timeOffset, glucoseConcentration, timestamp);
|
||||||
|
mRecords.put(record.sequenceNumber, record);
|
||||||
mCallbacks.onCGMValueReceived(device, record);
|
mCallbacks.onCGMValueReceived(device, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,13 +171,21 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
case CGM_OP_CODE_START_SESSION:
|
case CGM_OP_CODE_START_SESSION:
|
||||||
mSessionStartTime = System.currentTimeMillis();
|
mSessionStartTime = System.currentTimeMillis();
|
||||||
break;
|
break;
|
||||||
|
case CGM_OP_CODE_STOP_SESSION:
|
||||||
|
mSessionStartTime = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCGMSpecificOpsOperationError(@NonNull final BluetoothDevice device, final int requestCode, final int error, final boolean secured) {
|
public void onCGMSpecificOpsOperationError(@NonNull final BluetoothDevice device, final int requestCode, final int errorCode, final boolean secured) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case CGM_OP_CODE_START_SESSION:
|
case CGM_OP_CODE_START_SESSION:
|
||||||
|
if (errorCode == CGM_ERROR_PROCEDURE_NOT_COMPLETED) {
|
||||||
|
// Session was already started before.
|
||||||
|
// Looks like the CGM Status characteristic has not been read, otherwise we would have got the Session Start Time before.
|
||||||
|
// The Session Start Time will be calculated when a next CGM packet is received based on it's Time Offset.
|
||||||
|
}
|
||||||
case CGM_OP_CODE_STOP_SESSION:
|
case CGM_OP_CODE_STOP_SESSION:
|
||||||
mSessionStartTime = 0;
|
mSessionStartTime = 0;
|
||||||
break;
|
break;
|
||||||
@@ -162,6 +213,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
mCallbacks.onOperationAborted(device);
|
mCallbacks.onOperationAborted(device);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
mRecordAccessRequestInProgress = false;
|
||||||
mCallbacks.onOperationCompleted(device);
|
mCallbacks.onOperationCompleted(device);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -169,6 +221,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRecordAccessOperationCompletedWithNoRecordsFound(@NonNull final BluetoothDevice device, final int requestCode) {
|
public void onRecordAccessOperationCompletedWithNoRecordsFound(@NonNull final BluetoothDevice device, final int requestCode) {
|
||||||
|
mRecordAccessRequestInProgress = false;
|
||||||
mCallbacks.onOperationCompleted(device);
|
mCallbacks.onOperationCompleted(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +229,14 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
public void onNumberOfRecordsReceived(@NonNull final BluetoothDevice device, final int numberOfRecords) {
|
public void onNumberOfRecordsReceived(@NonNull final BluetoothDevice device, final int numberOfRecords) {
|
||||||
mCallbacks.onNumberOfRecordsRequested(device, numberOfRecords);
|
mCallbacks.onNumberOfRecordsRequested(device, numberOfRecords);
|
||||||
if (numberOfRecords > 0) {
|
if (numberOfRecords > 0) {
|
||||||
final int sequenceNumber = mRecords.keyAt(mRecords.size() - 1) + 1;
|
if (mRecords.size() > 0) {
|
||||||
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber));
|
final int sequenceNumber = mRecords.keyAt(mRecords.size() - 1) + 1;
|
||||||
|
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber));
|
||||||
|
} else {
|
||||||
|
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportAllStoredRecords());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
mRecordAccessRequestInProgress = false;
|
||||||
mCallbacks.onOperationCompleted(device);
|
mCallbacks.onOperationCompleted(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,16 +253,19 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
})
|
})
|
||||||
.fail((error) -> log(LogContract.Log.Level.WARNING, "Failed to enabled Record Access Control Point indications (error " + error + ")"));
|
.fail((error) -> log(LogContract.Log.Level.WARNING, "Failed to enabled Record Access Control Point indications (error " + error + ")"));
|
||||||
|
|
||||||
// Start Continuous Glucose session
|
// Start Continuous Glucose session if hasn't been started before
|
||||||
writeCharacteristic(mCGMSpecificOpsControlPointCharacteristic, CGMSpecificOpsControlPointData.startSession(mSecured))
|
if (mSessionStartTime == 0L) {
|
||||||
.done(() -> log(LogContract.Log.Level.APPLICATION, "\"" + CGMSpecificOpsControlPointParser.parse(mCGMSpecificOpsControlPointCharacteristic) + "\" sent"))
|
writeCharacteristic(mCGMSpecificOpsControlPointCharacteristic, CGMSpecificOpsControlPointData.startSession(mSecured))
|
||||||
.fail((error) -> log(LogContract.Log.Level.ERROR, "Failed to start session (error " + error + ")"));
|
.done(() -> log(LogContract.Log.Level.APPLICATION, "\"" + CGMSpecificOpsControlPointParser.parse(mCGMSpecificOpsControlPointCharacteristic) + "\" sent"))
|
||||||
|
.fail((error) -> log(LogContract.Log.Level.ERROR, "Failed to start session (error " + error + ")"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isRequiredServiceSupported(@NonNull final BluetoothGatt gatt) {
|
protected boolean isRequiredServiceSupported(@NonNull final BluetoothGatt gatt) {
|
||||||
final BluetoothGattService service = gatt.getService(CGMS_UUID);
|
final BluetoothGattService service = gatt.getService(CGMS_UUID);
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
|
mCGMStatusCharacteristic = service.getCharacteristic(CGM_STATUS_UUID);
|
||||||
mCGMFeatureCharacteristic = service.getCharacteristic(CGM_FEAURE_UUID);
|
mCGMFeatureCharacteristic = service.getCharacteristic(CGM_FEAURE_UUID);
|
||||||
mCGMMeasurementCharacteristic = service.getCharacteristic(CGM_MEASUREMENT_UUID);
|
mCGMMeasurementCharacteristic = service.getCharacteristic(CGM_MEASUREMENT_UUID);
|
||||||
mCGMSpecificOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID);
|
mCGMSpecificOpsControlPointCharacteristic = service.getCharacteristic(CGM_OPS_CONTROL_POINT_UUID);
|
||||||
@@ -217,6 +278,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onDeviceDisconnected() {
|
protected void onDeviceDisconnected() {
|
||||||
super.onDeviceDisconnected();
|
super.onDeviceDisconnected();
|
||||||
|
mCGMStatusCharacteristic = null;
|
||||||
mCGMFeatureCharacteristic = null;
|
mCGMFeatureCharacteristic = null;
|
||||||
mCGMMeasurementCharacteristic = null;
|
mCGMMeasurementCharacteristic = null;
|
||||||
mCGMSpecificOpsControlPointCharacteristic = null;
|
mCGMSpecificOpsControlPointCharacteristic = null;
|
||||||
@@ -249,6 +311,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
|
|
||||||
clear();
|
clear();
|
||||||
mCallbacks.onOperationStarted(getBluetoothDevice());
|
mCallbacks.onOperationStarted(getBluetoothDevice());
|
||||||
|
mRecordAccessRequestInProgress = true;
|
||||||
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportLastStoredRecord());
|
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportLastStoredRecord());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +326,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
|
|
||||||
clear();
|
clear();
|
||||||
mCallbacks.onOperationStarted(getBluetoothDevice());
|
mCallbacks.onOperationStarted(getBluetoothDevice());
|
||||||
|
mRecordAccessRequestInProgress = true;
|
||||||
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportFirstStoredRecord());
|
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportFirstStoredRecord());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +337,6 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
if (mRecordAccessControlPointCharacteristic == null)
|
if (mRecordAccessControlPointCharacteristic == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mAbort = true;
|
|
||||||
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.abortOperation());
|
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.abortOperation());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +352,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
|
|
||||||
clear();
|
clear();
|
||||||
mCallbacks.onOperationStarted(getBluetoothDevice());
|
mCallbacks.onOperationStarted(getBluetoothDevice());
|
||||||
|
mRecordAccessRequestInProgress = true;
|
||||||
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportNumberOfAllStoredRecords());
|
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportNumberOfAllStoredRecords());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +373,7 @@ public class CGMSManager extends BatteryManager<CGMSManagerCallbacks> {
|
|||||||
|
|
||||||
// Obtain the last sequence number
|
// Obtain the last sequence number
|
||||||
final int sequenceNumber = mRecords.keyAt(mRecords.size() - 1) + 1;
|
final int sequenceNumber = mRecords.keyAt(mRecords.size() - 1) + 1;
|
||||||
|
mRecordAccessRequestInProgress = true;
|
||||||
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber));
|
writeCharacteristic(mRecordAccessControlPointCharacteristic, RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber));
|
||||||
// Info:
|
// Info:
|
||||||
// Operators OPERATOR_GREATER_THEN_OR_EQUAL, OPERATOR_LESS_THEN_OR_EQUAL and OPERATOR_RANGE are not supported by the CGMS sample from SDK
|
// Operators OPERATOR_GREATER_THEN_OR_EQUAL, OPERATOR_LESS_THEN_OR_EQUAL and OPERATOR_RANGE are not supported by the CGMS sample from SDK
|
||||||
|
|||||||
Reference in New Issue
Block a user