Auto-retrying repeatable commands when there's a connection loss.
This commit is contained in:
parent
88f500417d
commit
f64e00fc79
|
@ -229,11 +229,11 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runCommand("Initializing", () -> ruffyScripter.readHistory(new PumpHistoryRequest()));
|
runCommand("Initializing", 3, () -> ruffyScripter.readHistory(new PumpHistoryRequest()));
|
||||||
pump.initialized = true;
|
pump.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
runCommand("Refreshing", ruffyScripter::readReservoirLevelAndLastBolus);
|
runCommand("Refreshing", 3, ruffyScripter::readReservoirLevelAndLastBolus);
|
||||||
|
|
||||||
// TODO fuse the below into 'sync'? or make checkForTbrMismatch jut a trigger to issue a sync if needed; don't run sync twice as is nice
|
// TODO fuse the below into 'sync'? or make checkForTbrMismatch jut a trigger to issue a sync if needed; don't run sync twice as is nice
|
||||||
// checkForTbrMismatch();
|
// checkForTbrMismatch();
|
||||||
|
@ -245,7 +245,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
* full pump history and update AAPS' DB.
|
* full pump history and update AAPS' DB.
|
||||||
*/
|
*/
|
||||||
private void checkPumpHistory() {
|
private void checkPumpHistory() {
|
||||||
CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_checking_history), () ->
|
CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_checking_history), 3, () ->
|
||||||
ruffyScripter.readHistory(
|
ruffyScripter.readHistory(
|
||||||
new PumpHistoryRequest()
|
new PumpHistoryRequest()
|
||||||
.bolusHistory(PumpHistoryRequest.LAST)
|
.bolusHistory(PumpHistoryRequest.LAST)
|
||||||
|
@ -410,6 +410,14 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) {
|
private PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) {
|
||||||
|
// TODO for non-SMB: read resorvoir level first to make sure there's enough insulin left
|
||||||
|
CommandResult reservoirBolusResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_refreshing), 3,
|
||||||
|
ruffyScripter::readReservoirLevelAndLastBolus);
|
||||||
|
if (!reservoirBolusResult.success) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
// List<Bolus> bolusHistory = reservoirBolusResult.history.bolusHistory;
|
||||||
|
// if (bolusHistory)
|
||||||
// TODO
|
// TODO
|
||||||
// before non-SMB: check enough insulin is available, check we're up to date on boluses
|
// before non-SMB: check enough insulin is available, check we're up to date on boluses
|
||||||
// after bolus: update reservoir level and check the bolus we just did is actually there
|
// after bolus: update reservoir level and check the bolus we just did is actually there
|
||||||
|
@ -495,11 +503,13 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
final int finalAdjustedPercent = adjustedPercent;
|
final int finalAdjustedPercent = adjustedPercent;
|
||||||
CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_setting_tbr), () -> ruffyScripter.setTbr(finalAdjustedPercent, durationInMinutes));
|
CommandResult commandResult= runCommand(MainApp.sResources.getString(R.string.combo_pump_action_setting_tbr), 3, () -> ruffyScripter.setTbr(finalAdjustedPercent, durationInMinutes));
|
||||||
|
|
||||||
if (commandResult.enacted) {
|
PumpState state = commandResult.state;
|
||||||
pump.tbrSetTime = System.currentTimeMillis();
|
if (state.tbrActive && state.tbrPercent == percent
|
||||||
TemporaryBasal tempStart = new TemporaryBasal(pump.tbrSetTime);
|
&& (state.tbrRemainingDuration == durationInMinutes || state.tbrRemainingDuration == durationInMinutes - 1)) {
|
||||||
|
pump.tbrSetTime = state.timestamp;
|
||||||
|
TemporaryBasal tempStart = new TemporaryBasal(state.timestamp);
|
||||||
// TODO commandResult.state.tbrRemainingDuration might already display 29 if 30 was set, since 29:59 is shown as 29 ...
|
// TODO commandResult.state.tbrRemainingDuration might already display 29 if 30 was set, since 29:59 is shown as 29 ...
|
||||||
// we should check this, but really ... something must be really screwed up if that number was anything different
|
// we should check this, but really ... something must be really screwed up if that number was anything different
|
||||||
// TODO actually ... might setting 29 help with gaps between TBRs? w/o the hack in TemporaryBasal?
|
// TODO actually ... might setting 29 help with gaps between TBRs? w/o the hack in TemporaryBasal?
|
||||||
|
@ -542,7 +552,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
if (activeTemp == null || userRequested) {
|
if (activeTemp == null || userRequested) {
|
||||||
/* v1 compatibility to sync DB to pump if they diverged (activeTemp == null) */
|
/* v1 compatibility to sync DB to pump if they diverged (activeTemp == null) */
|
||||||
log.debug("cancelTempBasal: hard-cancelling TBR since user requested");
|
log.debug("cancelTempBasal: hard-cancelling TBR since user requested");
|
||||||
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_cancelling_tbr), ruffyScripter::cancelTbr);
|
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_cancelling_tbr), 2, ruffyScripter::cancelTbr);
|
||||||
|
|
||||||
if (commandResult.enacted) {
|
if (commandResult.enacted) {
|
||||||
tempBasal = new TemporaryBasal(System.currentTimeMillis());
|
tempBasal = new TemporaryBasal(System.currentTimeMillis());
|
||||||
|
@ -565,7 +575,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
// on whether the TBR we're cancelling is above or below 100%.
|
// on whether the TBR we're cancelling is above or below 100%.
|
||||||
final int percentage = (activeTemp.percentRate > 100) ? 110 : 90;
|
final int percentage = (activeTemp.percentRate > 100) ? 110 : 90;
|
||||||
log.debug("cancelTempBasal: changing TBR to " + percentage + "% for 15 mins.");
|
log.debug("cancelTempBasal: changing TBR to " + percentage + "% for 15 mins.");
|
||||||
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_setting_tbr), () -> ruffyScripter.setTbr(percentage, 15));
|
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_setting_tbr), 2, () -> ruffyScripter.setTbr(percentage, 15));
|
||||||
|
|
||||||
if (commandResult.enacted) {
|
if (commandResult.enacted) {
|
||||||
tempBasal = new TemporaryBasal(System.currentTimeMillis());
|
tempBasal = new TemporaryBasal(System.currentTimeMillis());
|
||||||
|
@ -634,9 +644,12 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
pump.reservoirLevel = commandResult.reservoirLevel;
|
pump.reservoirLevel = commandResult.reservoirLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandResult.lastBolus != null) {
|
if (!commandResult.success && retries > 0) {
|
||||||
pump.lastBolus = commandResult.lastBolus;
|
for(int retryAttempts = 1; !commandResult.success && retryAttempts <= retries; retryAttempts++) {
|
||||||
}
|
log.debug("Command was not successful, retries request, doing retry #" + retryAttempts);
|
||||||
|
commandResult = commandExecution.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (commandResult.history != null) {
|
if (commandResult.history != null) {
|
||||||
if (!commandResult.history.bolusHistory.isEmpty()) {
|
if (!commandResult.history.bolusHistory.isEmpty()) {
|
||||||
|
@ -734,7 +747,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runFullSync(final PumpHistoryRequest request) {
|
private void runFullSync(final PumpHistoryRequest request) {
|
||||||
CommandResult result = runCommand("Syncing full pump history", () -> ruffyScripter.readHistory(request));
|
CommandResult result = runCommand("Syncing full pump history", 3, () -> ruffyScripter.readHistory(request));
|
||||||
|
|
||||||
// boluses
|
// boluses
|
||||||
|
|
||||||
|
|
|
@ -273,15 +273,22 @@ public class RuffyScripter implements RuffyCommands {
|
||||||
// (to fail before the next loop iteration issues the next command)
|
// (to fail before the next loop iteration issues the next command)
|
||||||
long dynamicTimeout = calculateCmdInactivityTimeout();
|
long dynamicTimeout = calculateCmdInactivityTimeout();
|
||||||
long overallTimeout = calculateOverallCmdTimeout();
|
long overallTimeout = calculateOverallCmdTimeout();
|
||||||
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 (!ruffyService.isConnected()) {
|
||||||
if (maxReconnectAttempts > 0) {
|
// on connection lose try to reconnect, confirm warning alerts caused by
|
||||||
maxReconnectAttempts--;
|
// the disconnected and then return the command as failed (the caller
|
||||||
cmdThread.interrupt();
|
// can retry if needed).
|
||||||
reconnect();
|
cmdThread.interrupt();
|
||||||
|
activeCmd.getResult().success = false;
|
||||||
|
for(int attempts = 4; attempts > 0; attempts--) {
|
||||||
|
boolean reconnected = recoverFromConnectionLoss();
|
||||||
|
if (reconnected) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// try again in 20 sec
|
||||||
|
SystemClock.sleep(20 * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,76 +346,47 @@ public class RuffyScripter implements RuffyCommands {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On connection lose the pump raises an error immediately (when setting a TBR or giving a bolus) -
|
* On connection lose the pump raises an alert 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
|
* there's no timeout before that happens. But: a reconnect is still possible which can then
|
||||||
* confirm the alarm and.
|
* confirm the alert.
|
||||||
*
|
*
|
||||||
* @return whether the reconnect and return to main menu was successful
|
* @return whether the reconnect and return to main menu was successful
|
||||||
*/
|
*/
|
||||||
// TODO only reconnect, confirm the warning the disconnect caused ond then return to ComboPlugin, which shall decide whether/how to restart
|
private boolean recoverFromConnectionLoss() {
|
||||||
// requires turning CommandResult from a return type into a command field
|
log.debug("Connection was lost, trying to reconnect");
|
||||||
|
ensureConnected();
|
||||||
// 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 ...
|
|
||||||
|
|
||||||
// TODO: only do the reconnect to confirm the alert, then return and let the ComboPlugin decide what to do next;
|
|
||||||
// for bolus: how ... run 'step 2': checking/reading history?! step1 being bolus delivery, so have different resume points? ggrrrmpf
|
|
||||||
// less logic in scripter; just reconnect to confirm alert if needed, then return with error;
|
|
||||||
// let CP read history.LAST to see what actually happened and then resume appropriately.
|
|
||||||
|
|
||||||
private boolean reconnect() {
|
|
||||||
try {
|
|
||||||
log.debug("Connection was lost, trying to reconnect");
|
|
||||||
ensureConnected();
|
|
||||||
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
|
||||||
String errorMessage = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
|
||||||
// TODO bolus cancelled is raised BEFORE a bolus is started. if disconnect occurs after
|
|
||||||
// bolus has started (or the user interacts with the pump, the bolus continues.
|
|
||||||
// in that case, reconnecting and restarting the command lets to a DUPLICATE bolus.
|
|
||||||
// add a method restartAllowed(), which accesses a flag bolusDelivering started, which
|
|
||||||
// is set false then?
|
|
||||||
if (activeCmd.getReconnectAlarm() != null && activeCmd.getReconnectAlarm().equals(errorMessage)) {
|
|
||||||
log.debug("Confirming alert caused by disconnect: " + errorMessage);
|
|
||||||
// confirm alert
|
|
||||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
|
||||||
pressCheckKey();
|
|
||||||
// dismiss alert
|
|
||||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
|
||||||
pressCheckKey();
|
|
||||||
waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR);
|
|
||||||
// it's possible that multiple alarms are raised, that can happen when
|
|
||||||
// a battery low/empty/occlusion alert occurs, which then causes a bolus/tbr
|
|
||||||
// cancelled alert.
|
|
||||||
// let's not try anything fancy, since it's non trivial to decide which of
|
|
||||||
// those cases can be dealt with and it's tricky to test it. Also,
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (CommandException e) {
|
|
||||||
// TODO ...
|
|
||||||
return false;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO ...
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||||
return false;
|
WarningOrErrorCode warningOrErrorCode = readWarningOrErrorCode();
|
||||||
|
if (Objects.equals(activeCmd.getReconnectWarningId(), warningOrErrorCode.warningCode)) {
|
||||||
|
log.debug("Confirming warning caused by disconnect: #" + warningOrErrorCode.warningCode);
|
||||||
|
// confirm alert
|
||||||
|
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||||
|
pressCheckKey();
|
||||||
|
// dismiss alert
|
||||||
|
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||||
|
pressCheckKey();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// aren't at main_menu after alert anyways? unless bolus i still going (also main_menu); low cartridge on bolus needs to be handled specially, by bolus command
|
// A bolus cancelled is raised BEFORE a bolus is started. If a disconnect occurs after a
|
||||||
returnToRootMenu();
|
// bolus has started (or the user interacts with the pump) the bolus continues.
|
||||||
|
// If that happened, wait till the pump has finished the bolus, then it can be read from
|
||||||
return getCurrentMenu().getType() == MenuType.MAIN_MENU;
|
// the history as delivered.
|
||||||
|
Double bolusRemaining = (Double) getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
|
||||||
|
try {
|
||||||
|
while (ruffyService.isConnected() && bolusRemaining != null) {
|
||||||
|
waitForScreenUpdate();
|
||||||
|
}
|
||||||
|
boolean connected = ruffyService.isConnected();
|
||||||
|
log.debug("Recovery from connection loss successful:" + connected);
|
||||||
|
return connected;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
log.debug("Recovery from connection loss failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** If there's an issue, this times out eventually and throws a CommandException */
|
||||||
* If there's an issue, this times out eventually and throws a CommandException
|
|
||||||
*/
|
|
||||||
private void ensureConnected() {
|
private void ensureConnected() {
|
||||||
try {
|
try {
|
||||||
if (ruffyService.isConnected()) {
|
if (ruffyService.isConnected()) {
|
||||||
|
@ -479,12 +457,15 @@ public class RuffyScripter implements RuffyCommands {
|
||||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||||
Integer warningCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
Integer warningCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
||||||
Integer errorCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.ERROR);
|
Integer errorCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.ERROR);
|
||||||
while (warningCode == null && errorCode == null) {
|
int retries = 3;
|
||||||
|
while (warningCode == null && errorCode == null && retries > 0) {
|
||||||
waitForScreenUpdate();
|
waitForScreenUpdate();
|
||||||
warningCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
warningCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
||||||
errorCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.ERROR);
|
errorCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.ERROR);
|
||||||
|
retries--;
|
||||||
}
|
}
|
||||||
return new WarningOrErrorCode(warningCode, errorCode);
|
return (warningCode != null || errorCode != null)
|
||||||
|
? new WarningOrErrorCode(warningCode, errorCode) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// below: methods to be used by commands
|
// below: methods to be used by commands
|
||||||
|
@ -772,23 +753,23 @@ public class RuffyScripter implements RuffyCommands {
|
||||||
/**
|
/**
|
||||||
* Confirms and dismisses the given alert if it's raised before the timeout
|
* Confirms and dismisses the given alert if it's raised before the timeout
|
||||||
*/
|
*/
|
||||||
public boolean confirmAlert(int warningCode, int maxWaitMs) {
|
public boolean confirmAlert(@NonNull Integer warningCode, int maxWaitMs) {
|
||||||
long timeout = System.currentTimeMillis() + maxWaitMs;
|
long timeout = System.currentTimeMillis() + maxWaitMs;
|
||||||
while (System.currentTimeMillis() < timeout) {
|
while (System.currentTimeMillis() < timeout) {
|
||||||
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||||
WarningOrErrorCode warningOrErrorCode = readWarningOrErrorCode();
|
WarningOrErrorCode warningOrErrorCode = readWarningOrErrorCode();
|
||||||
if (warningOrErrorCode.errorCode != 0) {
|
if (warningOrErrorCode.errorCode != null) {
|
||||||
// TODO proper way to display such things in the UI;
|
// TODO proper way to display such things in the UI;
|
||||||
throw new CommandException("Pump is in error state");
|
throw new CommandException("Pump is in error state");
|
||||||
}
|
}
|
||||||
int displayedWarningCode = warningOrErrorCode.warningCode;
|
Integer displayedWarningCode = warningOrErrorCode.warningCode;
|
||||||
String errorMsg = null;
|
String errorMsg = null;
|
||||||
try {
|
try {
|
||||||
errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
if (displayedWarningCode != warningCode) {
|
if (!Objects.equals(displayedWarningCode, warningCode)) {
|
||||||
throw new CommandException("An alert other than the expected warning " + warningCode + " was raised by the pump: "
|
throw new CommandException("An alert other than the expected warning " + warningCode + " was raised by the pump: "
|
||||||
+ displayedWarningCode + "(" + errorMsg + "). Please check the pump.");
|
+ displayedWarningCode + "(" + errorMsg + "). Please check the pump.");
|
||||||
}
|
}
|
||||||
|
@ -797,8 +778,12 @@ public class RuffyScripter implements RuffyCommands {
|
||||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||||
pressCheckKey();
|
pressCheckKey();
|
||||||
// dismiss alert
|
// dismiss alert
|
||||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
// if the user has confirmed the alert we have dismissed it with the button press
|
||||||
pressCheckKey();
|
// above already, so only do that if an alert is still displayed
|
||||||
|
waitForScreenUpdate();
|
||||||
|
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||||
|
pressCheckKey();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SystemClock.sleep(10);
|
SystemClock.sleep(10);
|
||||||
|
|
Loading…
Reference in a new issue