Work on reconnect & retry logic, incomplete.

This commit is contained in:
Johannes Mockenhaupt 2017-10-25 15:11:01 +02:00
parent e800bd5092
commit 6a01ca1d4d
No known key found for this signature in database
GPG key ID: 9E1EA6AF7BBBB0D1

View file

@ -62,7 +62,6 @@ public class RuffyScripter implements RuffyCommands {
private volatile long lastCmdExecutionTime; private volatile long lastCmdExecutionTime;
private volatile Command activeCmd = null; private volatile Command activeCmd = null;
private volatile int retries = 0;
private boolean started = false; private boolean started = false;
@ -222,7 +221,8 @@ public class RuffyScripter implements RuffyCommands {
public void returnToRootMenu() { public void returnToRootMenu() {
// returning to main menu using the 'back' key does not cause a vibration // returning to main menu using the 'back' key does not cause a vibration
while (getCurrentMenu().getType() != MenuType.MAIN_MENU && getCurrentMenu().getType() != MenuType.STOP) { MenuType menuType = getCurrentMenu().getType();
while (menuType != MenuType.MAIN_MENU && menuType != MenuType.STOP && menuType != MenuType.WARNING_OR_ERROR) {
// if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { // if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
// String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); // String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
// confirmAlert(errorMsg, 1000); // confirmAlert(errorMsg, 1000);
@ -231,9 +231,10 @@ public class RuffyScripter implements RuffyCommands {
// throw new CommandException().success(false).enacted(false) // throw new CommandException().success(false).enacted(false)
// .message("Warning/error " + errorMsg + " raised while returning to main menu"); // .message("Warning/error " + errorMsg + " raised while returning to main menu");
// } // }
log.debug("Going back to main menu, currently at " + getCurrentMenu().getType()); log.debug("Going back to main menu, currently at " + menuType);
pressBackKey(); pressBackKey();
waitForMenuUpdate(); waitForMenuUpdate();
menuType = getCurrentMenu().getType();
} }
} }
@ -258,7 +259,6 @@ public class RuffyScripter implements RuffyCommands {
synchronized (RuffyScripter.class) { synchronized (RuffyScripter.class) {
try { try {
activeCmd = cmd; activeCmd = cmd;
retries = 3;
long connectStart = System.currentTimeMillis(); long connectStart = System.currentTimeMillis();
ensureConnected(); ensureConnected();
final RuffyScripter scripter = this; final RuffyScripter scripter = this;
@ -320,12 +320,32 @@ public class RuffyScripter implements RuffyCommands {
// time out if nothing has been happening for more than 90s or after 4m // time out if nothing has been happening for more than 90s or after 4m
// (to fail before the next loop iteration issues the next command) // (to fail before the next loop iteration issues the next command)
long dynamicTimeout = System.currentTimeMillis() + 90 * 1000; long dynamicTimeout = calculateCmdInactivityTimeout();
long overallTimeout = System.currentTimeMillis() + 4 * 60 * 1000; long overallTimeout = calculateOverallCmdTimeout();
int retries = 3; int maxReconnectAttempts = 3;
while (cmdThread.isAlive()) { while (cmdThread.isAlive()) {
log.trace("Waiting for running command to complete"); log.trace("Waiting for running command to complete");
SystemClock.sleep(500); SystemClock.sleep(500);
if (!ruffyService.isConnected()) {
if (maxReconnectAttempts > 0) {
maxReconnectAttempts--;
cmdThread.interrupt();
reconnect();
// TODO at least for bigger boluses we should check history after reconnect to make sure
// we haven't issued that bolus within the last 1-2m? in case there's a bug in the code ...
cmdThread = new Thread(new Runnable() {
@Override
public void run() {
new CommandRunner().run();
}
}, cmd.toString());
cmdThread.start();
// reset timeouts after reconnect
dynamicTimeout = calculateCmdInactivityTimeout();
overallTimeout = calculateOverallCmdTimeout();
}
}
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now > dynamicTimeout) { if (now > dynamicTimeout) {
boolean menuRecentlyUpdated = now < menuLastUpdated + 5 * 1000; boolean menuRecentlyUpdated = now < menuLastUpdated + 5 * 1000;
@ -341,22 +361,6 @@ public class RuffyScripter implements RuffyCommands {
return new CommandResult().success(false).enacted(false).message("Command stalled, check pump!"); return new CommandResult().success(false).enacted(false).message("Command stalled, check pump!");
} }
} }
if (!ruffyService.isConnected()) {
if (retries > 0) {
retries--;
cmdThread.interrupt();
reconnect();
cmdThread = new Thread(new Runnable() {
@Override
public void run() {
new CommandRunner().run();
}
}, cmd.toString());
cmdThread.start();
dynamicTimeout = System.currentTimeMillis() + 90 * 1000;
overallTimeout = System.currentTimeMillis() + 4 * 60 * 1000;
}
}
if (now > overallTimeout) { if (now > overallTimeout) {
String msg = "Command " + cmd + " timed out after 4 min, check pump!"; String msg = "Command " + cmd + " timed out after 4 min, check pump!";
log.error(msg); log.error(msg);
@ -396,18 +400,29 @@ public class RuffyScripter implements RuffyCommands {
} }
} }
/** On connection lost the pump raises an error immediately (when setting a TBR or giving a bolus), private long calculateCmdInactivityTimeout() {
* there's no timeout. But: a reconnect is still possible which can then confirm the alarm and return System.currentTimeMillis() + 5 * 1000;
* foward it to an app.*/ }
public void reconnect() {
// try { private long calculateOverallCmdTimeout() {
return System.currentTimeMillis() + 3 * 60 * 1000;
}
/**
* On connection lose the pump raises an error immediately (when setting a TBR or giving a bolus) -
* there's no timeout before that happens. But: a reconnect is still possible which can then
* confirm the alarm and, return to the main menu and restart the command safely.
*
* @return whether the reconnect and return to main menu was successful
*/
private boolean reconnect() {
try {
log.debug("Connection was lost, trying to reconnect"); log.debug("Connection was lost, trying to reconnect");
ensureConnected(); ensureConnected();
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
String message = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); String errorMessage = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
if (activeCmd instanceof BolusCommand && message.equals("BOLUS CANCELLED") if (activeCmd.getReconnectAlarm() != null && activeCmd.getReconnectAlarm().equals(errorMessage)) {
|| (activeCmd instanceof CancelTbrCommand || activeCmd instanceof SetTbrCommand) log.debug("Confirming alert caused by disconnect: " + errorMessage);
&& message.equals("TBR CANCELLED")) {
// confirm alert // confirm alert
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
pressCheckKey(); pressCheckKey();
@ -415,16 +430,33 @@ public class RuffyScripter implements RuffyCommands {
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
pressCheckKey(); pressCheckKey();
waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR); waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR);
// TODO multiple alarms can be raised, e.g. if pump enters STOP mode due // it's possible that multiple alarms are raised, that can happen when
// to battery_empty, that alert is raised and then causes a TBR CANCELLED // a battery low/empty/occlusion alert occurs, which then causes a bolus/tbr
// if one was running // cancelled alert.
// TODO report those errors back! // let's not try anything fancy, since it's non trivial to decide which of
// ... need a more controlled way to 'assemble' return data // those cases can be dealt with and it's tricky to test it. Also,
// like adding a CommandResult field to a command and merge stuff into it? // those situations aren't likely to occurs all that often, so let the alarms
// ring and catch up with history later.
// TODO this needs some thought though as how to propagate such errors,
// esp. if a kid is carrying the pump and a supervisor is monitoring and
// controlling the loop.
} }
} }
returnToRootMenu(); } catch (CommandException e) {
// TODO ...
return false;
} catch (Exception e) {
// TODO ...
return false;
}
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
return false;
}
returnToRootMenu();
return getCurrentMenu().getType() == MenuType.MAIN_MENU;
} }
/** /**
@ -536,10 +568,10 @@ public class RuffyScripter implements RuffyCommands {
return state; return state;
} }
// below: methods to be used by commands // below: methods to be used by commands
// TODO move into a new Operations(scripter) class commands can delegate to, // TODO move into a new Operations(scripter) class commands can delegate to,
// 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)
public static class Key { public static class Key {
public static byte NO_KEY = (byte) 0x00; public static byte NO_KEY = (byte) 0x00;
@ -552,6 +584,7 @@ public class RuffyScripter implements RuffyCommands {
interface Step { interface Step {
void run(boolean waitForPumpUpdateAfterwards); void run(boolean waitForPumpUpdateAfterwards);
} }
/* /*
@ -735,7 +768,7 @@ public class RuffyScripter implements RuffyCommands {
// boolean movedOnce = false; // boolean movedOnce = false;
int retries = 20; int retries = 20;
while (getCurrentMenu().getType() != desiredMenu) { while (getCurrentMenu().getType() != desiredMenu) {
retries --; retries--;
MenuType currentMenuType = getCurrentMenu().getType(); MenuType currentMenuType = getCurrentMenu().getType();
log.debug("Navigating to menu " + desiredMenu + ", current menu: " + currentMenuType); log.debug("Navigating to menu " + desiredMenu + ", current menu: " + currentMenuType);
// if (movedOnce && currentMenuType == startedFrom) { // if (movedOnce && currentMenuType == startedFrom) {