Skip to content
Snippets Groups Projects
Unverified Commit cdd5f6dd authored by Johannes Mockenhaupt's avatar Johannes Mockenhaupt
Browse files

Ensure wake event is finished in all cases.

parent 39a92e72
No related branches found
No related tags found
No related merge requests found
......@@ -104,152 +104,148 @@ public class MedtronicCnlIntentService extends IntentService {
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent called");
try {
long timePollStarted = System.currentTimeMillis(),
timePollExpected = timePollStarted,
timeLastGoodSGV = dataStore.getLastPumpStatus().getEventDate().getTime();
long timePollStarted = System.currentTimeMillis(),
timePollExpected = timePollStarted,
timeLastGoodSGV = dataStore.getLastPumpStatus().getEventDate().getTime();
short pumpBatteryLevel = dataStore.getLastPumpStatus().getBatteryPercentage();
short pumpBatteryLevel = dataStore.getLastPumpStatus().getBatteryPercentage();
if (timeLastGoodSGV != 0) {
timePollExpected = timeLastGoodSGV + POLL_PERIOD_MS + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((timePollStarted - 1000L - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS));
}
if (timeLastGoodSGV != 0) {
timePollExpected = timeLastGoodSGV + POLL_PERIOD_MS + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((timePollStarted - 1000L - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS));
}
// avoid polling when too close to sensor-pump comms
if (((timePollExpected - timePollStarted) > 5000L) && ((timePollExpected - timePollStarted) < (POLL_GRACE_PERIOD_MS + 45000L))) {
sendStatus("Please wait: Poll due in " + ((timePollExpected - timePollStarted) / 1000L) + " seconds");
MedtronicCnlAlarmManager.setAlarm(timePollExpected);
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
return;
}
// avoid polling when too close to sensor-pump comms
if (((timePollExpected - timePollStarted) > 5000L) && ((timePollExpected - timePollStarted) < (POLL_GRACE_PERIOD_MS + 45000L))) {
sendStatus("Please wait: Poll due in " + ((timePollExpected - timePollStarted) / 1000L) + " seconds");
MedtronicCnlAlarmManager.setAlarm(timePollExpected);
return;
}
long pollInterval = configurationStore.getPollInterval();
if ((pumpBatteryLevel > 0) && (pumpBatteryLevel <= 25)) {
pollInterval = configurationStore.getLowBatteryPollInterval();
}
long pollInterval = configurationStore.getPollInterval();
if ((pumpBatteryLevel > 0) && (pumpBatteryLevel <= 25)) {
pollInterval = configurationStore.getLowBatteryPollInterval();
}
if (!hasUsbHostFeature()) {
sendStatus("It appears that this device doesn't support USB OTG.");
Log.e(TAG, "Device does not support USB OTG");
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
// TODO - throw, don't return
return;
}
if (!hasUsbHostFeature()) {
sendStatus("It appears that this device doesn't support USB OTG.");
Log.e(TAG, "Device does not support USB OTG");
// TODO - throw, don't return
return;
}
UsbDevice cnlStick = UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID);
if (cnlStick == null) {
sendStatus("USB connection error. Is the Contour Next Link plugged in?");
Log.w(TAG, "USB connection error. Is the CNL plugged in?");
UsbDevice cnlStick = UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID);
if (cnlStick == null) {
sendStatus("USB connection error. Is the Contour Next Link plugged in?");
Log.w(TAG, "USB connection error. Is the CNL plugged in?");
// TODO - set status if offline or Nightscout not reachable
uploadToNightscout();
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
// TODO - throw, don't return
return;
}
// TODO - set status if offline or Nightscout not reachable
uploadToNightscout();
// TODO - throw, don't return
return;
}
if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) {
sendMessage(Constants.ACTION_NO_USB_PERMISSION);
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
// TODO - throw, don't return
return;
}
mHidDevice = UsbHidDriver.acquire(mUsbManager, cnlStick);
if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) {
sendMessage(Constants.ACTION_NO_USB_PERMISSION);
// TODO - throw, don't return
return;
}
mHidDevice = UsbHidDriver.acquire(mUsbManager, cnlStick);
try {
mHidDevice.open();
} catch (Exception e) {
Log.e(TAG, "Unable to open serial device", e);
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
// TODO - throw, don't return
return;
}
try {
mHidDevice.open();
} catch (Exception e) {
Log.e(TAG, "Unable to open serial device", e);
// TODO - throw, don't return
return;
}
DateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.US);
DateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.US);
MedtronicCnlReader cnlReader = new MedtronicCnlReader(mHidDevice);
MedtronicCnlReader cnlReader = new MedtronicCnlReader(mHidDevice);
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
try {
sendStatus("Connecting to Contour Next Link");
Log.d(TAG, "Connecting to Contour Next Link");
cnlReader.requestDeviceInfo();
// Is the device already configured?
ContourNextLinkInfo info = realm
.where(ContourNextLinkInfo.class)
.equalTo("serialNumber", cnlReader.getStickSerial())
.findFirst();
if (info == null) {
info = realm.createObject(ContourNextLinkInfo.class, cnlReader.getStickSerial());
}
try {
sendStatus("Connecting to Contour Next Link");
Log.d(TAG, "Connecting to Contour Next Link");
cnlReader.requestDeviceInfo();
// Is the device already configured?
ContourNextLinkInfo info = realm
.where(ContourNextLinkInfo.class)
.equalTo("serialNumber", cnlReader.getStickSerial())
.findFirst();
cnlReader.getPumpSession().setStickSerial(info.getSerialNumber());
if (info == null) {
info = realm.createObject(ContourNextLinkInfo.class, cnlReader.getStickSerial());
}
cnlReader.enterControlMode();
cnlReader.getPumpSession().setStickSerial(info.getSerialNumber());
try {
cnlReader.enterPassthroughMode();
cnlReader.openConnection();
cnlReader.enterControlMode();
cnlReader.requestReadInfo();
try {
cnlReader.enterPassthroughMode();
cnlReader.openConnection();
String key = info.getKey();
cnlReader.requestReadInfo();
if (key == null) {
cnlReader.requestLinkKey();
String key = info.getKey();
info.setKey(MessageUtils.byteArrayToHexString(cnlReader.getPumpSession().getKey()));
key = info.getKey();
}
if (key == null) {
cnlReader.requestLinkKey();
cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key));
info.setKey(MessageUtils.byteArrayToHexString(cnlReader.getPumpSession().getKey()));
key = info.getKey();
}
long pumpMAC = cnlReader.getPumpSession().getPumpMAC();
Log.i(TAG, "PumpInfo MAC: " + (pumpMAC & 0xffffff));
PumpInfo activePump = realm
.where(PumpInfo.class)
.equalTo("pumpMac", pumpMAC)
.findFirst();
cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key));
if (activePump == null) {
activePump = realm.createObject(PumpInfo.class, pumpMAC);
}
long pumpMAC = cnlReader.getPumpSession().getPumpMAC();
Log.i(TAG, "PumpInfo MAC: " + (pumpMAC & 0xffffff));
PumpInfo activePump = realm
.where(PumpInfo.class)
.equalTo("pumpMac", pumpMAC)
.findFirst();
activePump.updateLastQueryTS();
if (activePump == null) {
activePump = realm.createObject(PumpInfo.class, pumpMAC);
}
activePump.updateLastQueryTS();
byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel());
if (radioChannel == 0) {
sendStatus("Could not communicate with the pump. Is it nearby?");
Log.i(TAG, "Could not communicate with the pump. Is it nearby?");
pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
} else {
dataStore.setActivePumpMac(pumpMAC);
byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel());
if (radioChannel == 0) {
sendStatus("Could not communicate with the pump. Is it nearby?");
Log.i(TAG, "Could not communicate with the pump. Is it nearby?");
pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
} else {
dataStore.setActivePumpMac(pumpMAC);
activePump.setLastRadioChannel(radioChannel);
sendStatus(String.format(Locale.getDefault(), "Connected on channel %d RSSI: %d%%", (int) radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage()));
Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel));
activePump.setLastRadioChannel(radioChannel);
sendStatus(String.format(Locale.getDefault(), "Connected on channel %d RSSI: %d%%", (int) radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage()));
Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel));
// read pump status
PumpStatusEvent pumpRecord = realm.createObject(PumpStatusEvent.class);
// read pump status
PumpStatusEvent pumpRecord = realm.createObject(PumpStatusEvent.class);
String deviceName = String.format("medtronic-600://%s", cnlReader.getStickSerial());
activePump.setDeviceName(deviceName);
String deviceName = String.format("medtronic-600://%s", cnlReader.getStickSerial());
activePump.setDeviceName(deviceName);
// TODO - this should not be necessary. We should reverse lookup the device name from PumpInfo
pumpRecord.setDeviceName(deviceName);
// TODO - this should not be necessary. We should reverse lookup the device name from PumpInfo
pumpRecord.setDeviceName(deviceName);
long pumpTime = cnlReader.getPumpTime().getTime();
long pumpOffset = pumpTime - System.currentTimeMillis();
Log.d(TAG, "Time offset between pump and device: " + pumpOffset + " millis.");
long pumpTime = cnlReader.getPumpTime().getTime();
long pumpOffset = pumpTime - System.currentTimeMillis();
Log.d(TAG, "Time offset between pump and device: " + pumpOffset + " millis.");
// TODO - send ACTION to MainActivity to show offset between pump and uploader.
pumpRecord.setPumpTimeOffset(pumpOffset);
pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset));
cnlReader.updatePumpStatus(pumpRecord);
// TODO - send ACTION to MainActivity to show offset between pump and uploader.
pumpRecord.setPumpTimeOffset(pumpOffset);
pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset));
cnlReader.updatePumpStatus(pumpRecord);
if (pumpRecord.getSgv() != 0) {
String offsetSign = "";
......@@ -290,81 +286,82 @@ public class MedtronicCnlIntentService extends IntentService {
dataStore.incUnavailableSGVCount(); // poll clash detection
}
realm.commitTransaction();
// Tell the Main Activity we have new data
sendMessage(Constants.ACTION_UPDATE_PUMP);
}
realm.commitTransaction();
// Tell the Main Activity we have new data
sendMessage(Constants.ACTION_UPDATE_PUMP);
}
} catch (UnexpectedMessageException e) {
Log.e(TAG, "Unexpected Message", e);
sendStatus("Communication Error: " + e.getMessage());
pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L);
} catch (UnexpectedMessageException e) {
Log.e(TAG, "Unexpected Message", e);
sendStatus("Communication Error: " + e.getMessage());
pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L);
} catch (TimeoutException e) {
Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
sendStatus("Timeout communicating with the Contour Next Link.");
pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L);
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Could not determine CNL HMAC", e);
sendStatus("Error connecting to Contour Next Link: Hashing error.");
} finally {
try {
cnlReader.closeConnection();
cnlReader.endPassthroughMode();
cnlReader.endControlMode();
} catch (NoSuchAlgorithmException e) {
}
}
} catch (IOException e) {
Log.e(TAG, "Error connecting to Contour Next Link.", e);
sendStatus("Error connecting to Contour Next Link.");
} catch (ChecksumException e) {
Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e);
sendStatus("Checksum error getting message from the Contour Next Link.");
} catch (EncryptionException e) {
Log.e(TAG, "Error decrypting messages from Contour Next Link.", e);
sendStatus("Error decrypting messages from Contour Next Link.");
} catch (TimeoutException e) {
Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
sendStatus("Timeout communicating with the Contour Next Link.");
pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L);
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Could not determine CNL HMAC", e);
sendStatus("Error connecting to Contour Next Link: Hashing error.");
} catch (UnexpectedMessageException e) {
Log.e(TAG, "Could not close connection.", e);
sendStatus("Could not close connection: " + e.getMessage());
} finally {
try {
cnlReader.closeConnection();
cnlReader.endPassthroughMode();
cnlReader.endControlMode();
} catch (NoSuchAlgorithmException e) {
if (!realm.isClosed()) {
if (realm.isInTransaction()) {
// If we didn't commit the transaction, we've run into an error. Let's roll it back
realm.cancelTransaction();
}
realm.close();
}
}
} catch (IOException e) {
Log.e(TAG, "Error connecting to Contour Next Link.", e);
sendStatus("Error connecting to Contour Next Link.");
} catch (ChecksumException e) {
Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e);
sendStatus("Checksum error getting message from the Contour Next Link.");
} catch (EncryptionException e) {
Log.e(TAG, "Error decrypting messages from Contour Next Link.", e);
sendStatus("Error decrypting messages from Contour Next Link.");
} catch (TimeoutException e) {
Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
sendStatus("Timeout communicating with the Contour Next Link.");
} catch (UnexpectedMessageException e) {
Log.e(TAG, "Could not close connection.", e);
sendStatus("Could not close connection: " + e.getMessage());
} finally {
if (!realm.isClosed()) {
if (realm.isInTransaction()) {
// If we didn't commit the transaction, we've run into an error. Let's roll it back
realm.cancelTransaction();
// TODO - set status if offline or Nightscout not reachable
sendToXDrip();
uploadToNightscout();
// smart polling and pump-sensor poll clash detection
long lastActualPollTime = timePollStarted;
if (timeLastGoodSGV > 0) {
lastActualPollTime = timeLastGoodSGV + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((System.currentTimeMillis() - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS));
}
realm.close();
}
// TODO - set status if offline or Nightscout not reachable
sendToXDrip();
uploadToNightscout();
// smart polling and pump-sensor poll clash detection
long lastActualPollTime = timePollStarted;
if (timeLastGoodSGV > 0) {
lastActualPollTime = timeLastGoodSGV + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((System.currentTimeMillis() - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS));
}
long nextActualPollTime = lastActualPollTime + POLL_PERIOD_MS;
long nextRequestedPollTime = lastActualPollTime + pollInterval;
if ((nextRequestedPollTime - System.currentTimeMillis()) < 10000L) {
nextRequestedPollTime = nextActualPollTime;
}
// extended unavailable SGV may be due to clash with the current polling time
// while we wait for a good SGV event, polling is auto adjusted by offsetting the next poll based on miss count
if (dataStore.getUnavailableSGVCount() > 0) {
if (timeLastGoodSGV == 0) {
nextRequestedPollTime += POLL_PERIOD_MS / 5L; // if there is a uploader/sensor poll clash on startup then this will push the next attempt out by 60 seconds
} else if (dataStore.getUnavailableSGVCount() > 2) {
sendStatus("Warning: No SGV available from pump for " + dataStore.getUnavailableSGVCount() + " attempts");
nextRequestedPollTime += ((long) ((dataStore.getUnavailableSGVCount() - 2) % 5)) * (POLL_PERIOD_MS / 10L); // adjust poll time in 1/10 steps to avoid potential poll clash (max adjustment at 5/10)
long nextActualPollTime = lastActualPollTime + POLL_PERIOD_MS;
long nextRequestedPollTime = lastActualPollTime + pollInterval;
if ((nextRequestedPollTime - System.currentTimeMillis()) < 10000L) {
nextRequestedPollTime = nextActualPollTime;
}
// extended unavailable SGV may be due to clash with the current polling time
// while we wait for a good SGV event, polling is auto adjusted by offsetting the next poll based on miss count
if (dataStore.getUnavailableSGVCount() > 0) {
if (timeLastGoodSGV == 0) {
nextRequestedPollTime += POLL_PERIOD_MS / 5L; // if there is a uploader/sensor poll clash on startup then this will push the next attempt out by 60 seconds
} else if (dataStore.getUnavailableSGVCount() > 2) {
sendStatus("Warning: No SGV available from pump for " + dataStore.getUnavailableSGVCount() + " attempts");
nextRequestedPollTime += ((long) ((dataStore.getUnavailableSGVCount() - 2) % 5)) * (POLL_PERIOD_MS / 10L); // adjust poll time in 1/10 steps to avoid potential poll clash (max adjustment at 5/10)
}
}
MedtronicCnlAlarmManager.setAlarm(nextRequestedPollTime);
sendStatus("Next poll due at: " + df.format(nextRequestedPollTime));
}
MedtronicCnlAlarmManager.setAlarm(nextRequestedPollTime);
sendStatus("Next poll due at: " + df.format(nextRequestedPollTime));
} finally {
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment