Merge remote-tracking branch 'fork/tbrDurationFix-90-110-cancel' into stable

* fork/tbrDurationFix-90-110-cancel:
  change ComboPlugin to change basal to 90 or 110 percent instead of cancel - depending on previous tbr
  missing stuf :/
  add unused (but tested) command to get basal rate from Profile 1
  update setTBRCommand to way faster methods in scripter
  add waitScreen (wait for refresh with timeout), gotToMainMenuScreen (with the help of back and menu navigate to a given main menu), enterMenu (to navigate withhin submenus), and step (send a key repeated)
  add back key
  make keypress wait up to 2000ms for key response
  add type of menu to MenuType (main or sub menu)
  - fix Multi connect - add key sent with 2500ms wait on keypress - fix Timing and npes in setTBR - make ruffyScripter not reinit every time - fix menu npe
  change Version info
  exceptions
  maybe better error recovery?
  alternate setTBR based on StateMachine
  more time
  increase Button Timing to 200ms
  wait till we get something currentDuration % 15 == 0
  slow pump fix
  if umbind fails while service is dead, bind would never be called

# Conflicts:
#	app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java
This commit is contained in:
Johannes Mockenhaupt 2017-08-05 11:42:14 +02:00
commit 134bc32dac
No known key found for this signature in database
GPG key ID: 9E1EA6AF7BBBB0D1
10 changed files with 728 additions and 349 deletions

View file

@ -44,7 +44,7 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 23 targetSdkVersion 23
versionCode 1500 versionCode 1500
version "1.5f" version "1.5f-combo-sandratest"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", generateGitBuild() buildConfigField "String", "BUILDVERSION", generateGitBuild()
} }

View file

@ -10,12 +10,17 @@ interface IRTHandler {
void fail(String message); void fail(String message);
void requestBluetooth(); void requestBluetooth();
boolean canDisconnect();
void rtStopped(); void rtStopped();
void rtStarted(); void rtStarted();
void rtClearDisplay(); void rtClearDisplay();
void rtUpdateDisplay(in byte[] quarter, int which); void rtUpdateDisplay(in byte[] quarter, int which);
void rtDisplayHandleMenu(in Menu menu); void rtDisplayHandleMenu(in Menu menu, in int sequence);
void rtDisplayHandleNoMenu(); void rtDisplayHandleNoMenu(in int sequence);
void keySent(in int sequence);
String getServiceIdentifier();
} }

View file

@ -13,10 +13,10 @@ interface IRuffyService {
* *
* @return 0 if successful, -1 otherwise * @return 0 if successful, -1 otherwise
*/ */
int doRTConnect(); int doRTConnect(IRTHandler handler);
/** Disconnect from the pump */ /** Disconnect from the pump */
void doRTDisconnect(); void doRTDisconnect(IRTHandler handler);
/*What's the meaning of 'changed'? /*What's the meaning of 'changed'?
* changed means if a button state has been changed, like btton pressed is a change and button release another*/ * changed means if a button state has been changed, like btton pressed is a change and button release another*/

View file

@ -1,6 +1,5 @@
package de.jotomo.ruffyscripter; package de.jotomo.ruffyscripter;
import android.os.DeadObjectException;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
@ -16,11 +15,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
import java.util.Objects;
import de.jotomo.ruffyscripter.commands.Command; import de.jotomo.ruffyscripter.commands.Command;
import de.jotomo.ruffyscripter.commands.CommandException; import de.jotomo.ruffyscripter.commands.CommandException;
import de.jotomo.ruffyscripter.commands.CommandResult; import de.jotomo.ruffyscripter.commands.CommandResult;
import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand; import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand;
import de.jotomo.ruffyscripter.commands.SetTbrCommand;
// TODO regularly read "My data" history (boluses, TBR) to double check all commands ran successfully. // TODO regularly read "My data" history (boluses, TBR) to double check all commands ran successfully.
// Automatically compare against AAPS db, or log all requests in the PumpInterface (maybe Milos // Automatically compare against AAPS db, or log all requests in the PumpInterface (maybe Milos
@ -35,7 +36,7 @@ public class RuffyScripter {
private static final Logger log = LoggerFactory.getLogger(RuffyScripter.class); private static final Logger log = LoggerFactory.getLogger(RuffyScripter.class);
private final IRuffyService ruffyService; private IRuffyService ruffyService;
private final long connectionTimeOutMs = 5000; private final long connectionTimeOutMs = 5000;
private String unrecoverableError = null; private String unrecoverableError = null;
@ -50,30 +51,41 @@ public class RuffyScripter {
private boolean started = false; private boolean started = false;
public RuffyScripter(final IRuffyService ruffyService) { private Object keylock = new Object();
this.ruffyService = ruffyService; private int keynotwait = 0;
private Object screenlock = new Object();
public RuffyScripter() {
} }
public void start() { public void start(IRuffyService newService) {
try { try {
ruffyService.addHandler(mHandler); if(newService!=null) {
this.ruffyService = newService;
// TODO this'll be done better in v2 via ConnectionManager // TODO this'll be done better in v2 via ConnectionManager
if (idleDisconnectMonitorThread.getState() == Thread.State.NEW) { if (idleDisconnectMonitorThread.getState() == Thread.State.NEW) {
idleDisconnectMonitorThread.start(); idleDisconnectMonitorThread.start();
} }
started = true; started = true;
} catch (RemoteException e) { try{newService.addHandler(mHandler);}catch (Exception e){}
}
} catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public void stop() { public void stop() {
if (started) { if (started) {
try { started=false;
// TODO ruffy removes dead handlers automatically by now.
// still, check this when going through recovery logic
/* try {
ruffyService.removeHandler(mHandler); ruffyService.removeHandler(mHandler);
} catch (RemoteException e) { } catch (RemoteException e) {
log.warn("Removing IRTHandler from Ruffy service failed, ignoring", e); log.warn("Removing IRTHandler from Ruffy service failed, ignoring", e);
} }*/
} }
} }
@ -81,6 +93,7 @@ public class RuffyScripter {
return started; return started;
} }
private boolean canDisconnect = false;
private Thread idleDisconnectMonitorThread = new Thread(new Runnable() { private Thread idleDisconnectMonitorThread = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -94,20 +107,24 @@ public class RuffyScripter {
&& now > lastDisconnect + 15 * 1000) { && now > lastDisconnect + 15 * 1000) {
log.debug("Disconnecting after " + (connectionTimeOutMs / 1000) + "s inactivity timeout"); log.debug("Disconnecting after " + (connectionTimeOutMs / 1000) + "s inactivity timeout");
lastDisconnect = now; lastDisconnect = now;
ruffyService.doRTDisconnect(); canDisconnect=true;
ruffyService.doRTDisconnect(mHandler);
connected = false; connected = false;
lastDisconnect = System.currentTimeMillis(); lastDisconnect = System.currentTimeMillis();
// don't attempt anything fancy in the next 10s, let the pump settle // don't attempt anything fancy in the next 10s, let the pump settle
SystemClock.sleep(10 * 1000); SystemClock.sleep(10 * 1000);
} }
} catch (DeadObjectException doe) { else
{
canDisconnect=false;
}
} catch (Exception e) {
// TODO do we need to catch this exception somewhere else too? right now it's // TODO do we need to catch this exception somewhere else too? right now it's
// converted into a command failure, but it's not classified as unrecoverable; // converted into a command failure, but it's not classified as unrecoverable;
// eventually we might try to recover ... check docs, there's also another // eventually we might try to recover ... check docs, there's also another
// execption we should watch interacting with a remote service. // execption we should watch interacting with a remote service.
// SecurityException was the other, when there's an AIDL mismatch; // SecurityException was the other, when there's an AIDL mismatch;
unrecoverableError = "Ruffy service went away"; //unrecoverableError = "Ruffy service went away";
} catch (RemoteException e) {
log.debug("Exception in idle disconnect monitor thread, carrying on", e); log.debug("Exception in idle disconnect monitor thread, carrying on", e);
} }
SystemClock.sleep(1000); SystemClock.sleep(1000);
@ -131,6 +148,11 @@ public class RuffyScripter {
log.trace("Ruffy invoked requestBluetooth callback"); log.trace("Ruffy invoked requestBluetooth callback");
} }
@Override
public boolean canDisconnect() throws RemoteException {
return canDisconnect;
}
@Override @Override
public void rtStopped() throws RemoteException { public void rtStopped() throws RemoteException {
log.debug("rtStopped callback invoked"); log.debug("rtStopped callback invoked");
@ -153,13 +175,18 @@ public class RuffyScripter {
} }
@Override @Override
public void rtDisplayHandleMenu(Menu menu) throws RemoteException { public void rtDisplayHandleMenu(Menu menu, int sequence) throws RemoteException {
// method is called every ~500ms // method is called every ~500ms
log.debug("rtDisplayHandleMenu: " + menu.getType()); log.debug("rtDisplayHandleMenu: " + menu.getType());
currentMenu = menu; currentMenu = menu;
menuLastUpdated = System.currentTimeMillis(); menuLastUpdated = System.currentTimeMillis();
synchronized (screenlock)
{
screenlock.notifyAll();
}
connected = true; connected = true;
// note that a WARNING_OR_ERROR menu can be a valid temporary state (cancelling TBR) // note that a WARNING_OR_ERROR menu can be a valid temporary state (cancelling TBR)
@ -170,16 +197,39 @@ public class RuffyScripter {
} }
@Override @Override
public void rtDisplayHandleNoMenu() throws RemoteException { public void rtDisplayHandleNoMenu(int sequence) throws RemoteException {
log.debug("rtDisplayHandleNoMenu callback invoked"); log.debug("rtDisplayHandleNoMenu callback invoked");
} }
@Override
public void keySent(int sequence) throws RemoteException {
synchronized (keylock)
{
if(keynotwait>0)
keynotwait--;
else
keylock.notify();
}
}
@Override
public String getServiceIdentifier() throws RemoteException {
return this.toString();
}
}; };
public boolean isPumpBusy() { public boolean isPumpBusy() {
return activeCmd != null; return activeCmd != null;
} }
public void unbind() {
if(ruffyService!=null)
try{ruffyService.removeHandler(mHandler);}catch (Exception e){}
this.ruffyService = null;
}
private static class Returnable { private static class Returnable {
CommandResult cmdResult; CommandResult cmdResult;
} }
@ -327,7 +377,8 @@ public class RuffyScripter {
SystemClock.sleep(10 * 1000); SystemClock.sleep(10 * 1000);
} }
boolean connectInitSuccessful = ruffyService.doRTConnect() == 0; canDisconnect=false;
boolean connectInitSuccessful = ruffyService.doRTConnect(mHandler) == 0;
log.debug("Connect init successful: " + connectInitSuccessful); log.debug("Connect init successful: " + connectInitSuccessful);
log.debug("Waiting for first menu update to be sent"); log.debug("Waiting for first menu update to be sent");
// Note: there was an 'if(currentMenu == null)' around the next call, since // Note: there was an 'if(currentMenu == null)' around the next call, since
@ -339,8 +390,8 @@ public class RuffyScripter {
// if the user just pressed a button on the combo, the screen needs to time first // if the user just pressed a button on the combo, the screen needs to time first
// before a connection is possible. In that case, it takes 45s before the // before a connection is possible. In that case, it takes 45s before the
// connection comes up. // connection comes up.
waitForMenuUpdate(90); waitForMenuUpdate(180);
} catch (RemoteException e) { } catch (Exception e) {
throw new CommandException().exception(e).message("Unexpected exception while initiating/restoring pump connection"); throw new CommandException().exception(e).message("Unexpected exception while initiating/restoring pump connection");
} }
} }
@ -350,38 +401,95 @@ public class RuffyScripter {
// so this class can focus on providing a connection to run commands // so this class can focus on providing a connection to run commands
// (or maybe reconsider putting it into a base class) // (or maybe reconsider putting it into a base class)
private static class Key { public static class Key {
static byte NO_KEY = (byte) 0x00; public static byte NO_KEY = (byte) 0x00;
static byte MENU = (byte) 0x03; public static byte MENU = (byte) 0x03;
static byte CHECK = (byte) 0x0C; public static byte CHECK = (byte) 0x0C;
static byte UP = (byte) 0x30; public static byte UP = (byte) 0x30;
static byte DOWN = (byte) 0xC0; public static byte DOWN = (byte) 0xC0;
public static byte BACK = (byte) 0x33;
} }
public void pressUpKey() { public void pressUpKey() {
log.debug("Pressing up key"); log.debug("Pressing up key");
pressKey(Key.UP); pressKey(Key.UP,2000);
log.debug("Releasing up key"); log.debug("Releasing up key");
} }
public void pressDownKey() { public void pressDownKey() {
log.debug("Pressing down key"); log.debug("Pressing down key");
pressKey(Key.DOWN); pressKey(Key.DOWN,2000);
log.debug("Releasing down key"); log.debug("Releasing down key");
} }
public void pressCheckKey() { public void pressCheckKey() {
log.debug("Pressing check key"); log.debug("Pressing check key");
pressKey(Key.CHECK); pressKey(Key.CHECK,2000);
log.debug("Releasing check key"); log.debug("Releasing check key");
} }
public void pressMenuKey() { public void pressMenuKey() {
log.debug("Pressing menu key"); log.debug("Pressing menu key");
pressKey(Key.MENU); pressKey(Key.MENU,2000);
log.debug("Releasing menu key"); log.debug("Releasing menu key");
} }
public void pressBackKey() {
log.debug("Pressing back key");
pressKey(Key.BACK,2000);
log.debug("Releasing back key");
}
public boolean waitScreen(long timeout)
{
synchronized (screenlock) {
try {
screenlock.wait(timeout);
} catch (Exception e) {
return false;
}
}
return true;
}
public boolean goToMainMenuScreen(MenuType screen, long timeout)
{
long start = System.currentTimeMillis();
while((currentMenu == null || currentMenu.getType()!=screen) && start+timeout>System.currentTimeMillis())
{
if(currentMenu!=null && currentMenu.getType()==MenuType.WARNING_OR_ERROR)
{
//FIXME bad thing to do :D
pressCheckKey();
}
else if(currentMenu!=null && !currentMenu.getType().isMaintype())
{
pressBackKey();
}
else
pressMenuKey();
waitScreen(250);
}
return currentMenu != null && currentMenu.getType()==screen;
}
public boolean enterMenu(MenuType startType, MenuType targetType, byte key, long timeout)
{
if(currentMenu==null || currentMenu.getType() != startType)
return false;
long start = System.currentTimeMillis();
pressKey(key,2000);
while((currentMenu == null || currentMenu.getType()!=targetType) && start+timeout>System.currentTimeMillis()) {
waitScreen(100);
}
return currentMenu!=null && currentMenu.getType()==targetType;
}
public void step(int steps, byte key, long timeout) {
for(int i = 0; i < Math.abs(steps);i++)
pressKey(key,timeout);
}
// TODO v2, rework these two methods: waitForMenuUpdate shoud only be used by commands // TODO v2, rework these two methods: waitForMenuUpdate shoud only be used by commands
// then anything longer than a few seconds is an error; // then anything longer than a few seconds is an error;
// only ensureConnected() uses the method with the timeout parameter; inline that code, // only ensureConnected() uses the method with the timeout parameter; inline that code,
@ -390,7 +498,7 @@ public class RuffyScripter {
* Wait until the menu update is in * Wait until the menu update is in
*/ */
public void waitForMenuUpdate() { public void waitForMenuUpdate() {
waitForMenuUpdate(60); waitForMenuUpdate(120);
} }
public void waitForMenuUpdate(long timeoutInSeconds) { public void waitForMenuUpdate(long timeoutInSeconds) {
@ -404,12 +512,26 @@ public class RuffyScripter {
} }
} }
private void pressKey(final byte key) { private void pressKey(final byte key, long timeout) {
try { try {
ruffyService.rtSendKey(key, true); ruffyService.rtSendKey(key, true);
SystemClock.sleep(100); //SystemClock.sleep(200);
ruffyService.rtSendKey(Key.NO_KEY, true); ruffyService.rtSendKey(Key.NO_KEY, true);
} catch (RemoteException e) { if(timeout > 0)
{
synchronized (keylock)
{
keylock.wait(timeout);
}
}
else
{
synchronized (keylock)
{
keynotwait++;
}
}
} catch (Exception e) {
throw new CommandException().exception(e).message("Error while pressing buttons"); throw new CommandException().exception(e).message("Error while pressing buttons");
} }
} }

View file

@ -58,7 +58,7 @@ public class BolusCommand implements Command {
Double bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); Double bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING);
while (bolusRemaining != null) { while (bolusRemaining != null) {
log.debug("Delivering bolus, remaining: " + bolusRemaining); log.debug("Delivering bolus, remaining: " + bolusRemaining);
SystemClock.sleep(200); SystemClock.sleep(300);
bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING); bolusRemaining = (Double) scripter.currentMenu.getAttribute(MenuAttribute.BOLUS_REMAINING);
} }
@ -122,11 +122,11 @@ public class BolusCommand implements Command {
for (int i = 0; i < steps; i++) { for (int i = 0; i < steps; i++) {
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER); scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER);
scripter.pressUpKey(); scripter.pressUpKey();
SystemClock.sleep(100); SystemClock.sleep(400);
} }
// Give the pump time to finish any scrolling that might still be going on, can take // 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 // up to 1100ms. Plus some extra time to be sure
SystemClock.sleep(2000); SystemClock.sleep(750);
} }
private void verifyDisplayedBolusAmount(RuffyScripter scripter) { private void verifyDisplayedBolusAmount(RuffyScripter scripter) {
@ -138,7 +138,7 @@ public class BolusCommand implements Command {
} }
// check again to ensure the displayed value hasn't change due to due scrolling taking extremely long // check again to ensure the displayed value hasn't change due to due scrolling taking extremely long
SystemClock.sleep(2000); SystemClock.sleep(750);
double refreshedDisplayedBolus = readDisplayedBolusAmount(scripter); double refreshedDisplayedBolus = readDisplayedBolusAmount(scripter);
if (Math.abs(displayedBolus - refreshedDisplayedBolus) > 0.05) { if (Math.abs(displayedBolus - refreshedDisplayedBolus) > 0.05) {
throw new CommandException().message("Failed to set bolus: bolus changed after input stopped from " throw new CommandException().message("Failed to set bolus: bolus changed after input stopped from "

View file

@ -0,0 +1,191 @@
package de.jotomo.ruffyscripter.commands;
import android.util.Log;
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.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import de.jotomo.ruffyscripter.PumpState;
import de.jotomo.ruffyscripter.RuffyScripter;
import static de.jotomo.ruffyscripter.commands.GetBasalCommand.State.BEFORE;
import static de.jotomo.ruffyscripter.commands.GetBasalCommand.State.MAIN;
import static de.jotomo.ruffyscripter.commands.GetBasalCommand.State.BASAL_1_MENU;
import static de.jotomo.ruffyscripter.commands.GetBasalCommand.State.BASAL_OVERVIEW;
import static de.jotomo.ruffyscripter.commands.GetBasalCommand.State.READ_BASAL;
import static de.jotomo.ruffyscripter.commands.GetBasalCommand.State.ERROR;
import static de.jotomo.ruffyscripter.commands.GetBasalCommand.State.AFTER;
public class GetBasalCommand implements Command {
private static final Logger log = LoggerFactory.getLogger(GetBasalCommand.class);
private RuffyScripter scripter;
public GetBasalCommand() {}
@Override
public List<String> validateArguments() {
List<String> violations = new ArrayList<>();
return violations;
}
enum State {
BEFORE,
MAIN,
BASAL_1_MENU,
BASAL_OVERVIEW,
READ_BASAL,
ERROR,
AFTER,
};
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 int retries = 0;
private double basalTotal = 0;
private Map<Integer,Double> rate = new HashMap<>();
private void tick()
{
switch (state)
{
case BEFORE:
if(scripter.currentMenu!=null && scripter.currentMenu.getType()==MenuType.MAIN_MENU)
{
updateState(MAIN,120);
lastMenu = MenuType.MAIN_MENU;
log.debug("found MAIN_MENU -> state:MAIN");
retries=3;
}
break;
case MAIN:
if(retries>0)
if(scripter.goToMainMenuScreen(MenuType.BASAL_1_MENU,30000))
{
if(scripter.enterMenu(MenuType.BASAL_1_MENU,MenuType.BASAL_TOTAL, RuffyScripter.Key.CHECK,2000));
{
updateState(BASAL_OVERVIEW, 30);
retries=0;
}
}
else
retries--;
else
updateState(ERROR,30);
break;
case BASAL_OVERVIEW:
if(scripter.currentMenu.getType()==MenuType.BASAL_TOTAL && scripter.currentMenu.getAttribute(MenuAttribute.BASAL_TOTAL)!=null && (Integer)scripter.currentMenu.getAttribute(MenuAttribute.BASAL_SELECTED)==1)
{
basalTotal = (Double)scripter.currentMenu.getAttribute(MenuAttribute.BASAL_TOTAL);
if(scripter.enterMenu(MenuType.BASAL_TOTAL,MenuType.BASAL_SET, RuffyScripter.Key.MENU,3000))
{
updateState(READ_BASAL,30);
retries = 96;
}
}
break;
case READ_BASAL:
if(scripter.currentMenu.getType()==MenuType.BASAL_SET && scripter.currentMenu.getAttribute(MenuAttribute.BASAL_START)!=null) {
Object rateObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE);
MenuTime time = (MenuTime) scripter.currentMenu.getAttribute(MenuAttribute.BASAL_START);
if (rateObj instanceof Double) {
rate.put(time.getHour(), (Double) rateObj);
}
boolean complete = true;
for (int t = 0; t < 24; t++) {
if (rate.get(t) == null)
complete = false;
}
if (retries > 0) {
if (complete) {
scripter.pressBackKey();
updateState(AFTER, 30);
} else {
retries--;
scripter.pressMenuKey();
scripter.waitScreen(250);
}
} else {
updateState(ERROR, 30);
}
}
break;
case ERROR:
case AFTER:
scripter.goToMainMenuScreen(MenuType.MAIN_MENU,2000);
synchronized(GetBasalCommand.this) {
GetBasalCommand.this.notify();
}
break;
}
}
@Override
public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) {
state = BEFORE;
this.scripter = scripter;
updateState(BEFORE,120);
timeoutThread.start();
try {
synchronized (this) {
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
return new CommandResult().success(false).message("failed to wait: "+e.getMessage());
}
if(state==AFTER)
{
for(int i = 0; i < 24;i++)
{
Log.v("BASAL_RATE","BASAL_RATE from "+String.format("%02d",i)+":00 = "+rate.get(i));
}
return new CommandResult().success(true).enacted(true).message("Basal Rate was read");
}
return new CommandResult().success(false).message("failed with state: "+state+" from: "+lastState);
}
@Override
public String toString() {
return "GetTbrCommand{}";
}
}

View file

@ -1,7 +1,5 @@
package de.jotomo.ruffyscripter.commands; 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.MenuAttribute;
import org.monkey.d.ruffy.ruffy.driver.display.MenuType; import org.monkey.d.ruffy.ruffy.driver.display.MenuType;
import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime; import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime;
@ -15,11 +13,22 @@ import java.util.Locale;
import de.jotomo.ruffyscripter.PumpState; import de.jotomo.ruffyscripter.PumpState;
import de.jotomo.ruffyscripter.RuffyScripter; 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;
import static de.jotomo.ruffyscripter.commands.SetTbrCommand.State.TIME;
public class SetTbrCommand implements Command { public class SetTbrCommand implements Command {
private static final Logger log = LoggerFactory.getLogger(SetTbrCommand.class); private static final Logger log = LoggerFactory.getLogger(SetTbrCommand.class);
private final long percentage; private final long percentage;
private final long duration; private final long duration;
private RuffyScripter scripter;
public SetTbrCommand(long percentage, long duration) { public SetTbrCommand(long percentage, long duration) {
this.percentage = percentage; this.percentage = percentage;
@ -53,244 +62,234 @@ public class SetTbrCommand implements Command {
return violations; return violations;
} }
enum State {
BEFORE,
MAIN,
TBR,
SET_TBR,
TIME,
SET_TIME,
SET,
AFTER,
ERROR
};
private State lastState,state;
private long last;
private long timeout;
private Thread timeoutThread = new Thread()
{
@Override @Override
public CommandResult execute(RuffyScripter scripter, PumpState initialPumpState) { 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 { try {
enterTbrMenu(scripter); Thread.sleep(100);
inputTbrPercentage(scripter); } catch (InterruptedException e) {
verifyDisplayedTbrPercentage(scripter); e.printStackTrace();
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();
} }
} }
tick();
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) { private void updateState(State newState,long timeoutSec)
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); {
long currentPercent = readDisplayedTbrPercentage(scripter); lastState = state;
log.debug("Current TBR %: " + currentPercent); state = newState;
long percentageChange = percentage - currentPercent; last = System.currentTimeMillis();
long percentageSteps = percentageChange / 10; timeout = timeoutSec*1000;
boolean increasePercentage = true;
if (percentageSteps < 0) {
increasePercentage = false;
percentageSteps = Math.abs(percentageSteps);
} }
log.debug("Pressing " + (increasePercentage ? "up" : "down") + " " + percentageSteps + " times"); private MenuType lastMenu;
for (int i = 0; i < percentageSteps; i++) { private int retries = 0;
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); private void tick()
if (increasePercentage) scripter.pressUpKey(); {
else scripter.pressDownKey(); switch (state)
SystemClock.sleep(100); {
log.debug("Push #" + (i + 1)); case BEFORE:
if(scripter.currentMenu!=null && scripter.currentMenu.getType()==MenuType.MAIN_MENU)
{
updateState(MAIN,120);
lastMenu = MenuType.MAIN_MENU;
log.debug("found MAIN_MENU -> state:MAIN");
retries=3;
} }
// Give the pump time to finish any scrolling that might still be going on, can take break;
// up to 1100ms. Plus some extra time to be sure case MAIN:
SystemClock.sleep(2000); if(retries>0)
if(scripter.goToMainMenuScreen(MenuType.TBR_MENU,30000))
{
updateState(TBR,30);
retries=0;
} }
else
private void verifyDisplayedTbrPercentage(RuffyScripter scripter) { retries--;
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET); else
long displayedPercentage = readDisplayedTbrPercentage(scripter); updateState(ERROR,30);
if (displayedPercentage != percentage) { break;
log.debug("Final displayed TBR percentage: " + displayedPercentage); case TBR:
throw new CommandException().message("Failed to set TBR percentage"); if(scripter.enterMenu(MenuType.TBR_MENU,MenuType.TBR_SET, RuffyScripter.Key.CHECK,20000))
{
updateState(SET_TBR,60);
retries = 10;
} }
else
// check again to ensure the displayed value hasn't change due to due scrolling taking extremely long updateState(ERROR,30);
SystemClock.sleep(2000); break;
long refreshedDisplayedTbrPecentage = readDisplayedTbrPercentage(scripter); case SET_TBR:
if (displayedPercentage != refreshedDisplayedTbrPecentage) { if(scripter.currentMenu!=null && scripter.currentMenu.getType()==MenuType.TBR_SET)
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); Object percentageObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE);
// this as a bit hacky, the display value is blinking, so we might catch that, so if(percentageObj != null && percentageObj instanceof Double)
// keep trying till we get the Double we want {
while (!(percentageObj instanceof Double)) { double currentPercentage = ((Double) percentageObj).doubleValue();
scripter.waitForMenuUpdate();
percentageObj = scripter.currentMenu.getAttribute(MenuAttribute.BASAL_RATE);
}
return ((Double) percentageObj).longValue();
}
private void inputTbrDuration(RuffyScripter scripter) { if(currentPercentage!=percentage)
scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); {
long currentDuration = readDisplayedTbrDuration(scripter); if(retries>0) {
if (currentDuration % 15 != 0) { retries--;
// The duration displayed is how long an active TBR will still run, int steps = (int) ((percentage - currentPercentage) / 10.0);
// which might be something like 0:13, hence not in 15 minute steps. scripter.step(steps,(steps<0? RuffyScripter.Key.DOWN: RuffyScripter.Key.UP), 3000);
// 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); else
long durationChange = duration - currentDuration; updateState(ERROR,30);
long durationSteps = durationChange / 15;
boolean increaseDuration = true;
if (durationSteps < 0) {
increaseDuration = false;
durationSteps = Math.abs(durationSteps);
} }
log.debug("Pressing " + (increaseDuration ? "up" : "down") + " " + durationSteps + " times"); else
for (int i = 0; i < durationSteps; i++) { {
scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); if(percentage==100)
if (increaseDuration) scripter.pressUpKey(); {
else scripter.pressDownKey(); scripter.pressCheckKey();
SystemClock.sleep(100); updateState(SET,30);
log.debug("Push #" + (i + 1));
} }
// Give the pump time to finish any scrolling that might still be going on, can take else {
// up to 1100ms. Plus some extra time to be sure updateState(TIME, 30);
SystemClock.sleep(2000);
} }
retries=10;
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) { else
// TODO v2 add timeout? Currently the command execution timeout would trigger if exceeded {
scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION); updateState(ERROR,30);
}
break;
case TIME:
if((scripter.currentMenu!=null && scripter.currentMenu.getType()==MenuType.TBR_DURATION) || scripter.enterMenu(MenuType.TBR_SET,MenuType.TBR_DURATION, RuffyScripter.Key.MENU,20000))
{
updateState(SET_TIME,60);
retries = 10;
}
else if(retries==0)
updateState(ERROR,30);
else retries--;
break;
case SET_TIME:
if(scripter.currentMenu!=null && scripter.currentMenu.getType()==MenuType.TBR_DURATION)
{
Object durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); Object durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME);
// this as a bit hacky, the display value is blinking, so we might catch that, so if(durationObj != null && durationObj instanceof MenuTime)
// keep trying till we get the Double we want {
while (!(durationObj instanceof MenuTime)) { MenuTime time = (MenuTime) durationObj;
scripter.waitForMenuUpdate(); double currentDuration = (time.getHour()*60)+time.getMinute();
durationObj = scripter.currentMenu.getAttribute(MenuAttribute.RUNTIME); if(currentDuration!=duration)
{
if(retries>0) {
retries--;
int steps = (int)((currentDuration - duration)/15.0);
if(currentDuration+(steps*15)<duration)
steps++;
else if(currentDuration+(steps*15)>duration)
steps--;
scripter.step(steps,(steps>0? RuffyScripter.Key.UP: RuffyScripter.Key.DOWN), 3000);
} }
MenuTime duration = (MenuTime) durationObj; else
return duration.getHour() * 60 + duration.getMinute(); updateState(ERROR,30);
} }
else {
private void cancelTbrAndConfirmCancellationWarning(RuffyScripter scripter) {
// confirm entered TBR
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
scripter.pressCheckKey(); scripter.pressCheckKey();
updateState(SET, 30);
// 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); }
else
{
updateState(ERROR,60);
}
break;
case SET:
if(scripter.currentMenu!=null && scripter.currentMenu.getType()==MenuType.WARNING_OR_ERROR)
{
lastMenu = scripter.currentMenu.getType();
scripter.pressCheckKey(); scripter.pressCheckKey();
// dismiss "TBR CANCELLED" alert updateState(SET, 30);
scripter.verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
scripter.pressCheckKey();
scripter.waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR);
alertProcessed = true;
} }
SystemClock.sleep(10); else if(scripter.currentMenu!=null && 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 {
private void verifyMainMenuShowsNoActiveTbr(RuffyScripter scripter) { double mmTbrPercentage = (Double) setPercentage;
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); MenuTime mmTbrDuration = (MenuTime) setDuration;
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 // ... and be the same as what we set
// note that displayed duration might have already counted down, e.g. from 30 minutes to // 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 // 29 minutes and 59 seconds, so that 29 minutes are displayed
int mmTbrDurationInMinutes = mmTbrDuration.getHour() * 60 + mmTbrDuration.getMinute(); int mmTbrDurationInMinutes = mmTbrDuration.getHour() * 60 + mmTbrDuration.getMinute();
if (mmTbrPercentage != percentage || (mmTbrDurationInMinutes != duration && mmTbrDurationInMinutes + 1 != duration)) { if (mmTbrPercentage == percentage && mmTbrDurationInMinutes <= duration) {
throw new CommandException().message("Setting TBR failed, TBR in MAIN_MENU differs from expected"); 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) {
state = BEFORE;
this.scripter = scripter;
updateState(BEFORE,120);
timeoutThread.start();
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");
return new CommandResult().success(true).enacted(true).message(
String.format(Locale.US, "TBR set to %d%% for %d min", percentage, duration));
}
return new CommandResult().success(false).message("failed with state: "+state+" from: "+lastState);
}
@Override @Override
public String toString() { public String toString() {

View file

@ -85,6 +85,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
MainApp.bus().register(this); MainApp.bus().register(this);
bindRuffyService(); bindRuffyService();
startAlerter(); startAlerter();
ruffyScripter = new RuffyScripter();
} }
private void definePumpCapabilities() { private void definePumpCapabilities() {
@ -160,6 +161,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
// TODO would it be useful to have a 'last error' field in the ui showing the most recent // TODO would it be useful to have a 'last error' field in the ui showing the most recent
// failed command? the next command that runs successful with will override this error // failed command? the next command that runs successful with will override this error
log.warn("Pump still in error state, but alarm raised recently, so not triggering again: " + localLastCmdResult.message); log.warn("Pump still in error state, but alarm raised recently, so not triggering again: " + localLastCmdResult.message);
refreshDataFromPump("from Error Recovery");
} }
} }
SystemClock.sleep(5 * 1000); SystemClock.sleep(5 * 1000);
@ -168,7 +170,8 @@ public class ComboPlugin implements PluginBase, PumpInterface {
}, "combo-alerter").start(); }, "combo-alerter").start();
} }
private void bindRuffyService() { private boolean bindRuffyService() {
Context context = MainApp.instance().getApplicationContext(); Context context = MainApp.instance().getApplicationContext();
boolean boundSucceeded = false; boolean boundSucceeded = false;
@ -181,6 +184,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
// full path to the driver // full path to the driver
// in the logs this service is mentioned as (note the slash) // in the logs this service is mentioned as (note the slash)
// "org.monkey.d.ruffy.ruffy/.driver.Ruffy" // "org.monkey.d.ruffy.ruffy/.driver.Ruffy"
//org.monkey.d.ruffy.ruffy is the base package identifier and /.driver.Ruffy the service within the package
"org.monkey.d.ruffy.ruffy.driver.Ruffy" "org.monkey.d.ruffy.ruffy.driver.Ruffy"
)); ));
context.startService(intent); context.startService(intent);
@ -189,16 +193,22 @@ public class ComboPlugin implements PluginBase, PumpInterface {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
ruffyScripter = new RuffyScripter(IRuffyService.Stub.asInterface(service)); keepUnbound=false;
ruffyScripter.start(); ruffyScripter.start(IRuffyService.Stub.asInterface(service));
log.debug("ruffy serivce connected"); log.debug("ruffy serivce connected");
} }
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
ruffyScripter.stop(); ruffyScripter.stop();
ruffyScripter = null;
log.debug("ruffy service disconnected"); log.debug("ruffy service disconnected");
if(!keepUnbound) {
try {
Thread.sleep(250);
} catch (Exception e) {
}
bindRuffyService();
}
} }
}; };
boundSucceeded = context.bindService(intent, mRuffyServiceConnection, Context.BIND_AUTO_CREATE); boundSucceeded = context.bindService(intent, mRuffyServiceConnection, Context.BIND_AUTO_CREATE);
@ -209,9 +219,13 @@ public class ComboPlugin implements PluginBase, PumpInterface {
if (!boundSucceeded) { if (!boundSucceeded) {
statusSummary = "No connection to ruffy. Pump control not available."; statusSummary = "No connection to ruffy. Pump control not available.";
} }
return true;
} }
private boolean keepUnbound = false;
private void unbindRuffyService() { private void unbindRuffyService() {
keepUnbound = true;
ruffyScripter.unbind();
MainApp.instance().getApplicationContext().unbindService(mRuffyServiceConnection); MainApp.instance().getApplicationContext().unbindService(mRuffyServiceConnection);
} }
@ -417,6 +431,12 @@ public class ComboPlugin implements PluginBase, PumpInterface {
try { try {
unbindRuffyService(); unbindRuffyService();
SystemClock.sleep(5000); SystemClock.sleep(5000);
} catch (Exception e) {
/*String msg = "No connection to ruffy. Pump control not available.";
statusSummary = msg;
return new CommandResult().message(msg);*/
}
try {
bindRuffyService(); bindRuffyService();
SystemClock.sleep(5000); SystemClock.sleep(5000);
} catch (Exception e) { } catch (Exception e) {
@ -530,20 +550,51 @@ public class ComboPlugin implements PluginBase, PumpInterface {
@Override @Override
public PumpEnactResult cancelTempBasal() { public PumpEnactResult cancelTempBasal() {
log.debug("cancelTempBasal called"); log.debug("cancelTempBasal called");
CommandResult commandResult = runCommand(new CancelTbrCommand());
CommandResult commandResult = null;
TemporaryBasal tempBasal = null;
PumpEnactResult pumpEnactResult = new PumpEnactResult();
final TemporaryBasal activeTemp = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis());
if (activeTemp == null) {
log.debug("cancelTempBasal really cancelling tbr.");
commandResult = runCommand(new CancelTbrCommand());
if (commandResult.enacted) { if (commandResult.enacted) {
TemporaryBasal tempStop = new TemporaryBasal(commandResult.completionTime); tempBasal = new TemporaryBasal(commandResult.completionTime);
tempStop.durationInMinutes = 0; // ending temp basal tempBasal.durationInMinutes = 0;
tempStop.source = Source.USER; tempBasal.source = Source.USER;
ConfigBuilderPlugin treatmentsInterface = MainApp.getConfigBuilder(); pumpEnactResult.isTempCancel = true;
treatmentsInterface.addToHistoryTempBasal(tempStop); }
} else if ((activeTemp.percentRate >= 90 && activeTemp.percentRate <= 110) && activeTemp.getPlannedRemainingMinutes() <= 15 ) {
// do nothing.
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.";
pumpEnactResult.enacted = true; // all fine though...
pumpEnactResult.success = true; // just roll with it...
} else {
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) {
tempBasal = new TemporaryBasal(commandResult.completionTime);
tempBasal.durationInMinutes = 15;
tempBasal.source = Source.USER;
tempBasal.percentRate = (int) percentage;
tempBasal.isAbsolute = false;
}
} }
PumpEnactResult pumpEnactResult = new PumpEnactResult(); if (tempBasal != null) {
ConfigBuilderPlugin treatmentsInterface = MainApp.getConfigBuilder();
treatmentsInterface.addToHistoryTempBasal(tempBasal);
}
if (commandResult != null) {
pumpEnactResult.success = commandResult.success; pumpEnactResult.success = commandResult.success;
pumpEnactResult.enacted = commandResult.enacted; pumpEnactResult.enacted = commandResult.enacted;
pumpEnactResult.comment = commandResult.message; pumpEnactResult.comment = commandResult.message;
pumpEnactResult.isTempCancel = true; }
return pumpEnactResult; return pumpEnactResult;
} }

View file

@ -35,6 +35,7 @@ public class Menu implements Parcelable{
String clas = in.readString(); String clas = in.readString();
String value = in.readString(); String value = in.readString();
if(attr!=null && clas!=null && value!=null) {
MenuAttribute a = MenuAttribute.valueOf(attr); MenuAttribute a = MenuAttribute.valueOf(attr);
Object o = null; Object o = null;
if (Integer.class.toString().equals(clas)) { if (Integer.class.toString().equals(clas)) {
@ -60,6 +61,7 @@ public class Menu implements Parcelable{
} else { } else {
Log.e("MenuIn", "failed to parse: " + attr + " / " + clas + " / " + value); Log.e("MenuIn", "failed to parse: " + attr + " / " + clas + " / " + value);
} }
}
}catch(Exception e) }catch(Exception e)
{ {
Log.e("MenuIn","Exception in read",e); Log.e("MenuIn","Exception in read",e);

View file

@ -5,38 +5,47 @@ package org.monkey.d.ruffy.ruffy.driver.display;
*/ */
public enum MenuType { public enum MenuType {
MAIN_MENU, MAIN_MENU(true),
STOP_MENU, STOP_MENU(true),
BOLUS_MENU, BOLUS_MENU(true),
BOLUS_ENTER, BOLUS_ENTER(false),
EXTENDED_BOLUS_MENU, EXTENDED_BOLUS_MENU(true),
BOLUS_DURATION, BOLUS_DURATION(false),
MULTIWAVE_BOLUS_MENU, MULTIWAVE_BOLUS_MENU(true),
IMMEDIATE_BOLUS, IMMEDIATE_BOLUS(false),
TBR_MENU, TBR_MENU(true),
MY_DATA_MENU, MY_DATA_MENU(true),
BASAL_MENU, BASAL_MENU(true),
BASAL_1_MENU, BASAL_1_MENU(true),
BASAL_2_MENU, BASAL_2_MENU(true),
BASAL_3_MENU, BASAL_3_MENU(true),
BASAL_4_MENU, BASAL_4_MENU(true),
BASAL_5_MENU, BASAL_5_MENU(true),
DATE_AND_TIME_MENU, DATE_AND_TIME_MENU(true),
ALARM_MENU, ALARM_MENU(true),
MENU_SETTINGS_MENU, MENU_SETTINGS_MENU(true),
BLUETOOTH_MENU, BLUETOOTH_MENU(true),
THERAPY_MENU, THERAPY_MENU(true),
PUMP_MENU, PUMP_MENU(true),
QUICK_INFO, QUICK_INFO(false),
BOLUS_DATA, BOLUS_DATA(false),
DAILY_DATA, DAILY_DATA(false),
TBR_DATA, TBR_DATA(false),
ERROR_DATA, ERROR_DATA(false),
TBR_SET, TBR_SET(false),
TBR_DURATION, TBR_DURATION(false),
STOP, STOP(false),
START_MENU, START_MENU(false),
BASAL_TOTAL, BASAL_TOTAL(false),
BASAL_SET, BASAL_SET(false),
WARNING_OR_ERROR, WARNING_OR_ERROR(false),;
private boolean maintype = false;
MenuType(boolean b) {
maintype=b;
}
public boolean isMaintype() {
return maintype;
}
} }