Insight: add preemptive connection, keep-alive, statistics

This commit is contained in:
Jamorham 2018-02-18 14:57:28 +00:00
parent 448ebbc7bf
commit 9a76cdb3a1
No known key found for this signature in database
GPG key ID: 0BC5C3E0AAD64DF9
4 changed files with 218 additions and 13 deletions

View file

@ -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");
@ -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<StatusItem> 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);
}
}

View file

@ -3,12 +3,22 @@ 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 sugar.free.sightparser.handling.ServiceConnectionCallback;
import sugar.free.sightparser.handling.SightServiceConnector;
import sugar.free.sightparser.handling.StatusCallback;
@ -31,11 +41,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<Status, Long> 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 +63,22 @@ public class Connector {
@Override
public synchronized void onStatusChange(Status status) {
if ((status != lastStatus) || (Helpers.msSince(lastStatusTime) > 2000)) {
log("Status change: " + status);
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 +121,7 @@ public class Connector {
private Connector() {
initializeHistoryReceiver();
MainApp.bus().register(this);
}
public static Connector get() {
@ -117,10 +142,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 +205,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 +460,48 @@ 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<StatusItem> getStatusStatistics() {
final List<StatusItem> 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) {
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;
}
}
}

View file

@ -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);

View file

@ -901,5 +901,8 @@
<string name="day">day</string>
<string name="week">week</string>
<string name="time_plural">s</string>
<string name="insight_keepalive_format_string">%ds expires %s</string>
<string name="insight_keep_alive_status">Keep-alive status</string>
<string name="statistics">Statistics</string>
</resources>