From f41d597c0706de3a8987e009e0bb5c28a58db959 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 17 Aug 2017 10:56:33 +0200 Subject: [PATCH 01/11] Formatting. --- .../java/de/jotomo/ruffyscripter/History.java | 4 +++- .../ruffyscripter/PumpCapabilities.java | 2 +- .../de/jotomo/ruffyscripter/PumpState.java | 11 +++++----- .../ruffyscripter/commands/Command.java | 3 ++- .../commands/CommandException.java | 3 ++- .../ruffyscripter/commands/CommandResult.java | 6 +++--- .../DetermineCapabilitiesCommand.java | 21 ++++++++----------- .../commands/GetBasalCommand.java | 16 +++++++------- .../plugins/PumpCombo/ComboPlugin.java | 15 ++++++------- 9 files changed, 42 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/History.java b/app/src/main/java/de/jotomo/ruffyscripter/History.java index 49e34d438f..cfe514565b 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/History.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/History.java @@ -1,5 +1,7 @@ package de.jotomo.ruffyscripter; -/** The history data read from "My data" */ +/** + * The history data read from "My data" + */ public class History { } diff --git a/app/src/main/java/de/jotomo/ruffyscripter/PumpCapabilities.java b/app/src/main/java/de/jotomo/ruffyscripter/PumpCapabilities.java index 1270851ea0..50016db80e 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/PumpCapabilities.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/PumpCapabilities.java @@ -2,7 +2,7 @@ package de.jotomo.ruffyscripter; /** * Created by adrian on 26/07/17. - * + *

* Contains the capabilities of the current pump model. */ diff --git a/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java b/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java index 4d519b4669..51ffafe40f 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/PumpState.java @@ -11,11 +11,12 @@ public class PumpState { public int tbrPercent = -1; public double tbrRate = -1; public int tbrRemainingDuration = -1; - /** This is the error message (if any) displayed by the pump if there is an alarm, - e.g. if a "TBR cancelled alarm" is active, the value will be "TBR CANCELLED". - Generally, an error code is also displayed, but it flashes and it might take - longer to read that and the pump connection gets interrupted if we're not - reacting quickly. + /** + * This is the error message (if any) displayed by the pump if there is an alarm, + * e.g. if a "TBR cancelled alarm" is active, the value will be "TBR CANCELLED". + * Generally, an error code is also displayed, but it flashes and it might take + * longer to read that and the pump connection gets interrupted if we're not + * reacting quickly. */ public String errorMsg; public boolean suspended; diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/Command.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/Command.java index 33d10fc223..ac87486e77 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/Command.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/Command.java @@ -7,12 +7,13 @@ import de.jotomo.ruffyscripter.RuffyScripter; /** * Interface for all commands to be executed by the pump. - * + *

* Note on cammond methods and timing: a method shall wait before and after executing * as necessary to not cause timing issues, so the caller can just call methods in * sequence, letting the methods take care of waits. */ public interface Command { CommandResult execute(RuffyScripter ruffyScripter, PumpState initialPumpState); + List validateArguments(); } diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandException.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandException.java index 5c25ba914c..d2f31cc8e2 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandException.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandException.java @@ -6,7 +6,8 @@ public class CommandException extends RuntimeException { public Exception exception = null; public String message = null; - public CommandException() {} + public CommandException() { + } public CommandException success(boolean success) { this.success = success; 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 296f61f056..41b07e3ba2 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/CommandResult.java @@ -30,7 +30,7 @@ public class CommandResult { } public CommandResult completionTime(long completionTime) { - this.completionTime = completionTime ; + this.completionTime = completionTime; return this; } @@ -51,12 +51,12 @@ public class CommandResult { public CommandResult history(History history) { this.history = history; - return this; + return this; } public CommandResult capabilities(PumpCapabilities capabilities) { this.capabilities = capabilities; - return this; + return this; } @Override diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/DetermineCapabilitiesCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/DetermineCapabilitiesCommand.java index adc740c13e..cf704024c6 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/DetermineCapabilitiesCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/DetermineCapabilitiesCommand.java @@ -2,8 +2,6 @@ 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; @@ -12,7 +10,6 @@ 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; @@ -34,7 +31,7 @@ public class DetermineCapabilitiesCommand implements Command { try { //read main menu 100% or TBR? Read remaining duration. - long durationBefore = readDisplayedTbrDurationMainMenu(scripter); + long durationBefore = readDisplayedTbrDurationMainMenu(scripter); long percentageBefore = readDisplayedTbrPercentageMainMenu(scripter); enterTbrMenu(scripter); @@ -49,14 +46,14 @@ public class DetermineCapabilitiesCommand implements Command { //TODO: check if TBR is still the same or duration was less than 5 minutes - long durationAfter = readDisplayedTbrDurationMainMenu(scripter); + long durationAfter = readDisplayedTbrDurationMainMenu(scripter); long percentageAfter = readDisplayedTbrPercentageMainMenu(scripter); - if(Math.abs(durationBefore-durationAfter) > 5){ + if (Math.abs(durationBefore - durationAfter) > 5) { throw new CommandException().message("Duration jump during DetermineCapabilities"); } - if(percentageAfter != percentageBefore){ - if(durationBefore<5 && percentageAfter == 100){ + 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."); @@ -99,7 +96,7 @@ public class DetermineCapabilitiesCommand implements Command { long percentageChange = maximumTempBasal - activeTempBasal; long percentageSteps = percentageChange / 10; - int retries= 0; + 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++) { @@ -139,7 +136,7 @@ public class DetermineCapabilitiesCommand implements Command { private int readDisplayedTbrDurationMainMenu(RuffyScripter scripter) { scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); - if(scripter.currentMenu.attributes().contains(MenuAttribute.RUNTIME)){ + 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; @@ -151,8 +148,8 @@ public class DetermineCapabilitiesCommand implements Command { 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(); + if (scripter.currentMenu.attributes().contains(MenuAttribute.TBR)) { + return (int) ((Double) scripter.currentMenu.getAttribute(MenuAttribute.TBR)).doubleValue(); } else { return 100; } diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetBasalCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/GetBasalCommand.java index 09958f6bf1..264474d1cc 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/GetBasalCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/GetBasalCommand.java @@ -18,7 +18,8 @@ public class GetBasalCommand implements Command { private RuffyScripter scripter; - public GetBasalCommand() {} + public GetBasalCommand() { + } @Override public List validateArguments() { @@ -27,7 +28,7 @@ public class GetBasalCommand implements Command { return violations; } -// private void tick() + // private void tick() // { // switch (state) // { @@ -104,15 +105,14 @@ public class GetBasalCommand implements Command { @Override public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { try { - Map rate = new HashMap<>(); + Map rate = new HashMap<>(); - for(int i = 0; i < 24;i++) - { - Log.v("BASAL_RATE","BASAL_RATE from "+String.format("%02d",i)+":00 = "+rate.get(i)); + for (int i = 0; i < 24; i++) { + Log.v("BASAL_RATE", "BASAL_RATE from " + String.format("%02d", i) + ":00 = " + rate.get(i)); } } catch (Exception e) { - log.error("failed to get basal",e); - return new CommandResult().success(false).message("failed to get basal: "+e.getMessage()); + log.error("failed to get basal", e); + return new CommandResult().success(false).message("failed to get basal: " + e.getMessage()); } return new CommandResult().success(true).enacted(true).message("Basal Rate was read"); } 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 a0bc347ea9..3cd45b4953 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 @@ -124,7 +124,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { * The alerter frequently checks the result of the last executed command via the lastCmdResult * field and shows a notification with sound and vibration if an error occurred. * More details on the error can then be looked up in the Combo tab. - * + *

* The alarm is re-raised every 5 minutes for as long as the error persist. As soon * as a command succeeds no more new alerts are raised. */ @@ -199,7 +199,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { @Override public void onServiceConnected(ComponentName name, IBinder service) { - keepUnbound=false; + keepUnbound = false; ruffyScripter.start(IRuffyService.Stub.asInterface(service)); log.debug("ruffy serivce connected"); } @@ -210,7 +210,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { log.debug("ruffy service disconnected"); // try to reconnect ruffy service unless unbind was explicitely requested // via unbindRuffyService - if(!keepUnbound) { + if (!keepUnbound) { SystemClock.sleep(250); bindRuffyService(); } @@ -228,6 +228,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { } private boolean keepUnbound = false; + private void unbindRuffyService() { keepUnbound = true; ruffyScripter.unbind(); @@ -543,7 +544,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { tempBasal.source = Source.USER; pumpEnactResult.isTempCancel = true; } - } else if ((activeTemp.percentRate >= 90 && activeTemp.percentRate <= 110) && activeTemp.getPlannedRemainingMinutes() <= 15 ) { + } else if ((activeTemp.percentRate >= 90 && activeTemp.percentRate <= 110) && activeTemp.getPlannedRemainingMinutes() <= 15) { // Let fake neutral temp keep running (see below) log.debug("cancelTempBasal: skipping changing tbr since it already is at " + activeTemp.percentRate + "% and running for another " + activeTemp.getPlannedRemainingMinutes() + " mins."); pumpEnactResult.comment = "cancelTempBasal skipping changing tbr since it already is at " + activeTemp.percentRate + "% and running for another " + activeTemp.getPlannedRemainingMinutes() + " mins."; @@ -556,7 +557,7 @@ 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; + long percentage = (activeTemp.percentRate > 100) ? 110 : 90; log.debug("cancelTempBasal: changing tbr to " + percentage + "% for 15 mins."); commandResult = runCommand(new SetTbrCommand(percentage, 15)); if (commandResult.enacted) { @@ -670,12 +671,12 @@ public class ComboPlugin implements PluginBase, PumpInterface { ToastUtils.showToastInUiThread(MainApp.instance(), "Ruffy not initialized."); return; } - if (isBusy()){ + if (isBusy()) { ToastUtils.showToastInUiThread(MainApp.instance(), "Pump busy!"); return; } CommandResult result = runCommand(new DetermineCapabilitiesCommand()); - if (result.success){ + if (result.success) { pumpDescription.maxTempPercent = (int) result.capabilities.maxTempPercent; SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance()); SharedPreferences.Editor editor = preferences.edit(); From a230501f74d896930fc26fed73b5d83cb4c828b3 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 17 Aug 2017 11:21:16 +0200 Subject: [PATCH 02/11] Add alternative SetTbrCommand for pumps with different behaviour. --- .../commands/SetTbrCommandAlt.java | 302 ++++++++++++++++++ .../plugins/PumpCombo/ComboPlugin.java | 9 +- 2 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommandAlt.java diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommandAlt.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommandAlt.java new file mode 100644 index 0000000000..14483b95ec --- /dev/null +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommandAlt.java @@ -0,0 +1,302 @@ +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.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import de.jotomo.ruffyscripter.PumpState; +import de.jotomo.ruffyscripter.RuffyScripter; + +public class SetTbrCommandAlt implements Command { + private static final Logger log = LoggerFactory.getLogger(SetTbrCommand.class); + + private final long percentage; + private final long duration; + + public SetTbrCommandAlt(long percentage, long duration) { + this.percentage = percentage; + this.duration = duration; + } + + @Override + public List validateArguments() { + List violations = new ArrayList<>(); + + if (percentage % 10 != 0) { + violations.add("TBR percentage must be set in 10% steps"); + } + if (percentage < 0 || percentage > 500) { + violations.add("TBR percentage must be within 0-500%"); + } + + if (percentage != 100) { + if (duration % 15 != 0) { + violations.add("TBR duration can only be set in 15 minute steps"); + } + if (duration > 60 * 24) { + violations.add("Maximum TBR duration is 24 hours"); + } + } + + if (percentage == 0 && duration > 120) { + violations.add("Max allowed zero-temp duration is 2h"); + } + + return violations; + } + + @Override + public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { + try { + enterTbrMenu(scripter); + inputTbrPercentage(scripter); + verifyDisplayedTbrPercentage(scripter); + + if (percentage == 100) { + cancelTbrAndConfirmCancellationWarning(scripter); + } else { + // switch to TBR_DURATION menu by pressing menu key + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + scripter.pressMenuKey(); + scripter.waitForMenuUpdate(); + scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); + + inputTbrDuration(scripter); + verifyDisplayedTbrDuration(scripter); + + // confirm TBR + scripter.pressCheckKey(); + scripter.waitForMenuToBeLeft(MenuType.TBR_DURATION); + } + + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU, + "Pump did not return to MAIN_MEU after setting TBR. " + + "Check pump manually, the TBR might not have been set/cancelled."); + + // check main menu shows the same values we just set + if (percentage == 100) { + verifyMainMenuShowsNoActiveTbr(scripter); + return new CommandResult().success(true).enacted(true).message("TBR was cancelled"); + } else { + verifyMainMenuShowsExpectedTbrActive(scripter); + return new CommandResult().success(true).enacted(true).message( + String.format(Locale.US, "TBR set to %d%% for %d min", percentage, duration)); + } + + } 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 void inputTbrPercentage(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + long currentPercent = readDisplayedTbrPercentage(scripter); + 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); + } + log.debug("Pressing " + (increasePercentage ? "up" : "down") + " " + percentageSteps + " times"); + for (int i = 0; i < percentageSteps; i++) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + if (increasePercentage) scripter.pressUpKey(); + else scripter.pressDownKey(); + SystemClock.sleep(100); + log.debug("Push #" + (i + 1)); + } + // 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 verifyDisplayedTbrPercentage(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + long displayedPercentage = readDisplayedTbrPercentage(scripter); + if (displayedPercentage != percentage) { + log.debug("Final displayed TBR percentage: " + displayedPercentage); + throw new CommandException().message("Failed to set TBR percentage"); + } + + // check again to ensure the displayed value hasn't change due to due scrolling taking extremely long + SystemClock.sleep(2000); + long refreshedDisplayedTbrPecentage = readDisplayedTbrPercentage(scripter); + if (displayedPercentage != refreshedDisplayedTbrPecentage) { + throw new CommandException().message("Failed to set TBR percentage: " + + "percentage changed after input stopped from " + + displayedPercentage + " -> " + refreshedDisplayedTbrPecentage); + } + } + + private long readDisplayedTbrPercentage(RuffyScripter scripter) { + // 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 void inputTbrDuration(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); + long currentDuration = readDisplayedTbrDuration(scripter); + if (currentDuration % 15 != 0) { + // The duration displayed is how long an active TBR will still run, + // which might be something like 0:13, hence not in 15 minute steps. + // Pressing up will go to the next higher 15 minute step. + // Don't press down, from 0:13 it can't go down, so press up. + // Pressing up from 23:59 works to go to 24:00. + scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); + scripter.pressUpKey(); + scripter.waitForMenuUpdate(); + currentDuration = readDisplayedTbrDuration(scripter); + } + log.debug("Current TBR duration: " + currentDuration); + long durationChange = duration - currentDuration; + long durationSteps = durationChange / 15; + boolean increaseDuration = true; + if (durationSteps < 0) { + increaseDuration = false; + durationSteps = Math.abs(durationSteps); + } + log.debug("Pressing " + (increaseDuration ? "up" : "down") + " " + durationSteps + " times"); + for (int i = 0; i < durationSteps; i++) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); + if (increaseDuration) scripter.pressUpKey(); + else scripter.pressDownKey(); + SystemClock.sleep(100); + log.debug("Push #" + (i + 1)); + } + // 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 verifyDisplayedTbrDuration(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); + long displayedDuration = readDisplayedTbrDuration(scripter); + if (displayedDuration != duration) { + log.debug("Final displayed TBR duration: " + displayedDuration); + throw new CommandException().message("Failed to set TBR duration"); + } + + // check again to ensure the displayed value hasn't change due to due scrolling taking extremely long + SystemClock.sleep(2000); + long refreshedDisplayedTbrDuration = readDisplayedTbrDuration(scripter); + if (displayedDuration != refreshedDisplayedTbrDuration) { + throw new CommandException().message("Failed to set TBR duration: " + + "duration changed after input stopped from " + + displayedDuration + " -> " + refreshedDisplayedTbrDuration); + } + } + + private long readDisplayedTbrDuration(RuffyScripter scripter) { + // TODO v2 add timeout? Currently the command execution timeout would trigger if exceeded + scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); + Object durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); + // 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 (!(durationObj instanceof MenuTime)) { + scripter.waitForMenuUpdate(); + durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); + } + MenuTime duration = (MenuTime) durationObj; + return duration.getHour() * 60 + duration.getMinute(); + } + + private void cancelTbrAndConfirmCancellationWarning(RuffyScripter scripter) { + // confirm entered TBR + scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); + scripter.pressCheckKey(); + + // 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 + // is raised and if so dismiss it + long inFiveSeconds = System.currentTimeMillis() + 5 * 1000; + boolean alertProcessed = false; + while (System.currentTimeMillis() < inFiveSeconds && !alertProcessed) { + if (scripter.currentMenu.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.currentMenu.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); + } + } + + private void verifyMainMenuShowsNoActiveTbr(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); + Double tbrPercentage = (Double) scripter.currentMenu.getAttribute(MenuAttribute.TBR); + boolean runtimeDisplayed = scripter.currentMenu.attributes().contains(MenuAttribute.RUNTIME); + if (tbrPercentage != 100 || runtimeDisplayed) { + throw new CommandException().message("Cancelling TBR failed, TBR is still set according to MAIN_MENU"); + } + } + + private void verifyMainMenuShowsExpectedTbrActive(RuffyScripter scripter) { + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); + // new TBR set; percentage and duration must be displayed ... + if (!scripter.currentMenu.attributes().contains(MenuAttribute.TBR) || + !scripter.currentMenu.attributes().contains(MenuAttribute.RUNTIME)) { + throw new CommandException().message("Setting TBR failed, according to MAIN_MENU no TBR is active"); + } + Double mmTbrPercentage = (Double) scripter.currentMenu.getAttribute(MenuAttribute.TBR); + MenuTime mmTbrDuration = (MenuTime) scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); + // ... and be the same as what we set + // note that displayed duration might have already counted down, e.g. from 30 minutes to + // 29 minutes and 59 seconds, so that 29 minutes are displayed + int mmTbrDurationInMinutes = mmTbrDuration.getHour() * 60 + mmTbrDuration.getMinute(); + if (mmTbrPercentage != percentage || (mmTbrDurationInMinutes != duration && mmTbrDurationInMinutes + 1 != duration)) { + throw new CommandException().message("Setting TBR failed, TBR in MAIN_MENU differs from expected"); + } + } + + @Override + public String toString() { + return "SetTbrCommand{" + + "percentage=" + percentage + + ", duration=" + duration + + '}'; + } +} 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 3cd45b4953..29e78847e0 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 @@ -25,6 +25,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; @@ -33,7 +34,7 @@ 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; +import de.jotomo.ruffyscripter.commands.SetTbrCommandAlt; import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -493,12 +494,16 @@ public class ComboPlugin implements PluginBase, PumpInterface { adjustedPercent = rounded.intValue(); } - CommandResult commandResult = runCommand(new SetTbrCommand(adjustedPercent, durationInMinutes)); + Command cmd = !BuildConfig.VERSION.contains("joe") + ? new SetTbrCommand(adjustedPercent, durationInMinutes) + : new SetTbrCommandAlt(adjustedPercent, durationInMinutes); + CommandResult commandResult = runCommand(cmd); if (commandResult.enacted) { TemporaryBasal tempStart = new TemporaryBasal(commandResult.completionTime); // TODO commandResult.state.tbrRemainingDuration might already display 29 if 30 was set, since 29:59 is shown as 29 ... // we should check this, but really ... something must be really screwed up if that number was anything different + // TODO actually ... might setting 29 help with gaps between TBRs? w/o the hack in TemporaryBasal? tempStart.durationInMinutes = durationInMinutes; tempStart.percentRate = adjustedPercent; tempStart.isAbsolute = false; From c1ecad1ed6c7b8928122ef4654c46ddceec40c6b Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Tue, 15 Aug 2017 20:20:30 +0200 Subject: [PATCH 03/11] Initial take on bolus progress reporting. --- .../ruffyscripter/commands/BolusCommand.java | 22 +++++++++++-- .../commands/ProgressReportCallback.java | 13 ++++++++ .../plugins/PumpCombo/ComboPlugin.java | 31 ++++++++++++++++++- 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index d5197452e3..073ef08653 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -14,12 +14,18 @@ import java.util.Locale; import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.RuffyScripter; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.BOLUSING; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.FINISHED; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.PREPARING; + public class BolusCommand implements Command { private static final Logger log = LoggerFactory.getLogger(BolusCommand.class); private final double bolus; + private final ProgressReportCallback progressReportCallback; - public BolusCommand(double bolus) { + public BolusCommand(double bolus, ProgressReportCallback progressReportCallback) { + this.progressReportCallback = progressReportCallback; this.bolus = bolus; } @@ -36,6 +42,7 @@ public class BolusCommand implements Command { @Override public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { + progressReportCallback.progress(PREPARING, 0, 0); try { enterBolusMenu(scripter); @@ -45,6 +52,7 @@ public class BolusCommand implements Command { // confirm bolus scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); scripter.pressCheckKey(); + progressReportCallback.progress(BOLUSING, 0, 0); // the pump displays the entered bolus and waits a bit to let user check and cancel scripter.waitForMenuToBeLeft(MenuType.BOLUS_ENTER); @@ -56,9 +64,15 @@ public class BolusCommand implements Command { // 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.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); + double lastBolusReported = 0; while (bolusRemaining != null) { - log.debug("Delivering bolus, remaining: " + bolusRemaining); - SystemClock.sleep(200); + if (lastBolusReported != bolusRemaining) { + log.debug("Delivering bolus, remaining: " + bolusRemaining); + int percentDelivered = (int) (100 - (bolusRemaining / bolus * 100)); + progressReportCallback.progress(BOLUSING, percentDelivered, bolus - bolusRemaining); + lastBolusReported = bolusRemaining; + } + SystemClock.sleep(50); bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); } @@ -70,6 +84,8 @@ public class BolusCommand implements Command { "Bolus delivery did not complete as expected. " + "Check pump manually, the bolus might not have been delivered."); + progressReportCallback.progress(FINISHED, 100, bolus); + // read last bolus record; those menus display static data and therefore // only a single menu update is sent scripter.navigateToMenu(MenuType.MY_DATA_MENU); diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java new file mode 100644 index 0000000000..c007dfb45f --- /dev/null +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java @@ -0,0 +1,13 @@ +package de.jotomo.ruffyscripter.commands; + +public interface ProgressReportCallback { + enum State { + PREPARING, + BOLUSING, + CANCELLING, + FINISHED, + CANCELLED + } + + void progress(State state, int percent, double delivered); +} 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 29e78847e0..bac8a960b6 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 @@ -31,6 +31,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.ProgressReportCallback; import de.jotomo.ruffyscripter.commands.DetermineCapabilitiesCommand; import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand; import de.jotomo.ruffyscripter.commands.SetTbrCommand; @@ -48,6 +49,7 @@ import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpDescription; 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.utils.DateUtil; import info.nightscout.utils.SP; @@ -369,7 +371,34 @@ public class ComboPlugin implements PluginBase, PumpInterface { if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) { if (detailedBolusInfo.insulin > 0) { // bolus needed, ask pump to deliver it - CommandResult bolusCmdResult = runCommand(new BolusCommand(detailedBolusInfo.insulin)); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + MainApp.bus().post(bolusingEvent); + CommandResult bolusCmdResult = runCommand(new BolusCommand(detailedBolusInfo.insulin, new ProgressReportCallback() { + @Override + public void progress(State state, int percent, double delivered) { + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + switch (state) { + // TODO move into enum as toString or so and make it translateb + case BOLUSING: + bolusingEvent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); + break; + case PREPARING: + bolusingEvent.status = "Preparing pump for bolus"; + break; + case FINISHED: + bolusingEvent.status = "Bolus delivery finished successfully"; + break; + case CANCELLED: + bolusingEvent.status = "Bolus delivery was cancelled"; + break; + case CANCELLING: + bolusingEvent.status = "Cancelling bolus delivery"; + break; + } + bolusingEvent.percent = percent; + MainApp.bus().post(bolusingEvent); + } + })); PumpEnactResult pumpEnactResult = new PumpEnactResult(); pumpEnactResult.success = bolusCmdResult.success; pumpEnactResult.enacted = bolusCmdResult.enacted; From a664bdeaaa40942317a9736798f177df1ebe4497 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Tue, 15 Aug 2017 20:42:31 +0200 Subject: [PATCH 04/11] Initial take on bolus cancellation. --- .../ruffyscripter/commands/BolusCommand.java | 27 ++++++++ .../plugins/PumpCombo/ComboPlugin.java | 63 ++++++++++--------- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index 073ef08653..2d70c5d1b1 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -15,6 +15,8 @@ import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.RuffyScripter; import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.BOLUSING; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.CANCELLED; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.CANCELLING; import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.FINISHED; import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.PREPARING; @@ -23,6 +25,7 @@ public class BolusCommand implements Command { private final double bolus; private final ProgressReportCallback progressReportCallback; + private volatile boolean cancelRequested; public BolusCommand(double bolus, ProgressReportCallback progressReportCallback) { this.progressReportCallback = progressReportCallback; @@ -52,10 +55,24 @@ public class BolusCommand implements Command { // confirm bolus scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); scripter.pressCheckKey(); + if (cancelRequested) { + scripter.goToMainTypeScreen(MenuType.MAIN_MENU, 30_000); + progressReportCallback.progress(CANCELLED, 0, 0); + return new CommandResult().success(true).enacted(false).message("Bolus cancelled as per user request with no insulin delivered"); + } progressReportCallback.progress(BOLUSING, 0, 0); // the pump displays the entered bolus and waits a bit to let user check and cancel + // TODO pressing up (and possible other keys) cancels the bolus scripter.waitForMenuToBeLeft(MenuType.BOLUS_ENTER); + while (scripter.currentMenu.getType() == MenuType.BOLUS_ENTER) { + if (cancelRequested) { + scripter.pressUpKey(); + // TODO deal with error; write a method to wait for and cancel a specific alarm + // wait happens if the keypress comes too late? just try agoin below? + } + SystemClock.sleep(50); + } scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU, "Pump did not return to MAIN_MEU from BOLUS_ENTER to deliver bolus. " @@ -66,12 +83,17 @@ public class BolusCommand implements Command { Double bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); double lastBolusReported = 0; while (bolusRemaining != null) { + if (cancelRequested) { + // TODO press up 3s, deal with bolus cancelled error, retrieved amount actually delivered from history and return it + // since the cancellation takes three seconds some insulin will have definately been delivered (delivery speed is roughly 0.1U/s) + } if (lastBolusReported != bolusRemaining) { log.debug("Delivering bolus, remaining: " + bolusRemaining); int percentDelivered = (int) (100 - (bolusRemaining / bolus * 100)); progressReportCallback.progress(BOLUSING, percentDelivered, bolus - bolusRemaining); lastBolusReported = bolusRemaining; } + // TODO deal with alarms that can arise; an oclussion with raise an oclussion alert as well as a bolus cancelled alert SystemClock.sleep(50); bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); } @@ -174,6 +196,11 @@ public class BolusCommand implements Command { return (double) amountObj; } + public void requestCancellation() { + cancelRequested = true; + progressReportCallback.progress(CANCELLING, 0, 0); + } + @Override public String toString() { return "BolusCommand{" + 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 bac8a960b6..904317d361 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 @@ -80,6 +80,8 @@ public class ComboPlugin implements PluginBase, PumpInterface { @NonNull volatile Date lastCmdTime = new Date(0); volatile PumpState pumpState = new PumpState(); + @Nullable + private volatile BolusCommand runningBolusCommand; private static PumpEnactResult OPERATION_NOT_SUPPORTED = new PumpEnactResult(); @@ -364,6 +366,33 @@ public class ComboPlugin implements PluginBase, PumpInterface { return basal; } + private static ProgressReportCallback bolusProgressReportCallback = new ProgressReportCallback() { + @Override + public void progress(ProgressReportCallback.State state, int percent, double delivered) { + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + switch (state) { + // TODO move into enum as toString or so and make it translateb + case BOLUSING: + bolusingEvent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); + break; + case PREPARING: + bolusingEvent.status = "Preparing pump for bolus"; + break; + case FINISHED: + bolusingEvent.status = "Bolus delivery finished successfully"; + break; + case CANCELLED: + bolusingEvent.status = "Bolus delivery was cancelled"; + break; + case CANCELLING: + bolusingEvent.status = "Cancelling bolus delivery"; + break; + } + bolusingEvent.percent = percent; + MainApp.bus().post(bolusingEvent); + } + }; + // what a mess: pump integration code reading carb info from Detailed**Bolus**Info, // writing carb treatments to the history table. What's PumpEnactResult for again? @Override @@ -373,32 +402,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { // bolus needed, ask pump to deliver it EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); MainApp.bus().post(bolusingEvent); - CommandResult bolusCmdResult = runCommand(new BolusCommand(detailedBolusInfo.insulin, new ProgressReportCallback() { - @Override - public void progress(State state, int percent, double delivered) { - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - switch (state) { - // TODO move into enum as toString or so and make it translateb - case BOLUSING: - bolusingEvent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); - break; - case PREPARING: - bolusingEvent.status = "Preparing pump for bolus"; - break; - case FINISHED: - bolusingEvent.status = "Bolus delivery finished successfully"; - break; - case CANCELLED: - bolusingEvent.status = "Bolus delivery was cancelled"; - break; - case CANCELLING: - bolusingEvent.status = "Cancelling bolus delivery"; - break; - } - bolusingEvent.percent = percent; - MainApp.bus().post(bolusingEvent); - } - })); + // TODO move into enum as toString or so and make it translateb + runningBolusCommand = new BolusCommand(detailedBolusInfo.insulin, bolusProgressReportCallback); + CommandResult bolusCmdResult = runCommand(runningBolusCommand); + runningBolusCommand = null; PumpEnactResult pumpEnactResult = new PumpEnactResult(); pumpEnactResult.success = bolusCmdResult.success; pumpEnactResult.enacted = bolusCmdResult.enacted; @@ -484,9 +491,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { @Override public void stopBolusDelivering() { - // there's no way to stop the combo once delivery has started - // but before that, we could interrupt the command thread ... pause - // till pump times out or raises an error + if (runningBolusCommand != null) runningBolusCommand.requestCancellation(); } // Note: AAPS calls this only to enact OpenAPS recommendations From 22e3517cd6dfa756c7c5478f8e8a82e0872db403 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Wed, 16 Aug 2017 23:03:21 +0200 Subject: [PATCH 05/11] wip cancelling --- .../jotomo/ruffyscripter/RuffyScripter.java | 1 + .../ruffyscripter/commands/BolusCommand.java | 83 ++++++++++++------- .../commands/ProgressReportCallback.java | 11 ++- .../plugins/PumpCombo/ComboPlugin.java | 15 ++-- 4 files changed, 65 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java index f329a71df3..eabcdbf505 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java @@ -356,6 +356,7 @@ public class RuffyScripter { } catch (CommandException e) { return e.toCommandResult(); } catch (Exception e) { + // TODO detect and report pump warnings/errors differently? log.error("Error in ruffyscripter/ruffy", e); return new CommandResult().exception(e).message("Unexpected exception communication with ruffy: " + e.getMessage()); } finally { diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index 2d70c5d1b1..6ae191bf64 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -14,10 +14,10 @@ import java.util.Locale; import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.RuffyScripter; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.BOLUSING; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.CANCELLED; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.CANCELLING; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.FINISHED; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.DELIVERING; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.STOPPED; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.STOPPING; +import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.DELIVERED; import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.PREPARING; public class BolusCommand implements Command { @@ -45,55 +45,75 @@ public class BolusCommand implements Command { @Override public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { - progressReportCallback.progress(PREPARING, 0, 0); try { enterBolusMenu(scripter); - inputBolusAmount(scripter); verifyDisplayedBolusAmount(scripter); + if (cancelRequested) { + progressReportCallback.report(STOPPING, 0, 0); + scripter.goToMainTypeScreen(MenuType.MAIN_MENU, 30 * 1000); + progressReportCallback.report(STOPPED, 0, 0); + return new CommandResult().success(true).enacted(false) + .message("Bolus cancelled as per user request with no insulin delivered"); + } + // confirm bolus scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); scripter.pressCheckKey(); - if (cancelRequested) { - scripter.goToMainTypeScreen(MenuType.MAIN_MENU, 30_000); - progressReportCallback.progress(CANCELLED, 0, 0); - return new CommandResult().success(true).enacted(false).message("Bolus cancelled as per user request with no insulin delivered"); - } - progressReportCallback.progress(BOLUSING, 0, 0); - // the pump displays the entered bolus and waits a bit to let user check and cancel - // TODO pressing up (and possible other keys) cancels the bolus - scripter.waitForMenuToBeLeft(MenuType.BOLUS_ENTER); + // the pump displays the entered bolus and waits a few seconds to let user check and cancel while (scripter.currentMenu.getType() == MenuType.BOLUS_ENTER) { if (cancelRequested) { + progressReportCallback.report(STOPPING, 0, 0); scripter.pressUpKey(); - // TODO deal with error; write a method to wait for and cancel a specific alarm - // wait happens if the keypress comes too late? just try agoin below? + // 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); + if (alertWasCancelled) { + progressReportCallback.report(STOPPED, 0, 0); + return new CommandResult().success(true).enacted(false) + .message("Bolus cancelled as per user request with no insulin delivered"); + } } - SystemClock.sleep(50); + SystemClock.sleep(10); } - 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. + progressReportCallback.report(DELIVERING, 0, 0); Double bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); double lastBolusReported = 0; + // wait for bolus delivery to complete; the remaining units to deliver are counted + // down and are displayed on the main menu. while (bolusRemaining != null) { if (cancelRequested) { + progressReportCallback.report(STOPPING, 0, 0); // TODO press up 3s, deal with bolus cancelled error, retrieved amount actually delivered from history and return it // since the cancellation takes three seconds some insulin will have definately been delivered (delivery speed is roughly 0.1U/s) + progressReportCallback.report(STOPPED, 0, 0); } if (lastBolusReported != bolusRemaining) { log.debug("Delivering bolus, remaining: " + bolusRemaining); int percentDelivered = (int) (100 - (bolusRemaining / bolus * 100)); - progressReportCallback.progress(BOLUSING, percentDelivered, bolus - bolusRemaining); + progressReportCallback.report(DELIVERING, percentDelivered, bolus - bolusRemaining); lastBolusReported = bolusRemaining; } // TODO deal with alarms that can arise; an oclussion with raise an oclussion alert as well as a bolus cancelled alert + // occlusion cancels the bolus -> abort routine to report back delivered bolus; + // low cartridge alert lets bolus run out + // also, any other error or warning can occur and we should return in a controlled fashion - + // communicating back what was actually delivered. + // generally: cancel an alert on the pump and raise the error in AAPS? + // letting the alert go off disrupts comms if the user interacts with the pump, + // then we need to schedule a history read in the near future, let thee user know + // the data will be out of sync for a bit. + // how does the dana handle pump errors? has no vibration, but sound i guess + // should this be configurabe? initially? + if (scripter.currentMenu.getType() == MenuType.WARNING_OR_ERROR) { + + } SystemClock.sleep(50); bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); } @@ -106,7 +126,7 @@ public class BolusCommand implements Command { "Bolus delivery did not complete as expected. " + "Check pump manually, the bolus might not have been delivered."); - progressReportCallback.progress(FINISHED, 100, bolus); + progressReportCallback.report(DELIVERED, 100, bolus); // read last bolus record; those menus display static data and therefore // only a single menu update is sent @@ -130,12 +150,11 @@ public class BolusCommand implements Command { } 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."); + if (!scripter.goToMainTypeScreen(MenuType.MAIN_MENU, 15 * 1000)) { + 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."); + } return new CommandResult().success(true).enacted(true) .message(String.format(Locale.US, "Delivered %02.1f U", bolus)); @@ -144,6 +163,10 @@ public class BolusCommand implements Command { } } + private boolean confirmAlert(String alertText, int maxWaitTillExpectedAlert) { + + } + private void enterBolusMenu(RuffyScripter scripter) { scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); scripter.navigateToMenu(MenuType.BOLUS_MENU); @@ -198,7 +221,7 @@ public class BolusCommand implements Command { public void requestCancellation() { cancelRequested = true; - progressReportCallback.progress(CANCELLING, 0, 0); + progressReportCallback.report(STOPPING, 0, 0); } @Override diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java index c007dfb45f..4491da501d 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java @@ -2,12 +2,11 @@ package de.jotomo.ruffyscripter.commands; public interface ProgressReportCallback { enum State { - PREPARING, - BOLUSING, - CANCELLING, - FINISHED, - CANCELLED + DELIVERING, + DELIVERED, + STOPPING, + STOPPED } - void progress(State state, int percent, double delivered); + void report(State state, int percent, double delivered); } 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 904317d361..82a9e051e9 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 @@ -368,23 +368,20 @@ public class ComboPlugin implements PluginBase, PumpInterface { private static ProgressReportCallback bolusProgressReportCallback = new ProgressReportCallback() { @Override - public void progress(ProgressReportCallback.State state, int percent, double delivered) { + public void report(ProgressReportCallback.State state, int percent, double delivered) { EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); switch (state) { // TODO move into enum as toString or so and make it translateb - case BOLUSING: + case DELIVERING: bolusingEvent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); break; - case PREPARING: - bolusingEvent.status = "Preparing pump for bolus"; - break; - case FINISHED: + case DELIVERED: bolusingEvent.status = "Bolus delivery finished successfully"; break; - case CANCELLED: + case STOPPED: bolusingEvent.status = "Bolus delivery was cancelled"; break; - case CANCELLING: + case STOPPING: bolusingEvent.status = "Cancelling bolus delivery"; break; } @@ -402,7 +399,6 @@ public class ComboPlugin implements PluginBase, PumpInterface { // bolus needed, ask pump to deliver it EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); MainApp.bus().post(bolusingEvent); - // TODO move into enum as toString or so and make it translateb runningBolusCommand = new BolusCommand(detailedBolusInfo.insulin, bolusProgressReportCallback); CommandResult bolusCmdResult = runCommand(runningBolusCommand); runningBolusCommand = null; @@ -433,6 +429,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { // TODO the ui freezes when the calculator issues a carb-only treatment // so just wait, yeah, this is dumb. for now; proper fix via GL#10 // info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog.scheduleDismiss() + // send event to indicate popup can be dismissed? SystemClock.sleep(6000); PumpEnactResult pumpEnactResult = new PumpEnactResult(); pumpEnactResult.success = true; From 738dd72f5bf64eb245b4b7a6f1c73f5856f7fef9 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 17 Aug 2017 10:14:43 +0200 Subject: [PATCH 06/11] Remove TODOs for extended bolus. --- .../nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 82a9e051e9..3ed1028434 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 @@ -103,7 +103,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { pumpDescription.isBolusCapable = true; pumpDescription.bolusStep = 0.1d; - pumpDescription.isExtendedBolusCapable = false; // TODO + pumpDescription.isExtendedBolusCapable = false; pumpDescription.extendedBolusStep = 0.1d; pumpDescription.extendedBolusDurationStep = 15; pumpDescription.extendedBolusMaxDuration = 12 * 60; @@ -618,7 +618,6 @@ public class ComboPlugin implements PluginBase, PumpInterface { return pumpEnactResult; } - // TODO @Override public PumpEnactResult cancelExtendedBolus() { return OPERATION_NOT_SUPPORTED; From fabaf7c2efbbbce6452ec2d467c9ed444e9bc934 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 17 Aug 2017 10:56:06 +0200 Subject: [PATCH 07/11] more --- .../ruffyscripter/commands/BolusCommand.java | 4 +- .../plugins/PumpCombo/ComboPlugin.java | 56 +++++++++---------- app/src/main/res/values/strings.xml | 2 + 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index 6ae191bf64..b9f54205d2 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -18,7 +18,6 @@ import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.DELI import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.STOPPED; import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.STOPPING; import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.DELIVERED; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.PREPARING; public class BolusCommand implements Command { private static final Logger log = LoggerFactory.getLogger(BolusCommand.class); @@ -164,7 +163,8 @@ public class BolusCommand implements Command { } private boolean confirmAlert(String alertText, int maxWaitTillExpectedAlert) { - + // TODO + return false; } private void enterBolusMenu(RuffyScripter scripter) { 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 3ed1028434..c2c7076e6c 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 @@ -369,36 +369,38 @@ public class ComboPlugin implements PluginBase, PumpInterface { private static ProgressReportCallback bolusProgressReportCallback = new ProgressReportCallback() { @Override public void report(ProgressReportCallback.State state, int percent, double delivered) { - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + EventOverviewBolusProgress enent = EventOverviewBolusProgress.getInstance(); switch (state) { - // TODO move into enum as toString or so and make it translateb case DELIVERING: - bolusingEvent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); + enent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered); break; case DELIVERED: - bolusingEvent.status = "Bolus delivery finished successfully"; - break; - case STOPPED: - bolusingEvent.status = "Bolus delivery was cancelled"; + enent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivered), delivered); break; case STOPPING: - bolusingEvent.status = "Cancelling bolus delivery"; + enent.status = MainApp.sResources.getString(R.string.bolusstopping); + break; + case STOPPED: + enent.status = MainApp.sResources.getString(R.string.bolusstopped); break; } - bolusingEvent.percent = percent; - MainApp.bus().post(bolusingEvent); + enent.percent = percent; + MainApp.bus().post(enent); } }; - // what a mess: pump integration code reading carb info from Detailed**Bolus**Info, - // writing carb treatments to the history table. What's PumpEnactResult for again? + /** Updates Treatment records with carbs and boluses and delivers a bolus if needed */ @Override public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) { if (detailedBolusInfo.insulin > 0) { // bolus needed, ask pump to deliver it - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - MainApp.bus().post(bolusingEvent); + + // TODO read history to ensure there are no boluses delivered on the pump we aren't + // aware of and haven't included in the bolus calulation + + // Note that the BolusCommand send progress updates to the bolusProgressReporterCallback, + // which then posts appropriate events on the bus, so in this branch no posts are needed runningBolusCommand = new BolusCommand(detailedBolusInfo.insulin, bolusProgressReportCallback); CommandResult bolusCmdResult = runCommand(runningBolusCommand); runningBolusCommand = null; @@ -407,12 +409,8 @@ public class ComboPlugin implements PluginBase, PumpInterface { pumpEnactResult.enacted = bolusCmdResult.enacted; pumpEnactResult.comment = bolusCmdResult.message; - // if enacted, add bolus and carbs to treatment history + // if enacted by pump, add bolus and carbs to treatment history if (pumpEnactResult.enacted) { - // TODO if no error occurred, the requested bolus is what the pump delievered, - // that has been checked. If an error occurred, we should check how much insulin - // was delivered, e.g. when the cartridge went empty mid-bolus - // For the first iteration, the alert the pump raises must suffice pumpEnactResult.bolusDelivered = detailedBolusInfo.insulin; pumpEnactResult.carbsDelivered = detailedBolusInfo.carbs; @@ -425,11 +423,6 @@ public class ComboPlugin implements PluginBase, PumpInterface { return pumpEnactResult; } else { // no bolus required, carb only treatment - - // TODO the ui freezes when the calculator issues a carb-only treatment - // so just wait, yeah, this is dumb. for now; proper fix via GL#10 - // info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog.scheduleDismiss() - // send event to indicate popup can be dismissed? SystemClock.sleep(6000); PumpEnactResult pumpEnactResult = new PumpEnactResult(); pumpEnactResult.success = true; @@ -438,6 +431,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { pumpEnactResult.carbsDelivered = detailedBolusInfo.carbs; pumpEnactResult.comment = MainApp.instance().getString(R.string.virtualpump_resultok); MainApp.getConfigBuilder().addToHistoryTreatment(detailedBolusInfo); + + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + bolusingEvent.percent = 100; + MainApp.bus().post(bolusingEvent); return pumpEnactResult; } } else { @@ -453,6 +450,12 @@ public class ComboPlugin implements PluginBase, PumpInterface { } } + @Override + public void stopBolusDelivering() { + BolusCommand localRunningBolusCommand = runningBolusCommand; + if (localRunningBolusCommand != null) localRunningBolusCommand.requestCancellation(); + } + private CommandResult runCommand(Command command) { if (ruffyScripter == null) { String msg = "No connection to ruffy. Pump control not available."; @@ -486,11 +489,6 @@ public class ComboPlugin implements PluginBase, PumpInterface { return commandResult; } - @Override - public void stopBolusDelivering() { - if (runningBolusCommand != null) runningBolusCommand.requestCancellation(); - } - // Note: AAPS calls this only to enact OpenAPS recommendations @Override public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, boolean force) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8597ac3d74..bc806b0426 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -702,6 +702,8 @@ Ultra-Rapid Oref "DIA of %s too short - using %s instead!" ACTIVATE PROFILE + Stopping bolus delivery + Bolus delivery stopped From d851637d4f88e066791869f3b3b8397d3cdf9360 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Fri, 18 Aug 2017 11:54:57 +0200 Subject: [PATCH 08/11] more --- .../ruffyscripter/commands/BolusCommand.java | 37 ++++++++++++++++--- .../commands/ProgressReportCallback.java | 12 ------ .../plugins/PumpCombo/ComboPlugin.java | 7 ++-- 3 files changed, 34 insertions(+), 22 deletions(-) delete mode 100644 app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index b9f54205d2..cc03ab0f86 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -14,10 +14,10 @@ import java.util.Locale; import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.RuffyScripter; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.DELIVERING; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.STOPPED; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.STOPPING; -import static de.jotomo.ruffyscripter.commands.ProgressReportCallback.State.DELIVERED; +import static de.jotomo.ruffyscripter.commands.BolusCommand.ProgressReportCallback.State.DELIVERING; +import static de.jotomo.ruffyscripter.commands.BolusCommand.ProgressReportCallback.State.STOPPED; +import static de.jotomo.ruffyscripter.commands.BolusCommand.ProgressReportCallback.State.STOPPING; +import static de.jotomo.ruffyscripter.commands.BolusCommand.ProgressReportCallback.State.DELIVERED; public class BolusCommand implements Command { private static final Logger log = LoggerFactory.getLogger(BolusCommand.class); @@ -86,11 +86,23 @@ public class BolusCommand implements Command { double lastBolusReported = 0; // wait for bolus delivery to complete; the remaining units to deliver are counted // down and are displayed on the main menu. + // TODO extract into method while (bolusRemaining != null) { if (cancelRequested) { progressReportCallback.report(STOPPING, 0, 0); + // TODO just press up 3s in a separated thread and let this loop run + // and at the end handle the outcome and returned raise alarms, whether cancel was reuqested etc + // TODO press up 3s, deal with bolus cancelled error, retrieved amount actually delivered from history and return it - // since the cancellation takes three seconds some insulin will have definately been delivered (delivery speed is roughly 0.1U/s) + // since the cancellation takes three seconds some insulin will have definitely been delivered (delivery speed is roughly 0.1U/s), + // but the pump may also finish delivering the bolus while we try to cancel it + // so, press a button, keep it press and deal with three outcomes: + // * delivery finished (no more remaining bolus displayed) + // * bolus was cancelled (warning raised) + // * any other error (low cartridge, occlusion, both will also trigger 'bolus cancelled' errors) + // cancelBolusInDelivery() + // TODO new thread to press button and then deal with outcomes below, since all errors can occur at all time, pressing + // abort just forces an error (if keyrpess is in time) progressReportCallback.report(STOPPED, 0, 0); } if (lastBolusReported != bolusRemaining) { @@ -111,7 +123,7 @@ public class BolusCommand implements Command { // how does the dana handle pump errors? has no vibration, but sound i guess // should this be configurabe? initially? if (scripter.currentMenu.getType() == MenuType.WARNING_OR_ERROR) { - + // see errors being dealt with trying to cancel } SystemClock.sleep(50); bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); @@ -162,6 +174,8 @@ public class BolusCommand implements Command { } } + // TODO confirmAlarms? and report back which were cancelled? + private boolean confirmAlert(String alertText, int maxWaitTillExpectedAlert) { // TODO return false; @@ -230,4 +244,15 @@ public class BolusCommand implements Command { "bolus=" + bolus + '}'; } + + public interface ProgressReportCallback { + enum State { + DELIVERING, + DELIVERED, + STOPPING, + STOPPED + } + + void report(State state, int percent, double delivered); + } } diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java deleted file mode 100644 index 4491da501d..0000000000 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/ProgressReportCallback.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.jotomo.ruffyscripter.commands; - -public interface ProgressReportCallback { - enum State { - DELIVERING, - DELIVERED, - STOPPING, - STOPPED - } - - void report(State state, int percent, double delivered); -} 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 c2c7076e6c..8d9344d98c 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 @@ -31,7 +31,6 @@ 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.ProgressReportCallback; import de.jotomo.ruffyscripter.commands.DetermineCapabilitiesCommand; import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand; import de.jotomo.ruffyscripter.commands.SetTbrCommand; @@ -366,9 +365,9 @@ public class ComboPlugin implements PluginBase, PumpInterface { return basal; } - private static ProgressReportCallback bolusProgressReportCallback = new ProgressReportCallback() { + private static BolusCommand.ProgressReportCallback bolusProgressReportCallback = new BolusCommand.ProgressReportCallback() { @Override - public void report(ProgressReportCallback.State state, int percent, double delivered) { + public void report(BolusCommand.ProgressReportCallback.State state, int percent, double delivered) { EventOverviewBolusProgress enent = EventOverviewBolusProgress.getInstance(); switch (state) { case DELIVERING: @@ -399,7 +398,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { // TODO read history to ensure there are no boluses delivered on the pump we aren't // aware of and haven't included in the bolus calulation - // Note that the BolusCommand send progress updates to the bolusProgressReporterCallback, + // Note that the BolusCommand sends progress updates to the bolusProgressReporterCallback, // which then posts appropriate events on the bus, so in this branch no posts are needed runningBolusCommand = new BolusCommand(detailedBolusInfo.insulin, bolusProgressReportCallback); CommandResult bolusCmdResult = runCommand(runningBolusCommand); From b162fcce0120568d49c49578667ffd1e0d427da8 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sun, 20 Aug 2017 20:47:58 +0200 Subject: [PATCH 09/11] wip --- .../ruffyscripter/commands/BolusCommand.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index cc03ab0f86..d57b8cbe0d 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -84,9 +84,13 @@ public class BolusCommand implements Command { progressReportCallback.report(DELIVERING, 0, 0); Double bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); double lastBolusReported = 0; + List alarmsRaised = new ArrayList<>(); // wait for bolus delivery to complete; the remaining units to deliver are counted // down and are displayed on the main menu. // 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; while (bolusRemaining != null) { if (cancelRequested) { progressReportCallback.report(STOPPING, 0, 0); @@ -122,13 +126,32 @@ public class BolusCommand implements Command { // the data will be out of sync for a bit. // how does the dana handle pump errors? has no vibration, but sound i guess // should this be configurabe? initially? + if (scripter.currentMenu.getType() == MenuType.WARNING_OR_ERROR) { - // see errors being dealt with trying to cancel + String message = (String) scripter.currentMenu.getAttribute(MenuAttribute.MESSAGE); + if (message.equals("LOW CARTRIDGE")) { + alarmsRaised.add(message); + // confirm, note alert was raised and continue bolusing) + } else { + // any other alert + break; + } } SystemClock.sleep(50); bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); } + // wait up to 2s for any possible warning to be raised + long minWait = System.currentTimeMillis() + 2 * 1000; + while (scripter.currentMenu.getType() != MenuType.WARNING_OR_ERROR || System.currentTimeMillis() < minWait) { + SystemClock.sleep(50); + } + + // process warnings (confirm them, report back to AAPS about them) + while (scripter.currentMenu.getType() == MenuType.WARNING_OR_ERROR || System.currentTimeMillis() < minWait) { + // TODO + } + // 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? @@ -137,7 +160,8 @@ public class BolusCommand implements Command { "Bolus delivery did not complete as expected. " + "Check pump manually, the bolus might not have been delivered."); - progressReportCallback.report(DELIVERED, 100, bolus); + + // TODO report back what was read from history // read last bolus record; those menus display static data and therefore // only a single menu update is sent @@ -167,6 +191,8 @@ public class BolusCommand implements Command { + "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)); } catch (CommandException e) { From 6cc73dcc1ca5de10c0a2e6c3bf8d1dbec2a1ab99 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sun, 20 Aug 2017 23:15:24 +0200 Subject: [PATCH 10/11] wip --- .../jotomo/ruffyscripter/RuffyScripter.java | 20 ++++++++ .../ruffyscripter/commands/BolusCommand.java | 46 ++++++++----------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java index eabcdbf505..0508335caa 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java @@ -450,6 +450,26 @@ public class RuffyScripter { log.debug("Releasing back key"); } + public void pressKeyMs(final byte key, long ms) { + long stepMs = 100; + try { + log.debug("Scroll: Pressing key for " + ms + " ms with step " + stepMs + " ms"); + ruffyService.rtSendKey(key, true); + ruffyService.rtSendKey(key, false); + while (ms > stepMs) { + SystemClock.sleep(stepMs); + ruffyService.rtSendKey(key, false); + ms -= stepMs; + } + SystemClock.sleep(ms); + ruffyService.rtSendKey(Key.NO_KEY, true); + log.debug("Releasing key"); + } catch (Exception e) { + throw new CommandException().exception(e).message("Error while pressing buttons"); + } + } + + public boolean waitForScreenUpdate(long timeout) { synchronized (screenlock) { try { diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index d57b8cbe0d..7d880a9424 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -84,7 +84,7 @@ public class BolusCommand implements Command { progressReportCallback.report(DELIVERING, 0, 0); Double bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); double lastBolusReported = 0; - List alarmsRaised = new ArrayList<>(); + boolean lowCartdrigeAlarmTriggered = false; // wait for bolus delivery to complete; the remaining units to deliver are counted // down and are displayed on the main menu. // TODO extract into method @@ -94,20 +94,19 @@ public class BolusCommand implements Command { while (bolusRemaining != null) { if (cancelRequested) { progressReportCallback.report(STOPPING, 0, 0); - // TODO just press up 3s in a separated thread and let this loop run - // and at the end handle the outcome and returned raise alarms, whether cancel was reuqested etc - - // TODO press up 3s, deal with bolus cancelled error, retrieved amount actually delivered from history and return it - // since the cancellation takes three seconds some insulin will have definitely been delivered (delivery speed is roughly 0.1U/s), - // but the pump may also finish delivering the bolus while we try to cancel it - // so, press a button, keep it press and deal with three outcomes: - // * delivery finished (no more remaining bolus displayed) - // * bolus was cancelled (warning raised) - // * any other error (low cartridge, occlusion, both will also trigger 'bolus cancelled' errors) - // cancelBolusInDelivery() - // TODO new thread to press button and then deal with outcomes below, since all errors can occur at all time, pressing - // abort just forces an error (if keyrpess is in time) + scripter.pressKeyMs(RuffyScripter.Key.UP, 3000); progressReportCallback.report(STOPPED, 0, 0); + // if the bolus finished while we attempted to cancel it, there'll be no alarm + long timeout = System.currentTimeMillis() + 2000; + while (scripter.currentMenu.getType() != MenuType.WARNING_OR_ERROR && System.currentTimeMillis() < timeout) { + SystemClock.sleep(10); + } + while (scripter.currentMenu.getType() == MenuType.WARNING_OR_ERROR) { + // TODO make this cleaner, extract method, needed below too + scripter.pressCheckKey(); + SystemClock.sleep(200); + } + break; } if (lastBolusReported != bolusRemaining) { log.debug("Delivering bolus, remaining: " + bolusRemaining); @@ -115,23 +114,12 @@ public class BolusCommand implements Command { progressReportCallback.report(DELIVERING, percentDelivered, bolus - bolusRemaining); lastBolusReported = bolusRemaining; } - // TODO deal with alarms that can arise; an oclussion with raise an oclussion alert as well as a bolus cancelled alert - // occlusion cancels the bolus -> abort routine to report back delivered bolus; - // low cartridge alert lets bolus run out - // also, any other error or warning can occur and we should return in a controlled fashion - - // communicating back what was actually delivered. - // generally: cancel an alert on the pump and raise the error in AAPS? - // letting the alert go off disrupts comms if the user interacts with the pump, - // then we need to schedule a history read in the near future, let thee user know - // the data will be out of sync for a bit. - // how does the dana handle pump errors? has no vibration, but sound i guess - // should this be configurabe? initially? if (scripter.currentMenu.getType() == MenuType.WARNING_OR_ERROR) { String message = (String) scripter.currentMenu.getAttribute(MenuAttribute.MESSAGE); if (message.equals("LOW CARTRIDGE")) { - alarmsRaised.add(message); - // confirm, note alert was raised and continue bolusing) + lowCartdrigeAlarmTriggered = true; + confirmAlert("LOW CARTRIDGE", 2000); } else { // any other alert break; @@ -141,7 +129,7 @@ public class BolusCommand implements Command { bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); } - // wait up to 2s for any possible warning to be raised + // wait up to 2s for any possible warning to be raised, if not raised already long minWait = System.currentTimeMillis() + 2 * 1000; while (scripter.currentMenu.getType() != MenuType.WARNING_OR_ERROR || System.currentTimeMillis() < minWait) { SystemClock.sleep(50); @@ -177,6 +165,8 @@ public class BolusCommand implements Command { .message("Bolus was delivered, but unable to confirm it with history record"); } + // TODO check date so we don't pick a false record if the previous bolus had the same amount; + // also, report back partial bolus. Just call ReadHsstory(timestamp, boluses=true) cmd ... double lastBolusInHistory = (double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS); if (Math.abs(bolus - lastBolusInHistory) > 0.05) { throw new CommandException().success(false).enacted(true) From 2eb86e1bd87477d4d8d7e1f219a7d32ded8f712a Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Mon, 21 Aug 2017 18:33:40 +0200 Subject: [PATCH 11/11] wip --- .../main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java index 7d880a9424..80fd1df285 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/BolusCommand.java @@ -45,6 +45,7 @@ public class BolusCommand implements Command { @Override public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { try { + // TODO read reservoir level and reject request if reservoir < bolus enterBolusMenu(scripter); inputBolusAmount(scripter); verifyDisplayedBolusAmount(scripter);