diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BaseCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BaseCommand.java deleted file mode 100644 index 0e83f0c1a1..0000000000 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BaseCommand.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.jotomo.ruffyscripter.commands; - -import de.jotomo.ruffyscripter.RuffyScripter; - -public abstract class BaseCommand implements Command { - // RS will inject itself here - protected RuffyScripter scripter; - @Override public void setScripter(RuffyScripter scripter) { this.scripter = scripter; } - - // TODO upcoming - protected final boolean canBeCancelled = true; - protected volatile boolean cancelRequested = false; - public void requestCancellation() { - cancelRequested = true; - } - public boolean isCancellable() { return canBeCancelled; } -} diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java deleted file mode 100644 index b18b09c0d0..0000000000 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ /dev/null @@ -1,169 +0,0 @@ -package de.jotomo.ruffyscripter.commands; - -import android.os.SystemClock; - -import org.monkey.d.ruffy.ruffy.driver.display.MenuAttribute; -import org.monkey.d.ruffy.ruffy.driver.display.MenuType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public class BolusCommand extends BaseCommand { - private static final Logger log = LoggerFactory.getLogger(BolusCommand.class); - - protected final double bolus; - - public BolusCommand(double bolus) { - this.bolus = bolus; - } - - @Override - public List validateArguments() { - List violations = new ArrayList<>(); - - if (bolus <= 0 || bolus > 25) { - violations.add("Requested bolus " + bolus + " out of limits (0-25)"); - } - - return violations; - } - - @Override - public CommandResult execute() { - try { - enterBolusMenu(); - - inputBolusAmount(); - verifyDisplayedBolusAmount(); - - // confirm bolus - scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); - scripter.pressCheckKey(); - - // the pump displays the entered bolus and waits a bit to let user check and cancel - // and then returns to the main menu - scripter.waitForMenuToBeLeft(MenuType.BOLUS_ENTER); - scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU, - "Pump did not return to MAIN_MEU from BOLUS_ENTER to deliver bolus. " - + "Check pump manually, the bolus might not have been delivered."); - - // wait for bolus delivery to complete; the remaining units to deliver are counted - // down and are displayed on the main menu. - Double bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING); - while (bolusRemaining != null) { - log.debug("Delivering bolus, remaining: " + bolusRemaining); - SystemClock.sleep(200); - if (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { - throw new CommandException().success(false).enacted(true) - .message("Warning/error raised after bolus delivery started. " + - "The treatment has been recorded, please check it against the " + - "pumps records and delete it if it hasn't finished successfully."); - } - bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING); - } - - - // TODO what if we hit 'cartridge low' alert here? is it immediately displayed or after the bolus? - // TODO how are error states reported back to the caller that occur outside of calls in genal? Low battery, low cartridge? - - // make sure no alert (occlusion, cartridge empty) has occurred. - scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU, - "Bolus delivery did not complete as expected. " - + "Check pump manually, the bolus might not have been delivered."); - - // read last bolus record; those menus display static data and therefore - // only a single menu update is sent - scripter.navigateToMenu(MenuType.MY_DATA_MENU); - scripter.verifyMenuIsDisplayed(MenuType.MY_DATA_MENU); - scripter.pressCheckKey(); - if (scripter.getCurrentMenu().getType() != MenuType.BOLUS_DATA) { - scripter.waitForMenuUpdate(); - } - - if (!scripter.getCurrentMenu().attributes().contains(MenuAttribute.BOLUS)) { - throw new CommandException().success(false).enacted(true) - .message("Bolus was delivered, but unable to confirm it with history record"); - } - - double lastBolusInHistory = (double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS); - if (Math.abs(bolus - lastBolusInHistory) > 0.05) { - throw new CommandException().success(false).enacted(true) - .message("Last bolus shows " + lastBolusInHistory - + " U delievered, but " + bolus + " U were requested"); - } - log.debug("Bolus record in history confirms delivered bolus"); - - // leave menu to go back to main menu - scripter.pressCheckKey(); - scripter.waitForMenuToBeLeft(MenuType.BOLUS_DATA); - scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU, - "Bolus was correctly delivered and checked against history, but we " - + "did not return the main menu successfully."); - - return new CommandResult().success(true).enacted(true) - .message(String.format(Locale.US, "Delivered %02.1f U", bolus)); - } catch (CommandException e) { - return e.toCommandResult(); - } - } - - protected void enterBolusMenu() { - scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); - scripter.navigateToMenu(MenuType.BOLUS_MENU); - scripter.verifyMenuIsDisplayed(MenuType.BOLUS_MENU); - scripter.pressCheckKey(); - scripter.waitForMenuUpdate(); - scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); - } - - protected void inputBolusAmount() { - scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); - // press 'up' once for each 0.1 U increment - long steps = Math.round(bolus * 10); - for (int i = 0; i < steps; i++) { - scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); - scripter.pressUpKey(); - SystemClock.sleep(100); - } - // Give the pump time to finish any scrolling that might still be going on, can take - // up to 1100ms. Plus some extra time to be sure - SystemClock.sleep(2000); - } - - protected void verifyDisplayedBolusAmount() { - scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); - - // wait up to 5s for any scrolling to finish - double displayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS); - long timeout = System.currentTimeMillis() + 10 * 1000; - while (timeout > System.currentTimeMillis() && bolus - displayedBolus > 0.05) { - log.debug("Waiting for pump to process scrolling input for amount, current: " + displayedBolus + ", desired: " + bolus); - SystemClock.sleep(50); - displayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS); - } - - log.debug("Final bolus: " + displayedBolus); - if (Math.abs(displayedBolus - bolus) > 0.05) { - throw new CommandException().message("Failed to set correct bolus. Expected: " + bolus + ", actual: " + displayedBolus); - } - - // check again to ensure the displayed value hasn't change due to due scrolling taking extremely long - SystemClock.sleep(1000); - scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); - double refreshedDisplayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS); - if (Math.abs(displayedBolus - refreshedDisplayedBolus) > 0.05) { - throw new CommandException().message("Failed to set bolus: bolus changed after input stopped from " - + displayedBolus + " -> " + refreshedDisplayedBolus); - } - } - - @Override - public String toString() { - return "BolusCommand{" + - "bolus=" + bolus + - '}'; - } -} diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetReservoirLevelCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/GetReservoirLevelCommand.java deleted file mode 100644 index 3a620a0fef..0000000000 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetReservoirLevelCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.jotomo.ruffyscripter.commands; - -import java.util.List; - -public class GetReservoirLevelCommand extends BaseCommand { - @Override - public CommandResult execute() { - // TODO stub - // watch out, level goes into PumpState, which is usually set by RuffyScripter - // after a command ran, unless a command has already set it ... I don't like - // that, it's too implicit ... - - // also, maybe ditch this command and add a parameter to GetPumpStateCommand to also - // read the reservoir level if possible (pump must be in a state to accept commands - // (possible on main, stop ...) - return null; - } - - @Override - public List validateArguments() { - // TODO stub - return null; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index b8ab7ed8dd..7f3aad2f93 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -47,13 +47,4 @@ public class Config { public static final boolean logDanaBTComm = true; public static final boolean logDanaMessageDetail = true; public static final boolean logDanaSerialEngine = true; - - // Combo specific - /** enable the UNFINISHED and currently BROKEN bolus cammand that reports progress and can be cancelled */ - public static final boolean comboExperimentalBolus = false; - - /** Very quick hack to split up bolus into 2 U parts, spaced roughly 45s apart. - * If there's an error during bolusing, no record is created in AAPS. - * Don't combine with experimental bolus! */ - public static final boolean comboExperimentalSplitBoluses = false && !comboExperimentalBolus; } 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 f4d49c0a94..ef974b8046 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 @@ -17,9 +17,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.jotomo.ruffyscripter.PumpState; -import de.jotomo.ruffyscripter.commands.Command; -import de.jotomo.ruffyscripter.commands.CommandResult; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.PumpState; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.Command; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.CommandResult; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI; 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 ea8280ff41..d79310fbdf 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 @@ -23,17 +23,7 @@ import org.slf4j.LoggerFactory; import java.util.Date; -import de.jotomo.ruffyscripter.PumpState; -import de.jotomo.ruffyscripter.RuffyScripter; -import de.jotomo.ruffyscripter.commands.BolusCommand; -import de.jotomo.ruffyscripter.commands.CancelTbrCommand; -import de.jotomo.ruffyscripter.commands.CancellableBolusCommand; -import de.jotomo.ruffyscripter.commands.Command; -import de.jotomo.ruffyscripter.commands.CommandResult; -import de.jotomo.ruffyscripter.commands.GetPumpStateCommand; -import de.jotomo.ruffyscripter.commands.SetTbrCommand; import info.nightscout.androidaps.BuildConfig; -import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.DetailedBolusInfo; @@ -48,6 +38,11 @@ import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.PumpState; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.Command; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.CommandResult; import info.nightscout.utils.DateUtil; import info.nightscout.utils.SP; @@ -55,7 +50,6 @@ import info.nightscout.utils.SP; * 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; @@ -68,6 +62,8 @@ public class ComboPlugin implements PluginBase, PumpInterface { private ComboPump pump = new ComboPump(); + private boolean ignoreLastSetTbrOrReadStateFailure = false; + @Nullable private volatile BolusCommand runningBolusCommand; @@ -99,7 +95,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { pumpDescription.isTempBasalCapable = true; pumpDescription.tempBasalStyle = PumpDescription.PERCENT; - pumpDescription.maxTempPercent = SP.getInt(COMBO_MAX_TEMP_PERCENT_SP, 500); + pumpDescription.maxTempPercent = 500; pumpDescription.tempPercentStep = 10; pumpDescription.tempDurationStep = 15; @@ -136,10 +132,12 @@ public class ComboPlugin implements PluginBase, PumpInterface { while (true) { Command localLastCmd = pump.lastCmd; CommandResult localLastCmdResult = pump.lastCmdResult; - if (localLastCmdResult != null && !localLastCmdResult.success) { + if (!SP.getBoolean(R.string.combo_disable_alerts, false) && + localLastCmdResult != null && !localLastCmdResult.success) { long now = System.currentTimeMillis(); long fiveMinutesSinceLastAlarm = lastAlarmTime + (5 * 60 * 1000) + (15 * 1000); - if (now > fiveMinutesSinceLastAlarm) { + boolean loopEnabled = ConfigBuilderPlugin.getActiveLoop() != null; + if (now > fiveMinutesSinceLastAlarm && loopEnabled) { log.error("Command failed: " + localLastCmd); log.error("Command result: " + localLastCmdResult); PumpState localPumpState = pump.state; @@ -345,7 +343,8 @@ public class ComboPlugin implements PluginBase, PumpInterface { if (notAUserRequest && wasRunAtLeastOnce && ranWithinTheLastMinute) { log.debug("Not fetching state from pump, since we did already within the last 60 seconds"); } else { - runCommand(new GetPumpStateCommand()); + // TODO +// runCommand(new GetPumpStateCommand()); } } @@ -359,27 +358,30 @@ public class ComboPlugin implements PluginBase, PumpInterface { return basal; } - private static CancellableBolusCommand.ProgressReportCallback bolusProgressReportCallback = - new CancellableBolusCommand.ProgressReportCallback() { + private static BolusCommand.ProgressReportCallback bolusProgressReportCallback = + new BolusCommand.ProgressReportCallback() { @Override - public void report(CancellableBolusCommand.ProgressReportCallback.State state, int percent, double delivered) { - EventOverviewBolusProgress enent = EventOverviewBolusProgress.getInstance(); + public void report(BolusCommand.ProgressReportCallback.State state, int percent, double delivered) { + EventOverviewBolusProgress event = EventOverviewBolusProgress.getInstance(); switch (state) { + case PROGRAMMING: + event.status = MainApp.sResources.getString(R.string.bolusprogramming); + break; case DELIVERING: - enent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); + event.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); break; case DELIVERED: - enent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivered), delivered); + event.status = String.format(MainApp.sResources.getString(R.string.bolusdelivered), delivered); break; case STOPPING: - enent.status = MainApp.sResources.getString(R.string.bolusstopping); + event.status = MainApp.sResources.getString(R.string.bolusstopping); break; case STOPPED: - enent.status = MainApp.sResources.getString(R.string.bolusstopped); + event.status = MainApp.sResources.getString(R.string.bolusstopped); break; } - enent.percent = percent; - MainApp.bus().post(enent); + event.percent = percent; + MainApp.bus().post(event); } }; @@ -389,7 +391,8 @@ public class ComboPlugin implements PluginBase, PumpInterface { if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) { if (detailedBolusInfo.insulin > 0) { // bolus needed, ask pump to deliver it - if (!Config.comboExperimentalSplitBoluses) { + if (!(SP.getBoolean(R.string.key_combo_enable_experimental_features, false) + && SP.getBoolean(R.string.key_combo_enable_experimental_split_bolus, false))) { return deliverBolus(detailedBolusInfo); } else { // split up bolus into 2 U parts @@ -456,9 +459,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { @NonNull private PumpEnactResult deliverBolus(DetailedBolusInfo detailedBolusInfo) { - runningBolusCommand = Config.comboExperimentalBolus - ? new CancellableBolusCommand(detailedBolusInfo.insulin, bolusProgressReportCallback) - : new BolusCommand(detailedBolusInfo.insulin); + runningBolusCommand = new BolusCommand(detailedBolusInfo.insulin, bolusProgressReportCallback); CommandResult bolusCmdResult = runCommand(runningBolusCommand); PumpEnactResult pumpEnactResult = new PumpEnactResult(); pumpEnactResult.success = bolusCmdResult.success; @@ -535,6 +536,20 @@ public class ComboPlugin implements PluginBase, PumpInterface { if (unroundedPercentage != roundedPercentage) { log.debug("Rounded requested rate " + unroundedPercentage + "% -> " + roundedPercentage + "%"); } + + TemporaryBasal activeTemp = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()); + if (!force && activeTemp != null) { + int minRequiredDelta = SP.getInt(R.string.key_combo_experimental_skip_tbr_changes_below_delta, 0); + boolean deltaBelowThreshold = Math.abs(activeTemp.percentRate - roundedPercentage) < minRequiredDelta; + if (deltaBelowThreshold) { + log.debug("Skipping setting APS-requested TBR change, since the requested change from " + + activeTemp.percentRate + " -> " + roundedPercentage + " is below the delta threshold of " + minRequiredDelta); + PumpEnactResult pumpEnactResult = new PumpEnactResult(); + pumpEnactResult.success = true; + pumpEnactResult.enacted = false; + return pumpEnactResult; + } + } return setTempBasalPercent(roundedPercentage, durationInMinutes); } @@ -556,7 +571,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { adjustedPercent = rounded.intValue(); } - CommandResult commandResult = runCommand(new SetTbrCommand(adjustedPercent, durationInMinutes)); + CommandResult commandResult = ruffyScripter.setTbr(adjustedPercent, durationInMinutes); if (commandResult.enacted) { TemporaryBasal tempStart = new TemporaryBasal(commandResult.completionTime); @@ -602,7 +617,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { if (activeTemp == null || userRequested) { /* v1 compatibility to sync DB to pump if they diverged (activeTemp == null) */ log.debug("cancelTempBasal: hard-cancelling TBR since user requested"); - commandResult = runCommand(new CancelTbrCommand()); + commandResult = ruffyScripter.cancelTbr(); if (commandResult.enacted) { tempBasal = new TemporaryBasal(commandResult.completionTime); tempBasal.durationInMinutes = 0; @@ -622,14 +637,14 @@ public class ComboPlugin implements PluginBase, PumpInterface { } else { // Set a fake neutral temp to avoid TBR cancel alert. Decide 90% vs 110% based on // on whether the TBR we're cancelling is above or below 100%. - long percentage = (activeTemp.percentRate > 100) ? 110 : 90; + int percentage = (activeTemp.percentRate > 100) ? 110 : 90; log.debug("cancelTempBasal: changing tbr to " + percentage + "% for 15 mins."); - commandResult = runCommand(new SetTbrCommand(percentage, 15)); + commandResult = ruffyScripter.setTbr(percentage, 15); if (commandResult.enacted) { tempBasal = new TemporaryBasal(commandResult.completionTime); tempBasal.durationInMinutes = 15; tempBasal.source = Source.USER; - tempBasal.percentRate = (int) percentage; + tempBasal.percentRate = percentage; tempBasal.isAbsolute = false; } } @@ -656,7 +671,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { // TODO v2 add battery, reservoir info when we start reading that and clean up the code @Override public JSONObject getJSONStatus() { - if (true) { //pump.lastCmdTime.getTime() + 5 * 60 * 1000L < System.currentTimeMillis()) { + if (pump.lastCmdTime.getTime() + 5 * 60 * 1000L < System.currentTimeMillis()) { return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java index d24a31607c..3160a32fee 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java @@ -5,9 +5,9 @@ import android.support.annotation.Nullable; import java.util.Date; -import de.jotomo.ruffyscripter.PumpState; -import de.jotomo.ruffyscripter.commands.Command; -import de.jotomo.ruffyscripter.commands.CommandResult; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.PumpState; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.Command; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.CommandResult; class ComboPump { @NonNull diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/BasalProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/BasalProfile.java new file mode 100644 index 0000000000..6b927ebc25 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/BasalProfile.java @@ -0,0 +1,4 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter; + +public class BasalProfile { +} diff --git a/app/src/main/java/de/jotomo/ruffyscripter/History.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/History.java similarity index 54% rename from app/src/main/java/de/jotomo/ruffyscripter/History.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/History.java index cfe514565b..ed9ac66061 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/History.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/History.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter; +package info.nightscout.androidaps.plugins.PumpCombo.scripter; /** * The history data read from "My data" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/PumpHistory.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/PumpHistory.java new file mode 100644 index 0000000000..74b3986f48 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/PumpHistory.java @@ -0,0 +1,4 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter; + +public class PumpHistory { +} diff --git a/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/PumpState.java similarity index 97% rename from app/src/main/java/de/jotomo/ruffyscripter/PumpState.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/PumpState.java index 9323ab71a5..0db577bf7a 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/PumpState.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter; +package info.nightscout.androidaps.plugins.PumpCombo.scripter; import java.util.Date; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/RuffyCommands.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/RuffyCommands.java new file mode 100644 index 0000000000..18939c4ba1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/RuffyCommands.java @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter; + +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.CommandResult; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand; + +/** + * Main entry point for clients, implemented by RuffyScripter. + */ +public interface RuffyCommands { + CommandResult deliverBolus(double amount, BolusCommand.ProgressReportCallback progressReportCallback); + + void cancelBolus(); + + CommandResult setTbr(int percent, int duraton); + + CommandResult cancelTbr(); + + CommandResult readReservoirLevel(); + + // PumpHistory.fields.*: null=don't care. empty history=we know nothing yet. filled history=this is what we know so far + CommandResult readHistory(PumpHistory knownHistory); + + CommandResult readBasalProfile(); + + CommandResult setBasalProfile(BasalProfile basalProfile); +} + diff --git a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/RuffyScripter.java similarity index 82% rename from app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/RuffyScripter.java index ae3269dcae..2eb40e26a6 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/RuffyScripter.java @@ -1,7 +1,8 @@ -package de.jotomo.ruffyscripter; +package info.nightscout.androidaps.plugins.PumpCombo.scripter; import android.os.RemoteException; import android.os.SystemClock; +import android.support.annotation.Nullable; import com.google.common.base.Joiner; @@ -16,10 +17,17 @@ import org.slf4j.LoggerFactory; import java.util.List; -import de.jotomo.ruffyscripter.commands.Command; -import de.jotomo.ruffyscripter.commands.CommandException; -import de.jotomo.ruffyscripter.commands.CommandResult; -import de.jotomo.ruffyscripter.commands.GetPumpStateCommand; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.CancelTbrCommand; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.Command; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.CommandException; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.CommandResult; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.GetPumpStateCommand; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.ReadBasalProfile; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.ReadHistoryCommand; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.ReadReserverLevelCommand; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.SetBasalProfile; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.SetTbrCommand; // TODO regularly read "My data" history (boluses, TBR) to double check all commands ran successfully. // Automatically compare against AAPS db, or log all requests in the PumpInterface (maybe Milos @@ -30,12 +38,13 @@ import de.jotomo.ruffyscripter.commands.GetPumpStateCommand; * class and inject that into executing commands, so that commands operately solely on * operations and are cleanly separated from the thread management, connection management etc */ -public class RuffyScripter { +public class RuffyScripter implements RuffyCommands { private static final Logger log = LoggerFactory.getLogger(RuffyScripter.class); private IRuffyService ruffyService; private String unrecoverableError = null; + @Nullable private volatile Menu currentMenu; private volatile long menuLastUpdated = 0; @@ -193,6 +202,23 @@ public class RuffyScripter { this.ruffyService = null; } + public void returnToMainMenu() { + // returning to main menu using the 'back' key does not cause a vibration + while (getCurrentMenu().getType() != MenuType.MAIN_MENU) { + if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { + String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); + confirmAlert(errorMsg, 1000); + // TODO this isn't gonna work out ... this method can't know if something was enacted ... + // gotta keep that state in the command instance + throw new CommandException().success(false).enacted(false) + .message("Warning/error " + errorMsg + " raised while returning to main menu"); + } + log.debug("Going back to main menu, currently at " + getCurrentMenu().getType()); + pressBackKey(); + waitForMenuUpdate(); + } + } + private static class Returnable { CommandResult cmdResult; } @@ -464,6 +490,17 @@ public class RuffyScripter { // === pump ops === public Menu getCurrentMenu() { + long timeout = System.currentTimeMillis() + 5 * 1000; + // TODO this is probably due to a disconnect and rtDisconnect having nulled currentMenu. + // This here might just work, but needs a more controlled approach when implementing + // something to deal with connection loses + while (currentMenu == null) { + if (System.currentTimeMillis() > timeout) { + throw new CommandException().message("Unable to read current menu"); + } + log.debug("currentMenu == null, waiting"); + waitForMenuUpdate(); + } return currentMenu; } @@ -534,52 +571,44 @@ public class RuffyScripter { return true; } - public boolean goToMainTypeScreen(MenuType screen, long timeout) { - long start = System.currentTimeMillis(); - while ((currentMenu == null || currentMenu.getType() != screen) && start + timeout > System.currentTimeMillis()) { - if (currentMenu != null && currentMenu.getType() == MenuType.WARNING_OR_ERROR) { - throw new CommandException().message("Warning/errors raised by pump, please check pump"); - // since warnings and errors can occur at any time, they should be dealt with in - // a more general way, see the handleMenuUpdate callback above - //FIXME bad thing to do :D - // yup, commenting this out since I don't want an occlusionn alert to hidden by this :-) - //pressCheckKey(); - } else if (currentMenu != null && !currentMenu.getType().isMaintype()) { - pressBackKey(); - } else - pressMenuKey(); - waitForScreenUpdate(250); - } - return currentMenu != null && currentMenu.getType() == screen; - } - - public boolean enterMenu(MenuType startType, MenuType targetType, byte key, long timeout) { - if (currentMenu.getType() == targetType) - return true; - if (currentMenu == null || currentMenu.getType() != startType) - return false; - long start = System.currentTimeMillis(); - pressKey(key, 2000); - while ((currentMenu == null || currentMenu.getType() != targetType) && start + timeout > System.currentTimeMillis()) { - waitForScreenUpdate(100); - } - return currentMenu != null && currentMenu.getType() == targetType; - } - - public void step(int steps, byte key, long timeout) { - for (int i = 0; i < Math.abs(steps); i++) - pressKey(key, timeout); - } - // TODO v2, rework these two methods: waitForMenuUpdate shoud only be used by commands // then anything longer than a few seconds is an error; // only ensureConnected() uses the method with the timeout parameter; inline that code, // so we can use a custom timeout and give a better error message in case of failure - /** - * Wait until the menu update is in - */ - // TODO donn't use this in ensureConnected + // TODO confirmAlarms? and report back which were cancelled? + + /** Confirms and dismisses the given alert if it's raised before the timeout */ + public boolean confirmAlert(String alertMessage, int maxWaitMs) { + long inFiveSeconds = System.currentTimeMillis() + maxWaitMs; + boolean alertProcessed = false; + while (System.currentTimeMillis() < inFiveSeconds && !alertProcessed) { + if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { + // Note that the message is permanently displayed, while the error code is blinking. + // A wait till the error code can be read results in the code hanging, despite + // menu updates coming in, so just check the message. + // TODO quick try if the can't make reading the error code work .. + String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); + if (!errorMsg.equals(alertMessage)) { + throw new CommandException().success(false).enacted(false) + .message("An alert other than the expected " + alertMessage + " was raised by the pump: " + + errorMsg + ". Please check the pump."); + } + // confirm alert + verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); + pressCheckKey(); + // dismiss alert + verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); + pressCheckKey(); + waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR); + alertProcessed = true; + } + SystemClock.sleep(10); + } + return alertProcessed; + } + + /** Wait until the menu is updated */ public void waitForMenuUpdate() { waitForMenuUpdate(60, "Timeout waiting for menu update"); } @@ -615,10 +644,10 @@ public class RuffyScripter { } public void navigateToMenu(MenuType desiredMenu) { - MenuType startedFrom = currentMenu.getType(); + MenuType startedFrom = getCurrentMenu().getType(); boolean movedOnce = false; - while (currentMenu.getType() != desiredMenu) { - MenuType currentMenuType = currentMenu.getType(); + while (getCurrentMenu().getType() != desiredMenu) { + MenuType currentMenuType = getCurrentMenu().getType(); log.debug("Navigating to menu " + desiredMenu + ", currenty menu: " + currentMenuType); if (movedOnce && currentMenuType == startedFrom) { throw new CommandException().message("Menu not found searching for " + desiredMenu @@ -630,12 +659,10 @@ public class RuffyScripter { } } - /** - * Wait till a menu changed has completed, "away" from the menu provided as argument. - */ + /** Wait till a menu changed has completed, "away" from the menu provided as argument. */ public void waitForMenuToBeLeft(MenuType menuType) { long timeout = System.currentTimeMillis() + 60 * 1000; - while (currentMenu.getType() == menuType) { + while (getCurrentMenu().getType() == menuType) { if (System.currentTimeMillis() > timeout) { throw new CommandException().message("Timeout waiting for menu " + menuType + " to be left"); } @@ -649,7 +676,7 @@ public class RuffyScripter { public void verifyMenuIsDisplayed(MenuType expectedMenu, String failureMessage) { int retries = 600; - while (currentMenu.getType() != expectedMenu) { + while (getCurrentMenu().getType() != expectedMenu) { if (retries > 0) { SystemClock.sleep(100); retries = retries - 1; @@ -665,9 +692,9 @@ public class RuffyScripter { @SuppressWarnings("unchecked") public T readBlinkingValue(Class expectedType, MenuAttribute attribute) { int retries = 5; - Object value = currentMenu.getAttribute(attribute); + Object value = getCurrentMenu().getAttribute(attribute); while (!expectedType.isInstance(value)) { - value = currentMenu.getAttribute(attribute); + value = getCurrentMenu().getAttribute(attribute); waitForScreenUpdate(1000); retries--; if (retries == 0) { @@ -677,8 +704,45 @@ public class RuffyScripter { return (T) value; } - public long readDisplayedDuration() { - MenuTime duration = readBlinkingValue(MenuTime.class, MenuAttribute.RUNTIME); - return duration.getHour() * 60 + duration.getMinute(); + @Override + public CommandResult deliverBolus(double amount, BolusCommand.ProgressReportCallback progressReportCallback) { + return runCommand(new BolusCommand(amount, progressReportCallback)); + } + + @Override + public void cancelBolus() { + if (activeCmd instanceof BolusCommand) { + ((BolusCommand) activeCmd).requestCancellation(); + } + } + + @Override + public CommandResult setTbr(int percent, int duraton) { + return runCommand(new SetTbrCommand(percent, duraton)); + } + + @Override + public CommandResult cancelTbr() { + return runCommand(new CancelTbrCommand()); + } + + @Override + public CommandResult readReservoirLevel() { + return runCommand(new ReadReserverLevelCommand()); + } + + @Override + public CommandResult readHistory(PumpHistory knownHistory) { + return runCommand(new ReadHistoryCommand(knownHistory)); + } + + @Override + public CommandResult readBasalProfile() { + return runCommand(new ReadBasalProfile()); + } + + @Override + public CommandResult setBasalProfile(BasalProfile basalProfile) { + return runCommand(new SetBasalProfile(basalProfile)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/BaseCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/BaseCommand.java new file mode 100644 index 0000000000..d47f654660 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/BaseCommand.java @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; + +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; + +public abstract class BaseCommand implements Command { + // RS will inject itself here + protected RuffyScripter scripter; + + @Override + public void setScripter(RuffyScripter scripter) { + this.scripter = scripter; + } + + // TODO upcoming + protected final boolean canBeCancelled = true; + protected volatile boolean cancelRequested = false; + + public void requestCancellation() { + cancelRequested = true; + } + + public boolean isCancellable() { + return canBeCancelled; + } +} diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/CancellableBolusCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/BolusCommand.java similarity index 62% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/CancellableBolusCommand.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/BolusCommand.java index dcf06424da..e2c177c78b 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/CancellableBolusCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/BolusCommand.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import android.os.SystemClock; @@ -11,22 +11,23 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import de.jotomo.ruffyscripter.PumpState; -import de.jotomo.ruffyscripter.RuffyScripter; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; -import static de.jotomo.ruffyscripter.commands.CancellableBolusCommand.ProgressReportCallback.State.DELIVERED; -import static de.jotomo.ruffyscripter.commands.CancellableBolusCommand.ProgressReportCallback.State.DELIVERING; -import static de.jotomo.ruffyscripter.commands.CancellableBolusCommand.ProgressReportCallback.State.STOPPED; -import static de.jotomo.ruffyscripter.commands.CancellableBolusCommand.ProgressReportCallback.State.STOPPING; +import static info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand.ProgressReportCallback.State.DELIVERED; +import static info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand.ProgressReportCallback.State.DELIVERING; +import static info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand.ProgressReportCallback.State.PROGRAMMING; +import static info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand.ProgressReportCallback.State.STOPPED; +import static info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.BolusCommand.ProgressReportCallback.State.STOPPING; -public class CancellableBolusCommand extends BolusCommand { - private static final Logger log = LoggerFactory.getLogger(CancellableBolusCommand.class); +public class BolusCommand extends BaseCommand { + private static final Logger log = LoggerFactory.getLogger(BolusCommand.class); + protected final double bolus; private final ProgressReportCallback progressReportCallback; private volatile boolean cancelRequested; - public CancellableBolusCommand(double bolus, ProgressReportCallback progressReportCallback) { - super(bolus); + public BolusCommand(double bolus, ProgressReportCallback progressReportCallback) { + this.bolus = bolus; this.progressReportCallback = progressReportCallback; } @@ -45,14 +46,17 @@ public class CancellableBolusCommand extends BolusCommand { public CommandResult execute() { try { // TODO read reservoir level and reject request if reservoir < bolus - enterBolusMenu(); + // TODO also check if there's a bolus in history we're not aware of + // press check twice to get reservoir level and last bolus quickly + progressReportCallback.report(PROGRAMMING, 0, 0); + enterBolusMenu(); inputBolusAmount(); verifyDisplayedBolusAmount(); if (cancelRequested) { progressReportCallback.report(STOPPING, 0, 0); - scripter.goToMainTypeScreen(MenuType.MAIN_MENU, 30 * 1000); + scripter.returnToMainMenu(); progressReportCallback.report(STOPPED, 0, 0); return new CommandResult().success(true).enacted(false) .message("Bolus cancelled as per user request with no insulin delivered"); @@ -69,7 +73,7 @@ public class CancellableBolusCommand extends BolusCommand { scripter.pressUpKey(); // wait up to 1s for a BOLUS_CANCELLED alert, if it doesn't happen we missed // the window, simply continue and let the next cancel attempt try its luck - boolean alertWasCancelled = confirmAlert("BOLUS CANCELLED", 1000); + boolean alertWasCancelled = scripter.confirmAlert("BOLUS CANCELLED", 1000); if (alertWasCancelled) { progressReportCallback.report(STOPPED, 0, 0); return new CommandResult().success(true).enacted(false) @@ -91,9 +95,11 @@ public class CancellableBolusCommand extends BolusCommand { // TODO extract into method // TODO 'low cartrdige' alarm must be handled inside, since the bolus continues regardless; - // it must be claread so we can see the remaining bolus again; + // it must be cleared so we can see the remaining bolus again; while (bolusRemaining != null) { if (cancelRequested) { + // cancel running bolus by pressing up for 3s, while raise a BOLUS CANCELLED + // alert, unless the bolus finished within those 3s. progressReportCallback.report(STOPPING, 0, 0); scripter.pressKeyMs(RuffyScripter.Key.UP, 3000); progressReportCallback.report(STOPPED, 0, 0); @@ -116,30 +122,42 @@ public class CancellableBolusCommand extends BolusCommand { lastBolusReported = bolusRemaining; } + /* + // TODO think through situatiotns where an alarm can be raised, not just when pressing a button, + // but a 'low battery' alarm can trigger at any time ... if (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { String message = (String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); if (message.equals("LOW CARTRIDGE")) { lowCartdrigeAlarmTriggered = true; - confirmAlert("LOW CARTRIDGE", 2000); + scripter.confirmAlert("LOW CARTRIDGE", 2000); } else { // any other alert break; } } + */ + SystemClock.sleep(50); bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING); } + progressReportCallback.report(DELIVERED, 100, bolus); + /* // wait up to 2s for any possible warning to be raised, if not raised already - long minWait = System.currentTimeMillis() + 2 * 1000; - while (scripter.getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR || System.currentTimeMillis() < minWait) { + // TODO what could be raised here, other than those alarms than can ring at any time anyways? + long timeout = System.currentTimeMillis() + 2 * 1000; + while (scripter.getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR && System.currentTimeMillis() < timeout) { SystemClock.sleep(50); } // process warnings (confirm them, report back to AAPS about them) - while (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR || System.currentTimeMillis() < minWait) { - // TODO +// while (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR || System.currentTimeMillis() < timeout) { + if (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { + scripter.confirmAlert(((String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE)), 1000); } +// SystemClock.sleep(50); +// } + */ // TODO what if we hit 'cartridge low' alert here? is it immediately displayed or after the bolus? // TODO how are error states reported back to the caller that occur outside of calls in genal? Low battery, low cartridge? @@ -149,7 +167,6 @@ public class CancellableBolusCommand extends BolusCommand { "Bolus delivery did not complete as expected. " + "Check pump manually, the bolus might not have been delivered."); - // TODO report back what was read from history // read last bolus record; those menus display static data and therefore @@ -176,13 +193,14 @@ public class CancellableBolusCommand extends BolusCommand { } log.debug("Bolus record in history confirms delivered bolus"); - if (!scripter.goToMainTypeScreen(MenuType.MAIN_MENU, 15 * 1000)) { + // TODO how would this call fail? more generally ...... + scripter.returnToMainMenu(); + if (scripter.getCurrentMenu().getType() != MenuType.MAIN_MENU) { throw new CommandException().success(false).enacted(true) .message("Bolus was correctly delivered and checked against history, but we " + "did not return the main menu successfully."); } - progressReportCallback.report(DELIVERED, 100, bolus); return new CommandResult().success(true).enacted(true) .message(String.format(Locale.US, "Delivered %02.1f U", bolus)); @@ -191,11 +209,54 @@ public class CancellableBolusCommand extends BolusCommand { } } - // TODO confirmAlarms? and report back which were cancelled? + private void enterBolusMenu() { + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); + scripter.navigateToMenu(MenuType.BOLUS_MENU); + scripter.verifyMenuIsDisplayed(MenuType.BOLUS_MENU); + scripter.pressCheckKey(); + scripter.waitForMenuUpdate(); + scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); + } - private boolean confirmAlert(String alertText, int maxWaitTillExpectedAlert) { - // TODO - return false; + private void inputBolusAmount() { + scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); + // press 'up' once for each 0.1 U increment + long steps = Math.round(bolus * 10); + for (int i = 0; i < steps; i++) { + scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); + scripter.pressUpKey(); + SystemClock.sleep(100); + } + // Give the pump time to finish any scrolling that might still be going on, can take + // up to 1100ms. Plus some extra time to be sure + SystemClock.sleep(2000); + } + + private void verifyDisplayedBolusAmount() { + scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); + + // wait up to 5s for any scrolling to finish + double displayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS); + long timeout = System.currentTimeMillis() + 10 * 1000; + while (timeout > System.currentTimeMillis() && bolus - displayedBolus > 0.05) { + log.debug("Waiting for pump to process scrolling input for amount, current: " + displayedBolus + ", desired: " + bolus); + SystemClock.sleep(50); + displayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS); + } + + log.debug("Final bolus: " + displayedBolus); + if (Math.abs(displayedBolus - bolus) > 0.05) { + throw new CommandException().message("Failed to set correct bolus. Expected: " + bolus + ", actual: " + displayedBolus); + } + + // check again to ensure the displayed value hasn't change due to due scrolling taking extremely long + SystemClock.sleep(1000); + scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); + double refreshedDisplayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS); + if (Math.abs(displayedBolus - refreshedDisplayedBolus) > 0.05) { + throw new CommandException().message("Failed to set bolus: bolus changed after input stopped from " + + displayedBolus + " -> " + refreshedDisplayedBolus); + } } public void requestCancellation() { @@ -212,6 +273,7 @@ public class CancellableBolusCommand extends BolusCommand { public interface ProgressReportCallback { enum State { + PROGRAMMING, DELIVERING, DELIVERED, STOPPING, diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/CancelTbrCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CancelTbrCommand.java similarity index 95% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/CancelTbrCommand.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CancelTbrCommand.java index 7d6fa98d37..de4bb27862 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/CancelTbrCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CancelTbrCommand.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import org.monkey.d.ruffy.ruffy.driver.display.MenuType; import org.slf4j.Logger; @@ -7,7 +7,7 @@ import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; -import de.jotomo.ruffyscripter.PumpState; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.PumpState; import info.nightscout.androidaps.MainApp; // TODO robustness: can a TBR run out, whilst we're trying to cancel it? diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/Command.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/Command.java similarity index 75% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/Command.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/Command.java index 60fa3c730f..f1572f56aa 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/Command.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/Command.java @@ -1,8 +1,8 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import java.util.List; -import de.jotomo.ruffyscripter.RuffyScripter; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; /** * Interface for all commands to be executed by the pump. diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandException.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CommandException.java similarity index 94% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/CommandException.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CommandException.java index d2f31cc8e2..75cac9014a 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandException.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CommandException.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; public class CommandException extends RuntimeException { public boolean success = false; diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CommandResult.java similarity index 88% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CommandResult.java index 9d1bbb321e..c99c6f29df 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/CommandResult.java @@ -1,9 +1,9 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import java.util.Date; -import de.jotomo.ruffyscripter.History; -import de.jotomo.ruffyscripter.PumpState; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.History; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.PumpState; public class CommandResult { public boolean success; diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetBasalRateProfileCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/GetBasalRateProfileCommand.java similarity index 96% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/GetBasalRateProfileCommand.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/GetBasalRateProfileCommand.java index ff842af5dd..4e4aeafbca 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetBasalRateProfileCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/GetBasalRateProfileCommand.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import android.util.Log; @@ -10,7 +10,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import de.jotomo.ruffyscripter.RuffyScripter; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; public class GetBasalRateProfileCommand extends BaseCommand { private static final Logger log = LoggerFactory.getLogger(GetBasalRateProfileCommand.class); diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetPumpStateCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/GetPumpStateCommand.java similarity index 96% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/GetPumpStateCommand.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/GetPumpStateCommand.java index b298fcb41e..1f29580216 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetPumpStateCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/GetPumpStateCommand.java @@ -1,11 +1,11 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import org.monkey.d.ruffy.ruffy.driver.display.MenuType; import java.util.Collections; import java.util.List; -import static de.jotomo.ruffyscripter.commands.GetPumpStateCommand.Stepper.runStep; +import static info.nightscout.androidaps.plugins.PumpCombo.scripter.commands.GetPumpStateCommand.Stepper.runStep; public class GetPumpStateCommand extends BaseCommand { interface Step { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadBasalProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadBasalProfile.java new file mode 100644 index 0000000000..d360634fe0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadBasalProfile.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; + +import java.util.List; + +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; + +public class ReadBasalProfile implements Command { + @Override + public CommandResult execute() { + return null; + } + + @Override + public List validateArguments() { + return null; + } + + @Override + public void setScripter(RuffyScripter scripter) { + + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadHistoryCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadHistoryCommand.java new file mode 100644 index 0000000000..97d8d6ed63 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadHistoryCommand.java @@ -0,0 +1,26 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; + +import java.util.List; + +import info.nightscout.androidaps.plugins.PumpCombo.scripter.PumpHistory; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; + +public class ReadHistoryCommand implements Command { + public ReadHistoryCommand(PumpHistory knownHistory) { + } + + @Override + public CommandResult execute() { + return null; + } + + @Override + public List validateArguments() { + return null; + } + + @Override + public void setScripter(RuffyScripter scripter) { + + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadReserverLevelCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadReserverLevelCommand.java new file mode 100644 index 0000000000..ff5787a407 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/ReadReserverLevelCommand.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; + +import java.util.List; + +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; + +public class ReadReserverLevelCommand implements Command { + @Override + public CommandResult execute() { + return null; + } + + @Override + public List validateArguments() { + return null; + } + + @Override + public void setScripter(RuffyScripter scripter) { + + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetBasalProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetBasalProfile.java new file mode 100644 index 0000000000..cdbe4b3aee --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetBasalProfile.java @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; + +import java.util.List; + +import info.nightscout.androidaps.plugins.PumpCombo.scripter.BasalProfile; +import info.nightscout.androidaps.plugins.PumpCombo.scripter.RuffyScripter; + +public class SetBasalProfile implements Command { + public SetBasalProfile(BasalProfile basalProfile) { + + } + + @Override + public CommandResult execute() { + return null; + } + + @Override + public List validateArguments() { + return null; + } + + @Override + public void setScripter(RuffyScripter scripter) { + + } +} diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/SetBasalRateProfileCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetBasalRateProfileCommand.java similarity index 80% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/SetBasalRateProfileCommand.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetBasalRateProfileCommand.java index 6d98c94d42..7dd2d029ba 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/SetBasalRateProfileCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetBasalRateProfileCommand.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import java.util.List; diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetTbrCommand.java similarity index 76% rename from app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommand.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetTbrCommand.java index 206e27ba9e..b5449ffc7d 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/scripter/commands/SetTbrCommand.java @@ -1,4 +1,4 @@ -package de.jotomo.ruffyscripter.commands; +package info.nightscout.androidaps.plugins.PumpCombo.scripter.commands; import android.os.SystemClock; @@ -12,8 +12,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import de.jotomo.ruffyscripter.PumpState; - public class SetTbrCommand extends BaseCommand { private static final Logger log = LoggerFactory.getLogger(SetTbrCommand.class); @@ -108,17 +106,13 @@ public class SetTbrCommand extends BaseCommand { private boolean inputTbrPercentage() { scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); - long currentPercent = scripter.readBlinkingValue(Double.class, MenuAttribute.BASAL_RATE).longValue(); + long currentPercent = readDisplayedPercentage(); log.debug("Current TBR %: " + currentPercent); long percentageChange = percentage - currentPercent; long percentageSteps = percentageChange / 10; - boolean increasePercentage = true; - if (percentageSteps < 0) { - increasePercentage = false; - percentageSteps = Math.abs(percentageSteps); - } + boolean increasePercentage = percentageSteps > 0; log.debug("Pressing " + (increasePercentage ? "up" : "down") + " " + percentageSteps + " times"); - for (int i = 0; i < percentageSteps; i++) { + for (int i = 0; i < Math.abs(percentageSteps); i++) { scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); log.debug("Push #" + (i + 1)); if (increasePercentage) scripter.pressUpKey(); @@ -128,29 +122,31 @@ public class SetTbrCommand extends BaseCommand { return increasePercentage; } - // TODO refactor: extract verification into a method TBR percentage, duration and bolus amount private void verifyDisplayedTbrPercentage(boolean increasingPercentage) { scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); // wait up to 5s for any scrolling to finish - long displayedPercentage = scripter.readBlinkingValue(Double.class, MenuAttribute.BASAL_RATE).longValue(); + long displayedPercentage = readDisplayedPercentage(); long timeout = System.currentTimeMillis() + 10 * 1000; while (timeout > System.currentTimeMillis() && ((increasingPercentage && displayedPercentage < percentage) || (!increasingPercentage && displayedPercentage > percentage))) { log.debug("Waiting for pump to process scrolling input for percentage, current: " - + displayedPercentage + ", desired: " + percentage + ", scrolling up: " + increasingPercentage); + + displayedPercentage + ", desired: " + percentage + ", scrolling " + + (increasingPercentage ? "up" : "down")); SystemClock.sleep(50); - displayedPercentage = scripter.readBlinkingValue(Double.class, MenuAttribute.BASAL_RATE).longValue(); + displayedPercentage = readDisplayedPercentage(); } log.debug("Final displayed TBR percentage: " + displayedPercentage); if (displayedPercentage != percentage) { - throw new CommandException().message("Failed to set TBR percentage, requested: " + percentage + ", actual: " + displayedPercentage); + throw new CommandException().message("Failed to set TBR percentage, requested: " + + percentage + ", actual: " + displayedPercentage); } - // check again to ensure the displayed value hasn't change due to due scrolling taking extremely long + // check again to ensure the displayed value hasn't change and scrolled past the desired + // value due to due scrolling taking extremely long SystemClock.sleep(1000); scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); - long refreshedDisplayedTbrPecentage = scripter.readBlinkingValue(Double.class, MenuAttribute.BASAL_RATE).longValue(); + long refreshedDisplayedTbrPecentage = readDisplayedPercentage(); if (displayedPercentage != refreshedDisplayedTbrPecentage) { throw new CommandException().message("Failed to set TBR percentage: " + "percentage changed after input stopped from " @@ -174,7 +170,7 @@ public class SetTbrCommand extends BaseCommand { } private long calculateDurationSteps() { - long currentDuration = scripter.readDisplayedDuration(); + long currentDuration = readDisplayedDuration(); log.debug("Initial TBR duration: " + currentDuration); long difference = duration - currentDuration; @@ -190,26 +186,29 @@ public class SetTbrCommand extends BaseCommand { scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); // wait up to 5s for any scrolling to finish - long displayedDuration = scripter.readDisplayedDuration(); + long displayedDuration = readDisplayedDuration(); long timeout = System.currentTimeMillis() + 10 * 1000; while (timeout > System.currentTimeMillis() && ((increasingPercentage && displayedDuration < duration) || (!increasingPercentage && displayedDuration > duration))) { log.debug("Waiting for pump to process scrolling input for duration, current: " - + displayedDuration + ", desired: " + duration + ", scrolling up: " + increasingPercentage); + + displayedDuration + ", desired: " + duration + + ", scrolling " + (increasingPercentage ? "up" : "down")); SystemClock.sleep(50); - displayedDuration = scripter.readDisplayedDuration(); + displayedDuration = readDisplayedDuration(); } log.debug("Final displayed TBR duration: " + displayedDuration); if (displayedDuration != duration) { - throw new CommandException().message("Failed to set TBR duration, requested: " + duration + ", actual: " + displayedDuration); + throw new CommandException().message("Failed to set TBR duration, requested: " + + duration + ", actual: " + displayedDuration); } - // check again to ensure the displayed value hasn't change due to due scrolling taking extremely long + // check again to ensure the displayed value hasn't change and scrolled past the desired + // value due to due scrolling taking extremely long SystemClock.sleep(1000); scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); - long refreshedDisplayedTbrDuration = scripter.readDisplayedDuration(); + long refreshedDisplayedTbrDuration = readDisplayedDuration(); if (displayedDuration != refreshedDisplayedTbrDuration) { throw new CommandException().message("Failed to set TBR duration: " + "duration changed after input stopped from " @@ -217,8 +216,6 @@ public class SetTbrCommand extends BaseCommand { } } - - private void cancelTbrAndConfirmCancellationWarning() { // confirm entered TBR scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); @@ -227,35 +224,9 @@ public class SetTbrCommand extends BaseCommand { // A "TBR CANCELLED alert" is only raised by the pump when the remaining time is // greater than 60s (displayed as 0:01, the pump goes from there to finished. // We could read the remaining duration from MAIN_MENU, but by the time we're here, - // the pumup could have moved from 0:02 to 0:01, so instead, check if a "TBR CANCELLED" alert + // the pump could have moved from 0:02 to 0:01, so instead, check if a "TBR CANCELLED" alert // is raised and if so dismiss it - long inFiveSeconds = System.currentTimeMillis() + 5 * 1000; - boolean alertProcessed = false; - while (System.currentTimeMillis() < inFiveSeconds && !alertProcessed) { - if (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { - // Check the raised alarm is TBR CANCELLED, so we're not accidentally cancelling - // a different alarm that might be raised at the same time. - // Note that the message is permanently displayed, while the error code is blinking. - // A wait till the error code can be read results in the code hanging, despite - // menu updates coming in, so just check the message. - // TODO v2 this only works when the pump's language is English - String errorMsg = (String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); - if (!errorMsg.equals("TBR CANCELLED")) { - throw new CommandException().success(false).enacted(false) - .message("An alert other than the expected TBR CANCELLED was raised by the pump: " - + errorMsg + ". Please check the pump."); - } - // confirm "TBR CANCELLED" alert - scripter.verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); - scripter.pressCheckKey(); - // dismiss "TBR CANCELLED" alert - scripter.verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); - scripter.pressCheckKey(); - scripter.waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR); - alertProcessed = true; - } - SystemClock.sleep(10); - } + scripter.confirmAlert("TBR CANCELLED", 5000); } private void verifyMainMenuShowsNoActiveTbr() { @@ -285,6 +256,15 @@ public class SetTbrCommand extends BaseCommand { } } + private long readDisplayedDuration() { + MenuTime duration = scripter.readBlinkingValue(MenuTime.class, MenuAttribute.RUNTIME); + return duration.getHour() * 60 + duration.getMinute(); + } + + private long readDisplayedPercentage() { + return scripter.readBlinkingValue(Double.class, MenuAttribute.BASAL_RATE).longValue(); + } + @Override public String toString() { return "SetTbrCommand{" + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3640f13f2a..25020904a5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -712,5 +712,23 @@ wizard_include_basal_iob Stopping bolus delivery Bolus delivery stopped + combo_enable_experimental_features + Enable experimental features + Unlocks experimental features which are in development and might be broken entirely. + combo_experimental_split_bolus + Experimental split bolus feature + Splits boluses into 2 U parts and waits around 45s after each to slow down bolus delivery (only active with non-experimental bolus). + combo_experimental_reject_tbr_changes_below_delta + Skip TBR changes below threshold (%). + Don\'t set a TBR if the difference between the new and a running TBR is below this threshold in percent. Specifying 0 disables this option. + combo_disable_alerts + Disable alerts + Ignore all errors encountered while communicating with the pump. Alerts raised by the pump (including those caused by AAPS) will still be raised. + Programming pump for bolusing + wizard_include_bg + wizard_include_cob + wizard_include_trend_bg + wizard_include_bolus_iob + wizard_include_basal_iob diff --git a/app/src/main/res/xml/pref_combo.xml b/app/src/main/res/xml/pref_combo.xml index 3f2a8800b7..1a73dd3599 100644 --- a/app/src/main/res/xml/pref_combo.xml +++ b/app/src/main/res/xml/pref_combo.xml @@ -4,6 +4,33 @@ android:key="combopump" android:title="@string/combopump_settings"> + + + + + + + + \ No newline at end of file