diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index ddf768c77f..91446ada32 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -35,9 +35,9 @@ import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import info.nightscout.androidaps.Services.AlarmSoundService; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.events.EventAppExit; +import info.nightscout.androidaps.events.EventFeatureRunning; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; import info.nightscout.androidaps.interfaces.PluginBase; @@ -222,6 +222,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe super.onResume(); askForSMSPermissions(); askForLocationPermissions(); + MainApp.bus().post(new EventFeatureRunning(EventFeatureRunning.Feature.MAIN)); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventFeatureRunning.java b/app/src/main/java/info/nightscout/androidaps/events/EventFeatureRunning.java new file mode 100644 index 0000000000..0d07cd6c61 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventFeatureRunning.java @@ -0,0 +1,36 @@ +package info.nightscout.androidaps.events; + +/** + * Created by jamorham on 07/02/2018. + * + * Event to indicate that an app feature is being used, for example bolus wizard being opened + * + * The purpose this has been created for is to enable opportunistic connection to the pump + * so that it is already connected before the user wishes to enact a pump function + * + */ + +public class EventFeatureRunning extends Event { + + private Feature feature = Feature.UNKNOWN; + + public EventFeatureRunning() { + } + + public EventFeatureRunning(Feature feature) { + this.feature = feature; + } + + public Feature getFeature() { + return feature; + } + + public enum Feature { + UNKNOWN, + MAIN, + WIZARD, + + JUST_ADD_MORE_HERE + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java index 185ee5996d..594523cea6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java @@ -50,6 +50,7 @@ import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; +import info.nightscout.androidaps.events.EventFeatureRunning; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; @@ -57,8 +58,6 @@ import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; -import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAPlugin; -import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; import info.nightscout.androidaps.queue.Callback; import info.nightscout.utils.BolusWizard; import info.nightscout.utils.DateUtil; @@ -132,6 +131,7 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com public void onResume() { super.onResume(); MainApp.bus().register(this); + MainApp.bus().post(new EventFeatureRunning(EventFeatureRunning.Feature.WIZARD)); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPumpPlugin.java index f75689fcff..519306e0e0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPumpPlugin.java @@ -70,6 +70,7 @@ import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.round * */ +@SuppressWarnings("AccessStaticViaInstance") public class InsightPumpPlugin implements PluginBase, PumpInterface, ConstraintsInterface { private static final long BUSY_WAIT_TIME = 20000; @@ -285,7 +286,7 @@ public class InsightPumpPlugin implements PluginBase, PumpInterface, Constraints if (!connector.isPumpConnected()) { if (Helpers.ratelimit("insight-connect-timer", 40)) { log("Actually requesting a connect"); - connector.getServiceConnector().connect(); + connector.connectToPump(); } } else { log("Already connected"); @@ -306,7 +307,7 @@ public class InsightPumpPlugin implements PluginBase, PumpInterface, Constraints try { if (!SP.getBoolean("insight_always_connected", false)) { log("Requesting disconnect"); - connector.getServiceConnector().disconnect(); + connector.disconnectFromPump(); } else { log("Not disconnecting due to preference"); } @@ -322,7 +323,7 @@ public class InsightPumpPlugin implements PluginBase, PumpInterface, Constraints if (isConnecting()) { if (!SP.getBoolean("insight_always_connected", false)) { log("Requesting disconnect"); - connector.getServiceConnector().disconnect(); + connector.disconnectFromPump(); } else { log("Not disconnecting due to preference"); } @@ -470,6 +471,22 @@ public class InsightPumpPlugin implements PluginBase, PumpInterface, Constraints if (percent_amount > 250) percent_amount = 250; + // TODO this is experimental and not enabled due to preference option + // ignore TBR changes if the setting is enabled and they are not 0% but are +- 30% of where we are now or we've been running is TBR < 15 minutes + if ((percent_amount != 0) + && (SP.getBoolean("insight_dont_change_active_tbr", false) + && MainApp.getConfigBuilder().isTempBasalInProgress())) { + final TemporaryBasal temporaryBasalFromHistory = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()); + if ((temporaryBasalFromHistory != null) && (temporaryBasalFromHistory.isInProgress())) { + // only change if it is +- 30% from where we are now or we have done more than 15 minutes already + if ((Math.abs(temporaryBasalFromHistory.percentRate - percent_amount) <= 30) + || (temporaryBasalFromHistory.getRealDuration() < 15)) { + log("Refusing to change TBR due to Insight plugin setting: " + percent_amount + " vs " + temporaryBasalFromHistory.percentRate + " running for: " + temporaryBasalFromHistory.getRealDuration()); + return new PumpEnactResult().enacted(false).success(true); + } + } + } + final SetTBRTaskRunner task = new SetTBRTaskRunner(connector.getServiceConnector(), percent_amount, durationInMinutes); final UUID cmd = aSyncTaskRunner(task, "Set TBR abs: " + absoluteRate + " " + durationInMinutes + "m"); @@ -616,7 +633,7 @@ public class InsightPumpPlugin implements PluginBase, PumpInterface, Constraints final UUID cmd; if (fauxTBRcancel) { - cmd = aSyncTaskRunner(new SetTBRTaskRunner(connector.getServiceConnector(), 100, 1), "Faux Cancel TBR - setting " + "90%" + " 1m"); + cmd = aSyncTaskRunner(new SetTBRTaskRunner(connector.getServiceConnector(), 100, 1), "Faux Cancel TBR - setting " + "90%" + " 1m"); } else { cmd = aSyncSingleCommand(new CancelTBRMessage(), "Cancel Temp Basal"); } @@ -893,6 +910,16 @@ public class InsightPumpPlugin implements PluginBase, PumpInterface, Constraints l.add(new StatusItem(gs(R.string.insight_last_completed_action), LiveHistory.getStatus())); } + final String keep_alive_status = Connector.getKeepAliveString(); + if (keep_alive_status != null) { + l.add(new StatusItem(gs(R.string.insight_keep_alive_status), keep_alive_status)); + } + + final List status_statistics = connector.getStatusStatistics(); + if (status_statistics.size() > 0) { + l.addAll(status_statistics); + } + if (Helpers.ratelimit("insight-status-ui-refresh", 10)) { connector.tryToGetPumpStatusAgain(); } @@ -910,7 +937,7 @@ public class InsightPumpPlugin implements PluginBase, PumpInterface, Constraints public void run() { updateGui(); } - }, 500); + }, 1000); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/Connector.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/Connector.java index fea7895a27..bb1d16a22d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/Connector.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/Connector.java @@ -3,12 +3,23 @@ package info.nightscout.androidaps.plugins.PumpInsight.connector; import android.content.Intent; import android.os.PowerManager; +import com.squareup.otto.Subscribe; + +import java.util.ArrayList; +import java.util.Formatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventFeatureRunning; import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightPumpUpdateGui; import info.nightscout.androidaps.plugins.PumpInsight.history.HistoryReceiver; import info.nightscout.androidaps.plugins.PumpInsight.history.LiveHistory; import info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers; +import info.nightscout.androidaps.plugins.PumpInsight.utils.StatusItem; +import info.nightscout.utils.SP; import sugar.free.sightparser.handling.ServiceConnectionCallback; import sugar.free.sightparser.handling.SightServiceConnector; import sugar.free.sightparser.handling.StatusCallback; @@ -31,11 +42,17 @@ import static sugar.free.sightparser.handling.SightService.COMPATIBILITY_VERSION public class Connector { + // TODO connection statistics + private static final String TAG = "InsightConnector"; private static final String COMPANION_APP_PACKAGE = "sugar.free.sightremote"; private final static long FRESH_MS = 70000; + private static final Map statistics = new HashMap<>(); private static volatile Connector instance; private static volatile HistoryReceiver historyReceiver; + private static volatile long stayConnectedTill = -1; + private static volatile long stayConnectedTime = 0; + private static volatile boolean disconnect_thread_running = false; private volatile SightServiceConnector serviceConnector; private volatile Status lastStatus = null; private String compatabilityMessage = null; @@ -47,14 +64,22 @@ public class Connector { @Override public synchronized void onStatusChange(Status status) { - log("Status change: " + status); - lastStatus = status; - lastStatusTime = Helpers.tsl(); - if (status == Status.CONNECTED) { - lastContactTime = lastStatusTime; - } + if ((status != lastStatus) || (Helpers.msSince(lastStatusTime) > 2000)) { + log("Status change: " + status); - MainApp.bus().post(new EventInsightPumpUpdateGui()); + updateStatusStatistics(lastStatus, lastStatusTime); + lastStatus = status; + lastStatusTime = Helpers.tsl(); + + if (status == Status.CONNECTED) { + lastContactTime = lastStatusTime; + extendKeepAliveIfActive(); + } + + MainApp.bus().post(new EventInsightPumpUpdateGui()); + } else { + log("Same status as before: " + status); + } } }; @@ -97,6 +122,7 @@ public class Connector { private Connector() { initializeHistoryReceiver(); + MainApp.bus().register(this); } public static Connector get() { @@ -117,10 +143,30 @@ public class Connector { } public static void connectToPump() { - log("Attempting to connect to pump"); + connectToPump(0); + } + + public synchronized static void connectToPump(long keep_alive) { + log("Attempting to connect to pump."); + if (keep_alive > 0) { + stayConnectedTime = keep_alive; + stayConnectedTill = Helpers.tsl() + keep_alive; + log("Staying connected till: " + Helpers.dateTimeText(stayConnectedTill)); + delayedDisconnectionThread(); + } get().getServiceConnector().connect(); } + public static void disconnectFromPump() { + if (Helpers.tsl() >= stayConnectedTill) { + log("Requesting real pump disconnect"); + get().getServiceConnector().disconnect(); + } else { + log("Cannot disconnect as due to keep alive till: " + Helpers.dateTimeText(stayConnectedTill)); + // TODO set a disconnection timer? + } + } + static void log(String msg) { android.util.Log.e("INSIGHTPUMP", msg); } @@ -160,6 +206,67 @@ public class Connector { return MainApp.instance().getString(id); } + private static synchronized void extendKeepAliveIfActive() { + if (keepAliveActive()) { + if (Helpers.ratelimit("extend-insight-keepalive", 10)) { + stayConnectedTill = Helpers.tsl() + stayConnectedTime; + log("Keep-alive extended until: " + Helpers.dateTimeText(stayConnectedTill)); + } + } + } + + private static boolean keepAliveActive() { + return Helpers.tsl() <= stayConnectedTill; + } + + public static String getKeepAliveString() { + if (keepAliveActive()) { + return MainApp.instance().getString(R.string.insight_keepalive_format_string, + stayConnectedTime / 1000, Helpers.hourMinuteSecondString(stayConnectedTill)); + + } else { + return null; + } + } + + + private static synchronized void delayedDisconnectionThread() { + if (keepAliveActive()) { + if (!disconnect_thread_running) { + disconnect_thread_running = true; + new Thread(new Runnable() { + @Override + public void run() { + final PowerManager.WakeLock wl = Helpers.getWakeLock("insight-disconnection-timer", 600000); + try { + while (keepAliveActive()) { + if (Helpers.ratelimit("insight-expiry-notice", 5)) { + log("Staying connected thread expires: " + Helpers.dateTimeText(stayConnectedTill)); + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // + } + } + log("Sending the real delayed disconnect"); + get().getServiceConnector().disconnect(); + } finally { + Helpers.releaseWakeLock(wl); + disconnect_thread_running = false; + } + } + }).start(); + } else { + log("Disconnect thread already running"); + } + } + } + + private static long percentage(long t, long total) { + return (long) (Helpers.roundDouble(((double) t * 100) / total, 0)); + } + @SuppressWarnings("AccessStaticViaInstance") private synchronized void initializeHistoryReceiver() { if (historyReceiver == null) { @@ -354,4 +461,50 @@ public class Connector { return true; // TODO evaluate whether current } + private void updateStatusStatistics(Status last, long since) { + if ((last != null) && (since > 0)) { + Long total = statistics.get(last); + if (total == null) total = 0L; + statistics.put(last, total + Helpers.msSince(since)); + log("Updated statistics for: " + last + " total: " + Helpers.niceTimeScalar(statistics.get(last))); + // TODO persist data + } + } + + public List getStatusStatistics() { + final List l = new ArrayList<>(); + long total = 0; + for (Map.Entry entry : statistics.entrySet()) { + total += getEntryTime(entry); + } + for (Map.Entry entry : statistics.entrySet()) { + if ((long) entry.getValue() > 1000) { + l.add(new StatusItem(gs(R.string.statistics) + " " + Helpers.capitalize(entry.getKey().toString()), + new Formatter().format("%4s %12s", + percentage(getEntryTime(entry), total) + "%", + Helpers.niceTimeScalar(getEntryTime(entry))).toString())); + } + } + return l; + } + + private long getEntryTime(Map.Entry entry) { + return (long) entry.getValue() + (entry.getKey().equals(lastStatus) ? Helpers.msSince(lastStatusTime) : 0); + } + + @Subscribe + public void onStatusEvent(final EventFeatureRunning ev) { + if (SP.getBoolean("insight_preemptive_connect", true)) { + switch (ev.getFeature()) { + case WIZARD: + log("Wizard feature detected, preconnecting to pump"); + connectToPump(120 * 1000); + break; + case MAIN: + log("Main feature detected, preconnecting to pump"); + connectToPump(30 * 1000); + break; + } + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java index 5375d88b7b..b4618ae3b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java @@ -36,7 +36,10 @@ class HistoryIntentAdapter { final int pump_tbr_duration = intent.getIntExtra(HistoryBroadcast.EXTRA_DURATION, -1); final int pump_tbr_percent = intent.getIntExtra(HistoryBroadcast.EXTRA_TBR_AMOUNT, -1); - final int pump_record_id = intent.getIntExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1); + long pump_record_id = intent.getLongExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1); + if (pump_record_id == -1) { + pump_record_id = intent.getIntExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1); + } final long pump_serial_number = Long.parseLong(intent.getStringExtra(HistoryBroadcast.EXTRA_PUMP_SERIAL_NUMBER)); final Date event_time = getDateExtra(intent, HistoryBroadcast.EXTRA_EVENT_TIME); final Date start_time = getDateExtra(intent, HistoryBroadcast.EXTRA_START_TIME); @@ -60,8 +63,11 @@ class HistoryIntentAdapter { void processDeliveredBolusIntent(Intent intent) { final String bolus_type = intent.getStringExtra(HistoryBroadcast.EXTRA_BOLUS_TYPE); - final int bolus_id = intent.getIntExtra(HistoryBroadcast.EXTRA_BOLUS_ID,-1); - final int pump_record_id = intent.getIntExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1); + final int bolus_id = intent.getIntExtra(HistoryBroadcast.EXTRA_BOLUS_ID, -1); + long pump_record_id = intent.getLongExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1); + if (pump_record_id == -1) { + pump_record_id = intent.getIntExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1); + } final long pump_serial_number = Long.parseLong(intent.getStringExtra(HistoryBroadcast.EXTRA_PUMP_SERIAL_NUMBER)); final Date event_time = getDateExtra(intent, HistoryBroadcast.EXTRA_EVENT_TIME); final Date start_time = getDateExtra(intent, HistoryBroadcast.EXTRA_START_TIME); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/utils/Helpers.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/utils/Helpers.java index ce86f33e16..a2abda40d7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/utils/Helpers.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/utils/Helpers.java @@ -162,6 +162,31 @@ public class Helpers { return niceTimeScalar(t).replaceFirst("^1 ", ""); } + public static String niceTimeScalarBrief(long t) { + // TODO i18n wont work for non-latin characterset + return niceTimeScalar(t).replaceFirst("([a-z])[a-z]*", "$1").replace(" ",""); + } + + public static String hourMinuteString(long timestamp) { + return android.text.format.DateFormat.format("kk:mm", timestamp).toString(); + } + + public static String hourMinuteSecondString(long timestamp) { + return android.text.format.DateFormat.format("kk:mm:ss", timestamp).toString(); + } + + public static String dateTimeText(long timestamp) { + return android.text.format.DateFormat.format("yyyy-MM-dd kk:mm:ss", timestamp).toString(); + } + + public static String dateText(long timestamp) { + return android.text.format.DateFormat.format("yyyy-MM-dd", timestamp).toString(); + } + + public static String capitalize(String text) { + return text.substring(0, 1).toUpperCase() + text.substring(1).toLowerCase(); + } + public static double roundDouble(double value, int places) { if (places < 0) throw new IllegalArgumentException("Invalid decimal places"); BigDecimal bd = new BigDecimal(value); diff --git a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java index c5fb9821bc..10efffffe8 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java @@ -41,7 +41,8 @@ public class QueueThread extends Thread { this.queue = queue; PowerManager powerManager = (PowerManager) MainApp.instance().getApplicationContext().getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "QueueThread"); + //mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "QueueThread"); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "QueueThread"); } @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ed8ce1552e..2b6077860c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -913,6 +913,12 @@ day week s + %ds expires %s + Keep-alive status + Statistics + Connect preemptively + Automatically connect when AndroidAPS screens are opened, before any pump command is requested, to reduce connection delay + Not recommended due to battery drain enableSMB_always enableSMB_with_COB enableSMB_with_temptarget diff --git a/app/src/main/res/xml/pref_insightpump.xml b/app/src/main/res/xml/pref_insightpump.xml index 9dc3c24d91..93d51f1318 100644 --- a/app/src/main/res/xml/pref_insightpump.xml +++ b/app/src/main/res/xml/pref_insightpump.xml @@ -7,7 +7,13 @@ + android:title="@string/insight_stay_always_connected" + android:summary="@string/not_recommended_due_to_battery_drain"/> +