diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java
index 56c712f629..bad0f88d6a 100644
--- a/app/src/main/java/info/nightscout/androidaps/MainApp.java
+++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java
@@ -229,6 +229,10 @@ public class MainApp extends Application {
return sBus;
}
+ public static String gs(int id) {
+ return sResources.getString(id);
+ }
+
public static MainApp instance() {
return sInstance;
}
diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java
index b2e5d0d9dc..db34f9d692 100644
--- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java
+++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java
@@ -120,7 +120,7 @@ public class Profile {
}
} catch (JSONException e) {
log.error("Unhandled exception", e);
- ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.sResources.getString(R.string.invalidprofile));
+ ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.invalidprofile));
}
}
@@ -386,6 +386,8 @@ public class Profile {
}
public BasalValue[] getBasalValues() {
+ if (basal_v == null)
+ basal_v = convertToSparseArray(basal);
BasalValue[] ret = new BasalValue[basal_v.size()];
for (Integer index = 0; index < basal_v.size(); index++) {
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java
index 62848b7e5e..9cbb0bef4c 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java
@@ -421,23 +421,6 @@ public class ConfigBuilderPlugin implements PluginBase, ConstraintsInterface, Tr
}
-/*
- @Override
- public PumpDescription getPumpDescription() {
- if (activePump != null)
- return activePump.getPumpDescription();
- else {
- PumpDescription emptyDescription = new PumpDescription();
- emptyDescription.isBolusCapable = false;
- emptyDescription.isExtendedBolusCapable = false;
- emptyDescription.isSetBasalProfileCapable = false;
- emptyDescription.isTempBasalCapable = true; // needs to be true before real driver is selected
- emptyDescription.isRefillingCapable = false;
- return emptyDescription;
- }
- }
-*/
-
/**
* Constraints interface
**/
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java
index bd6f5ca690..7790257640 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java
@@ -258,6 +258,7 @@ public class DanaRSService extends Service {
MainApp.bus().post(bolusingEvent);
SystemClock.sleep(1000);
}
+ // do not call loadEvents() directly, reconnection may be needed
ConfigBuilderPlugin.getCommandQueue().loadEvents(new Callback() {
@Override
public void run() {
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java
index 6d6ba34838..71a2f9ac79 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java
@@ -424,6 +424,7 @@ public class DanaRv2ExecutionService extends Service {
MainApp.bus().post(bolusingEvent);
SystemClock.sleep(1000);
}
+ // do not call loadEvents() directly, reconnection may be needed
ConfigBuilderPlugin.getCommandQueue().loadEvents(new Callback() {
@Override
public void run() {
diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java
index 0e7e18ec7e..3e696e9857 100644
--- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java
+++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java
@@ -1,5 +1,6 @@
package info.nightscout.androidaps.queue;
+import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
@@ -37,42 +38,41 @@ import info.nightscout.androidaps.queue.commands.CommandTempBasalPercent;
/**
* Created by mike on 08.11.2017.
- *
+ *
* DATA FLOW:
* ---------
- *
+ *
* (request) - > ConfigBuilder.getCommandQueue().bolus(...)
- *
+ *
* app no longer waits for result but passes Callback
- *
+ *
* request is added to queue, if another request of the same type already exists in queue, it's removed prior adding
* but if request of the same type is currently executed (probably important only for bolus which is running long time), new request is declined
* new QueueThread is created and started if current if finished
* CommandReadStatus is added automatically before command if queue is empty
- *
+ *
* biggest change is we don't need exec pump commands in Handler because it's finished immediately
* command queueing if not realized by stacking in different Handlers and threads anymore but by internal queue with better control
- *
+ *
* QueueThread calls ConfigBuilder#connect which is passed to getActivePump().connect
* connect should be executed on background and return immediately. afterwards isConnecting() is expected to be true
- *
+ *
* while isConnecting() == true GUI is updated by posting connection progress
- *
+ *
* if connect is successful: isConnected() becomes true, isConnecting() becomes false
- * CommandQueue starts calling execute() of commands. execute() is expected to be blocking (return after finish).
- * callback with result is called after finish automatically
+ * CommandQueue starts calling execute() of commands. execute() is expected to be blocking (return after finish).
+ * callback with result is called after finish automatically
* if connect failed: isConnected() becomes false, isConnecting() becomes false
- * connect() is called again
- *
+ * connect() is called again
+ *
* when queue is empty, disconnect is called
- *
*/
public class CommandQueue {
private static Logger log = LoggerFactory.getLogger(CommandQueue.class);
private LinkedList queue = new LinkedList<>();
- private Command performing;
+ protected Command performing;
private QueueThread thread = null;
@@ -94,10 +94,19 @@ public class CommandQueue {
}
}
+ private synchronized boolean isLastScheduled(Command.CommandType type) {
+ if (queue.size() > 0 && queue.get(queue.size() - 1).commandType == type) {
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized void inject(Command command) {
+ // inject as a first command
+ queue.addFirst(command);
+ }
+
private synchronized void add(Command command) {
- // inject reading of status when adding first command to the queue
- if (queue.size() == 0 && command.commandType != Command.CommandType.READSTATUS)
- queue.add(new CommandReadStatus("Queue", null));
queue.add(command);
}
@@ -128,7 +137,7 @@ public class CommandQueue {
// After new command added to the queue
// start thread again if not already running
- private synchronized void notifyAboutNewCommand() {
+ protected synchronized void notifyAboutNewCommand() {
if (thread == null || thread.getState() == Thread.State.TERMINATED) {
thread = new QueueThread(this);
thread.start();
@@ -166,17 +175,7 @@ public class CommandQueue {
MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin));
// Bring up bolus progress dialog
- if (detailedBolusInfo.context != null) {
- BolusProgressDialog bolusProgressDialog = new BolusProgressDialog();
- bolusProgressDialog.setInsulin(detailedBolusInfo.insulin);
- bolusProgressDialog.show(((AppCompatActivity) detailedBolusInfo.context).getSupportFragmentManager(), "BolusProgress");
- } else {
- Intent i = new Intent();
- i.putExtra("insulin", detailedBolusInfo.insulin);
- i.setClass(MainApp.instance(), BolusProgressHelperActivity.class);
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- MainApp.instance().startActivity(i);
- }
+ showBolusProgressDialog(detailedBolusInfo.insulin, detailedBolusInfo.context);
return true;
}
@@ -231,7 +230,7 @@ public class CommandQueue {
return false;
}
- Double rateAfterConstraints = MainApp.getConfigBuilder().applyBolusConstraints(insulin);
+ Double rateAfterConstraints = MainApp.getConfigBuilder().applyBolusConstraints(insulin);
// remove all unfinished
removeAll(Command.CommandType.EXTENDEDBOLUS);
@@ -326,11 +325,12 @@ public class CommandQueue {
// returns true if command is queued
public boolean readStatus(String reason, Callback callback) {
- //if (isRunning(Command.CommandType.READSTATUS)) {
- // if (callback != null)
- // callback.result(executingNowError()).run();
- // return false;
- //}
+ if (isLastScheduled(Command.CommandType.READSTATUS)) {
+ log.debug("QUEUE: READSTATUS " + reason + " ignored as duplicated");
+ if (callback != null)
+ callback.result(executingNowError()).run();
+ return false;
+ }
// remove all unfinished
//removeAll(Command.CommandType.READSTATUS);
@@ -409,4 +409,18 @@ public class CommandQueue {
} else return true;
}
+ protected void showBolusProgressDialog(Double insulin, Context context) {
+ if (context != null) {
+ BolusProgressDialog bolusProgressDialog = new BolusProgressDialog();
+ bolusProgressDialog.setInsulin(insulin);
+ bolusProgressDialog.show(((AppCompatActivity) context).getSupportFragmentManager(), "BolusProgress");
+ } else {
+ Intent i = new Intent();
+ i.putExtra("insulin", insulin);
+ i.setClass(MainApp.instance(), BolusProgressHelperActivity.class);
+ i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ MainApp.instance().startActivity(i);
+ }
+ }
+
}
diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java
index 4624e2385f..5129c7983f 100644
--- a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java
+++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java
@@ -29,7 +29,7 @@ public abstract class Command {
public void cancel() {
PumpEnactResult result = new PumpEnactResult();
result.success = false;
- result.comment = MainApp.sResources.getString(R.string.connectiontimedout);
+ result.comment = MainApp.gs(R.string.connectiontimedout);
if (callback != null)
callback.result(result).run();
}
diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.java
index 46c524fa29..44c778c5f7 100644
--- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.java
+++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.java
@@ -27,6 +27,6 @@ public class CommandReadStatus extends Command {
@Override
public String status() {
- return "READSTATUS";
+ return "READSTATUS " + reason;
}
}
diff --git a/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueTest.java b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueTest.java
new file mode 100644
index 0000000000..31d8636de2
--- /dev/null
+++ b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueTest.java
@@ -0,0 +1,143 @@
+package info.nightscout.androidaps.queue;
+
+import android.content.Context;
+import android.text.Html;
+
+import com.squareup.otto.Bus;
+import com.squareup.otto.ThreadEnforcer;
+
+import junit.framework.Assert;
+
+import org.json.JSONObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import info.nightscout.androidaps.Constants;
+import info.nightscout.androidaps.MainApp;
+import info.nightscout.androidaps.data.DetailedBolusInfo;
+import info.nightscout.androidaps.data.Profile;
+import info.nightscout.androidaps.interfaces.PumpInterface;
+import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
+import info.nightscout.androidaps.plugins.PumpMDI.MDIPlugin;
+import info.nightscout.androidaps.queue.commands.Command;
+import info.nightscout.utils.ToastUtils;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Created by mike on 14.01.2018.
+ */
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, ConfigBuilderPlugin.class, ToastUtils.class, Context.class})
+public class CommandQueueTest extends CommandQueue {
+
+ String profileJson = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}";
+
+ @Test
+ public void doTests() throws Exception {
+ prepareMock(0d, 0);
+
+ // start with empty queue
+ Assert.assertEquals(0, size());
+
+ // add bolus command
+ bolus(new DetailedBolusInfo(), null);
+ Assert.assertEquals(1, size());
+
+ // add READSTATUS
+ readStatus("anyString", null);
+ Assert.assertEquals(2, size());
+
+ // adding another bolus should remove the first one (size still == 2)
+ bolus(new DetailedBolusInfo(), null);
+ Assert.assertEquals(2, size());
+
+ // clear the queue should reset size
+ clear();
+ Assert.assertEquals(0, size());
+
+ // add tempbasal
+ tempBasalAbsolute(0, 30, true, null);
+ Assert.assertEquals(1, size());
+
+ // add tempbasal percent. it should replace previous TEMPBASAL
+ tempBasalPercent(0, 30, true, null);
+ Assert.assertEquals(1, size());
+
+ // add extended bolus
+ extendedBolus(1, 30, null);
+ Assert.assertEquals(2, size());
+
+ // add cancel temp basal should remove previous 2 temp basal setting
+ extendedBolus(1, 30, null);
+ Assert.assertEquals(2, size());
+
+ // cancel extended bolus should replace previous extended
+ extendedBolus(1, 30, null);
+ Assert.assertEquals(2, size());
+
+ // add setProfile
+ setProfile(new Profile(new JSONObject(profileJson), Constants.MGDL), null);
+ Assert.assertEquals(3, size());
+
+ // add loadHistory
+ loadHistory((byte) 0, null);
+ Assert.assertEquals(4, size());
+
+ // add loadEvents
+ loadEvents(null);
+ Assert.assertEquals(5, size());
+
+ clear();
+ tempBasalAbsolute(0, 30, true, null);
+ pickup();
+ Assert.assertEquals(0, size());
+ Assert.assertNotNull(performing);
+ Assert.assertEquals(Command.CommandType.TEMPBASAL, performing.commandType);
+ resetPerforming();
+ Assert.assertNull(performing);
+ }
+
+ private void prepareMock(Double insulin, Integer carbs) throws Exception {
+ ConfigBuilderPlugin configBuilderPlugin = mock(ConfigBuilderPlugin.class);
+ when(configBuilderPlugin.applyBolusConstraints(insulin)).thenReturn(insulin);
+ when(configBuilderPlugin.applyCarbsConstraints(carbs)).thenReturn(carbs);
+
+ PowerMockito.mockStatic(ConfigBuilderPlugin.class);
+ PumpInterface pump = MDIPlugin.getPlugin();
+ when(ConfigBuilderPlugin.getActivePump()).thenReturn(pump);
+
+ PowerMockito.mockStatic(MainApp.class);
+ MainApp mainApp = mock(MainApp.class);
+ when(MainApp.getConfigBuilder()).thenReturn(configBuilderPlugin);
+ when(MainApp.instance()).thenReturn(mainApp);
+
+ PowerMockito.mockStatic(ToastUtils.class);
+ Context context = mock(Context.class);
+ String message = null;
+ PowerMockito.doNothing().when(ToastUtils.class, "showToastInUiThread", context, message);
+
+ Bus bus = new Bus(ThreadEnforcer.ANY);
+
+ when(MainApp.bus()).thenReturn(bus);
+ when(MainApp.gs(0)).thenReturn("");
+ }
+
+ @Override
+ protected synchronized void notifyAboutNewCommand() {
+ }
+
+ @Override
+ protected void showBolusProgressDialog(Double insulin, Context context) {
+ }
+
+ @Override
+ public boolean isThisProfileSet(Profile profile) {
+ return false;
+ }
+}