From 8ecf6922f7b41d3a89e1420566843538505776a0 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sat, 15 Jul 2017 18:40:01 +0200 Subject: [PATCH] Misc improvements: * Make command execution (RuffyScripter/ComoboPlugin.runCommand) more robust (I still suck at threading). * Return all possible states in PumpState * Add absolute TBR to PumpState * Add NoOpCommand to fetch state data from pump * Display returned pump state in Combo fragment/tab. --- .../jotomo/ruffyscripter/RuffyScripter.java | 100 ++++++++++-------- .../ruffyscripter/commands/NoOpCommand.java | 13 ++- .../ruffyscripter/commands/PumpState.java | 7 ++ .../plugins/PumpCombo/ComboFragment.java | 19 +++- .../plugins/PumpCombo/ComboPlugin.java | 61 +++++++---- .../main/res/layout/combopump_fragment.xml | 8 ++ 6 files changed, 142 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java index f9fe9893ba..7b200221e9 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java @@ -108,17 +108,16 @@ public class RuffyScripter { private volatile Command activeCmd = null; public CommandResult runCommand(final Command cmd) { - try { - if (isPumpBusy()) { - return new CommandResult().message("Pump is busy"); - } - ensureConnected(); + synchronized (this) { + try { + if (isPumpBusy()) { + return new CommandResult().message("Pump is busy"); + } + ensureConnected(); - // TODO reuse thread, scheduler ... - Thread cmdThread; + // TODO reuse thread, scheduler ... + Thread cmdThread; - // TODO make this a safe lock - synchronized (this) { cmdResult = null; activeCmd = cmd; // wait till pump is ready for input @@ -126,7 +125,8 @@ public class RuffyScripter { // check if pump is an an error state if (currentMenu != null && currentMenu.getType() == MenuType.WARNING_OR_ERROR) { try { - return new CommandResult().message("Pump is in an error state: " + currentMenu.getAttribute(MenuAttribute.MESSAGE)); + PumpState pumpState = readPumpState(); + return new CommandResult().message("Pump is in an error state: " + currentMenu.getAttribute(MenuAttribute.MESSAGE)).state(pumpState); } catch (Exception e) { return new CommandResult().message("Pump is in an error state, reading the error state resulted in the attached exception").exception(e); } @@ -147,33 +147,32 @@ public class RuffyScripter { } }); cmdThread.start(); - } - // TODO really? - long timeout = System.currentTimeMillis() + 90 * 1000; - while (activeCmd != null) { - SystemClock.sleep(500); - log.trace("Waiting for running command to complete"); - if (System.currentTimeMillis() > timeout) { - log.error("Running command " + activeCmd + " timed out"); - cmdThread.interrupt(); - activeCmd = null; - cmdResult = null; - return new CommandResult().success(false).enacted(false).message("Command timed out"); + // TODO really? + long timeout = System.currentTimeMillis() + 90 * 1000; + while (activeCmd != null) { + SystemClock.sleep(500); + log.trace("Waiting for running command to complete"); + if (System.currentTimeMillis() > timeout) { + log.error("Running command " + activeCmd + " timed out"); + cmdThread.interrupt(); + activeCmd = null; + return new CommandResult().success(false).enacted(false).message("Command timed out"); + } } - } - if (cmdResult.state == null) { - cmdResult.state = readPumpState(); + if (cmdResult.state == null) { + cmdResult.state = readPumpState(); + } + log.debug("Command result: " + cmdResult); + return cmdResult; + } catch (CommandException e) { + return e.toCommandResult(); + } catch (Exception e) { + return new CommandResult().exception(e).message("Unexpected exception communication with ruffy"); + } finally { + activeCmd = null; } - log.debug("Command result: " + cmdResult); - CommandResult r = cmdResult; - cmdResult = null; - return r; - } catch (CommandException e) { - return e.toCommandResult(); - } catch (Exception e) { - return new CommandResult().exception(e).message("Unexpected exception communication with ruffy"); } } @@ -288,7 +287,7 @@ public class RuffyScripter { if (System.currentTimeMillis() > timeout) { throw new CommandException().message("Timeout waiting for menu " + menuType + " to be left"); } - SystemClock.sleep(50); + SystemClock.sleep(10); } } @@ -312,16 +311,31 @@ public class RuffyScripter { } private PumpState readPumpState() { - verifyMenuIsDisplayed(MenuType.MAIN_MENU); PumpState state = new PumpState(); - Double tbrPercentage = (Double) currentMenu.getAttribute(MenuAttribute.TBR); - if (tbrPercentage != 100) { - state.tbrActive = true; - Double displayedTbr = (Double) currentMenu.getAttribute(MenuAttribute.TBR); - state.tbrPercent = displayedTbr.intValue(); - MenuTime durationMenuTime = ((MenuTime) currentMenu.getAttribute(MenuAttribute.RUNTIME)); - state.tbrRemainingDuration = durationMenuTime.getHour() * 60 + durationMenuTime.getMinute(); - state.tbrRate = ((double) currentMenu.getAttribute(MenuAttribute.BASAL_RATE)); + Menu menu = this.currentMenu; + MenuType menuType = menu.getType(); + if (menuType == MenuType.MAIN_MENU) { + Double tbrPercentage = (Double) menu.getAttribute(MenuAttribute.TBR); + if (tbrPercentage != 100) { + state.tbrActive = true; + Double displayedTbr = (Double) menu.getAttribute(MenuAttribute.TBR); + state.tbrPercent = displayedTbr.intValue(); + MenuTime durationMenuTime = ((MenuTime) menu.getAttribute(MenuAttribute.RUNTIME)); + state.tbrRemainingDuration = durationMenuTime.getHour() * 60 + durationMenuTime.getMinute(); + state.tbrRate = ((double) menu.getAttribute(MenuAttribute.BASAL_RATE)); + } + } else if (menuType == MenuType.WARNING_OR_ERROR) { + state.isErrorOrWarning = true; + state.errorMsg = (String) menu.getAttribute(MenuAttribute.MESSAGE); + } else { + StringBuilder sb = new StringBuilder(); + for (MenuAttribute menuAttribute : menu.attributes()) { + sb.append(menuAttribute); + sb.append(": "); + sb.append(menu.getAttribute(menuAttribute)); + sb.append("\n"); + } + state.errorMsg = "Pump is on menu " + menuType + ", listing attributes: \n" + sb.toString(); } return state; } diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/NoOpCommand.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/NoOpCommand.java index 25f3e2127c..9ae5f361c3 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/NoOpCommand.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/NoOpCommand.java @@ -1,4 +1,15 @@ package de.jotomo.ruffyscripter.commands; -public class NoOpCommand { +import de.jotomo.ruffyscripter.RuffyScripter; + +public class NoOpCommand implements Command { + @Override + public CommandResult execute(RuffyScripter ruffyScripter) { + return new CommandResult().success(true).enacted(false).message("Returning pump state only"); + } + + @Override + public String toString() { + return "NoOpCommand{}"; + } } diff --git a/app/src/main/java/de/jotomo/ruffyscripter/commands/PumpState.java b/app/src/main/java/de/jotomo/ruffyscripter/commands/PumpState.java index 95a5aa2bbf..47260dc5f7 100644 --- a/app/src/main/java/de/jotomo/ruffyscripter/commands/PumpState.java +++ b/app/src/main/java/de/jotomo/ruffyscripter/commands/PumpState.java @@ -9,6 +9,7 @@ public class PumpState { public Date timestamp = new Date(); public boolean tbrActive = false; public int tbrPercent = -1; + public double tbrRate = -1; public int tbrRemainingDuration = -1; public boolean isErrorOrWarning = false; public String errorMsg; @@ -23,6 +24,11 @@ public class PumpState { return this; } + public PumpState tbrRate(double tbrRate) { + this.tbrRate = tbrRate; + return this; + } + public PumpState tbrRemainingDuration(int tbrRemainingDuration) { this.tbrRemainingDuration = tbrRemainingDuration; return this; @@ -43,6 +49,7 @@ public class PumpState { return "PumpState{" + "tbrActive=" + tbrActive + ", tbrPercent=" + tbrPercent + + ", tbrRate=" + tbrRate + ", tbrRemainingDuration=" + tbrRemainingDuration + ", isErrorOrWarning=" + isErrorOrWarning + ", errorMsg=" + errorMsg + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java index 49fc90c6ae..58367abc37 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboFragment.java @@ -9,6 +9,7 @@ import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.TextView; import com.squareup.otto.Subscribe; @@ -33,11 +34,15 @@ public class ComboFragment extends Fragment { return comboPlugin; } + private EditText statusText; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.combopump_fragment, container, false); + statusText = (EditText) view.findViewById(R.id.comboStatusEditText); + updateGUI(); return view; } @@ -65,9 +70,17 @@ public class ComboFragment extends Fragment { activity.runOnUiThread(new Runnable() { @Override public void run() { - -// your rendering code here - + if (getPlugin() == null) { + statusText.setText("Initializing"); + } else { + StringBuilder sb = new StringBuilder(); + sb.append(getPlugin().statusSummary); + if (getPlugin().pumpState != null) { + sb.append("\n\n"); + sb.append(getPlugin().pumpState.toString().replaceAll(",", "\n")); + } + statusText.setText(sb.toString()); + } } }); } 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 707cde8764..5baf90ae3d 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 @@ -23,7 +23,7 @@ import de.jotomo.ruffyscripter.commands.BolusCommand; import de.jotomo.ruffyscripter.commands.CancelTbrCommand; import de.jotomo.ruffyscripter.commands.Command; import de.jotomo.ruffyscripter.commands.CommandResult; -import de.jotomo.ruffyscripter.commands.ReadStateCommand; +import de.jotomo.ruffyscripter.commands.NoOpCommand; import de.jotomo.ruffyscripter.commands.SetTbrCommand; import de.jotomo.ruffyscripter.commands.PumpState; import info.nightscout.androidaps.BuildConfig; @@ -36,11 +36,11 @@ import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI; import info.nightscout.utils.DateUtil; /** @@ -59,7 +59,9 @@ public class ComboPlugin implements PluginBase, PumpInterface { private ServiceConnection mRuffyServiceConnection; @Nullable - private volatile PumpState pumpState; + volatile PumpState pumpState; + + volatile String statusSummary = "Initializing"; private static PumpEnactResult OPERATION_NOT_SUPPORTED = new PumpEnactResult(); @@ -96,6 +98,12 @@ public class ComboPlugin implements PluginBase, PumpInterface { public void onServiceConnected(ComponentName name, IBinder service) { ruffyScripter = new RuffyScripter(IRuffyService.Stub.asInterface(service)); log.debug("ruffy serivce connected"); + new Thread(new Runnable() { + @Override + public void run() { + runCommand(new NoOpCommand()); + } + }).start(); } @Override @@ -299,18 +307,27 @@ public class ComboPlugin implements PluginBase, PumpInterface { } private CommandResult runCommand(Command command) { - // TODO use this to dispatch methods to a service thread, like DanaRs executionService - // will be required when doing multiple commands in sequence. - // Alternatively provide 'composite commands' to return everything needed in one go? - try { - CommandResult commandResult = ruffyScripter.runCommand(command); - if (commandResult.success && commandResult.state != null) { - pumpState = commandResult.state; + synchronized (this) { + // TODO use this to dispatch methods to a service thread, like DanaRs executionService + // will be required when doing multiple commands in sequence. + // Alternatively provide 'composite commands' to return everything needed in one go? + try { + statusSummary = "Busy running " + command; + pumpState = null; + MainApp.bus().post(new EventComboPumpUpdateGUI()); + CommandResult commandResult = ruffyScripter.runCommand(command); + if (commandResult.success && commandResult.state != null) { + pumpState = commandResult.state; + } + return commandResult; + } finally { + lastCmdTime = new Date(); + statusSummary = pumpState != null && !pumpState.isErrorOrWarning + ? "Idle" + : "Error: " + pumpState.errorMsg; + ruffyScripter.disconnect(); + MainApp.bus().post(new EventComboPumpUpdateGUI()); } - return commandResult; - } finally { - lastCmdTime = new Date(); - ruffyScripter.disconnect(); } } @@ -358,7 +375,6 @@ public class ComboPlugin implements PluginBase, PumpInterface { log.debug("Rounded requested percentage from " + percent + " to " + rounded); percent = rounded; } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingtempbasal))); CommandResult commandResult = runCommand(new SetTbrCommand(percent, durationInMinutes)); if (commandResult.enacted) { TemporaryBasal tempStart = new TemporaryBasal(System.currentTimeMillis()); @@ -387,10 +403,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { return OPERATION_NOT_SUPPORTED; } + // TODO untested, probably not working @Override public PumpEnactResult cancelTempBasal() { log.debug("cancelTempBasal called"); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal))); CommandResult commandResult = runCommand(new CancelTbrCommand()); if (commandResult.enacted) { TemporaryBasal tempStop = new TemporaryBasal(System.currentTimeMillis()); @@ -422,13 +438,20 @@ public class ComboPlugin implements PluginBase, PumpInterface { JSONObject status = new JSONObject(); JSONObject extended = new JSONObject(); try { - status.put("status", "normal"); + status.put("status", statusSummary); extended.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION); try { extended.put("ActiveProfile", MainApp.getConfigBuilder().getProfileName()); } catch (Exception e) { } - status.put("timestamp", DateUtil.toISOString(new Date())); + status.put("timestamp", lastCmdTime); + + if (pumpState != null) { + extended.put("TempBasalAbsoluteRate", pumpState.tbrRate); + // TODO best guess at this point ... + extended.put("TempBasalStart", DateUtil.dateAndTimeString(System.currentTimeMillis() - (pumpState.tbrRemainingDuration - 15 * 60 * 1000))); + extended.put("TempBasalRemaining", pumpState.tbrRemainingDuration); + } // more info here .... look at dana plugin @@ -454,7 +477,7 @@ public class ComboPlugin implements PluginBase, PumpInterface { @Override public String shortStatus(boolean veryShort) { - return deviceID(); + return statusSummary; } @Override diff --git a/app/src/main/res/layout/combopump_fragment.xml b/app/src/main/res/layout/combopump_fragment.xml index dd0948ef4c..cb3662d56a 100644 --- a/app/src/main/res/layout/combopump_fragment.xml +++ b/app/src/main/res/layout/combopump_fragment.xml @@ -17,4 +17,12 @@ + +