From ec2f90f7ecb12a8305e4ed9166342fafcd065918 Mon Sep 17 00:00:00 2001 From: Jamorham Date: Sat, 17 Jun 2023 12:56:48 +0000 Subject: [PATCH] Improve device status handling --- app/src/main/AndroidManifest.xml | 6 ++ .../dexdrip/NSClientReceiver.java | 16 ++-- .../eveningoutpost/dexdrip/alert/Persist.java | 47 +++++++++++- .../insulin/aaps/AAPSStatusHandler.java | 75 +++++++++++++++++++ .../dexdrip/models/Treatments.java | 4 + .../dexdrip/utilitymodels/Intents.java | 2 + .../dexdrip/alert/PersistTest.java | 53 +++++++++++++ 7 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/eveningoutpost/dexdrip/insulin/aaps/AAPSStatusHandler.java create mode 100644 app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4e2226f092..3f3a49feac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -502,6 +502,12 @@ + + + + + + diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/NSClientReceiver.java b/app/src/main/java/com/eveningoutpost/dexdrip/NSClientReceiver.java index 903320a319..4c752d84a8 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/NSClientReceiver.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/NSClientReceiver.java @@ -11,6 +11,7 @@ import android.preference.PreferenceManager; import android.util.Log; +import com.eveningoutpost.dexdrip.insulin.aaps.AAPSStatusHandler; import com.eveningoutpost.dexdrip.models.BgReading; import com.eveningoutpost.dexdrip.models.JoH; import com.eveningoutpost.dexdrip.models.Treatments; @@ -19,7 +20,6 @@ import com.eveningoutpost.dexdrip.utilitymodels.Intents; import com.eveningoutpost.dexdrip.utilitymodels.Pref; import com.eveningoutpost.dexdrip.profileeditor.ImportAapsProfile; -import com.google.gson.GsonBuilder; import org.json.JSONArray; import org.json.JSONException; @@ -28,7 +28,6 @@ import java.util.HashMap; import java.util.UUID; -import info.nightscout.sdk.localmodel.devicestatus.NSDeviceStatus; import lombok.val; @@ -60,6 +59,7 @@ public void onReceive(Context context, Intent intent) { if (action == null) return; switch (action) { + case Intents.ACTION_NEW_DEVICESTATUS: case Intents.ACTION_NS_BRIDGE: if (bundle == null) break; if (prefs.getBoolean("accept_nsclient_treatments", true)) { @@ -67,9 +67,7 @@ public void onReceive(Context context, Intent intent) { final String device_status_json = bundle.getString("devicestatus", ""); if (!emptyString(device_status_json)) { try { - val gson = new GsonBuilder().create(); // TODO optimize - val ds = gson.fromJson(device_status_json, NSDeviceStatus.class); - Log.e(TAG, "DEBUG: got device status: " + ds.toString()); + AAPSStatusHandler.processDeviceStatus(device_status_json); } catch (Exception e) { Log.e(TAG, "Exception processing device status in NS_BRIDGE action: " + e); } @@ -104,6 +102,7 @@ public void onReceive(Context context, Intent intent) { } break; + case Intents.ACTION_NEW_FOOD: // action changed unexpectedly case Intents.ACTION_NEW_TREATMENT: if (bundle == null) break; if (prefs.getBoolean("accept_nsclient_treatments", true)) { @@ -281,7 +280,12 @@ private String toTreatmentJSON(HashMap trt_map) { try { // jsonObject.put("uuid", UUID.fromString(trt_map.get("_id").toString()).toString()); - jsonObject.put("timestamp", trt_map.get("mills")); + Object ts = trt_map.get("mills"); + if (ts == null) { + ts = trt_map.get("date"); // identifier changed at some point + } + + jsonObject.put("timestamp", ts); jsonObject.put("eventType", trt_map.get("eventType")); jsonObject.put("enteredBy", trt_map.get("enteredBy")); if (trt_map.containsKey("carbs")) { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java index 800c20414e..b51b1ad368 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/alert/Persist.java @@ -1,5 +1,7 @@ package com.eveningoutpost.dexdrip.alert; +import static com.eveningoutpost.dexdrip.models.JoH.msSince; +import static com.eveningoutpost.dexdrip.models.JoH.tsl; import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.getLong; import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.getString; import static com.eveningoutpost.dexdrip.utilitymodels.PersistentStore.setLong; @@ -9,12 +11,14 @@ /** * JamOrHam - * + *

* Generic persistence property helper class */ public class Persist { + private static final java.lang.String PREF_TIMEOUT = "TIMEOUT__"; + @RequiredArgsConstructor public static class String { private final java.lang.String pref; @@ -40,4 +44,45 @@ public void set(final long value) { setLong(pref, value); } } + + public static class LongTimeout extends Long { + + private final long millis; + + public LongTimeout(final java.lang.String pref, final long millis) { + super(pref); + this.millis = millis; + } + + public void set() { + set(tsl()); + } + + public boolean expired() { + return msSince(get()) > millis; + } + } + + public static class StringTimeout extends String { + + private final LongTimeout timeout; + + public StringTimeout(final java.lang.String pref, final long millis) { + super(pref); + timeout = new LongTimeout(PREF_TIMEOUT + pref, millis); + } + + @Override + public void set(final java.lang.String value) { + super.set(value); + timeout.set(); + } + + @Override + public java.lang.String get() { + return !timeout.expired() ? super.get() : null; + } + + } + } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/insulin/aaps/AAPSStatusHandler.java b/app/src/main/java/com/eveningoutpost/dexdrip/insulin/aaps/AAPSStatusHandler.java new file mode 100644 index 0000000000..65fefbf46d --- /dev/null +++ b/app/src/main/java/com/eveningoutpost/dexdrip/insulin/aaps/AAPSStatusHandler.java @@ -0,0 +1,75 @@ +package com.eveningoutpost.dexdrip.insulin.aaps; + +import com.eveningoutpost.dexdrip.alert.Persist; +import com.eveningoutpost.dexdrip.models.UserError.Log; +import com.eveningoutpost.dexdrip.utilitymodels.Constants; +import com.eveningoutpost.dexdrip.utilitymodels.PumpStatus; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import info.nightscout.sdk.localmodel.devicestatus.NSDeviceStatus; +import lombok.val; + +/** + * JamOrHam + *

+ * Handle processing of AAPS device status updates + */ + +public class AAPSStatusHandler { + + private static final String TAG = AAPSStatusHandler.class.getSimpleName(); + private static final Gson gson = new GsonBuilder().create(); + private static final Persist.StringTimeout store = + new Persist.StringTimeout("AAPS_DEVICE_STATUS", Constants.MINUTE_IN_MS * 21); + private static volatile NSDeviceStatus last; + + // process and store received json in to object and maintain persistent time limited cache + public static void processDeviceStatus(final String json) { + synchronized (AAPSStatusHandler.class) { + try { + last = gson.fromJson(json, NSDeviceStatus.class); + Log.d(TAG, "DEBUG: got device status: " + last.toString()); + if (last != null) { + store.set(json); + val pump = last.getPump(); + if (pump != null) { + val r = pump.getReservoir(); + if (r != null) { + PumpStatus.setReservoir(r); + } + val b = pump.getBattery(); + if (b != null) { + val pc = b.getPercent(); + if (pc != null) { + PumpStatus.setBattery(pc); + } + } + } + } + } catch (Exception e) { + Log.e(TAG, "Error processing device status: " + e); + } + } + } + + // get instance either from cache or persistent store if still valid + public static NSDeviceStatus get() { + synchronized (AAPSStatusHandler.class) { + val json = store.get(); // local copy + if (json != null) { + if (last == null) { + // needs reconstructing + try { + last = gson.fromJson(json, NSDeviceStatus.class); + } catch (Exception e) { + Log.wtf(TAG, "Unusual problem reconstructing device status: " + e); + } + } + return last; + } + return null; + } + } + +} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java index 48bca899bd..67f316c86c 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/models/Treatments.java @@ -694,6 +694,10 @@ public static synchronized boolean pushTreatmentFromJson(String json, boolean fr Log.d(TAG, "Skipping Temp Basal msg"); return false; } + if (mytreatment.timestamp < 1) { + Log.e(TAG, "Invalid treatment timestamp or 0 or less"); + return false; + } if (mytreatment.uuid == null) { try { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Intents.java b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Intents.java index 4bfb4078ba..c6ee4c6827 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Intents.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utilitymodels/Intents.java @@ -37,6 +37,8 @@ public interface Intents { // From NS Android Client // send String ACTION_NEW_TREATMENT = "info.nightscout.client.NEW_TREATMENT"; + String ACTION_NEW_FOOD = "info.nightscout.client.NEW_FOOD"; + String ACTION_NEW_DEVICESTATUS = "info.nightscout.client.NEW_DEVICESTATUS"; String ACTION_CHANGED_TREATMENT = "info.nightscout.client.CHANGED_TREATMENT"; String ACTION_REMOVED_TREATMENT = "info.nightscout.client.REMOVED_TREATMENT"; String ACTION_NEW_PROFILE = "info.nightscout.client.NEW_PROFILE"; diff --git a/app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java b/app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java new file mode 100644 index 0000000000..35ee313ba4 --- /dev/null +++ b/app/src/test/java/com/eveningoutpost/dexdrip/alert/PersistTest.java @@ -0,0 +1,53 @@ +package com.eveningoutpost.dexdrip.alert; + + +import com.eveningoutpost.dexdrip.RobolectricTestWithConfig; +import com.eveningoutpost.dexdrip.models.JoH; +import com.eveningoutpost.dexdrip.utilitymodels.Constants; +import com.eveningoutpost.dexdrip.utilitymodels.PersistentStore; + +import static com.google.common.truth.Truth.assertWithMessage; + +import org.junit.Test; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowSystemClock; + +import java.time.Duration; + +import lombok.val; + +@Config(instrumentedPackages = {"com.eveningoutpost.dexdrip.models.JoH"}) +public class PersistTest extends RobolectricTestWithConfig { + + @Test + public void testTimeoutString() { + + val PREF_NAME = "TEST_TIMEOUT_STRING"; + val testString = "Hello world"; + + // setup + PersistentStore.removeItem(PREF_NAME); + ShadowSystemClock.advanceBy(Duration.ofHours(100)); + + val store = + new Persist.StringTimeout(PREF_NAME, Constants.MINUTE_IN_MS * 21); + + assertWithMessage("Time not zero").that(JoH.tsl()).isGreaterThan(Constants.HOUR_IN_MS); + assertWithMessage("test empty null").that(store.get()).isNull(); + + store.set(testString); + assertWithMessage("test ok 1").that(store.get()).isEqualTo(testString); + ShadowSystemClock.advanceBy(Duration.ofMinutes(1)); + assertWithMessage("test ok 2").that(store.get()).isEqualTo(testString); + ShadowSystemClock.advanceBy(Duration.ofMinutes(10)); + assertWithMessage("test ok 3").that(store.get()).isEqualTo(testString); + ShadowSystemClock.advanceBy(Duration.ofMinutes(11)); + assertWithMessage("test expired 4").that(store.get()).isNull(); + ShadowSystemClock.advanceBy(Duration.ofMinutes(11)); + assertWithMessage("test expired 5").that(store.get()).isNull(); + store.set(testString); + ShadowSystemClock.advanceBy(Duration.ofMinutes(10)); + assertWithMessage("test ok 4").that(store.get()).isEqualTo(testString); + } + +} \ No newline at end of file