Skip to content
Snippets Groups Projects
Commit 4261d48d authored by Lennart Goedhart's avatar Lennart Goedhart Committed by GitHub
Browse files

Merge pull request #119 from jotomo/pazaan/develop-basal-rates

Fix timing issues, refactorings.
parents 39a92e72 f182e8e9
Branches
Tags
No related merge requests found
......@@ -70,7 +70,6 @@ import info.nightscout.android.USB.UsbHidDriver;
import info.nightscout.android.eula.Eula;
import info.nightscout.android.eula.Eula.OnEulaAgreedTo;
import info.nightscout.android.medtronic.service.MedtronicCnlAlarmManager;
import info.nightscout.android.medtronic.service.MedtronicCnlAlarmReceiver;
import info.nightscout.android.medtronic.service.MedtronicCnlIntentService;
import info.nightscout.android.model.medtronicNg.PumpInfo;
import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
......@@ -93,7 +92,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
private int chartZoom = 3;
private boolean hasZoomedChart = false;
private NumberFormat sgvFormatter;
private boolean mEnableCgmService = true;
private SharedPreferences prefs = null;
......@@ -104,7 +102,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
private Runnable mUiRefreshRunnable = new RefreshDisplayRunnable();
private Realm mRealm;
private StatusMessageReceiver statusMessageReceiver = new StatusMessageReceiver();
private MedtronicCnlAlarmReceiver medtronicCnlAlarmReceiver = new MedtronicCnlAlarmReceiver();
/**
* calculate the next poll timestamp based on last svg event
......@@ -167,15 +164,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
configurationStore.setMmolxl(prefs.getBoolean("mmolxl", false));
configurationStore.setMmolxlDecimals(prefs.getBoolean("mmolDecimals", false));
if (configurationStore.isMmolxl()) {
if (configurationStore.isMmolxlDecimals())
sgvFormatter = new DecimalFormat("0.00");
else
sgvFormatter = new DecimalFormat("0.0");
} else {
sgvFormatter = new DecimalFormat("0");
}
// Disable battery optimization to avoid missing values on 6.0+
// taken from https://github.com/NightscoutFoundation/xDrip/blob/master/app/src/main/java/com/eveningoutpost/dexdrip/Home.java#L277L298
......@@ -559,14 +547,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
} else if (key.equals("mmolxl") || key.equals("mmolDecimals")) {
configurationStore.setMmolxl(sharedPreferences.getBoolean("mmolxl", false));
configurationStore.setMmolxlDecimals(sharedPreferences.getBoolean("mmolDecimals", false));
if (configurationStore.isMmolxl()) {
if (configurationStore.isMmolxlDecimals())
sgvFormatter = new DecimalFormat("0.00");
else
sgvFormatter = new DecimalFormat("0.0");
} else {
sgvFormatter = new DecimalFormat("0");
}
refreshDisplay();
} else if (key.equals("pollInterval")) {
configurationStore.setPollInterval(Long.parseLong(sharedPreferences.getString("pollInterval",
......@@ -944,6 +924,9 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
private class UsbReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO move this somewhere else ... wherever it belongs
// realm might be closed ... sometimes occurs when USB is disconnected and replugged ...
if (mRealm.isClosed()) mRealm = Realm.getDefaultInstance();
String action = intent.getAction();
if (MedtronicCnlIntentService.Constants.ACTION_USB_PERMISSION.equals(action)) {
boolean permissionGranted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
......
......@@ -16,11 +16,10 @@ import info.nightscout.android.utils.ConfigurationStore;
*/
public class MedtronicCnlAlarmManager {
private static final String TAG = MedtronicCnlAlarmManager.class.getSimpleName();
private static final int ALARM_ID = 102; // Alarm id
private static final int ALARM_ID = 102;
private static PendingIntent pendingIntent = null;
private static AlarmManager alarmManager = null;
private static long nextAlarm = Long.MAX_VALUE;
public static void setContext(Context context) {
cancelAlarm();
......@@ -30,11 +29,6 @@ public class MedtronicCnlAlarmManager {
pendingIntent = PendingIntent.getBroadcast(context, ALARM_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
// Setting the alarm in 15 seconds from now
public static void setAlarm() {
setAlarm(System.currentTimeMillis());
}
/**
* set the alarm in the future
*
......@@ -44,7 +38,7 @@ public class MedtronicCnlAlarmManager {
setAlarm(System.currentTimeMillis() + inFuture);
}
// Setting the alarm to call onRecieve
// Setting the alarm to call onReceive
public static void setAlarm(long millis) {
if (alarmManager == null || pendingIntent == null)
return;
......@@ -56,21 +50,14 @@ public class MedtronicCnlAlarmManager {
if (millis < now)
millis = now;
// only accept alarm nearer than the last one
//if (nextAlarm < millis && nextAlarm > now) {
// return;
//}
cancelAlarm();
nextAlarm = millis;
Log.d(TAG, "Alarm set to fire at " + new Date(millis));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(millis, null), pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Android 5.0.0 + 5.0.1 (e.g. Galaxy S4) has a bug.
// Alarms are not exact. Fixed in 5.0.2 oder CM12
// Alarms are not exact. Fixed in 5.0.2 and CM12
alarmManager.setExact(AlarmManager.RTC_WAKEUP, millis, pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, millis, pendingIntent);
......
......@@ -57,6 +57,8 @@ public class MedtronicCnlIntentService extends IntentService {
private UsbManager mUsbManager;
private DataStore dataStore = DataStore.getInstance();
private ConfigurationStore configurationStore = ConfigurationStore.getInstance();
private DateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss", Locale.US);
public MedtronicCnlIntentService() {
super(MedtronicCnlIntentService.class.getName());
......@@ -104,68 +106,45 @@ public class MedtronicCnlIntentService extends IntentService {
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent called");
long timePollStarted = System.currentTimeMillis(),
timePollExpected = timePollStarted,
timeLastGoodSGV = dataStore.getLastPumpStatus().getEventDate().getTime();
short pumpBatteryLevel = dataStore.getLastPumpStatus().getBatteryPercentage();
try {
// TODO use of ConfigurationStore is confusinng if pollInterval uses the CS, which
// uses the POLL_PERIOD_MS, while the latter constant is also used directly.
// Note that the variable pollInterval refers to the poll we'd like to make to the pump,
// based on settings and battery level, while POLL_PERIOD_MS is used to calculate
// when the pump is going to poll data from the transmitter again.
// Thus POLL_PERIOD_MS is important to calculate times we'd be clashing with transmitter
// to pump transmissions, which are then checked against the time the uploader would
// like to poll, which is calculated using the pollInterval variable.
// TODO find better variable names to make this distinction clearer and/or if possible
// do more method extraction refactorings to make this method easier to grasp
final long timePollStarted = System.currentTimeMillis();
final long timeLastGoodSGV = dataStore.getLastPumpStatus().getSgvDate().getTime();
final long timePollExpected;
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));
} else {
timePollExpected = timePollStarted;
}
// 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;
}
final short pumpBatteryLevel = dataStore.getLastPumpStatus().getBatteryPercentage();
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;
}
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;
}
if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) {
sendMessage(Constants.ACTION_NO_USB_PERMISSION);
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
// TODO - throw, don't return
if (!openUsbDevice())
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;
}
DateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.US);
MedtronicCnlReader cnlReader = new MedtronicCnlReader(mHidDevice);
......@@ -256,7 +235,7 @@ public class MedtronicCnlIntentService extends IntentService {
if (pumpOffset > 0) {
offsetSign = "+";
}
sendStatus("SGV: " + MainActivity.strFormatSGV(pumpRecord.getSgv()) + " At: " + df.format(pumpRecord.getEventDate().getTime()) + " Pump: " + offsetSign + (pumpOffset / 1000L) + "sec"); //note: event time is currently stored with offset
sendStatus("SGV: " + MainActivity.strFormatSGV(pumpRecord.getSgv()) + " At: " + dateFormatter.format(pumpRecord.getSgvDate().getTime()) + " Pump: " + offsetSign + (pumpOffset / 1000L) + "sec"); //note: event time is currently stored with offset
// Check if pump sent old event when new expected
if (pumpRecord != null &&
......@@ -267,15 +246,12 @@ public class MedtronicCnlIntentService extends IntentService {
sendStatus("Pump sent old SGV event, re-polling...");
}
//MainActivity.timeLastGoodSGV = pumpRecord.getEventDate().getTime(); // track last good sgv event time
//MainActivity.pumpBattery = pumpRecord.getBatteryPercentage(); // track pump battery
timeLastGoodSGV = pumpRecord.getEventDate().getTime();
dataStore.clearUnavailableSGVCount(); // reset unavailable sgv count
// Check that the record doesn't already exist before committing
RealmResults<PumpStatusEvent> checkExistingRecords = activePump.getPumpHistory()
.where()
.equalTo("eventDate", pumpRecord.getEventDate()) // >>>>>>> check as event date may not = exact pump event date due to it being stored with offset added this could lead to dup events due to slight variability in time offset
.equalTo("sgvDate", pumpRecord.getSgvDate())
.equalTo("sgv", pumpRecord.getSgv())
.findAll();
......@@ -338,10 +314,16 @@ public class MedtronicCnlIntentService extends IntentService {
}
realm.close();
}
// TODO - set status if offline or Nightscout not reachable
sendToXDrip();
uploadToNightscout();
uploadPollResults();
scheduleNextPoll(timePollStarted, timeLastGoodSGV, pollInterval);
}
} finally {
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
}
}
private void scheduleNextPoll(long timePollStarted, long timeLastGoodSGV, long pollInterval) {
// smart polling and pump-sensor poll clash detection
long lastActualPollTime = timePollStarted;
if (timeLastGoodSGV > 0) {
......@@ -363,10 +345,41 @@ public class MedtronicCnlIntentService extends IntentService {
}
}
MedtronicCnlAlarmManager.setAlarm(nextRequestedPollTime);
sendStatus("Next poll due at: " + df.format(nextRequestedPollTime));
sendStatus("Next poll due at: " + dateFormatter.format(nextRequestedPollTime));
}
MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
/**
* @return if device acquisition was successful
*/
private boolean openUsbDevice() {
if (!hasUsbHostFeature()) {
sendStatus("It appears that this device doesn't support USB OTG.");
Log.e(TAG, "Device does not support USB OTG");
return false;
}
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?");
return false;
}
if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) {
sendMessage(Constants.ACTION_NO_USB_PERMISSION);
return false;
}
mHidDevice = UsbHidDriver.acquire(mUsbManager, cnlStick);
try {
mHidDevice.open();
} catch (Exception e) {
sendStatus("Unable to open USB device");
Log.e(TAG, "Unable to open serial device", e);
return false;
}
return true;
}
// reliable wake alarm manager wake up for all android versions
......@@ -380,6 +393,11 @@ public class MedtronicCnlIntentService extends IntentService {
alarm.set(AlarmManager.RTC_WAKEUP, wakeTime, pendingIntent);
}
private void uploadPollResults() {
sendToXDrip();
uploadToNightscout();
}
private void sendToXDrip() {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (prefs.getBoolean(getString(R.string.preference_enable_xdrip_plus), false)) {
......@@ -392,6 +410,7 @@ public class MedtronicCnlIntentService extends IntentService {
}
private void uploadToNightscout() {
// TODO - set status if offline or Nightscout not reachable
Intent receiverIntent = new Intent(this, NightscoutUploadReceiver.class);
final long timestamp = System.currentTimeMillis() + 1000L;
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, (int) timestamp, receiverIntent, PendingIntent.FLAG_ONE_SHOT);
......
......@@ -71,6 +71,8 @@ public class EntriesSerializer implements JsonSerializer<PumpStatusEvent> {
}
}
// TODO currentnly unused, see info.nightscout.android.xdrip_plus.XDripPlusUploadIntentService.addSgvEntry()
// TODO also, proper method name
@Override
public JsonElement serialize(PumpStatusEvent src, Type typeOfSrc, JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
......@@ -78,8 +80,8 @@ public class EntriesSerializer implements JsonSerializer<PumpStatusEvent> {
jsonObject.addProperty("direction", getDirectionString(src.getCgmTrend()));
jsonObject.addProperty("device", src.getDeviceName());
jsonObject.addProperty("type", "sgv");
jsonObject.addProperty("date", src.getEventDate().getTime());
jsonObject.addProperty("dateString", String.valueOf(src.getEventDate()));
jsonObject.addProperty("date", src.getSgvDate().getTime());
jsonObject.addProperty("dateString", String.valueOf(src.getSgvDate()));
return jsonObject;
}
......
......@@ -26,6 +26,7 @@ public class DataStore {
// set some initial dummy values
PumpStatusEvent dummyStatus = new PumpStatusEvent();
dummyStatus.setSgvDate(new Date());
// bypass setter to avoid dealing with a real Realm object
instance.lastPumpStatus = dummyStatus;
......
......@@ -145,8 +145,8 @@ public class XDripPlusUploadIntentService extends IntentService {
json.put("direction", EntriesSerializer.getDirectionString(pumpRecord.getCgmTrend()));
json.put("device", pumpRecord.getDeviceName());
json.put("type", "sgv");
json.put("date", pumpRecord.getEventDate().getTime());
json.put("dateString", pumpRecord.getEventDate());
json.put("date", pumpRecord.getSgvDate().getTime());
json.put("dateString", pumpRecord.getSgvDate());
entriesArray.put(json);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment