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.
This commit is contained in:
Johannes Mockenhaupt 2017-07-15 18:40:01 +02:00
parent f251427d1b
commit 8ecf6922f7
No known key found for this signature in database
GPG key ID: 9E1EA6AF7BBBB0D1
6 changed files with 142 additions and 66 deletions

View file

@ -108,17 +108,16 @@ public class RuffyScripter {
private volatile Command activeCmd = null; private volatile Command activeCmd = null;
public CommandResult runCommand(final Command cmd) { public CommandResult runCommand(final Command cmd) {
try { synchronized (this) {
if (isPumpBusy()) { try {
return new CommandResult().message("Pump is busy"); if (isPumpBusy()) {
} return new CommandResult().message("Pump is busy");
ensureConnected(); }
ensureConnected();
// TODO reuse thread, scheduler ... // TODO reuse thread, scheduler ...
Thread cmdThread; Thread cmdThread;
// TODO make this a safe lock
synchronized (this) {
cmdResult = null; cmdResult = null;
activeCmd = cmd; activeCmd = cmd;
// wait till pump is ready for input // wait till pump is ready for input
@ -126,7 +125,8 @@ public class RuffyScripter {
// check if pump is an an error state // check if pump is an an error state
if (currentMenu != null && currentMenu.getType() == MenuType.WARNING_OR_ERROR) { if (currentMenu != null && currentMenu.getType() == MenuType.WARNING_OR_ERROR) {
try { 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) { } catch (Exception e) {
return new CommandResult().message("Pump is in an error state, reading the error state resulted in the attached exception").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(); cmdThread.start();
}
// TODO really? // TODO really?
long timeout = System.currentTimeMillis() + 90 * 1000; long timeout = System.currentTimeMillis() + 90 * 1000;
while (activeCmd != null) { while (activeCmd != null) {
SystemClock.sleep(500); SystemClock.sleep(500);
log.trace("Waiting for running command to complete"); log.trace("Waiting for running command to complete");
if (System.currentTimeMillis() > timeout) { if (System.currentTimeMillis() > timeout) {
log.error("Running command " + activeCmd + " timed out"); log.error("Running command " + activeCmd + " timed out");
cmdThread.interrupt(); cmdThread.interrupt();
activeCmd = null; activeCmd = null;
cmdResult = null; return new CommandResult().success(false).enacted(false).message("Command timed out");
return new CommandResult().success(false).enacted(false).message("Command timed out"); }
} }
}
if (cmdResult.state == null) { if (cmdResult.state == null) {
cmdResult.state = readPumpState(); 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) { if (System.currentTimeMillis() > timeout) {
throw new CommandException().message("Timeout waiting for menu " + menuType + " to be left"); 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() { private PumpState readPumpState() {
verifyMenuIsDisplayed(MenuType.MAIN_MENU);
PumpState state = new PumpState(); PumpState state = new PumpState();
Double tbrPercentage = (Double) currentMenu.getAttribute(MenuAttribute.TBR); Menu menu = this.currentMenu;
if (tbrPercentage != 100) { MenuType menuType = menu.getType();
state.tbrActive = true; if (menuType == MenuType.MAIN_MENU) {
Double displayedTbr = (Double) currentMenu.getAttribute(MenuAttribute.TBR); Double tbrPercentage = (Double) menu.getAttribute(MenuAttribute.TBR);
state.tbrPercent = displayedTbr.intValue(); if (tbrPercentage != 100) {
MenuTime durationMenuTime = ((MenuTime) currentMenu.getAttribute(MenuAttribute.RUNTIME)); state.tbrActive = true;
state.tbrRemainingDuration = durationMenuTime.getHour() * 60 + durationMenuTime.getMinute(); Double displayedTbr = (Double) menu.getAttribute(MenuAttribute.TBR);
state.tbrRate = ((double) currentMenu.getAttribute(MenuAttribute.BASAL_RATE)); 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; return state;
} }

View file

@ -1,4 +1,15 @@
package de.jotomo.ruffyscripter.commands; 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{}";
}
} }

View file

@ -9,6 +9,7 @@ public class PumpState {
public Date timestamp = new Date(); public Date timestamp = new Date();
public boolean tbrActive = false; public boolean tbrActive = false;
public int tbrPercent = -1; public int tbrPercent = -1;
public double tbrRate = -1;
public int tbrRemainingDuration = -1; public int tbrRemainingDuration = -1;
public boolean isErrorOrWarning = false; public boolean isErrorOrWarning = false;
public String errorMsg; public String errorMsg;
@ -23,6 +24,11 @@ public class PumpState {
return this; return this;
} }
public PumpState tbrRate(double tbrRate) {
this.tbrRate = tbrRate;
return this;
}
public PumpState tbrRemainingDuration(int tbrRemainingDuration) { public PumpState tbrRemainingDuration(int tbrRemainingDuration) {
this.tbrRemainingDuration = tbrRemainingDuration; this.tbrRemainingDuration = tbrRemainingDuration;
return this; return this;
@ -43,6 +49,7 @@ public class PumpState {
return "PumpState{" + return "PumpState{" +
"tbrActive=" + tbrActive + "tbrActive=" + tbrActive +
", tbrPercent=" + tbrPercent + ", tbrPercent=" + tbrPercent +
", tbrRate=" + tbrRate +
", tbrRemainingDuration=" + tbrRemainingDuration + ", tbrRemainingDuration=" + tbrRemainingDuration +
", isErrorOrWarning=" + isErrorOrWarning + ", isErrorOrWarning=" + isErrorOrWarning +
", errorMsg=" + errorMsg + ", errorMsg=" + errorMsg +

View file

@ -9,6 +9,7 @@ import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import com.squareup.otto.Subscribe; import com.squareup.otto.Subscribe;
@ -33,11 +34,15 @@ public class ComboFragment extends Fragment {
return comboPlugin; return comboPlugin;
} }
private EditText statusText;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.combopump_fragment, container, false); View view = inflater.inflate(R.layout.combopump_fragment, container, false);
statusText = (EditText) view.findViewById(R.id.comboStatusEditText);
updateGUI(); updateGUI();
return view; return view;
} }
@ -65,9 +70,17 @@ public class ComboFragment extends Fragment {
activity.runOnUiThread(new Runnable() { activity.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
if (getPlugin() == null) {
// your rendering code here 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());
}
} }
}); });
} }

View file

@ -23,7 +23,7 @@ import de.jotomo.ruffyscripter.commands.BolusCommand;
import de.jotomo.ruffyscripter.commands.CancelTbrCommand; import de.jotomo.ruffyscripter.commands.CancelTbrCommand;
import de.jotomo.ruffyscripter.commands.Command; import de.jotomo.ruffyscripter.commands.Command;
import de.jotomo.ruffyscripter.commands.CommandResult; 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.SetTbrCommand;
import de.jotomo.ruffyscripter.commands.PumpState; import de.jotomo.ruffyscripter.commands.PumpState;
import info.nightscout.androidaps.BuildConfig; 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.Source;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventPumpStatusChanged;
import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI;
import info.nightscout.utils.DateUtil; import info.nightscout.utils.DateUtil;
/** /**
@ -59,7 +59,9 @@ public class ComboPlugin implements PluginBase, PumpInterface {
private ServiceConnection mRuffyServiceConnection; private ServiceConnection mRuffyServiceConnection;
@Nullable @Nullable
private volatile PumpState pumpState; volatile PumpState pumpState;
volatile String statusSummary = "Initializing";
private static PumpEnactResult OPERATION_NOT_SUPPORTED = new PumpEnactResult(); 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) { public void onServiceConnected(ComponentName name, IBinder service) {
ruffyScripter = new RuffyScripter(IRuffyService.Stub.asInterface(service)); ruffyScripter = new RuffyScripter(IRuffyService.Stub.asInterface(service));
log.debug("ruffy serivce connected"); log.debug("ruffy serivce connected");
new Thread(new Runnable() {
@Override
public void run() {
runCommand(new NoOpCommand());
}
}).start();
} }
@Override @Override
@ -299,18 +307,27 @@ public class ComboPlugin implements PluginBase, PumpInterface {
} }
private CommandResult runCommand(Command command) { private CommandResult runCommand(Command command) {
// TODO use this to dispatch methods to a service thread, like DanaRs executionService synchronized (this) {
// will be required when doing multiple commands in sequence. // TODO use this to dispatch methods to a service thread, like DanaRs executionService
// Alternatively provide 'composite commands' to return everything needed in one go? // will be required when doing multiple commands in sequence.
try { // Alternatively provide 'composite commands' to return everything needed in one go?
CommandResult commandResult = ruffyScripter.runCommand(command); try {
if (commandResult.success && commandResult.state != null) { statusSummary = "Busy running " + command;
pumpState = commandResult.state; 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); log.debug("Rounded requested percentage from " + percent + " to " + rounded);
percent = rounded; percent = rounded;
} }
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingtempbasal)));
CommandResult commandResult = runCommand(new SetTbrCommand(percent, durationInMinutes)); CommandResult commandResult = runCommand(new SetTbrCommand(percent, durationInMinutes));
if (commandResult.enacted) { if (commandResult.enacted) {
TemporaryBasal tempStart = new TemporaryBasal(System.currentTimeMillis()); TemporaryBasal tempStart = new TemporaryBasal(System.currentTimeMillis());
@ -387,10 +403,10 @@ public class ComboPlugin implements PluginBase, PumpInterface {
return OPERATION_NOT_SUPPORTED; return OPERATION_NOT_SUPPORTED;
} }
// TODO untested, probably not working
@Override @Override
public PumpEnactResult cancelTempBasal() { public PumpEnactResult cancelTempBasal() {
log.debug("cancelTempBasal called"); log.debug("cancelTempBasal called");
MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal)));
CommandResult commandResult = runCommand(new CancelTbrCommand()); CommandResult commandResult = runCommand(new CancelTbrCommand());
if (commandResult.enacted) { if (commandResult.enacted) {
TemporaryBasal tempStop = new TemporaryBasal(System.currentTimeMillis()); TemporaryBasal tempStop = new TemporaryBasal(System.currentTimeMillis());
@ -422,13 +438,20 @@ public class ComboPlugin implements PluginBase, PumpInterface {
JSONObject status = new JSONObject(); JSONObject status = new JSONObject();
JSONObject extended = new JSONObject(); JSONObject extended = new JSONObject();
try { try {
status.put("status", "normal"); status.put("status", statusSummary);
extended.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION); extended.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
try { try {
extended.put("ActiveProfile", MainApp.getConfigBuilder().getProfileName()); extended.put("ActiveProfile", MainApp.getConfigBuilder().getProfileName());
} catch (Exception e) { } 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 // more info here .... look at dana plugin
@ -454,7 +477,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
@Override @Override
public String shortStatus(boolean veryShort) { public String shortStatus(boolean veryShort) {
return deviceID(); return statusSummary;
} }
@Override @Override

View file

@ -17,4 +17,12 @@
</ScrollView> </ScrollView>
<TextView
android:id="@+id/comboStatusEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:textAlignment="center"
android:layout_gravity="center_horizontal"/>
</FrameLayout> </FrameLayout>