diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommand.java index 5815425e51..93adbbd8ba 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/SetTbrCommand.java @@ -1,7 +1,5 @@ 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; @@ -15,11 +13,21 @@ import java.util.Locale; import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.RuffyScripter; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.AFTER; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.BEFORE; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.ERROR; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.MAIN; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.SET; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.SET_TBR; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.SET_TIME; +import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.TBR; + public class SetTbrCommand implements Command { private static final Logger log = LoggerFactory.getLogger(SetTbrCommand.class); private final long percentage; private final long duration; + private RuffyScripter scripter; public SetTbrCommand(long percentage, long duration) { this.percentage = percentage; @@ -53,247 +61,295 @@ public class SetTbrCommand implements Command { return violations; } + enum State { + BEFORE, + MAIN, + TBR, + SET_TBR, + SET_TIME, + SET, + AFTER, + ERROR + }; + private State lastState,state; + private long last; + private long timeout; + private Thread timeoutThread = new Thread() + { + @Override + public void run() { + while(state != ERROR && state!=AFTER) { + if (timeout + last < System.currentTimeMillis()) { + lastState = state; + state = ERROR; + log.debug("timeout reached -> state:ERROR"); + } + tick(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + tick(); + } + }; + private void updateState(State newState,long timeoutSec) + { + lastState = state; + state = newState; + last = System.currentTimeMillis(); + timeout = timeoutSec*1000; + } + private MenuType lastMenu; + private void tick() + { + switch (state) + { + case BEFORE: + if(scripter.currentMenu.getType()==MenuType.MAIN_MENU) + { + updateState(MAIN,120); + lastMenu = MenuType.MAIN_MENU; + log.debug("found MAIN_MENU -> state:MAIN"); + } + break; + case MAIN: + if(scripter.currentMenu.getType()==MenuType.TBR_MENU) + { + updateState(TBR,30); + scripter.pressCheckKey(); + log.debug("found TBR_MENU -> state:TBR"); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else if(scripter.currentMenu.getType()!=lastMenu) + { + lastMenu = scripter.currentMenu.getType(); + updateState(MAIN,30); + scripter.pressMenuKey(); + log.debug("found Menu:"+lastMenu+" -> state:MAIN"); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else + { + scripter.pressMenuKey(); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + break; + case TBR: + if(scripter.currentMenu.getType()==MenuType.TBR_SET) + { + updateState(SET_TBR,60); + } + else + { + scripter.pressMenuKey(); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + updateState(TBR,60); + } + break; + case SET_TBR: + if(scripter.currentMenu.getType()==MenuType.TBR_SET) + { + Object percentageObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE); + if(percentageObj != null && percentageObj instanceof Double) + { + double currentPercentage = ((Double) percentageObj).doubleValue(); + if(currentPercentage < percentage) + { + scripter.pressUpKey(); + updateState(SET_TBR,30); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else if(currentPercentage > percentage) + { + scripter.pressDownKey(); + updateState(SET_TBR,30); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else + { + if(percentage==100) + { + scripter.pressCheckKey(); + updateState(SET, 30); + } + else { + scripter.pressMenuKey(); + updateState(SET_TIME, 30); + } + } + } + } + else if(scripter.currentMenu.getType()==MenuType.TBR_DURATION) + { + scripter.pressMenuKey(); + updateState(TBR,60); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else + { + updateState(ERROR,30); + } + break; + case SET_TIME: + if(scripter.currentMenu.getType()==MenuType.TBR_DURATION) + { + Object durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); + if(durationObj != null && durationObj instanceof MenuTime) + { + MenuTime time = (MenuTime) durationObj; + double currentDuration = (time.getHour()*60)+time.getMinute(); + if(currentDuration < duration) + { + scripter.pressUpKey(); + updateState(SET_TIME,30); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else if(currentDuration > duration) + { + scripter.pressDownKey(); + updateState(SET_TIME,30); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else + { + scripter.pressCheckKey(); + updateState(SET, 30); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + else if(scripter.currentMenu.getType()==MenuType.TBR_SET) + { + scripter.pressMenuKey(); + updateState(SET_TIME,60); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else + { + updateState(ERROR,60); + } + break; + case SET: + if(scripter.currentMenu.getType()==MenuType.WARNING_OR_ERROR) + { + lastMenu = scripter.currentMenu.getType(); + scripter.pressCheckKey(); + updateState(SET, 30); + try { + Thread.sleep(750); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + else if(scripter.currentMenu.getType()==MenuType.MAIN_MENU) { + Object setPercentage = scripter.currentMenu.getAttribute(MenuAttribute.TBR); + Object setDuration = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); + if (setPercentage== null ||setDuration==null) { + if(percentage!=100) + { + updateState(ERROR,10); + } + else + { + if(lastMenu==MenuType.WARNING_OR_ERROR) + updateState(AFTER,10); + else + updateState(SET,10); + } + } + else { + double mmTbrPercentage = (Double) setPercentage; + MenuTime mmTbrDuration = (MenuTime) setDuration; + // ... 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) { + updateState(AFTER, 10); + } else { + updateState(ERROR, 10); + } + } + } + break; + case ERROR: + case AFTER: + synchronized(SetTbrCommand.this) { + SetTbrCommand.this.notify(); + } + break; + } + } @Override public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { - try { - enterTbrMenu(scripter); - inputTbrPercentage(scripter); - verifyDisplayedTbrPercentage(scripter); + state = BEFORE; + this.scripter = scripter; + updateState(BEFORE,120); + timeoutThread.start(); - 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); + try { + synchronized (this) { + this.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + return new CommandResult().success(false).message("failed to wait: "+e.getMessage()); } + if(state==AFTER) + { + if(percentage==100) + return new CommandResult().success(true).enacted(true).message("TBR was cancelled"); - 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(400); - 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(750); - } - - 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(750); - 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); - while(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(); - SystemClock.sleep(300); - scripter.waitForMenuUpdate(); - currentDuration = readDisplayedTbrDuration(scripter); - } - SystemClock.sleep(300); - 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(400); - 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(750); - } - - 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(750); - 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(20); - } - } - - 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"); - } + return new CommandResult().success(false).message("failed with state: "+state+" from: "+lastState); } @Override