diff --git a/app/src/main/java/de/jotomo/ruffyscripter/PumpCapabilities.java b/app/src/main/java/de/jotomo/ruffyscripter/PumpCapabilities.java new file mode 100644 index 0000000000..1270851ea0 --- /dev/null +++ b/app/src/main/java/de/jotomo/ruffyscripter/PumpCapabilities.java @@ -0,0 +1,23 @@ +package de.jotomo.ruffyscripter; + +/** + * Created by adrian on 26/07/17. + * + * Contains the capabilities of the current pump model. + */ + +public class PumpCapabilities { + public long maxTempPercent; + + public PumpCapabilities maxTempPercent(long maxTempPercent) { + this.maxTempPercent = maxTempPercent; + return this; + } + + @Override + public String toString() { + return "PumpCapabilities{" + + "maxTempPercent=" + maxTempPercent + + '}'; + } +} diff --git a/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java b/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java index 25d6eea7e8..4d519b4669 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java @@ -19,6 +19,8 @@ public class PumpState { */ public String errorMsg; public boolean suspended; + public boolean lowBattery; + public int insulinState; public PumpState tbrActive(boolean tbrActive) { this.tbrActive = tbrActive; @@ -59,6 +61,8 @@ public class PumpState { ", tbrRemainingDuration=" + tbrRemainingDuration + ", errorMsg=" + errorMsg + ", suspended=" + suspended + + ", lowBattery=" + lowBattery + + ", insulinState=" + insulinState + ", timestamp=" + timestamp + '}'; } diff --git a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java index 72fe99c75d..5a29c46985 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java @@ -478,6 +478,8 @@ public class RuffyScripter { state.tbrRemainingDuration = durationMenuTime.getHour() * 60 + durationMenuTime.getMinute(); state.tbrRate = ((double) menu.getAttribute(MenuAttribute.BASAL_RATE)); } + state.lowBattery = ((boolean) menu.getAttribute(MenuAttribute.LOW_BATTERY)); + state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE)); // TODO v2, read current base basal rate, which is shown center when no TBR is active. // Check if that holds true when an extended bolus is running. // Add a field to PumpStatus, rather than renaming/overloading tbrRate to mean @@ -486,6 +488,8 @@ public class RuffyScripter { state.errorMsg = (String) menu.getAttribute(MenuAttribute.MESSAGE); } else if (menuType == MenuType.STOP) { state.suspended = true; + state.lowBattery = ((boolean) menu.getAttribute(MenuAttribute.LOW_BATTERY)); + state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE)); } else { StringBuilder sb = new StringBuilder(); for (MenuAttribute menuAttribute : menu.attributes()) { diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java index baba5e5216..296f61f056 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java @@ -3,6 +3,7 @@ package de.jotomo.ruffyscripter.commands; import java.util.Date; import de.jotomo.ruffyscripter.History; +import de.jotomo.ruffyscripter.PumpCapabilities; import de.jotomo.ruffyscripter.PumpState; public class CommandResult { @@ -13,6 +14,7 @@ public class CommandResult { public String message; public PumpState state; public History history; + public PumpCapabilities capabilities; public CommandResult() { } @@ -52,6 +54,11 @@ public class CommandResult { return this; } + public CommandResult capabilities(PumpCapabilities capabilities) { + this.capabilities = capabilities; + return this; + } + @Override public String toString() { return "CommandResult{" + diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/DetermineCapabilitiesCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/DetermineCapabilitiesCommand.java new file mode 100644 index 0000000000..adc740c13e --- /dev/null +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/DetermineCapabilitiesCommand.java @@ -0,0 +1,165 @@ +package de.jotomo.ruffyscripter.commands; + +import android.os.SystemClock; + +import com.j256.ormlite.stmt.query.In; + +import org.monkey.d.ruffy.ruffy.driver.display.MenuAttribute; +import org.monkey.d.ruffy.ruffy.driver.display.MenuType; +import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import de.jotomo.ruffyscripter.PumpCapabilities; +import de.jotomo.ruffyscripter.PumpState; +import de.jotomo.ruffyscripter.RuffyScripter; + + +public class DetermineCapabilitiesCommand implements Command { + private static final Logger log = LoggerFactory.getLogger(DetermineCapabilitiesCommand.class); + public static final int UP_STEPS = 75; + public static final int RETRIES = 5; + + @Override + public List validateArguments() { + return Collections.emptyList(); + } + + @Override + public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { + try { + + //read main menu 100% or TBR? Read remaining duration. + long durationBefore = readDisplayedTbrDurationMainMenu(scripter); + long percentageBefore = readDisplayedTbrPercentageMainMenu(scripter); + + enterTbrMenu(scripter); + long maxTbrPercentage = findMaxTbrPercentage(scripter); + + // TODO v2 this can probably be removed by now + SystemClock.sleep(750); + + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU, + "Pump did not return to MAIN_MEU after finding max tbr. " + + "Check pump manually, the TBR might be wrong."); + + + //TODO: check if TBR is still the same or duration was less than 5 minutes + long durationAfter = readDisplayedTbrDurationMainMenu(scripter); + long percentageAfter = readDisplayedTbrPercentageMainMenu(scripter); + + if(Math.abs(durationBefore-durationAfter) > 5){ + throw new CommandException().message("Duration jump during DetermineCapabilities"); + } + if(percentageAfter != percentageBefore){ + if(durationBefore<5 && percentageAfter == 100){ + log.debug("(percentageBefore != percentageAfter) - ignoring as tbr is now 100% and had a very short duration left"); + } + throw new CommandException().message("TBR changed while determining maxTBR."); + } + + //TODO return Result + return new CommandResult().success(true).enacted(false).message("Capablities: {maxTbrPercentage = " + maxTbrPercentage + ", success=" + "success }").capabilities((new PumpCapabilities()).maxTempPercent(maxTbrPercentage)); + } catch (CommandException e) { + return e.toCommandResult(); + } + } + + private void enterTbrMenu(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); + scripter.navigateToMenu(MenuType.TBR_MENU); + scripter.verifyMenuIsDisplayed(MenuType.TBR_MENU); + scripter.pressCheckKey(); + scripter.waitForMenuUpdate(); + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + } + + private long findMaxTbrPercentage(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + long activeTempBasal = readDisplayedTbrPercentage(scripter); + + // pretend to increase the TBR to more than 500% + log.debug("Pressing up " + UP_STEPS + " times to get to maximum"); + for (int i = 0; i < UP_STEPS; i++) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + scripter.pressUpKey(); + SystemClock.sleep(200); + log.debug("Push #" + (i + 1)); + } + + //read the displayed maximum value + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + long maximumTempBasal = readDisplayedTbrPercentage(scripter); + + //reset the TBR in a controlled manner + long percentageChange = maximumTempBasal - activeTempBasal; + long percentageSteps = percentageChange / 10; + + int retries= 0; + while (percentageSteps > 0 && retries < RETRIES) { + log.debug("Pressing down " + percentageSteps + " times to get to previous value. Retry " + retries); + for (int i = 0; i < percentageSteps; i++) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + scripter.pressDownKey(); + SystemClock.sleep(200); + log.debug("Push #" + (i + 1)); + } + //do the rest if button-presses failed. + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + long currentPercentage = readDisplayedTbrPercentage(scripter); + percentageChange = currentPercentage - activeTempBasal; + percentageSteps = percentageChange / 10; + retries++; + } + + + //exit menu + scripter.pressCheckKey(); + scripter.waitForMenuToBeLeft(MenuType.TBR_SET); + return maximumTempBasal; + } + + + private long readDisplayedTbrPercentage(RuffyScripter scripter) { + SystemClock.sleep(1000); + // TODO v2 add timeout? Currently the command execution timeout would trigger if exceeded + Object percentageObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE); + // this as a bit hacky, the display value is blinking, so we might catch that, so + // keep trying till we get the Double we want + while (!(percentageObj instanceof Double)) { + scripter.waitForMenuUpdate(); + percentageObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE); + } + return ((Double) percentageObj).longValue(); + } + + private int readDisplayedTbrDurationMainMenu(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); + if(scripter.currentMenu.attributes().contains(MenuAttribute.RUNTIME)){ + // TODO v2 add timeout? Currently the command execution timeout would trigger if exceeded + Object durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); + MenuTime duration = (MenuTime) durationObj; + return duration.getHour() * 60 + duration.getMinute(); + } else { + return 0; + } + } + + private int readDisplayedTbrPercentageMainMenu(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); + if(scripter.currentMenu.attributes().contains(MenuAttribute.TBR)){ + return (int)((Double) scripter.currentMenu.getAttribute(MenuAttribute.TBR)).doubleValue(); + } else { + return 100; + } + } + + @Override + public String toString() { + return "DetermineCapabilitiesCommand{}"; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java index 569f57bb25..403bbaf6e2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.PumpCombo; import android.app.Activity; +import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -33,6 +34,8 @@ public class ComboFragment extends Fragment implements View.OnClickListener { } private Button refresh; + private TextView updateCapabilities; + private TextView statusText; private TextView tbrPercentageText; @@ -44,12 +47,19 @@ public class ComboFragment extends Fragment implements View.OnClickListener { private TextView lastCmdTimeText; private TextView lastCmdResultText; + private TextView tbrCapabilityText; + private TextView pumpstateBatteryText; + private TextView insulinstateText; + + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.combopump_fragment, container, false); refresh = (Button) view.findViewById(R.id.combo_refresh); + updateCapabilities = (TextView) view.findViewById(R.id.combo_update_capabilities); + statusText = (TextView) view.findViewById(R.id.combo_status); tbrPercentageText = (TextView) view.findViewById(R.id.combo_tbr_percentage); @@ -60,8 +70,12 @@ public class ComboFragment extends Fragment implements View.OnClickListener { lastCmdText = (TextView) view.findViewById(R.id.combo_last_command); lastCmdTimeText = (TextView) view.findViewById(R.id.combo_last_command_time); lastCmdResultText = (TextView) view.findViewById(R.id.combo_last_command_result); + tbrCapabilityText = (TextView) view.findViewById(R.id.combo_tbr_capability); + pumpstateBatteryText = (TextView) view.findViewById(R.id.combo_pumpstate_battery); + insulinstateText = (TextView) view.findViewById(R.id.combo_insulinstate); refresh.setOnClickListener(this); + updateCapabilities.setOnClickListener(this); updateGUI(); return view; @@ -97,6 +111,32 @@ public class ComboFragment extends Fragment implements View.OnClickListener { }); thread.start(); break; + case R.id.combo_update_capabilities: + (new Thread(new Runnable() { + @Override + public void run() { + Activity activity = getActivity(); + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + updateCapabilities.setText("{fa-bluetooth spin}"); + } + }); + + getPlugin().updateCapabilities(); + + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + updateCapabilities.setText("{fa-bluetooth-b}"); + } + }); + + } + })).start(); + break; } } @@ -121,6 +161,24 @@ public class ComboFragment extends Fragment implements View.OnClickListener { tbrRateText.setText(""); } pumpErrorText.setText(ps.errorMsg != null ? ps.errorMsg : ""); + if(ps.lowBattery){ + pumpstateBatteryText.setText("{fa-battery-empty}"); + pumpstateBatteryText.setTextColor(Color.RED); + } else { + pumpstateBatteryText.setText("{fa-battery-three-quarters}"); + pumpstateBatteryText.setTextColor(Color.WHITE); + } + switch (ps.insulinState){ + case 0: insulinstateText.setText("ok"); + insulinstateText.setTextColor(Color.WHITE); + break; + case 1: insulinstateText.setText("low"); + insulinstateText.setTextColor(Color.YELLOW); + break; + case 2: insulinstateText.setText("empty"); + insulinstateText.setTextColor(Color.RED); + break; + } } Command lastCmd = getPlugin().lastCmd; @@ -139,6 +197,7 @@ public class ComboFragment extends Fragment implements View.OnClickListener { lastCmdResultText.setText(""); } } + tbrCapabilityText.setText(getPlugin().getPumpDescription().maxTempPercent + "%"); } }); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java index c037b3dc2e..2625b4699b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java @@ -5,11 +5,13 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.graphics.Color; import android.media.RingtoneManager; import android.net.Uri; import android.os.IBinder; import android.os.SystemClock; +import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; @@ -28,6 +30,7 @@ import de.jotomo.ruffyscripter.commands.BolusCommand; import de.jotomo.ruffyscripter.commands.CancelTbrCommand; import de.jotomo.ruffyscripter.commands.Command; import de.jotomo.ruffyscripter.commands.CommandResult; +import de.jotomo.ruffyscripter.commands.DetermineCapabilitiesCommand; import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand; import de.jotomo.ruffyscripter.commands.SetTbrCommand; import de.jotomo.ruffyscripter.PumpState; @@ -46,11 +49,14 @@ import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI; import info.nightscout.utils.DateUtil; +import info.nightscout.utils.SP; +import info.nightscout.utils.ToastUtils; /** * Created by mike on 05.08.2016. */ public class ComboPlugin implements PluginBase, PumpInterface { + public static final String COMBO_MAX_TEMP_PERCENT_SP = "combo_maxTempPercent"; private static Logger log = LoggerFactory.getLogger(ComboPlugin.class); private boolean fragmentEnabled = false; @@ -99,7 +105,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { pumpDescription.isTempBasalCapable = true; pumpDescription.tempBasalStyle = PumpDescription.PERCENT; - pumpDescription.maxTempPercent = 500; + pumpDescription.maxTempPercent = SP.getInt(COMBO_MAX_TEMP_PERCENT_SP, 500); pumpDescription.tempPercentStep = 10; pumpDescription.tempDurationStep = 15; @@ -319,9 +325,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { return; } - if (!reason.toLowerCase().contains("user") - && lastCmdTime.getTime() > 0 - && System.currentTimeMillis() > lastCmdTime.getTime() + 60 * 1000) { + boolean notAUserRequest = !reason.toLowerCase().contains("user"); + boolean wasRunAtLeastOnce = lastCmdTime.getTime() > 0; + boolean ranWithinTheLastMinute = System.currentTimeMillis() < lastCmdTime.getTime() + 60 * 1000; + if (notAUserRequest && wasRunAtLeastOnce && ranWithinTheLastMinute) { log.debug("Not fetching state from pump, since we did already within the last 60 seconds"); } else { runCommand(new ReadPumpStateCommand()); @@ -623,6 +630,34 @@ public class ComboPlugin implements PluginBase, PumpInterface { public void onStatusEvent(final EventAppExit e) { unbindRuffyService(); } + + + public void updateCapabilities() { + + // if Android is sluggish this might get called before ruffy is bound + if (ruffyScripter == null) { + log.warn("Rejecting call to RefreshDataFromPump: ruffy service not bound (yet)"); + ToastUtils.showToastInUiThread(MainApp.instance(), "Ruffy not initialized."); + return; + } + if (isBusy()){ + ToastUtils.showToastInUiThread(MainApp.instance(), "Pump busy!"); + return; + } + CommandResult result = runCommand(new DetermineCapabilitiesCommand()); + if (result.success){ + pumpDescription.maxTempPercent = (int) result.capabilities.maxTempPercent; + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance()); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt(COMBO_MAX_TEMP_PERCENT_SP, pumpDescription.maxTempPercent); + editor.commit(); + MainApp.bus().post(new EventComboPumpUpdateGUI()); + } else { + ToastUtils.showToastInUiThread(MainApp.instance(), "No success."); + } + } + + } diff --git a/app/src/main/res/layout/combopump_fragment.xml b/app/src/main/res/layout/combopump_fragment.xml index a0d85fb849..0ac36b1a42 100644 --- a/app/src/main/res/layout/combopump_fragment.xml +++ b/app/src/main/res/layout/combopump_fragment.xml @@ -20,7 +20,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Refresh" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +