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 57bae5b35c..7fb5b98199 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 @@ -147,6 +147,8 @@ public class ComboFragment extends Fragment implements View.OnClickListener { insulinstateText.setTextColor(Color.RED); break; } + int reservoirLevel = plugin.getPump().history.reservoirLevel; + insulinstateText.setText(reservoirLevel == - 1 ? "" : "" + reservoirLevel); } CommandResult lastCmdResult1 = plugin.getPump().lastCmdResult; 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 33bf24f56d..4161bef80f 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 @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory; import java.util.Date; +import de.jotomo.ruffy.spi.history.PumpHistoryRequest; import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -258,6 +259,8 @@ public class ComboPlugin implements PluginBase, PumpInterface { } // this method is regularly called from info.nightscout.androidaps.receivers.KeepAliveReceiver + // TODO check this is eithor called regularly even with other commansd being fired; if not, + // request this periodically @Override public void refreshDataFromPump(String reason) { log.debug("RefreshDataFromPump called"); @@ -269,12 +272,19 @@ public class ComboPlugin implements PluginBase, PumpInterface { } boolean notAUserRequest = !reason.toLowerCase().contains("user"); + if (notAUserRequest) SystemClock.sleep(2000); boolean wasRunAtLeastOnce = pump.lastCmdTime.getTime() > 0; boolean ranWithinTheLastMinute = System.currentTimeMillis() < pump.lastCmdTime.getTime() + 60 * 1000; if (notAUserRequest && wasRunAtLeastOnce && ranWithinTheLastMinute) { log.debug("Not fetching state from pump, since we did already within the last 60 seconds"); } else { - ruffyScripter.readPumpState(); + CommandResult commandResult = ruffyScripter.readPumpState(); + pump.lastCmdResult = commandResult; + pump.lastCmdTime = new Date(commandResult.completionTime); + MainApp.bus().post(new EventComboPumpUpdateGUI()); + + CommandResult reservoirQueryResult = ruffyScripter.readHistory(new PumpHistoryRequest().reservoirLevel(true)); + pump.history = reservoirQueryResult.history; } } @@ -363,6 +373,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { @NonNull private PumpEnactResult deliverBolus(DetailedBolusInfo detailedBolusInfo) { CommandResult bolusCmdResult = ruffyScripter.deliverBolus(detailedBolusInfo.insulin, bolusProgressReporter); + pump.lastCmdResult = bolusCmdResult; + pump.lastCmdTime = new Date(bolusCmdResult.completionTime); + MainApp.bus().post(new EventComboPumpUpdateGUI()); + PumpEnactResult pumpEnactResult = new PumpEnactResult(); pumpEnactResult.success = bolusCmdResult.success; pumpEnactResult.enacted = bolusCmdResult.enacted; @@ -427,6 +441,9 @@ public class ComboPlugin implements PluginBase, PumpInterface { } CommandResult commandResult = ruffyScripter.setTbr(adjustedPercent, durationInMinutes); + pump.lastCmdResult = commandResult; + pump.lastCmdTime = new Date(commandResult.completionTime); + MainApp.bus().post(new EventComboPumpUpdateGUI()); if (commandResult.enacted) { TemporaryBasal tempStart = new TemporaryBasal(commandResult.completionTime); @@ -442,8 +459,6 @@ public class ComboPlugin implements PluginBase, PumpInterface { treatmentsInterface.addToHistoryTempBasal(tempStart); } - MainApp.bus().post(new EventComboPumpUpdateGUI()); - PumpEnactResult pumpEnactResult = new PumpEnactResult(); pumpEnactResult.success = commandResult.success; pumpEnactResult.enacted = commandResult.enacted; @@ -476,6 +491,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { /* v1 compatibility to sync DB to pump if they diverged (activeTemp == null) */ log.debug("cancelTempBasal: hard-cancelling TBR since user requested"); commandResult = ruffyScripter.cancelTbr(); + pump.lastCmdResult = commandResult; + pump.lastCmdTime = new Date(commandResult.completionTime); + MainApp.bus().post(new EventComboPumpUpdateGUI()); + if (commandResult.enacted) { tempBasal = new TemporaryBasal(commandResult.completionTime); tempBasal.durationInMinutes = 0; @@ -498,6 +517,10 @@ public class ComboPlugin implements PluginBase, PumpInterface { int percentage = (activeTemp.percentRate > 100) ? 110 : 90; log.debug("cancelTempBasal: changing tbr to " + percentage + "% for 15 mins."); commandResult = ruffyScripter.setTbr(percentage, 15); + pump.lastCmdResult = commandResult; + pump.lastCmdTime = new Date(commandResult.completionTime); + MainApp.bus().post(new EventComboPumpUpdateGUI()); + if (commandResult.enacted) { tempBasal = new TemporaryBasal(commandResult.completionTime); tempBasal.durationInMinutes = 15; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java index 8f65a2b1a3..08a63f62aa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPump.java @@ -7,6 +7,7 @@ import java.util.Date; import de.jotomo.ruffy.spi.PumpState; import de.jotomo.ruffy.spi.CommandResult; +import de.jotomo.ruffy.spi.history.PumpHistory; class ComboPump { @Nullable @@ -14,4 +15,5 @@ class ComboPump { @NonNull volatile Date lastCmdTime = new Date(0); volatile PumpState state = new PumpState(); + volatile PumpHistory history = new PumpHistory(); } diff --git a/app/src/main/res/layout/objectives_fragment.xml b/app/src/main/res/layout/objectives_fragment.xml index 45d0578531..a752a57740 100644 --- a/app/src/main/res/layout/objectives_fragment.xml +++ b/app/src/main/res/layout/objectives_fragment.xml @@ -14,7 +14,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/objectives_fake_layout" - android:visibility="gone"> + android:visibility="visible"> bolusHistory; + public List bolusHistory = Collections.emptyList(); @NonNull - public final List tbrHistory; + public List tbrHistory = Collections.emptyList(); @NonNull - public final List errorHistory; + public List errorHistory = Collections.emptyList(); @NonNull - public final List tddHistory; + public List tddHistory = Collections.emptyList(); - public PumpHistory(int reservoirLevel, List bolusHistory, List tbrHistory, List errorHistory, List tddHistory) { - this.reservoirLevel = reservoirLevel; - this.bolusHistory = Objects.requireNonNull(bolusHistory); - this.tbrHistory = Objects.requireNonNull(tbrHistory); - this.errorHistory = Objects.requireNonNull(errorHistory); - this.tddHistory = Objects.requireNonNull(tddHistory); + public PumpHistory reservoirLevel(int reservoirLevel) { + this.reservoirLevel = reservoirLevel + ; + return this; + } + + public PumpHistory bolusHistory(List bolusHistory) { + this.bolusHistory = bolusHistory; + return this; + } + + public PumpHistory tbrHistory(List tbrHistory) { + this.tbrHistory = tbrHistory; + return this; + } + + public PumpHistory errorHistory(List errorHistory) { + this.errorHistory = errorHistory; + 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; } } diff --git a/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistoryRequest.java b/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistoryRequest.java index 994cfe7237..6ced0d4d39 100644 --- a/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistoryRequest.java +++ b/ruffy-spi/src/main/java/de/jotomo/ruffy/spi/history/PumpHistoryRequest.java @@ -11,9 +11,9 @@ public class PumpHistoryRequest { public static final long SKIP = -1; public static final long FULL = 0; - public long bolusHistory; - public long tbrHistory; - public long errorHistory; + public long bolusHistory = SKIP; + public long tbrHistory = SKIP; + public long errorHistory = SKIP; public PumpHistoryRequest reservoirLevel(boolean reservoirLevel) { this.reservoirLevel = reservoirLevel; diff --git a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java index 6eb00670b6..ffaec45d23 100644 --- a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java +++ b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/RuffyScripter.java @@ -20,29 +20,25 @@ import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collections; import java.util.Date; import java.util.List; +import de.jotomo.ruffy.spi.BasalProfile; +import de.jotomo.ruffy.spi.BolusProgressReporter; +import de.jotomo.ruffy.spi.CommandResult; +import de.jotomo.ruffy.spi.PumpState; +import de.jotomo.ruffy.spi.RuffyCommands; +import de.jotomo.ruffy.spi.history.PumpHistoryRequest; import de.jotomo.ruffyscripter.commands.BolusCommand; import de.jotomo.ruffyscripter.commands.CancelTbrCommand; import de.jotomo.ruffyscripter.commands.Command; import de.jotomo.ruffyscripter.commands.CommandException; import de.jotomo.ruffyscripter.commands.GetPumpStateCommand; import de.jotomo.ruffyscripter.commands.ReadBasalProfileCommand; +import de.jotomo.ruffyscripter.commands.ReadHistoryCommand; import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand; import de.jotomo.ruffyscripter.commands.SetBasalProfileCommand; import de.jotomo.ruffyscripter.commands.SetTbrCommand; -import de.jotomo.ruffy.spi.BasalProfile; -import de.jotomo.ruffy.spi.BolusProgressReporter; -import de.jotomo.ruffy.spi.CommandResult; -import de.jotomo.ruffy.spi.history.Bolus; -import de.jotomo.ruffy.spi.history.PumpHistory; -import de.jotomo.ruffy.spi.PumpState; -import de.jotomo.ruffy.spi.RuffyCommands; -import de.jotomo.ruffy.spi.history.PumpHistoryRequest; -import de.jotomo.ruffy.spi.history.Tbr; -import de.jotomo.ruffy.spi.history.Tdd; // TODO regularly read "My data" history (boluses, TBR) to double check all commands ran successfully. // Automatically compare against AAPS db, or log all requests in the PumpInterface (maybe Milos @@ -70,91 +66,6 @@ public class RuffyScripter implements RuffyCommands { private final Object screenlock = new Object(); - private ServiceConnection mRuffyServiceConnection; - - public RuffyScripter(Context context) { - boolean boundSucceeded = false; - - try { - Intent intent = new Intent() - .setComponent(new ComponentName( - // this must be the base package of the app (check package attribute in - // manifest element in the manifest file of the providing app) - "org.monkey.d.ruffy.ruffy", - // full path to the driver; - // in the logs this service is mentioned as (note the slash) - // "org.monkey.d.ruffy.ruffy/.driver.Ruffy"; - // org.monkey.d.ruffy.ruffy is the base package identifier - // and /.driver.Ruffy the service within the package - "org.monkey.d.ruffy.ruffy.driver.Ruffy" - )); - context.startService(intent); - - mRuffyServiceConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - ruffyService = IRuffyService.Stub.asInterface(service); - log.debug("ruffy serivce connected"); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - log.debug("ruffy service disconnected"); - } - }; - boundSucceeded = context.bindService(intent, mRuffyServiceConnection, Context.BIND_AUTO_CREATE); - } catch (Exception e) { - log.error("Binding to ruffy service failed", e); - } - - if (!boundSucceeded) { - log.error("No connection to ruffy. Pump control unavailable."); - } else { - started = true; - } - } - - @Override - public boolean isPumpAvailable() { - return started; - } - - private volatile boolean connected = false; - private volatile long lastDisconnected = 0; - - private Thread idleDisconnectMonitorThread = new Thread(new Runnable() { - @Override - public void run() { - while (unrecoverableError == null) { - try { - long now = System.currentTimeMillis(); - long connectionTimeOutMs = 5000; - if (connected && activeCmd == null - && now > lastCmdExecutionTime + connectionTimeOutMs - // don't disconnect too frequently, confuses ruffy? - && now > lastDisconnected + 15 * 1000) { - log.debug("Disconnecting after " + (connectionTimeOutMs / 1000) + "s inactivity timeout"); - lastDisconnected = now; - ruffyService.doRTDisconnect(); - connected = false; - // don't attempt anything fancy in the next 10s, let the pump settle - SystemClock.sleep(10 * 1000); - } - } catch (Exception e) { - // TODO do we need to catch this exception somewhere else too? right now it's - // converted into a command failure, but it's not classified as unrecoverable; - // eventually we might try to recover ... check docs, there's also another - // execption we should watch interacting with a remote service. - // SecurityException was the other, when there's an AIDL mismatch; - //unrecoverableError = "Ruffy service went away"; - log.debug("Exception in idle disconnect monitor thread, carrying on", e); - } - SystemClock.sleep(1000); - } - } - }, "idle-disconnect-monitor"); - private IRTHandler mHandler = new IRTHandler.Stub() { @Override public void log(String message) throws RemoteException { @@ -221,6 +132,93 @@ public class RuffyScripter implements RuffyCommands { } }; + public RuffyScripter(Context context) { + boolean boundSucceeded = false; + + try { + Intent intent = new Intent() + .setComponent(new ComponentName( + // this must be the base package of the app (check package attribute in + // manifest element in the manifest file of the providing app) + "org.monkey.d.ruffy.ruffy", + // full path to the driver; + // in the logs this service is mentioned as (note the slash) + // "org.monkey.d.ruffy.ruffy/.driver.Ruffy"; + // org.monkey.d.ruffy.ruffy is the base package identifier + // and /.driver.Ruffy the service within the package + "org.monkey.d.ruffy.ruffy.driver.Ruffy" + )); + context.startService(intent); + + ServiceConnection mRuffyServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + log.debug("ruffy service connected"); + ruffyService = IRuffyService.Stub.asInterface(service); + try { + ruffyService.setHandler(mHandler); + } catch (Exception e) { + log.error("Ruffy handler has issues", e); + } + idleDisconnectMonitorThread.start(); + started = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + log.debug("ruffy service disconnected"); + } + }; + boundSucceeded = context.bindService(intent, mRuffyServiceConnection, Context.BIND_AUTO_CREATE); + } catch (Exception e) { + log.error("Binding to ruffy service failed", e); + } + + if (!boundSucceeded) { + log.error("No connection to ruffy. Pump control unavailable."); + } + } + + @Override + public boolean isPumpAvailable() { + return started; + } + + private volatile boolean connected = false; + private volatile long lastDisconnected = 0; + + private Thread idleDisconnectMonitorThread = new Thread(new Runnable() { + @Override + public void run() { + while (unrecoverableError == null) { + try { + long now = System.currentTimeMillis(); + long connectionTimeOutMs = 5000; + if (connected && activeCmd == null + && now > lastCmdExecutionTime + connectionTimeOutMs + // don't disconnect too frequently, confuses ruffy? + && now > lastDisconnected + 15 * 1000) { + log.debug("Disconnecting after " + (connectionTimeOutMs / 1000) + "s inactivity timeout"); + lastDisconnected = now; + ruffyService.doRTDisconnect(); + connected = false; + // don't attempt anything fancy in the next 10s, let the pump settle + SystemClock.sleep(10 * 1000); + } + } catch (Exception e) { + // TODO do we need to catch this exception somewhere else too? right now it's + // converted into a command failure, but it's not classified as unrecoverable; + // eventually we might try to recover ... check docs, there's also another + // execption we should watch interacting with a remote service. + // SecurityException was the other, when there's an AIDL mismatch; + //unrecoverableError = "Ruffy service went away"; + log.debug("Exception in idle disconnect monitor thread, carrying on", e); + } + SystemClock.sleep(1000); + } + } + }, "idle-disconnect-monitor"); + @Override public boolean isPumpBusy() { return activeCmd != null; @@ -266,14 +264,13 @@ public class RuffyScripter implements RuffyCommands { return new CommandResult().message(Joiner.on("\n").join(violations)).state(readPumpStateInternal()); } -// synchronized (RuffyScripter.class) { + synchronized (RuffyScripter.class) { try { activeCmd = cmd; long connectStart = System.currentTimeMillis(); ensureConnected(); final RuffyScripter scripter = this; final Returnable returnable = new Returnable(); - returnable.cmdResult.request = cmd.toString(); Thread cmdThread = new Thread(new Runnable() { @Override public void run() { @@ -370,6 +367,7 @@ public class RuffyScripter implements RuffyCommands { long connectDurationSec = (executionStart - connectStart) / 1000; long executionDurationSec = (System.currentTimeMillis() - executionStart) / 1000; returnable.cmdResult.duration = "Connect: " + connectDurationSec + "s, execution: " + executionDurationSec + "s"; + returnable.cmdResult.request = cmd.toString(); log.debug("Command result: " + returnable.cmdResult); return returnable.cmdResult; } catch (CommandException e) { @@ -381,7 +379,7 @@ public class RuffyScripter implements RuffyCommands { } finally { activeCmd = null; } -// } + } } /** @@ -431,8 +429,10 @@ public class RuffyScripter implements RuffyCommands { // TODO v2 add remaining info we can extract from the main menu, low battery and low // cartridge warnings, running extended bolus (how does that look if a TBR is active as well?) - /** This reads the state of the, which is whatever is currently displayed on the display, - * no actions are performed. */ + /** + * This reads the state of the, which is whatever is currently displayed on the display, + * no actions are performed. + */ public PumpState readPumpStateInternal() { PumpState state = new PumpState(); Menu menu = currentMenu; @@ -541,9 +541,9 @@ public class RuffyScripter implements RuffyCommands { // wrapNoNotTheSubwayKind(new Step() { // @Override // public void doStep() { - log.debug("Pressing up key"); - pressKey(Key.UP, 2000); - log.debug("Releasing up key"); + log.debug("Pressing up key"); + pressKey(Key.UP, 2000); + log.debug("Releasing up key"); // } // }); } @@ -611,7 +611,9 @@ public class RuffyScripter implements RuffyCommands { // TODO confirmAlarms? and report back which were cancelled? - /** 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(String alertMessage, int maxWaitMs) { long inFiveSeconds = System.currentTimeMillis() + maxWaitMs; boolean alertProcessed = false; @@ -641,7 +643,9 @@ public class RuffyScripter implements RuffyCommands { return alertProcessed; } - /** Wait until the menu is updated */ + /** + * Wait until the menu is updated + */ public void waitForMenuUpdate() { waitForMenuUpdate(60, "Timeout waiting for menu update"); } @@ -692,7 +696,9 @@ public class RuffyScripter implements RuffyCommands { } } - /** Wait till a menu changed has completed, "away" from the menu provided as argument. */ + /** + * Wait till a menu changed has completed, "away" from the menu provided as argument. + */ public void waitForMenuToBeLeft(MenuType menuType) { long timeout = System.currentTimeMillis() + 60 * 1000; while (getCurrentMenu().getType() == menuType) { @@ -761,12 +767,7 @@ public class RuffyScripter implements RuffyCommands { @Override public CommandResult readHistory(PumpHistoryRequest request) { - return new CommandResult().history( - new PumpHistory(50, - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList())); + return runCommand(new ReadHistoryCommand(request)); } @Override diff --git a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadHistoryCommand.java b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadHistoryCommand.java index 5885f10b14..cef4439c51 100644 --- a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadHistoryCommand.java +++ b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadHistoryCommand.java @@ -1,27 +1,45 @@ package de.jotomo.ruffyscripter.commands; +import org.monkey.d.ruffy.ruffy.driver.display.MenuAttribute; +import org.monkey.d.ruffy.ruffy.driver.display.MenuType; + +import java.util.Collections; import java.util.List; import de.jotomo.ruffy.spi.history.PumpHistory; -import de.jotomo.ruffyscripter.RuffyScripter; +import de.jotomo.ruffy.spi.history.PumpHistoryRequest; import de.jotomo.ruffy.spi.CommandResult; -public class ReadHistoryCommand implements Command { - public ReadHistoryCommand(PumpHistory knownHistory) { +public class ReadHistoryCommand extends BaseCommand { + private final PumpHistoryRequest request; + + public ReadHistoryCommand(PumpHistoryRequest request) { + this.request = request; } @Override public CommandResult execute() { - return null; + PumpHistory history = new PumpHistory(); + if (request.reservoirLevel) readReservoirLevel(history); + if (request.bolusHistory != PumpHistoryRequest.SKIP) readBolusHistory(history, request.bolusHistory); + return new CommandResult().success(true).enacted(false).history(history); + } + + private void readBolusHistory(PumpHistory history, long bolusHistory) { + } + + private void readReservoirLevel(PumpHistory history) { + scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU); + scripter.pressCheckKey(); + scripter.waitForMenuToBeLeft(MenuType.MAIN_MENU); + scripter.verifyMenuIsDisplayed(MenuType.QUICK_INFO); + int remainingInsulin = ((Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.REMAINING_INSULIN)).intValue(); + scripter.returnToMainMenu(); + history.reservoirLevel = remainingInsulin; } @Override public List validateArguments() { - return null; - } - - @Override - public void setScripter(RuffyScripter scripter) { - + return Collections.emptyList(); } } diff --git a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadPumpStateCommand.java b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadPumpStateCommand.java index 71f8c9bd24..c02beddf13 100644 --- a/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadPumpStateCommand.java +++ b/ruffyscripter/src/main/java/de/jotomo/ruffyscripter/commands/ReadPumpStateCommand.java @@ -19,4 +19,9 @@ public class ReadPumpStateCommand implements Command { @Override public void setScripter(RuffyScripter scripter) {} + + @Override + public String toString() { + return "ReadPumpStateCommand{}"; + } }