Restore bolus comands alongside.

This commit is contained in:
Johannes Mockenhaupt 2017-08-27 19:04:59 +02:00
parent 95fda165a3
commit 3d2732a68e
No known key found for this signature in database
GPG key ID: 9E1EA6AF7BBBB0D1
2 changed files with 305 additions and 140 deletions

View file

@ -11,23 +11,14 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import de.jotomo.ruffyscripter.PumpState;
import de.jotomo.ruffyscripter.RuffyScripter;
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 extends BaseCommand {
private static final Logger log = LoggerFactory.getLogger(BolusCommand.class);
private final double bolus;
private final ProgressReportCallback progressReportCallback;
private volatile boolean cancelRequested;
protected final double bolus;
public BolusCommand(double bolus, ProgressReportCallback progressReportCallback) {
this.progressReportCallback = progressReportCallback;
public BolusCommand(double bolus) {
this.bolus = bolus;
}
@ -45,103 +36,31 @@ public class BolusCommand extends BaseCommand {
@Override
public CommandResult execute() {
try {
// TODO read reservoir level and reject request if reservoir < bolus
enterBolusMenu();
enterBolusMenu(scripter);
inputBolusAmount();
verifyDisplayedBolusAmount();
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");
}
inputBolusAmount(scripter);
verifyDisplayedBolusAmount(scripter);
// confirm bolus
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER);
scripter.pressCheckKey();
// the pump displays the entered bolus and waits a few seconds to let user check and cancel
while (scripter.getCurrentMenu().getType() == MenuType.BOLUS_ENTER) {
if (cancelRequested) {
progressReportCallback.report(STOPPING, 0, 0);
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);
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(10);
}
// the pump displays the entered bolus and waits a bit to let user check and cancel
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.");
progressReportCallback.report(DELIVERING, 0, 0);
Double bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
double lastBolusReported = 0;
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
// 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;
Double bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
while (bolusRemaining != null) {
if (cancelRequested) {
progressReportCallback.report(STOPPING, 0, 0);
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.getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR && System.currentTimeMillis() < timeout) {
SystemClock.sleep(10);
}
while (scripter.getCurrentMenu().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);
int percentDelivered = (int) (100 - (bolusRemaining / bolus * 100));
progressReportCallback.report(DELIVERING, percentDelivered, bolus - bolusRemaining);
lastBolusReported = bolusRemaining;
}
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);
} else {
// any other alert
break;
}
}
SystemClock.sleep(50);
log.debug("Delivering bolus, remaining: " + bolusRemaining);
SystemClock.sleep(200);
bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
}
// 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) {
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
}
// 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?
@ -150,9 +69,6 @@ public class BolusCommand extends BaseCommand {
"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
// only a single menu update is sent
scripter.navigateToMenu(MenuType.MY_DATA_MENU);
@ -167,8 +83,6 @@ public class BolusCommand extends BaseCommand {
.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.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
if (Math.abs(bolus - lastBolusInHistory) > 0.05) {
throw new CommandException().success(false).enacted(true)
@ -177,13 +91,12 @@ public class BolusCommand extends BaseCommand {
}
log.debug("Bolus record in history confirms delivered bolus");
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.");
}
progressReportCallback.report(DELIVERED, 100, 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));
@ -192,14 +105,7 @@ public class BolusCommand extends BaseCommand {
}
}
// TODO confirmAlarms? and report back which were cancelled?
private boolean confirmAlert(String alertText, int maxWaitTillExpectedAlert) {
// TODO
return false;
}
private void enterBolusMenu() {
private void enterBolusMenu(RuffyScripter scripter) {
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU);
scripter.navigateToMenu(MenuType.BOLUS_MENU);
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_MENU);
@ -208,7 +114,7 @@ public class BolusCommand extends BaseCommand {
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER);
}
private void inputBolusAmount() {
private void inputBolusAmount(RuffyScripter scripter) {
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER);
// press 'up' once for each 0.1 U increment
long steps = Math.round(bolus * 10);
@ -222,35 +128,33 @@ public class BolusCommand extends BaseCommand {
SystemClock.sleep(2000);
}
private void verifyDisplayedBolusAmount() {
private void verifyDisplayedBolusAmount(RuffyScripter scripter) {
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);
displayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS);
}
double displayedBolus = readDisplayedBolusAmount(scripter);
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);
SystemClock.sleep(2000);
double refreshedDisplayedBolus = readDisplayedBolusAmount(scripter);
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() {
cancelRequested = true;
progressReportCallback.report(STOPPING, 0, 0);
private double readDisplayedBolusAmount(RuffyScripter scripter) {
// TODO v2 add timeout? Currently the command execution timeout would trigger if exceeded
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER);
// bolus amount is blinking, so we need to make sure we catch it at the right moment
Object amountObj = scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
while (!(amountObj instanceof Double)) {
scripter.waitForMenuUpdate();
amountObj = scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
}
return (double) amountObj;
}
@Override
@ -259,15 +163,4 @@ public class BolusCommand extends BaseCommand {
"bolus=" + bolus +
'}';
}
public interface ProgressReportCallback {
enum State {
DELIVERING,
DELIVERED,
STOPPING,
STOPPED
}
void report(State state, int percent, double delivered);
}
}

View file

@ -0,0 +1,272 @@
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;
import de.jotomo.ruffyscripter.PumpState;
import de.jotomo.ruffyscripter.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;
public class CancellableBolusCommand extends BolusCommand {
private static final Logger log = LoggerFactory.getLogger(CancellableBolusCommand.class);
private final ProgressReportCallback progressReportCallback;
private volatile boolean cancelRequested;
public CancellableBolusCommand(double bolus, ProgressReportCallback progressReportCallback) {
super(bolus);
this.progressReportCallback = progressReportCallback;
}
@Override
public List<String> validateArguments() {
List<String> 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 {
// TODO read reservoir level and reject request if reservoir < bolus
enterBolusMenu();
inputBolusAmount();
verifyDisplayedBolusAmount();
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();
// the pump displays the entered bolus and waits a few seconds to let user check and cancel
while (scripter.getCurrentMenu().getType() == MenuType.BOLUS_ENTER) {
if (cancelRequested) {
progressReportCallback.report(STOPPING, 0, 0);
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);
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(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.");
progressReportCallback.report(DELIVERING, 0, 0);
Double bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
double lastBolusReported = 0;
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
// 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);
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.getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR && System.currentTimeMillis() < timeout) {
SystemClock.sleep(10);
}
while (scripter.getCurrentMenu().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);
int percentDelivered = (int) (100 - (bolusRemaining / bolus * 100));
progressReportCallback.report(DELIVERING, percentDelivered, bolus - bolusRemaining);
lastBolusReported = bolusRemaining;
}
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);
} else {
// any other alert
break;
}
}
SystemClock.sleep(50);
bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
}
// 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) {
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
}
// 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.");
// 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
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");
}
// 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.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");
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.");
}
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) {
return e.toCommandResult();
}
}
// TODO confirmAlarms? and report back which were cancelled?
private boolean confirmAlert(String alertText, int maxWaitTillExpectedAlert) {
// TODO
return false;
}
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 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);
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() {
cancelRequested = true;
progressReportCallback.report(STOPPING, 0, 0);
}
@Override
public String toString() {
return "BolusCommand{" +
"bolus=" + bolus +
'}';
}
public interface ProgressReportCallback {
enum State {
DELIVERING,
DELIVERED,
STOPPING,
STOPPED
}
void report(State state, int percent, double delivered);
}
}