From fc60edc15ab25e8aebc176084f7e58d310bfe545 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sat, 21 Oct 2017 17:44:16 +0200 Subject: [PATCH] wip --- .../plugins/PumpCombo/ComboPlugin.java | 52 +++++-- .../de/jotomo/ruffy/spi/RuffyCommands.java | 2 + .../jotomo/ruffy/spi/history/PumpHistory.java | 22 +-- .../jotomo/ruffyscripter/RuffyScripter.java | 144 ++++++++++++++---- .../d/ruffy/ruffy/driver/BTConnection.java | 5 + 5 files changed, 168 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java index 2921f16b06..a64f51e2ad 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java @@ -161,7 +161,6 @@ public class ComboPlugin implements PluginBase, PumpInterface { // 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 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); @@ -265,9 +264,8 @@ public class ComboPlugin implements PluginBase, PumpInterface { return lastCmdResult != null ? new Date(lastCmdResult.completionTime) : new Date(0); } - @Override - public void initialize() { - CommandResult commandResult = runCommand("Syncing pump state", new CommandExecution() { + private void initializePump() { + CommandResult commandResult = runCommand("Checking pump history", false, new CommandExecution() { @Override public CommandResult execute() { return ruffyScripter.readHistory( @@ -279,6 +277,12 @@ public class ComboPlugin implements PluginBase, PumpInterface { } }); + if (!commandResult.success || commandResult.history == null) { + // TODO error case, command + return; + } + + // TODO opt, construct PumpHistoryRequest to requset only what needs updating boolean syncNeeded = false; // last bolus @@ -340,7 +344,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { // TODO check this is eithor called regularly even with other commansd being fired; if not, // request this periodically @Override - public void refreshDataFromPump(String reason) { + public synchronized void refreshDataFromPump(String reason) { log.debug("RefreshDataFromPump called"); // if Android is sluggish this might get called before ruffy is bound @@ -356,13 +360,17 @@ public class ComboPlugin implements PluginBase, PumpInterface { // if (notAUserRequest && wasRunAtLeastOnce && ranWithinTheLastMinute) { // log.debug("Not fetching state from pump, since we did already within the last 60 seconds"); // } else { - runCommand("Refreshing", new CommandExecution() { - @Override - public CommandResult execute() { - return ruffyScripter.readHistory(new PumpHistoryRequest().reservoirLevel(true).bolusHistory(PumpHistoryRequest.LAST)); - } - }); -// } + + if (pump.lastCmdResult == null) { + initializePump(); + } else { + runCommand("Refreshing", new CommandExecution() { + @Override + public CommandResult execute() { + return ruffyScripter.readHistory(new PumpHistoryRequest().reservoirLevel(true).bolusHistory(PumpHistoryRequest.LAST)); + } + }); + } } // TODO uses profile values for the time being @@ -637,6 +645,11 @@ public class ComboPlugin implements PluginBase, PumpInterface { } private CommandResult runCommand(String status, CommandExecution commandExecution) { + return runCommand(status, true, commandExecution); + + } + + private CommandResult runCommand(String status, boolean checkTbrMisMatch, CommandExecution commandExecution) { MainApp.bus().post(new EventComboPumpUpdateGUI(status)); CommandResult commandResult = commandExecution.execute(); @@ -654,7 +667,9 @@ public class ComboPlugin implements PluginBase, PumpInterface { pump.state = commandResult.state; // TODO are there cases when this check should NOT be performed? perform this explicitly or have a flag to skip this? - checkForTbrMismatch(); + if (checkTbrMisMatch) { + checkForTbrMismatch(); + } // TODO not propely set all the time ... @@ -685,6 +700,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { // TODO check if this works with pump suspend, esp. around pump suspend there'll be syncing to do; TemporaryBasal aapsTbr = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()); +// if (true) { +// +// // not yet +// } else if (aapsTbr == null && pump.state.tbrActive) { // pump runs TBR AAPS is unaware off // => fetch full history so the full TBR is added to treatments @@ -723,7 +742,12 @@ public class ComboPlugin implements PluginBase, PumpInterface { } private void runFullSync() { - CommandResult commandResult = runCommand("Syncing full pump history", new CommandExecution() { + // TODO separate fetching and comparing + if (1 == 1 ) { + log.error("Skipping full sync - not implemented yet"); + return; + } + CommandResult commandResult = runCommand("Syncing full pump history", false, new CommandExecution() { @Override public CommandResult execute() { return ruffyScripter.readHistory( diff --git a/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/RuffyCommands.java b/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/RuffyCommands.java index 1adf301228..9ec283af1d 100644 --- a/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/RuffyCommands.java +++ b/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/RuffyCommands.java @@ -16,6 +16,8 @@ public interface RuffyCommands { /** Confirms an active alarm on the pump. The state returned is the state after the alarm * has been confirmed. The message field contains the displayed error message that was * confirmed. */ + // TODO multiple alarms can occur -> empty battery, stops pump -> tbr cancelled + // return them as history.errors? CommandResult takeOverAlarm(); boolean isPumpAvailable(); diff --git a/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistory.java b/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistory.java index 2b0768f319..82b7112517 100644 --- a/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistory.java +++ b/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistory.java @@ -38,19 +38,19 @@ public class PumpHistory { return this; } - @Override - public String toString() { - return "PumpHistory{" + - "reservoirLevel=" + reservoirLevel + - ", bolusHistory=" + bolusHistory + - ", tbrHistory=" + tbrHistory + - ", errorHistory=" + errorHistory + - ", tddHistory=" + tddHistory + - '}'; - } - public PumpHistory tddHistory(List tddHistory) { this.tddHistory = tddHistory; return this; } + + @Override + public String toString() { + return "PumpHistory{" + + "reservoirLevel=" + reservoirLevel + + ", bolusHistory=" + bolusHistory.size() + + ", tbrHistory=" + tbrHistory.size() + + ", errorHistory=" + errorHistory.size() + + ", tddHistory=" + tddHistory.size() + + '}'; + } } diff --git a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java index 90c904c81c..e7a2a6a576 100644 --- a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java +++ b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java @@ -61,6 +61,7 @@ public class RuffyScripter implements RuffyCommands { private volatile long lastCmdExecutionTime; private volatile Command activeCmd = null; + private volatile int retries = 0; private boolean started = false; @@ -221,14 +222,14 @@ public class RuffyScripter implements RuffyCommands { public void returnToRootMenu() { // returning to main menu using the 'back' key does not cause a vibration while (getCurrentMenu().getType() != MenuType.MAIN_MENU && getCurrentMenu().getType() != MenuType.STOP) { - if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { - String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); - confirmAlert(errorMsg, 1000); - // TODO this isn't gonna work out ... this method can't know if something was enacted ... - // gotta keep that state in the command instance - throw new CommandException().success(false).enacted(false) - .message("Warning/error " + errorMsg + " raised while returning to main menu"); - } +// if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { +// String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); +// confirmAlert(errorMsg, 1000); +// // TODO this isn't gonna work out ... this method can't know if something was enacted ... +// // gotta keep that state in the command instance +// throw new CommandException().success(false).enacted(false) +// .message("Warning/error " + errorMsg + " raised while returning to main menu"); +// } log.debug("Going back to main menu, currently at " + getCurrentMenu().getType()); pressBackKey(); waitForMenuUpdate(); @@ -256,12 +257,12 @@ public class RuffyScripter implements RuffyCommands { synchronized (RuffyScripter.class) { try { activeCmd = cmd; + retries = 3; long connectStart = System.currentTimeMillis(); ensureConnected(); final RuffyScripter scripter = this; final Returnable returnable = new Returnable(); - Thread cmdThread = new Thread(new Runnable() { - @Override + class CommandRunner { public void run() { try { // check if pump is an an error state @@ -315,6 +316,12 @@ public class RuffyScripter implements RuffyCommands { lastCmdExecutionTime = System.currentTimeMillis(); } } + } + Thread cmdThread = new Thread(new Runnable() { + @Override + public void run() { + new CommandRunner().run(); + } }, cmd.toString()); long executionStart = System.currentTimeMillis(); cmdThread.start(); @@ -323,6 +330,7 @@ public class RuffyScripter implements RuffyCommands { // (to fail before the next loop iteration issues the next command) long dynamicTimeout = System.currentTimeMillis() + 90 * 1000; long overallTimeout = System.currentTimeMillis() + 4 * 60 * 1000; + int retries = 3; while (cmdThread.isAlive()) { log.trace("Waiting for running command to complete"); SystemClock.sleep(500); @@ -341,6 +349,22 @@ public class RuffyScripter implements RuffyCommands { 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) { String msg = "Command " + cmd + " timed out after 4 min, check pump!"; log.error(msg); @@ -358,10 +382,21 @@ public class RuffyScripter implements RuffyCommands { log.debug("Command result: " + returnable.cmdResult); return returnable.cmdResult; } catch (CommandException e) { - return e.toCommandResult(); + CommandResult commandResult = e.toCommandResult(); + if (commandResult.state == null) commandResult.state = readPumpStateInternal(); + return commandResult; } catch (Exception e) { + // TODO catching E here AND in CommandRunner? // TODO detect and report pump warnings/errors differently? log.error("Error in ruffyscripter/ruffy", e); + try { + return new CommandResult() + .exception(e) + .message("Unexpected exception communication with ruffy: " + e.getMessage()) + .state(readPumpStateInternal()); + } catch (Exception e1) { + // nothing more we can try + } return new CommandResult().exception(e).message("Unexpected exception communication with ruffy: " + e.getMessage()); } finally { activeCmd = null; @@ -369,6 +404,37 @@ public class RuffyScripter implements RuffyCommands { } } + /** On connection lost the pump raises an error immediately (when setting a TBR or giving a bolus), + * there's no timeout. But: a reconnect is still possible which can then confirm the alarm and + * foward it to an app.*/ + public void reconnect() { +// try { + log.debug("Connection was lost, trying to reconnect"); + ensureConnected(); + if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { + String message = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); + if (activeCmd instanceof BolusCommand && message.equals("BOLUS CANCELLED") + || (activeCmd instanceof CancelTbrCommand || activeCmd instanceof SetTbrCommand) + && message.equals("TBR CANCELLED")) { + // confirm alert + verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); + pressCheckKey(); + // dismiss alert + verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR); + pressCheckKey(); + waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR); + // TODO multiple alarms can be raised, e.g. if pump enters STOP mode due + // to battery_empty, that alert is raised and then causes a TBR CANCELLED + // if one was running + // TODO report those errors back! + // ... need a more controlled way to 'assemble' return data + // like adding a CommandResult field to a command and merge stuff into it? + } + + } + returnToRootMenu(); + } + /** * If there's an issue, this times out eventually and throws a CommandException */ @@ -405,7 +471,23 @@ public class RuffyScripter implements RuffyCommands { // 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 // connection comes up. - waitForMenuUpdate(90, "Timeout connecting to pump"); +// waitForMenuUpdate(90, "Timeout connecting to pump"); + long timeoutExpired = System.currentTimeMillis() + 90 * 1000; + long initialUpdateTime = menuLastUpdated; + long again = System.currentTimeMillis() + 30 * 1000; + while (initialUpdateTime == menuLastUpdated) { + if (System.currentTimeMillis() > timeoutExpired) { + throw new CommandException().message("Timeout connecting to pump"); + } + SystemClock.sleep(50); + if (again < System.currentTimeMillis()) { + // TODO test + ruffyService.doRTDisconnect(); + SystemClock.sleep(2000); + ruffyService.doRTConnect(); + again = System.currentTimeMillis() + 30 * 1000; + } + } } catch (CommandException e) { throw e; } catch (Exception e) { @@ -527,7 +609,7 @@ public class RuffyScripter implements RuffyCommands { // @Override // public void doStep() { log.debug("Pressing up key"); - pressKey(Key.UP, 2000); + pressKey(Key.UP); log.debug("Releasing up key"); // } // }); @@ -535,25 +617,25 @@ public class RuffyScripter implements RuffyCommands { public void pressDownKey() { log.debug("Pressing down key"); - pressKey(Key.DOWN, 2000); + pressKey(Key.DOWN); log.debug("Releasing down key"); } public void pressCheckKey() { log.debug("Pressing check key"); - pressKey(Key.CHECK, 2000); + pressKey(Key.CHECK); log.debug("Releasing check key"); } public void pressMenuKey() { log.debug("Pressing menu key"); - pressKey(Key.MENU, 2000); + pressKey(Key.MENU); log.debug("Releasing menu key"); } public void pressBackKey() { log.debug("Pressing back key"); - pressKey(Key.BACK, 2000); + pressKey(Key.BACK); log.debug("Releasing back key"); } @@ -646,38 +728,36 @@ public class RuffyScripter implements RuffyCommands { } } - private void pressKey(final byte key, long timeout) { + private void pressKey(final byte key) { try { ruffyService.rtSendKey(key, true); SystemClock.sleep(200); ruffyService.rtSendKey(Key.NO_KEY, true); -// 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"); } } public void navigateToMenu(MenuType desiredMenu) { - MenuType startedFrom = getCurrentMenu().getType(); - boolean movedOnce = false; +// MenuType startedFrom = getCurrentMenu().getType(); +// boolean movedOnce = false; + int retries = 20; while (getCurrentMenu().getType() != desiredMenu) { + retries --; MenuType currentMenuType = getCurrentMenu().getType(); log.debug("Navigating to menu " + desiredMenu + ", current menu: " + currentMenuType); - if (movedOnce && currentMenuType == startedFrom) { +// if (movedOnce && currentMenuType == startedFrom) { +// throw new CommandException().message("Menu not found searching for " + desiredMenu +// + ". Check menu settings on your pump to ensure it's not hidden."); +// } + if (retries == 0) { throw new CommandException().message("Menu not found searching for " + desiredMenu + ". Check menu settings on your pump to ensure it's not hidden."); } pressMenuKey(); - waitForMenuToBeLeft(currentMenuType); - movedOnce = true; +// waitForMenuToBeLeft(currentMenuType); + SystemClock.sleep(200); +// movedOnce = true; } } diff --git a/ruffyscripter/src/main/java/org/monkey/d/ruffy/ruffy/driver/BTConnection.java b/ruffyscripter/src/main/java/org/monkey/d/ruffy/ruffy/driver/BTConnection.java index 08bc14b509..430460d409 100644 --- a/ruffyscripter/src/main/java/org/monkey/d/ruffy/ruffy/driver/BTConnection.java +++ b/ruffyscripter/src/main/java/org/monkey/d/ruffy/ruffy/driver/BTConnection.java @@ -7,6 +7,7 @@ import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Intent; import android.content.IntentFilter; +import android.util.Log; import java.io.IOException; import java.io.InputStream; @@ -94,6 +95,10 @@ public class BTConnection { public void connect(PumpData pumpData, int retries) { +// if (pumpData == null) { +// Log.e("JOE", "pumpdata null 1"); +// return; +// } this.pumpData = pumpData; connect(pumpData.getPumpMac(),retries); }