diff --git a/pom.xml b/pom.xml
index 2f4950f..a06542f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.micro-manager.acqengj
AcqEngJ
- 0.27.0
+ 0.28.0
jar
AcqEngJ
Java-based Acquisition engine for Micro-Manager
diff --git a/src/main/java/org/micromanager/acqj/internal/Engine.java b/src/main/java/org/micromanager/acqj/internal/Engine.java
index 3066f8f..08a65cc 100644
--- a/src/main/java/org/micromanager/acqj/internal/Engine.java
+++ b/src/main/java/org/micromanager/acqj/internal/Engine.java
@@ -19,6 +19,7 @@
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import mmcorej.org.json.JSONException;
import org.micromanager.acqj.api.AcquisitionAPI;
import org.micromanager.acqj.main.AcquisitionEvent;
import org.micromanager.acqj.api.AcquisitionHook;
@@ -534,6 +535,12 @@ private void acquireImages(final AcquisitionEvent event) throws HardwareControlE
//add metadata
AcqEngMetadata.addImageMetadata(ti.tags, correspondingEvent,
currentTime - correspondingEvent.acquisition_.getStartTime_ms(), exposure);
+ try {
+ correspondingEvent.acquisition_.addTagsToTaggedImage(ti.tags,
+ correspondingEvent.getTags());
+ } catch (JSONException jse) {
+ core_.logMessage("Error adding tags to image metadata", false);
+ }
correspondingEvent.acquisition_.addToImageMetadata(ti.tags);
correspondingEvent.acquisition_.addToOutput(ti);
diff --git a/src/main/java/org/micromanager/acqj/main/AcqEngMetadata.java b/src/main/java/org/micromanager/acqj/main/AcqEngMetadata.java
index 52c0655..7254d7d 100644
--- a/src/main/java/org/micromanager/acqj/main/AcqEngMetadata.java
+++ b/src/main/java/org/micromanager/acqj/main/AcqEngMetadata.java
@@ -77,10 +77,11 @@ public class AcqEngMetadata {
public static final String CORE_XYSTAGE = "Core-XYStage";
public static final String CORE_FOCUS = "Core-Focus";
public static final String AXES = "Axes";
-
public static final String CHANNEL_AXIS = "channel";
public static final String TIME_AXIS = "time";
public static final String Z_AXIS = "z";
+ public static final String TAGS = "tags";
+
private static final String ACQUISITION_EVENT = "Event";
diff --git a/src/main/java/org/micromanager/acqj/main/Acquisition.java b/src/main/java/org/micromanager/acqj/main/Acquisition.java
index 0fbcfc9..6b44a10 100644
--- a/src/main/java/org/micromanager/acqj/main/Acquisition.java
+++ b/src/main/java/org/micromanager/acqj/main/Acquisition.java
@@ -16,7 +16,9 @@
//
package org.micromanager.acqj.main;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Consumer;
@@ -156,11 +158,35 @@ public void addToImageMetadata(JSONObject tags) {
}
}
+ /**
+ * Add provided tags (Key-Value pairs of type String) to the Tagged Image tags.
+ * These will appear as JSONObjects under the key:
+ * {@link org.micromanager.acqj.main.AcqEngMetadata#TAGS "Tags"}.
+ *
+ * @param tags Tagged Image tags
+ * @param moreTags User-provided tags as Key-Value pairs
+ */
+ public void addTagsToTaggedImage(JSONObject tags, HashMap moreTags)
+ throws JSONException {
+ if (moreTags.isEmpty()) {
+ return;
+ }
+ JSONObject moreTagsObject = new JSONObject();
+ for (Map.Entry entry : moreTags.entrySet()) {
+ try {
+ moreTagsObject.put(entry.getKey(), entry.getValue());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ tags.put(AcqEngMetadata.TAGS, moreTagsObject);
+ }
+
@Override
public Future submitEventIterator(Iterator evt) {
if (!started_) {
start();
- }
+ }
return Engine.getInstance().submitEventIterator(evt);
}
@@ -283,19 +309,21 @@ public void waitForCompletion() {
* 3) Initialize data sink.
*/
protected void initialize() {
- JSONObject summaryMetadata = AcqEngMetadata.makeSummaryMD(this);
- addToSummaryMetadata(summaryMetadata);
+ if (core_ != null) {
+ JSONObject summaryMetadata = AcqEngMetadata.makeSummaryMD(this);
+ addToSummaryMetadata(summaryMetadata);
- try {
- // Make a local in copy in case something else modifies it
- summaryMetadata_ = new JSONObject(summaryMetadata.toString());
- } catch (JSONException ex) {
- System.err.print("Couldn't copy summaary metadata");
- ex.printStackTrace();
- }
- if (dataSink_ != null) {
- //It could be null if not using saving and viewing and diverting with custom processor
- dataSink_.initialize(this, summaryMetadata);
+ try {
+ // Make a local in copy in case something else modifies it
+ summaryMetadata_ = new JSONObject(summaryMetadata.toString());
+ } catch (JSONException ex) {
+ System.err.print("Couldn't copy summaary metadata");
+ ex.printStackTrace();
+ }
+ if (dataSink_ != null) {
+ //It could be null if not using saving and viewing and diverting with custom processor
+ dataSink_.initialize(this, summaryMetadata);
+ }
}
}
diff --git a/src/main/java/org/micromanager/acqj/main/AcquisitionEvent.java b/src/main/java/org/micromanager/acqj/main/AcquisitionEvent.java
index 9b59336..34d3a5e 100644
--- a/src/main/java/org/micromanager/acqj/main/AcquisitionEvent.java
+++ b/src/main/java/org/micromanager/acqj/main/AcquisitionEvent.java
@@ -21,7 +21,9 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import mmcorej.org.json.JSONArray;
@@ -64,6 +66,8 @@ enum SpecialFlag {
private HashMap stageCoordinates_ = new HashMap();
// Mapping from device names to axis names
private HashMap stageDeviceNamesToAxisNames_ = new HashMap();
+ // tags to be added to the acquired image
+ private final HashMap tags_ = new HashMap();
//Option to not acquire an image for SLM events
private Boolean acquireImage_ = null;
@@ -142,6 +146,7 @@ public AcquisitionEvent copy() {
e.acquireImage_ = acquireImage_;
e.properties_ = new TreeSet(this.properties_);
e.camera_ = camera_;
+ e.setTags(tags_);
return e;
}
@@ -213,6 +218,14 @@ private static JSONObject eventToJSON(AcquisitionEvent e) {
json.put("camera", e.camera_);
}
+ if (e.getTags() != null) {
+ JSONObject jsonTags = new JSONObject();
+ for (Map.Entry entry : e.getTags().entrySet()) {
+ jsonTags.put(entry.getKey(), entry.getValue());
+ }
+ json.put("tags", jsonTags);
+ }
+
//TODO: galvo
//TODO: more support for imperative API calls (i.e. SLM set image)
//Arbitrary extra properties
@@ -223,7 +236,7 @@ private static JSONObject eventToJSON(AcquisitionEvent e) {
prop.put(t.prop);
prop.put(t.val);
props.put(prop);
- }
+ }
if (props.length() > 0) {
json.put("properties", props);
}
@@ -318,6 +331,17 @@ private static AcquisitionEvent eventFromJSON(JSONObject json, AcquisitionAPI ac
event.camera_ = json.getString("camera");
}
+ if (json.has("tags")) {
+ HashMap tags = new HashMap<>();
+ JSONObject jsonTags = json.getJSONObject("tags");
+ Iterator keys = jsonTags.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ tags.put(key, jsonTags.getString(key));
+ }
+ event.setTags(tags);
+ }
+
//TODO: galvo, etc (i.e. other aspects of imperative API)
@@ -434,9 +458,9 @@ public void setProperty(String device, String property, String value) {
}
/**
- * Set the minimum start time in ms relative to when the acq started
+ * Set the minimum start time in ms relative to when the acq started.
*
- * @param l
+ * @param l Minimum start time in ms.
*/
public void setMinimumStartTime(Long l) {
miniumumStartTime_ms_ = l;
@@ -544,7 +568,7 @@ public Double getZPosition() {
}
/**
- * get the minimum start timein system time
+ * Get the minimum start time in system time.
*
* @return
*/
@@ -621,6 +645,18 @@ public void setY(double y) {
yPosition_ = y;
}
+ public void setTags(HashMap tags) {
+ tags_.clear();
+ if (tags != null) {
+ tags_.putAll(tags);
+ }
+ }
+ public HashMap getTags () {
+ HashMap tags = new HashMap<>(tags_.size());
+ tags.putAll(tags_);
+ return tags;
+ }
+
//For debugging
@Override
diff --git a/tests/org/micromanager/acqj/main/TestAcquisitionEvent.java b/tests/org/micromanager/acqj/main/TestAcquisitionEvent.java
new file mode 100644
index 0000000..555db5e
--- /dev/null
+++ b/tests/org/micromanager/acqj/main/TestAcquisitionEvent.java
@@ -0,0 +1,41 @@
+package org.micromanager.acqj.main;
+
+import java.util.HashMap;
+import mmcorej.org.json.JSONObject;
+import org.junit.Assert;
+import org.junit.Test;
+import org.micromanager.acqj.example.BlackHoleDataSink;
+
+public class TestAcquisitionEvent {
+ @Test
+ public void testSetupIsSane() {
+ Assert.assertTrue("must be true", true);
+ }
+
+ @Test
+ public void testEventSerialization() {
+ BlackHoleDataSink bhds = new BlackHoleDataSink();
+ Acquisition acq = new Acquisition(bhds);
+ AcquisitionEvent event = new AcquisitionEvent(acq);
+ final int z = 2;
+ event.setZ(z, z * 0.5);
+ final int t = 3;
+ event.setTimeIndex(t);
+ final long minimumStartTime = 892567265;
+ event.setMinimumStartTime(minimumStartTime);
+ HashMap tags = new HashMap<>();
+ tags.put("test", "test");
+ tags.put("test2", "test2");
+
+ JSONObject eventSerial = event.toJSON();
+ AcquisitionEvent eventCopy = AcquisitionEvent.fromJSON(eventSerial, acq);
+
+ Assert.assertTrue(eventCopy.getZIndex() == z);
+ Assert.assertEquals(eventCopy.getZPosition(), z * 0.5, 0.0000001);
+ Assert.assertTrue(eventCopy.getTIndex() == t);
+ Assert.assertEquals(eventCopy.getMinimumStartTimeAbsolute(), minimumStartTime, 2000);
+ Assert.assertEquals(eventCopy.getTags().get("test"), event.getTags().get("test"));
+ Assert.assertEquals(eventCopy.getTags().get("test2"), event.getTags().get("test2"));
+ }
+}
+