diff --git a/.classpath b/.classpath
index 26c568d7..188c7291 100644
--- a/.classpath
+++ b/.classpath
@@ -6,9 +6,8 @@
-
+
-
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
index 217f240b..fd1fe9e4 100644
--- a/dependency-reduced-pom.xml
+++ b/dependency-reduced-pom.xml
@@ -4,7 +4,7 @@
org.pamguard
Pamguard
Pamguard Java12+
- 2.02.10ae
+ 2.02.10bb
Pamguard for Java 12+, using Maven to control dependcies
www.pamguard.org
diff --git a/pom.xml b/pom.xml
index 8051e75d..e6b66c5f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
org.pamguard
Pamguard
- 2.02.10ae
+ 2.02.10bb
Pamguard Java12+
Pamguard for Java 12+, using Maven to control dependcies
www.pamguard.org
diff --git a/src/PamController/DataIntegrityChecker.java b/src/PamController/DataIntegrityChecker.java
new file mode 100644
index 00000000..566298e4
--- /dev/null
+++ b/src/PamController/DataIntegrityChecker.java
@@ -0,0 +1,12 @@
+package PamController;
+
+/**
+ * Provides a set of functions that can check and repair data.
+ * @author dg50
+ *
+ */
+public interface DataIntegrityChecker {
+
+ public boolean checkDataStore();
+
+}
diff --git a/src/PamController/DataOutputStore.java b/src/PamController/DataOutputStore.java
index a3536410..05c88d66 100644
--- a/src/PamController/DataOutputStore.java
+++ b/src/PamController/DataOutputStore.java
@@ -29,4 +29,11 @@ public interface DataOutputStore extends OfflineDataStore {
*/
public boolean deleteDataFrom(long timeMillis);
+ /**
+ * Get a data integrity checker. This can be called at startup to see if there is a problem.
+ * @return
+ */
+ public DataIntegrityChecker getInegrityChecker();
+
+
}
diff --git a/src/PamController/PamguardVersionInfo.java b/src/PamController/PamguardVersionInfo.java
index f4710929..f579eca6 100644
--- a/src/PamController/PamguardVersionInfo.java
+++ b/src/PamController/PamguardVersionInfo.java
@@ -31,12 +31,12 @@ public class PamguardVersionInfo {
* Version number, major version.minorversion.sub-release.
* Note: can't go higher than sub-release 'f'
*/
- static public final String version = "2.02.10ae";
+ static public final String version = "2.02.10bb";
/**
* Release date
*/
- static public final String date = "15 March 2024";
+ static public final String date = "22 March 2024";
// /**
// * Release type - Beta or Core
diff --git a/src/PamUtils/worker/PamWorkDialog.java b/src/PamUtils/worker/PamWorkDialog.java
index b4a41d3f..2a3d89c8 100644
--- a/src/PamUtils/worker/PamWorkDialog.java
+++ b/src/PamUtils/worker/PamWorkDialog.java
@@ -1,5 +1,7 @@
package PamUtils.worker;
+import java.awt.Dimension;
+import java.awt.Point;
import java.awt.Window;
import javax.swing.BoxLayout;
@@ -23,6 +25,9 @@ public class PamWorkDialog extends PamDialog {
mainPanel.setBorder(new TitledBorder("Task Progress"));
// GridBagConstraints c = new PamGridBagContraints();
mainPanel.add(progressBar = new JProgressBar(0, 100));
+ Dimension sz = progressBar.getPreferredSize();
+ sz.width = 300;
+ progressBar.setPreferredSize(sz);
textRows = new PamTextDisplay[nTextRows];
for (int i = 0; i < nTextRows; i++) {
// c.gridy++;
@@ -35,6 +40,18 @@ public class PamWorkDialog extends PamDialog {
getButtonPanel().setVisible(false);
setDialogComponent(mainPanel);
setResizable(true);
+
+ if (parentFrame != null) {
+ Dimension prefSize = this.getPreferredSize();
+ Point screenLoc = parentFrame.getLocationOnScreen();
+ int x = (parentFrame.getWidth()-prefSize.width)/2;
+ int y = (parentFrame.getHeight()-prefSize.height)/2;
+ if (screenLoc != null) {
+ x += screenLoc.x;
+ y += screenLoc.y;
+ }
+ setLocation(x, y);
+ }
}
public void update(PamWorkProgressMessage progressMsg) {
diff --git a/src/PamView/panel/SeparatorBorder.java b/src/PamView/panel/SeparatorBorder.java
new file mode 100644
index 00000000..f42192d4
--- /dev/null
+++ b/src/PamView/panel/SeparatorBorder.java
@@ -0,0 +1,27 @@
+package PamView.panel;
+
+import java.awt.Component;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+
+import javax.swing.border.TitledBorder;
+
+public class SeparatorBorder extends TitledBorder {
+
+ private static final long serialVersionUID = 1L;
+
+ public SeparatorBorder(String title) {
+ super(title);
+ // TODO Auto-generated constructor stub
+ }
+ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+ FontMetrics fm = g.getFontMetrics();
+ if (fm != null) {
+ height = fm.getHeight()-EDGE_SPACING*2-1;
+ }
+ else {
+ height = 12;
+ }
+ super.paintBorder(c, g, x, y, width, height);
+ }
+}
diff --git a/src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java b/src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java
index 3577bd12..caff472e 100644
--- a/src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java
+++ b/src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java
@@ -29,6 +29,7 @@ public class RWTethysDataProvider extends AutoTethysProvider {
Parameters parameters = detection.getParameters();
parameters.setScore((double) rweDataUnit.rweSound.soundType);
double snr = 20.*Math.log10(rweDataUnit.rweSound.signal/rweDataUnit.rweSound.noise);
+ snr = AutoTethysProvider.roundDecimalPlaces(snr, 1);
parameters.setSNRDB(snr);
return detection;
diff --git a/src/binaryFileStorage/BinaryStore.java b/src/binaryFileStorage/BinaryStore.java
index 96b89b3f..1785eb6f 100644
--- a/src/binaryFileStorage/BinaryStore.java
+++ b/src/binaryFileStorage/BinaryStore.java
@@ -37,6 +37,7 @@ import dataMap.OfflineDataMap;
import dataMap.OfflineDataMapPoint;
import PamController.AWTScheduler;
import PamController.DataInputStore;
+import PamController.DataIntegrityChecker;
import PamController.DataOutputStore;
import PamController.OfflineDataStore;
import PamController.PamControlledUnit;
@@ -74,6 +75,7 @@ import annotation.binary.AnnotationBinaryData;
import annotation.handler.AnnotationHandler;
import backupmanager.BackupInformation;
import binaryFileStorage.backup.BinaryBackupStream;
+import binaryFileStorage.checker.BinaryIntegrityChecker;
import binaryFileStorage.layoutFX.BinaryStoreGUIFX;
/**
@@ -946,6 +948,8 @@ PamSettingsSource, DataOutputStore {
PamController.getInstance().notifyModelChanged(PamControllerInterface.CHANGED_OFFLINE_DATASTORE);
// System.out.println("BinaryDataMapMaker really done " + this);
dataMapMaker = null;
+
+ getInegrityChecker().checkDataStore();
}
@Override
@@ -2593,5 +2597,9 @@ PamSettingsSource, DataOutputStore {
public String getDataLocation() {
return binaryStoreSettings.getStoreLocation();
}
+ @Override
+ public DataIntegrityChecker getInegrityChecker() {
+ return new BinaryIntegrityChecker(this);
+ }
}
diff --git a/src/binaryFileStorage/checker/BinaryIntegrityChecker.java b/src/binaryFileStorage/checker/BinaryIntegrityChecker.java
new file mode 100644
index 00000000..011b9c05
--- /dev/null
+++ b/src/binaryFileStorage/checker/BinaryIntegrityChecker.java
@@ -0,0 +1,54 @@
+package binaryFileStorage.checker;
+
+import java.util.ArrayList;
+
+import PamController.DataIntegrityChecker;
+import PamController.PamController;
+import PamView.dialog.warn.WarnOnce;
+import PamguardMVC.PamDataBlock;
+import binaryFileStorage.BinaryStore;
+import dataMap.OfflineDataMap;
+
+public class BinaryIntegrityChecker implements DataIntegrityChecker {
+
+ private BinaryStore binaryStore;
+
+ public BinaryIntegrityChecker(BinaryStore binaryStore) {
+ this.binaryStore = binaryStore;
+ }
+
+ public boolean checkDataStore() {
+ checkMapOverlaps();
+
+
+ return true;
+ }
+
+ private boolean checkMapOverlaps() {
+ boolean ok = true;
+ ArrayList dataBlocks = binaryStore.getStreamingDataBlocks(false);
+ for (PamDataBlock aBlock : dataBlocks) {
+ ok &= checkMapOverlaps(aBlock);
+ }
+ return ok;
+ }
+
+ private boolean checkMapOverlaps(PamDataBlock aBlock) {
+ OfflineDataMap dataMap = aBlock.getOfflineDataMap(binaryStore);
+ if (dataMap == null) {
+ return true;
+ }
+ long overlaps = dataMap.checkOverlaps();
+ if (overlaps <= 0) {
+ return true;
+ }
+ String warn = String.format("Binary data %s has overlapping data files, with a maximum overlap of %3.1fs
"
+ + "This can occur when data have been reprocessed multiple times offline into "
+ + "the same folders.
Since files sometimes get slightly different names, old files are"
+ + "not always overwritten.
"
+ + "You should determine which files are 'old' by looking at file creation dates and delete them.",
+ aBlock.getLongDataName(), (double) overlaps / 1000.);
+ WarnOnce.showNamedWarning("BINOVERLAPWARNING", PamController.getMainFrame(), "Data Integrity Warning", warn, WarnOnce.WARNING_MESSAGE);
+ return false;
+ }
+}
diff --git a/src/clickDetector/dataSelector/ClickDataSelector.java b/src/clickDetector/dataSelector/ClickDataSelector.java
index a7a1d4d8..5be77c2a 100644
--- a/src/clickDetector/dataSelector/ClickDataSelector.java
+++ b/src/clickDetector/dataSelector/ClickDataSelector.java
@@ -112,14 +112,23 @@ public class ClickDataSelector extends DataSelector {
// see if there is a super detection and see if it's got a comment.
String comment = oev.getComment();
+
+ boolean isAutomatic = false;
if (comment != null) {
- if (clickAlarmParameters.onlineAutoEvents && comment.startsWith("Automatic")) {
- return true;
- }
- if (clickAlarmParameters.onlineManualEvents && comment.startsWith("Manual")) {
- return true;
- }
+ isAutomatic = comment.startsWith("Automatic");
}
+ if (isAutomatic && clickAlarmParameters.onlineAutoEvents) {
+ return true;
+ }
+ else if (clickAlarmParameters.onlineManualEvents) {
+ return true;
+ }
+// if (clickAlarmParameters.onlineAutoEvents && comment.startsWith("Automatic")) {
+// return true;
+// }
+// if (clickAlarmParameters.onlineManualEvents && comment.startsWith("Manual")) {
+// return true;
+// }
/*
* Otherwise need to work out where the hell the event type is in the
* list of event types and see if it's wanted.
diff --git a/src/clickDetector/dataSelector/ClickSelectPanel.java b/src/clickDetector/dataSelector/ClickSelectPanel.java
index 859ee841..512e088d 100644
--- a/src/clickDetector/dataSelector/ClickSelectPanel.java
+++ b/src/clickDetector/dataSelector/ClickSelectPanel.java
@@ -30,6 +30,8 @@ import PamView.dialog.PamDialog;
import PamView.dialog.PamDialogPanel;
import PamView.dialog.PamGridBagContraints;
import PamView.panel.PamAlignmentPanel;
+import PamView.panel.SeparatorBorder;
+import PamView.panel.WestAlignedPanel;
public class ClickSelectPanel implements PamDialogPanel {
@@ -40,6 +42,8 @@ public class ClickSelectPanel implements PamDialogPanel {
private ClickDataSelector clickDataSelector;
private JPanel mainPanel;
private boolean isViewer;
+
+ public static final String mainTip = "You should select options in both the Click Type and the Event Type panels";
public ClickSelectPanel(ClickDataSelector clickDataSelector, boolean allowScores, boolean useEventTypes) {
this.clickDataSelector = clickDataSelector;
@@ -79,7 +83,9 @@ public class ClickSelectPanel implements PamDialogPanel {
private LookupList lutList;
public EventTypePanel() {
- setBorder(new TitledBorder("Event Type Selection"));
+ setBorder(new SeparatorBorder("Event Type Selection"));
+
+ setToolTipText(mainTip);
}
void setParams() {
@@ -102,16 +108,24 @@ public class ClickSelectPanel implements PamDialogPanel {
boxPanel.add(onlineAuto = new JCheckBox("Automatically detected click trains"), c);
onlineAuto.setSelected(clickAlarmParameters.onlineAutoEvents);
+ useUnassigned.setToolTipText("Clicks that are NOT part of a manual or automatic click train");
+ onlineManual.setToolTipText("Clicks that are part of a manually marked click train");
+ onlineAuto.setToolTipText("Clicks that are part of an automatically detected click train");
+
lutList = LookUpTables.getLookUpTables().getLookupList(ClicksOffline.ClickTypeLookupName);
if (lutList == null) {
return;
}
+ c.gridy++;
+ boxPanel.add(new JLabel("OR the following click train types ...", JLabel.LEFT), c);
useType = new JCheckBox[lutList.getList().size()];
for (int i = 0; i < useType.length; i++) {
c.gridy++;
boxPanel.add(useType[i] = new JCheckBox(lutList.getList().get(i).getText()), c);
useType[i].setSelected(clickAlarmParameters.isUseEventType(lutList.getList().get(i).getCode()));
+ String tip = String.format("Clicks that are part of a click train labelled as %s", lutList.getList().get(i).getText());
+ useType[i].setToolTipText(tip);
}
}
@@ -158,10 +172,9 @@ public class ClickSelectPanel implements PamDialogPanel {
northPanel = new JPanel();
northPanel.setLayout(new GridBagLayout());
GridBagConstraints c = new PamGridBagContraints();
- northPanel.setBorder(new TitledBorder("Echoes"));
c.gridwidth = 3;
c.anchor = GridBagConstraints.WEST;
- northPanel.add(useEchoes = new JCheckBox("Use Echoes"), c);
+ northPanel.add(new PamAlignmentPanel(useEchoes = new JCheckBox("Use Echoes"), BorderLayout.WEST), c);
c.gridwidth = 1;
c.gridy++;
c.gridx = 0;
@@ -178,11 +191,13 @@ public class ClickSelectPanel implements PamDialogPanel {
northPanel.add(scoreByAmplitude = new JCheckBox("Score by amplitude"), c);
scoreByAmplitude.setVisible(allowScores);
scoreByAmplitude.addActionListener(new AllSpeciesListener());
- add(BorderLayout.NORTH, northPanel);
+ WestAlignedPanel walpn;
+ add(BorderLayout.NORTH, walpn = new WestAlignedPanel(northPanel));
+ walpn.setBorder(new SeparatorBorder("Echoes"));
JPanel centralOuterPanel = new JPanel(new BorderLayout());
centralPanel.setLayout(new GridBagLayout());
- centralOuterPanel.setBorder(new TitledBorder("Click Type Selection"));
+ centralOuterPanel.setBorder(new SeparatorBorder("Click Type Selection"));
add(BorderLayout.CENTER, centralOuterPanel);
JScrollPane scrollPane = new DialogScrollPane(new PamAlignmentPanel(centralPanel, BorderLayout.WEST), 10);
@@ -197,6 +212,9 @@ public class ClickSelectPanel implements PamDialogPanel {
clearAll.addActionListener(new AutoSelect(false));
centralOuterPanel.add(BorderLayout.SOUTH, new PamAlignmentPanel(centralEastPanel, BorderLayout.WEST));
+ centralOuterPanel.setToolTipText(mainTip);
+
+ setToolTipText(mainTip);
}
void setParams() {
diff --git a/src/dataMap/OfflineDataMap.java b/src/dataMap/OfflineDataMap.java
index d51ee120..d62f2224 100644
--- a/src/dataMap/OfflineDataMap.java
+++ b/src/dataMap/OfflineDataMap.java
@@ -844,4 +844,23 @@ abstract public class OfflineDataMap {
obs.updateDataMap(this, mapPoint);
}
}
+
+ /**
+ * Check to see if any data maps overlap in time.
+ * @return largest overlap between maps. 0 or -ve means no overlap.
+ */
+ public long checkOverlaps() {
+ long bigLap = Long.MIN_VALUE;
+ TmapPoint prevPoint = null;
+ for (TmapPoint mapPoint : mapPoints) {
+ if (prevPoint != null) {
+ long olap = prevPoint.getEndTime() - mapPoint.getStartTime();
+// if (mapPoint.getStartTime() < prevPoint.getEndTime()) {
+ bigLap = Math.max(olap, bigLap);
+// }
+ }
+ prevPoint = mapPoint;
+ }
+ return bigLap;
+ }
}
diff --git a/src/generalDatabase/DBControlUnit.java b/src/generalDatabase/DBControlUnit.java
index 0b4239b8..a48971da 100644
--- a/src/generalDatabase/DBControlUnit.java
+++ b/src/generalDatabase/DBControlUnit.java
@@ -18,6 +18,7 @@ import generalDatabase.backup.DatabaseBackupStream;
import pamScrollSystem.ViewLoadObserver;
import pamViewFX.pamTask.PamTaskUpdate;
import PamController.AWTScheduler;
+import PamController.DataIntegrityChecker;
import PamController.DataOutputStore;
import PamController.OfflineDataStore;
import PamController.PamConfiguration;
@@ -524,5 +525,10 @@ public class DBControlUnit extends DBControl implements DataOutputStore {
}
return state;
}
+ @Override
+ public DataIntegrityChecker getInegrityChecker() {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
diff --git a/src/tethys/CollectionHandler.java b/src/tethys/CollectionHandler.java
new file mode 100644
index 00000000..66208fa0
--- /dev/null
+++ b/src/tethys/CollectionHandler.java
@@ -0,0 +1,38 @@
+package tethys;
+
+abstract public class CollectionHandler {
+
+ private Collection collection;
+
+ protected TethysControl tethysControl;
+
+ /**
+ * @param tethysControl
+ * @param collection
+ */
+ public CollectionHandler(TethysControl tethysControl, Collection collection) {
+ this.tethysControl = tethysControl;
+ this.collection = collection;
+ }
+
+ public String collectionName() {
+ return collection.collectionName();
+ }
+
+ /**
+ * @return the collection
+ */
+ public Collection getCollection() {
+ return collection;
+ }
+
+ /**
+ * @return the tethysControl
+ */
+ public TethysControl getTethysControl() {
+ return tethysControl;
+ }
+
+ public abstract String getHelpPoint();
+
+}
diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java
index 83e445b7..42889f45 100644
--- a/src/tethys/TethysControl.java
+++ b/src/tethys/TethysControl.java
@@ -32,6 +32,8 @@ import PamController.PamSettingManager;
import PamController.PamSettings;
import PamUtils.PamFileChooser;
import PamUtils.PamFileFilter;
+import PamUtils.worker.PamWorkWrapper;
+import PamUtils.worker.PamWorker;
import PamView.PamTabPanel;
import PamView.dialog.warn.WarnOnce;
import PamguardMVC.PamDataBlock;
@@ -104,7 +106,9 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
serverCheckTimer = new Timer(10000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- checkServer();
+ ServerStatus serverState = checkServer();
+ // check less often when it's OK to generate less debug output.
+ serverCheckTimer.setDelay(serverState.ok ? 600000 : 5000);
}
});
serverCheckTimer.setInitialDelay(0);
@@ -469,6 +473,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
// case PamControllerInterface.INITIALIZATION_COMPLETE:
initializationStuff();
break;
+ case PamControllerInterface.HYDROPHONE_ARRAY_CHANGED:
+ sendStateUpdate(new TethysState(StateType.UPDATEMETADATA));
}
}
@@ -525,7 +531,32 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
}
}
- private void countProjectDetections() {
+// /**
+// * Count project detections in a worker thread;
+// */
+// public void countProjectDetectionsT() {
+// sendStateUpdate(new TethysState(StateType.EXPORTRDATA, Collection.Detections));
+// PamWorker worker = new PamWorker(new PamWorkWrapper() {
+//
+// @Override
+// public String runBackgroundTask(PamWorker pamWorker) {
+// countProjectDetections();
+// return null;
+// }
+//
+// @Override
+// public void taskFinished(String result) {
+// sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION, Collection.Detections));
+//
+// }
+// }, getGuiFrame(), 0, "Updating detections counts");
+// worker.start();
+// }
+
+ /**
+ * Count project detections in the current thread.
+ */
+ private synchronized void countProjectDetections() {
if (dataBlockSynchInfos == null) {
return;
}
@@ -549,12 +580,6 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
i++;
}
-// int[] counts = dbxmlQueries.countDataForProject(deplData.getProject(), dataPrefixes);
-// if (counts != null) {
-// for ( i = 0; i < counts.length; i++ ) {
-// dataBlockSynchInfos.get(i).setDataCount(counts[i]);
-// }
-// }
}
/**
@@ -688,11 +713,9 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
* @param dataBlock
*/
public void exportedDetections(PamDataBlock dataBlock) {
- sendStateUpdate(new TethysState(StateType.EXPORTRDATA, Collection.Detections));
countProjectDetections();
- sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION, Collection.Detections));
+// sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION, Collection.Detections));
}
-
/**
* @return the calibrationHandler
*/
diff --git a/src/tethys/TethysTimeFuncs.java b/src/tethys/TethysTimeFuncs.java
index 70d11704..e238fb22 100644
--- a/src/tethys/TethysTimeFuncs.java
+++ b/src/tethys/TethysTimeFuncs.java
@@ -12,6 +12,7 @@ import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import PamUtils.PamCalendar;
+import nilus.Helper;
public class TethysTimeFuncs {
@@ -54,6 +55,18 @@ public class TethysTimeFuncs {
* @return
*/
public static XMLGregorianCalendar fromGregorianXML(String gregorianString) {
+
+ try {
+ XMLGregorianCalendar xmlCal = Helper.timestamp(gregorianString);
+ return xmlCal;
+ } catch (DatatypeConfigurationException e1) {
+ // TODO Auto-generated catch block
+// e1.printStackTrace();
+ }
+ /**
+ * Above should work just fine. If it doesn't use my own code below...
+ */
+
// typical string is 2018-10-20T00:00:00Z
if (gregorianString == null) {
return null;
diff --git a/src/tethys/calibration/CalibrationHandler.java b/src/tethys/calibration/CalibrationHandler.java
index 37dc186e..2aff00d8 100644
--- a/src/tethys/calibration/CalibrationHandler.java
+++ b/src/tethys/calibration/CalibrationHandler.java
@@ -1,6 +1,5 @@
package tethys.calibration;
-import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -37,6 +36,7 @@ import nilus.MetadataInfo;
import nilus.QualityValueBasic;
import nilus.ResponsibleParty;
import tethys.Collection;
+import tethys.CollectionHandler;
import tethys.DocumentInfo;
import tethys.DocumentNilusObject;
import tethys.TethysControl;
@@ -52,9 +52,7 @@ import tethys.niluswraps.NilusUnpacker;
import tethys.pamdata.AutoTethysProvider;
import tethys.reporter.TethysReporter;
-public class CalibrationHandler implements TethysStateObserver {
-
- private TethysControl tethysControl;
+public class CalibrationHandler extends CollectionHandler implements TethysStateObserver {
private ArrayList> calibrationsList;
@@ -65,10 +63,14 @@ public class CalibrationHandler implements TethysStateObserver {
public static final String[] qaTypes = {"unverified", "valid", "invalid"};
private Helper nilusHelper;
+
+ public static final String helpPoint = "utilities.tethys.docs.calibrations";
+
/**
* @param tethysControl
*/
public CalibrationHandler(TethysControl tethysControl) {
+ super(tethysControl, Collection.Calibrations);
this.tethysControl = tethysControl;
calibrationsList = new ArrayList();
tethysControl.addStateObserver(this); try {
@@ -504,7 +506,7 @@ public class CalibrationHandler implements TethysStateObserver {
}
String seachPattern = makeChannelNamePart(iChan);
for (int i = 0; i < calibrationsList.size(); i++) {
- String docName = calibrationsList.get(i).getDocumentName();
+ String docName = calibrationsList.get(i).getDocumentId();
if (docName.endsWith(seachPattern)) {
return true;
}
@@ -570,4 +572,9 @@ public class CalibrationHandler implements TethysStateObserver {
}
return theseCals;
}
+
+ @Override
+ public String getHelpPoint() {
+ return helpPoint;
+ }
}
diff --git a/src/tethys/calibration/swing/CalibrationsContactCard.java b/src/tethys/calibration/swing/CalibrationsContactCard.java
index 2dc6e581..0ad03c36 100644
--- a/src/tethys/calibration/swing/CalibrationsContactCard.java
+++ b/src/tethys/calibration/swing/CalibrationsContactCard.java
@@ -5,6 +5,7 @@ import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.util.ArrayList;
import java.util.Date;
import javax.swing.BoxLayout;
@@ -18,6 +19,9 @@ import javax.xml.datatype.XMLGregorianCalendar;
import org.jdesktop.swingx.JXDatePicker;
+import Array.ArrayManager;
+import Array.Hydrophone;
+import Array.PamArray;
import PamView.dialog.PamDialog;
import PamView.dialog.PamGridBagContraints;
import PamView.panel.WestAlignedPanel;
@@ -43,6 +47,8 @@ public class CalibrationsContactCard extends CalibrationsCard {
private JButton copyDown, copyUp;
+ private JComboBox hydrophoneSelection;
+
public CalibrationsContactCard(PamWizard pamWizard) {
super(pamWizard, "Contact Details");
// TODO Auto-generated constructor stub
@@ -57,17 +63,26 @@ public class CalibrationsContactCard extends CalibrationsCard {
JPanel datePanel = new JPanel(new GridBagLayout());
JPanel lp = new WestAlignedPanel(datePanel);
- lp.setBorder(new TitledBorder("Calibration date"));
+ lp.setBorder(new TitledBorder("Date and hydrophones"));
GridBagConstraints c = new PamGridBagContraints();
datePanel.add(new JLabel("Calibration date: ", JLabel.RIGHT), c);
datePicker = new JXDatePicker();
c.gridx++;
datePanel.add(datePicker, c);
- c.gridx = 0;
- c.gridy++;
- datePanel.add(new JLabel("Update Frequency", JLabel.RIGHT), c);
+// c.gridx = 0;
+ c.gridx++;
+ datePanel.add(new JLabel(" Update Frequency ", JLabel.RIGHT), c);
c.gridx++;
datePanel.add(updateInterval, c);
+ c.gridx = 0;
+ c.gridy++;
+ datePanel.add(new JLabel(" Hydrophones ", JLabel.RIGHT), c);
+ c.gridwidth = 5;
+ c.gridx++;
+ hydrophoneSelection = new JComboBox<>();
+ datePanel.add(hydrophoneSelection, c);
+ hydrophoneSelection.setToolTipText("Select which hydrophone calibrations to export");
+
calibrator = new ResponsiblePartyPanel("Technical Person");
dataManager = new ResponsiblePartyPanel("Data Manager");
@@ -202,6 +217,16 @@ public class CalibrationsContactCard extends CalibrationsCard {
datePicker.setDate(new Date(TethysTimeFuncs.millisFromGregorianXML(ts)));
}
+ hydrophoneSelection.removeAllItems();
+ hydrophoneSelection.addItem("All hydrophones");
+ PamArray array = ArrayManager.getArrayManager().getCurrentArray();
+ ArrayList phones = array.getHydrophoneArray();
+ int i = 0;
+ for (Hydrophone phone : phones) {
+ String txt = String.format("Hydrophone %d, %s, %3.1f dBre1\u00B5Pa", i, phone.getType(), phone.getSensitivity());
+ hydrophoneSelection.addItem(txt);
+ i++;
+ }
}
diff --git a/src/tethys/calibration/swing/CalibrationsExportWizard.java b/src/tethys/calibration/swing/CalibrationsExportWizard.java
index 74bb356a..1ab7bc6e 100644
--- a/src/tethys/calibration/swing/CalibrationsExportWizard.java
+++ b/src/tethys/calibration/swing/CalibrationsExportWizard.java
@@ -5,6 +5,7 @@ import java.awt.Window;
import PamView.wizard.PamWizard;
import PamView.wizard.PamWizardCard;
import nilus.Calibration;
+import tethys.calibration.CalibrationHandler;
public class CalibrationsExportWizard extends PamWizard {
@@ -13,8 +14,9 @@ public class CalibrationsExportWizard extends PamWizard {
private CalibrationsExportWizard(Window parentFrame, Calibration sampleDocument) {
super(parentFrame, "Calibrations Export");
this.sampleDocument = sampleDocument;
- addCard(new CalibrationProcessCard(this));
addCard(new CalibrationsContactCard(this));
+ addCard(new CalibrationProcessCard(this));
+ setHelpPoint(CalibrationHandler.helpPoint);
}
public static Calibration showWizard(Window parentFrame, Calibration sampleDocument) {
diff --git a/src/tethys/calibration/swing/CalibrationsMainPanel.java b/src/tethys/calibration/swing/CalibrationsMainPanel.java
index e13365ef..cd1275f7 100644
--- a/src/tethys/calibration/swing/CalibrationsMainPanel.java
+++ b/src/tethys/calibration/swing/CalibrationsMainPanel.java
@@ -15,53 +15,47 @@ import PamView.panel.PamPanel;
import tethys.TethysControl;
import tethys.TethysState;
import tethys.calibration.CalibrationHandler;
+import tethys.deployment.PInstrument;
+import tethys.swing.TethysExportPanel;
import tethys.swing.TethysGUIPanel;
+import tethys.swing.TippedButton;
-public class CalibrationsMainPanel extends TethysGUIPanel {
+public class CalibrationsMainPanel extends TethysExportPanel {
private CalibrationHandler calibrationHandler;
private CalibrationsTable calibrationsTable;
- private JPanel mainPanel;
-
- private JPanel ctrlPanel;
-
- private JButton exportButton;
-
- private JLabel warning;
+// private JPanel mainPanel;
+//
+// private JPanel ctrlPanel;
+//
+// private TippedButton exportButton;
+//
+// private JLabel warning;
public CalibrationsMainPanel(TethysControl tethysControl, CalibrationHandler calibrationHandler) {
- super(tethysControl);
+ super(tethysControl, calibrationHandler, false);
this.calibrationHandler = calibrationHandler;
- mainPanel = new PamPanel(new BorderLayout());
+ JPanel mainPanel = getMainPanel();
+// mainPanel = new PamPanel(new BorderLayout());
mainPanel.setBorder(new TitledBorder("Instrument calibration information"));
calibrationsTable = new CalibrationsTable(tethysControl, calibrationHandler);
mainPanel.add(BorderLayout.CENTER, calibrationsTable.getComponent());
- ctrlPanel = new PamPanel(new BorderLayout());
- exportButton = new JButton("Export ...");
- ctrlPanel.add(BorderLayout.WEST, exportButton);
- warning = new JLabel();
- ctrlPanel.add(BorderLayout.CENTER, warning);
- mainPanel.add(BorderLayout.NORTH, ctrlPanel);
- exportButton.setToolTipText("Export calibration data to database");
- exportButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- exportCalibrations();
- }
- });
- }
-
- protected void exportCalibrations() {
- calibrationHandler.exportAllCalibrations();
- }
-
- @Override
- public JComponent getComponent() {
- return mainPanel;
+// ctrlPanel = new PamPanel(new BorderLayout());
+// exportButton = new TippedButton("Export ...", "Export calibration data to database");
+// ctrlPanel.add(BorderLayout.WEST, exportButton);
+// warning = new JLabel();
+// ctrlPanel.add(BorderLayout.CENTER, warning);
+// mainPanel.add(BorderLayout.NORTH, ctrlPanel);
+// exportButton.addActionListener(new ActionListener() {
+// @Override
+// public void actionPerformed(ActionEvent e) {
+// exportCalibrations();
+// }
+// });
}
@Override
@@ -71,7 +65,45 @@ public class CalibrationsMainPanel extends TethysGUIPanel {
}
private void enableControls() {
- exportButton.setEnabled(getTethysControl().isServerOk());
+ if (getTethysControl().isServerOk() == false) {
+ disableExport("Tethys Server not running");
+ return;
+ }
+ if (isHydrophoneNamed() == false) {
+ disableExport("Can't export calibrations until the Hydrophone array has been correctly named");
+ return;
+ };
+ enableExport(true);
+ }
+
+ /**
+ * Check to see if hydrophone is named correctly.
+ * @return
+ */
+ private boolean isHydrophoneNamed() {
+ PInstrument currentInstrument = getTethysControl().getDeploymentHandler().getCurrentArrayInstrument();
+ if (currentInstrument == null) {
+ return false;
+ }
+ if (currentInstrument.instrumentId == null || currentInstrument.instrumentType == null) {
+ return false;
+ }
+
+ if (currentInstrument.instrumentId.length() == 0 || currentInstrument.instrumentType.length() == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected void exportButtonPressed(ActionEvent e) {
+ calibrationHandler.exportAllCalibrations();
+ }
+
+ @Override
+ protected void optionsButtonPressed(ActionEvent e) {
+ // TODO Auto-generated method stub
+
}
}
diff --git a/src/tethys/calibration/swing/CalibrationsTable.java b/src/tethys/calibration/swing/CalibrationsTable.java
index 45b91f18..c9405f1a 100644
--- a/src/tethys/calibration/swing/CalibrationsTable.java
+++ b/src/tethys/calibration/swing/CalibrationsTable.java
@@ -120,6 +120,7 @@ public class CalibrationsTable extends TethysGUIPanel {
});
popMenu.add(menuItem);
}
+ popMenu.addSeparator();
if (n > 1) {
menuItem = new JMenuItem("Delete selected documents");
menuItem.addActionListener(new ActionListener() {
diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java
index 9b12ecd5..1da977f0 100644
--- a/src/tethys/dbxml/DBXMLConnect.java
+++ b/src/tethys/dbxml/DBXMLConnect.java
@@ -3,14 +3,17 @@ package tethys.dbxml;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.zip.GZIPOutputStream;
import javax.xml.bind.JAXBException;
@@ -67,7 +70,7 @@ public class DBXMLConnect {
}
return true;
}
-
+
/**
* Get the client. The client will only be recreated if the url changes
@@ -113,9 +116,9 @@ public class DBXMLConnect {
}
return file;
}
-
+
/**
- * Create a temporary nilus file.
+ * Create a temporary nilus file.
* @param nilusObject
* @return
* @throws TethysException
@@ -124,20 +127,20 @@ public class DBXMLConnect {
String tempName = getTempFileName(nilusObject);
tempName = tempDirectory.getAbsolutePath() + File.separator + tempName + ".xml";
File tempFile = new File(tempName);
- File retFile = createXMLDocument(nilusObject, tempFile);
+ File retFile = createXMLDocument(nilusObject, tempFile);
retFile.deleteOnExit();
return retFile;
}
-
-
+
+
public boolean postAndLog(Object nilusObject) throws TethysException
{
return postAndLog(nilusObject, null);
}
-
+
/**
* I don't think this should ever be used since everything goes a bit pear
- * shaped if the documentName isn't the same as the Id. However, for Calibration
+ * shaped if the documentName isn't the same as the Id. However, for Calibration
* documents this is no longer the case, since a Calibration can have multiple
* entries on different dates, so allow it !
* @param nilusObject
@@ -145,11 +148,11 @@ public class DBXMLConnect {
* @return
* @throws TethysException
*/
- public boolean postAndLog(Object nilusObject, String documentName) throws TethysException
- {
+ public boolean postAndLog(Object nilusObject, String documentName) throws TethysException
+ {
boolean ok = NilusChecker.warnEmptyFields(tethysControl.getGuiFrame(), nilusObject);
-
-
+
+
TethysException e = null;
boolean success = false;
try {
@@ -168,13 +171,13 @@ public class DBXMLConnect {
}
return success;
}
-
+
/**
* take a nilus object loaded with PamGuard data and post it to the Tethys database
*
* @param pamGuardObjs a nilus object loaded with PamGuard data
* @return error string, null string means there are no errors
- * @throws TethysException
+ * @throws TethysException
*/
private boolean postToTethys(Object nilusObject, String documentName) throws TethysException
{
@@ -192,12 +195,35 @@ public class DBXMLConnect {
MarshalXML marshal = new MarshalXML();
marshal.createInstance(objClass);
marshal.marshal(nilusObject, tempFile.toString());
+ // above lines have made a file. Are now going to gzip it before sending to Tethys
+ File zipFile = null;
+ try {
+ zipFile = zipOutputFile(tempFile);
+ }
+ catch (FileNotFoundException e1){
+ System.out.println(e1.getMessage());
+ }
+ catch (IOException e2) {
+ System.out.println(e2.getMessage());
+ }
+ String finalName;
+ if (zipFile == null) {
+ finalName = bodgeName;
+ }
+ else {
+ finalName = zipFile.toString();
+ }
+
+
// tempFile = stripXMLHeader(tempFile);
importReturn = Importer.ImportFiles(params.getFullServerName(), collection.collectionName(),
- new String[] { bodgeName }, "", "", false);
+ new String[] { finalName }, "", "", false);
tempFile.deleteOnExit();
+ if (zipFile != null) {
+ zipFile.deleteOnExit();
+ }
} catch(IllegalArgumentException e) {
throw new TethysException("IllegalArgumentException posting to Tethys: " + e.getMessage(), null);
} catch (IOException e) {
@@ -207,21 +233,49 @@ public class DBXMLConnect {
}
/*
- * The returned string consists of the file name, then an XML report.
- * Quite hard to see much common structure in this, so just look for
+ * The returned string consists of the file name, then an XML report.
+ * Quite hard to see much common structure in this, so just look for
* two words, and
*/
boolean error = importReturn.contains("");
String name = tempFile.getName();
TethysReporter.getTethysReporter().addReport(new TethysReport(success, collection, name, name));
-// error = !success; might be a better options.
+// error = !success; might be a better options.
if (error) {
throw new TethysException("Error posting to Tethys", importReturn);
}
return success;
}
+ /**
+ * Zip an xml file (or any file) into a gz file with a new end
+ * @param xmlFile
+ * @return
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ private File zipOutputFile(File xmlFile) throws FileNotFoundException, IOException {
+ String zipName = xmlFile.toString() + "-temp-.gz";
+ File zipFile = new File(zipName);
+ GZIPOutputStream opStream = new GZIPOutputStream(new FileOutputStream(zipFile));
+ InputStream fis = new FileInputStream(xmlFile);
+ int chunkSize = 100*1024;
+ byte[] buffer = new byte[chunkSize];
+// ZipEntry zipEntry = new ZipEntry(xmlFile.getName());
+ int bytesRead;
+ while ((bytesRead = fis.read(buffer)) >= 0) {
+ opStream.write(buffer, 0, bytesRead);
+ }
+
+ opStream.close();
+
+ fis.close();
+
+
+ return zipFile;
+ }
+
/**
* Update a document within Tethys. We're assuming that a
* document with the same name in the same collection already
@@ -229,7 +283,7 @@ public class DBXMLConnect {
* the removedocument function
* @param nilusDocument
* @return
- * @throws TethysException
+ * @throws TethysException
*/
public boolean updateDocument(Object nilusDocument) throws TethysException {
deleteDocument(nilusDocument);
@@ -242,7 +296,7 @@ public class DBXMLConnect {
* class to identify the correct collection.
* @param nilusDocument
* @return
- * @throws TethysException
+ * @throws TethysException
*/
public boolean deleteDocument(Object nilusDocument) throws TethysException {
@@ -259,7 +313,7 @@ public class DBXMLConnect {
- ['ECoastNARW0']
-An error will throw an exception.
+An error will throw an exception.
*/
}
catch (Exception e) {
@@ -275,7 +329,7 @@ An error will throw an exception.
* Delete a Deploymnet and any contained Detections document. Doesn't work !
* @param deploymentId
* @return
- * @throws TethysException
+ * @throws TethysException
*/
public boolean deleteDeployment(String deploymentId) throws TethysException {
ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocuments(deploymentId);
@@ -292,8 +346,8 @@ An error will throw an exception.
}
/**
- * Remove a document based on a collection name and a cdocument Id.
- * @param collection collection name.
+ * Remove a document based on a collection name and a cdocument Id.
+ * @param collection collection name.
* @param documentName document name (not the internal Document Id)
* @return
* @throws TethysException
@@ -303,8 +357,8 @@ An error will throw an exception.
}
/**
- * Remove a document based on a collection name and a document namw.
- * @param collectionName collection name.
+ * Remove a document based on a collection name and a document namw.
+ * @param collectionName collection name.
* @param documentName document name (not the internal Document Id)
* @return
* @throws TethysException
@@ -319,7 +373,7 @@ An error will throw an exception.
- ['ECoastNARW0']
- An error will throw an exception.
+ An error will throw an exception.
*/
}
catch (Exception e) {
@@ -330,14 +384,14 @@ An error will throw an exception.
}
/**
- * check the return string from importFiles and if it's an
- * error, throw an exception. Otherwise do nothing.
+ * check the return string from importFiles and if it's an
+ * error, throw an exception. Otherwise do nothing.
* @param fileError
*/
private void checkReturnString(String fileError) {
/**
* Example good string is
- *
+ *
C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xml: 7360 bytes
@@ -367,7 +421,7 @@ C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xmlnot: 0 b
/**
* Seems we have to get rid of the line
- * which is being put there by the marshaller ?
+ * which is being put there by the marshaller ?
* @param tempFile
*/
private File stripXMLHeader(File tempFile) {
@@ -377,7 +431,7 @@ C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xmlnot: 0 b
try {
BufferedReader reader = new BufferedReader(new FileReader(tempFile));
BufferedWriter writer = new BufferedWriter(new FileWriter(tempTemp));
- String line = reader.readLine();
+ String line = reader.readLine();
while (line != null) {
// see if the line has any unicode in it
int len = line.length();
diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java
index 460fbc66..d4c0be9a 100644
--- a/src/tethys/dbxml/DBXMLQueries.java
+++ b/src/tethys/dbxml/DBXMLQueries.java
@@ -515,6 +515,9 @@ public class DBXMLQueries {
* first query for Detections documents associated with this deployment and datablock.
* updated May 23
*/
+ if (dataBlock == null) {
+ return null;
+ }
String queryNoDepl = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"}],\"enclose\":1}";
String queryWithDepl = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"TheDeploymentId\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"}],\"enclose\":1}";
String query;
diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java
index 0c435fc0..8d82ac48 100644
--- a/src/tethys/deployment/DeploymentHandler.java
+++ b/src/tethys/deployment/DeploymentHandler.java
@@ -73,6 +73,7 @@ import nilus.UnknownSensor;
import pamMaths.PamVector;
import pamMaths.STD;
import tethys.Collection;
+import tethys.CollectionHandler;
import tethys.TethysControl;
import tethys.TethysLocationFuncs;
import tethys.TethysState;
@@ -83,6 +84,7 @@ import tethys.TethysState.StateType;
import tethys.dbxml.DBXMLConnect;
import tethys.dbxml.TethysException;
import tethys.deployment.swing.DeploymentWizard;
+import tethys.deployment.swing.EffortProblemDialog;
import tethys.deployment.swing.RecordingGapDialog;
import tethys.niluswraps.PDeployment;
import tethys.output.TethysExportParams;
@@ -98,16 +100,9 @@ import tethys.swing.DeploymentTableObserver;
* @author dg50
*
*/
-public class DeploymentHandler implements TethysStateObserver, DeploymentTableObserver {
+public class DeploymentHandler extends CollectionHandler implements TethysStateObserver, DeploymentTableObserver {
- private TethysControl tethysControl;
-
- /**
- * @return the tethysControl
- */
- public TethysControl getTethysControl() {
- return tethysControl;
- }
+// private TethysControl tethysControl;
private EffortFunctions effortFunctions;
@@ -119,8 +114,11 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
private DeploymentExportOpts deploymentExportOptions = new DeploymentExportOpts();
+ public static final String helpPoint = "utilities.tethys.docs.deployments";
+
public DeploymentHandler(TethysControl tethysControl) {
- super();
+
+ super(tethysControl, Collection.Deployments);
this.tethysControl = tethysControl;
@@ -368,8 +366,39 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
public void createPamguardOverview() {
deploymentOverview = effortFunctions.makeRecordingOverview();
+
+ checkDeploymentOverview(deploymentOverview);
+
updateProjectDeployments();
matchPamguard2Tethys(deploymentOverview, projectDeployments);
+
+ tethysControl.sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION));
+ }
+
+ /**
+ * Check the deployment overview for consistency.
+ * Take the raw audio information and the binary information and check they are similar.
+ * if not, ask the user what to do.
+ * @param deploymentOverview
+ */
+ private void checkDeploymentOverview(DeploymentOverview overview) {
+ RecordingList rawList = overview.getRawDataList();
+ RecordingList binList = overview.getBinaryDataList();
+ if (rawList == null || binList == null) {
+ return; // nothing to do
+ }
+ double similarity = rawList.getSimilarity(binList);
+ if (similarity > 0.95) {
+ return;
+ }
+ /*
+ * if we get here, it seems like the two lists are very different, so
+ * show a dialog to ask the user what to do.
+ */
+ RecordingList selList = EffortProblemDialog.showDialog(tethysControl.getGuiFrame(), overview);
+ if (selList != null) {
+ tethysControl.getTethysExportParams().setEffortSourceName(selList.getSourceName());
+ }
}
/**
@@ -381,22 +410,22 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
if (exportOptions != null) {
this.deploymentExportOptions = exportOptions;
deploymentOverview = getDeploymentOverview();
- ArrayList allPeriods = deploymentOverview.getRecordingPeriods();
+ RecordingList allPeriods = deploymentOverview.getMasterList(getTethysControl());
exportDeployments(allPeriods);
}
}
/**
* Export deployments docs. Playing with a couple of different ways of doing this.
- * @param selectedDeployments
+ * @param allPeriods
*/
- public void exportDeployments(ArrayList selectedDeployments) {
+ public void exportDeployments(RecordingList allPeriods) {
TethysReporter.getTethysReporter().clear();
if (deploymentExportOptions.separateDeployments) {
- exportSeparateDeployments(selectedDeployments);
+ exportSeparateDeployments(allPeriods);
}
else {
- exportOneDeploymnet(selectedDeployments);
+ exportOneDeploymnet(allPeriods);
}
TethysReporter.getTethysReporter().showReport(tethysControl.getGuiFrame(), true);
}
@@ -404,7 +433,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
/**
* Make one big deployment document with all the recording periods in it.
*/
- private void exportOneDeploymnet(ArrayList selectedDeployments) {
+ private void exportOneDeploymnet(RecordingList recordingList) {
// do the lot, whatever ...
Float sampleRate = null;
AcquisitionControl daq = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.class, null);
@@ -414,10 +443,9 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
sampleRate = daqParams.sampleRate;
}
- selectedDeployments = getDeploymentOverview().getRecordingPeriods();
int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId();
- RecordingPeriod onePeriod = new RecordingPeriod(selectedDeployments.get(0).getRecordStart(),
- selectedDeployments.get(selectedDeployments.size()-1).getRecordStop());
+ RecordingPeriod onePeriod = new RecordingPeriod(recordingList.getStart(),
+ recordingList.getEnd());
TethysExportParams exportParams = tethysControl.getTethysExportParams();
String id = String.format("%s_%s", exportParams.getDatasetName(), "all");
Deployment deployment = createDeploymentDocument(freeId, onePeriod, id);
@@ -425,7 +453,8 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
Deployment globalMeta = getTethysControl().getGlobalDeplopymentData();
deployment.setCruise(globalMeta.getCruise());
deployment.setSite(globalMeta.getSite());
- if (selectedDeployments.size() > 1) {
+ ArrayList effortPeriods = recordingList.getEffortPeriods();
+ if (recordingList.size() > 1) {
// // now need to remove the sampling details - don't though, add invalid periods instead.
// SamplingDetails samplingDetails = deployment.getSamplingDetails();
// samplingDetails.getChannel().clear();
@@ -440,9 +469,9 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
deployment.setQualityAssurance(qa = new AcousticDataQAType());
}
List qualityList = qa.getQuality();
- for (int i = 1; i < selectedDeployments.size(); i++) {
- long end = selectedDeployments.get(i-1).getRecordStop();
- long start = selectedDeployments.get(i).getRecordStart();
+ for (int i = 1; i < recordingList.size(); i++) {
+ long end = effortPeriods.get(i-1).getRecordStop();
+ long start = effortPeriods.get(i).getRecordStart();
Quality q = new Quality();
q.setStart(TethysTimeFuncs.xmlGregCalFromMillis(end));
q.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(start));
@@ -479,14 +508,15 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
/**
* Make a separate deployment document for every recording period.
*/
- private void exportSeparateDeployments(ArrayList selectedDeployments) {
+ private void exportSeparateDeployments(RecordingList recordingList) {
int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId();
// fill in a few things from here
Deployment globalMeta = getTethysControl().getGlobalDeplopymentData();
TethysExportParams exportParams = tethysControl.getTethysExportParams();
- for (int i = 0; i < selectedDeployments.size(); i++) {
- RecordingPeriod recordPeriod = selectedDeployments.get(i);
+ ArrayList effortPeriods = recordingList.getEffortPeriods();
+ for (int i = 0; i < recordingList.size(); i++) {
+ RecordingPeriod recordPeriod = effortPeriods.get(i);
PDeployment exDeploymnet = recordPeriod.getMatchedTethysDeployment();
Deployment deployment = null;
String id = String.format("%s_%d", exportParams.getDatasetName(), i);
@@ -532,8 +562,9 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
if (deployments == null || deploymentOverview == null) {
return;
}
- ArrayList recordingPeriods = deploymentOverview.getRecordingPeriods();
- for (RecordingPeriod aPeriod : recordingPeriods) {
+ RecordingList recordingList = deploymentOverview.getMasterList(getTethysControl());
+ ArrayList effortPeriods = recordingList.getEffortPeriods();
+ for (RecordingPeriod aPeriod : effortPeriods) {
PDeployment closestDeployment = findClosestDeployment(aPeriod, deployments);
aPeriod.setMatchedTethysDeployment(closestDeployment);
if (closestDeployment != null) {
@@ -592,7 +623,8 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
if (deploymentOverview == null) {
return matched;
}
- for (RecordingPeriod period : deploymentOverview.getRecordingPeriods()) {
+ ArrayList effortPeriods = deploymentOverview.getMasterList(getTethysControl()).getEffortPeriods();
+ for (RecordingPeriod period : effortPeriods) {
PDeployment deployment = period.getMatchedTethysDeployment();
if (deployment != null) {
if (matched.contains(deployment) == false) {
@@ -1230,7 +1262,8 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
}
regimens.add(regimen);
- DutyCycleInfo dutyCycleInf = deploymentOverview.getDutyCycleInfo();
+ RecordingList recordingList = deploymentOverview.getMasterList(getTethysControl());
+ DutyCycleInfo dutyCycleInf = recordingList.assessDutyCycle();
boolean isDS = dutyCycleInf != null && dutyCycleInf.isDutyCycled;
if (isDS) {
DutyCycle dutyCycle = new DutyCycle();
@@ -1307,4 +1340,9 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
return deploymentExportOptions;
}
+ @Override
+ public String getHelpPoint() {
+ return helpPoint;
+ }
+
}
diff --git a/src/tethys/deployment/DeploymentOverview.java b/src/tethys/deployment/DeploymentOverview.java
index f296df8e..3f39c653 100644
--- a/src/tethys/deployment/DeploymentOverview.java
+++ b/src/tethys/deployment/DeploymentOverview.java
@@ -1,16 +1,6 @@
package tethys.deployment;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.ListIterator;
-
-import Acquisition.AcquisitionControl;
-import Acquisition.AcquisitionParameters;
-import Acquisition.DaqStatusDataUnit;
-import PamController.PamControlledUnit;
-import PamController.PamController;
-import PamguardMVC.PamDataBlock;
+import tethys.TethysControl;
/**
* Class to give a general overview of all the effort in PAMGuard which will form the
@@ -22,59 +12,83 @@ import PamguardMVC.PamDataBlock;
*/
public class DeploymentOverview {
- private ArrayList recordingPeriods = new ArrayList<>();
+ private RecordingList rawDataList;
- private DutyCycleInfo dutyCycleInfo;
+ private RecordingList binaryDataList;
+
+// private DutyCycleInfo dutyCycleInfo;
- public DeploymentOverview(DutyCycleInfo dutyCycleInfo) {
- super();
- this.dutyCycleInfo = dutyCycleInfo;
- }
+// public DeploymentOverview(DutyCycleInfo dutyCycleInfo) {
+// super();
+// this.dutyCycleInfo = dutyCycleInfo;
+// }
- public DeploymentOverview(DutyCycleInfo dutyCycleInfo, ArrayList tempPeriods) {
- this.dutyCycleInfo = dutyCycleInfo;
- this.recordingPeriods = tempPeriods;
- }
-
-
- public void addRecordingPeriod(long start, long stop) {
- addRecordingPeriod(new RecordingPeriod(start, stop));
+ public DeploymentOverview(DutyCycleInfo dutyCycleInfo, RecordingList rawDataList, RecordingList binaryDataList) {
+// this.dutyCycleInfo = dutyCycleInfo;
+ this.rawDataList = rawDataList;
+ this.binaryDataList = binaryDataList;
}
- private void addRecordingPeriod(RecordingPeriod recordingPeriod) {
- recordingPeriods.add(recordingPeriod);
+ /**
+ * @return the rawDataList
+ */
+ public RecordingList getRawDataList() {
+ return rawDataList;
}
- public ArrayList getRecordingPeriods() {
- return recordingPeriods;
+ /**
+ * @return the binaryDataList
+ */
+ public RecordingList getBinaryDataList() {
+ return binaryDataList;
}
- public DutyCycleInfo getDutyCycleInfo() {
- return dutyCycleInfo;
+// /**
+// * @return the dutyCycleInfo
+// */
+// public DutyCycleInfo getDutyCycleInfo() {
+// return dutyCycleInfo;
+// }
+
+ public RecordingList getMasterList(TethysControl tethysControl) {
+ return getMasterList(tethysControl.getTethysExportParams().getEffortSourceName());
+ }
+
+ public RecordingList getMasterList(String effortSourceName) {
+ if (effortSourceName == null) {
+ return getLongestList();
+ }
+ if (binaryDataList != null & binaryDataList.getSourceName().equals(effortSourceName)) {
+ return binaryDataList;
+ }
+ if (rawDataList != null & rawDataList.getSourceName().equals(effortSourceName)) {
+ return rawDataList;
+ }
+ return getLongestList();
}
/**
- * Get the start time of the first recording
+ * Get the recording list with the greatest duration (start to end)
+ * not looking at coverage between those times.
* @return
*/
- public Long getFirstStart() {
- if (recordingPeriods.size() > 0) {
- return recordingPeriods.get(0).getRecordStart();
+ public RecordingList getLongestList() {
+ if (binaryDataList == null) {
+ return rawDataList;
}
- return null;
- }
-
- /**
- * Get the end time of the last recording
- * @return
- */
- public Long getLastEnd() {
- if (recordingPeriods.size() > 0) {
- return recordingPeriods.get(recordingPeriods.size()-1).getRecordStop();
+ if (rawDataList == null) {
+ return binaryDataList;
+ }
+ long lRaw = rawDataList.duration();
+ long lBin = binaryDataList.duration();
+
+ if (lRaw > lBin) {
+ return rawDataList;
+ }
+ else {
+ return binaryDataList;
}
- return null;
}
-
-
+
}
diff --git a/src/tethys/deployment/EffortFunctions.java b/src/tethys/deployment/EffortFunctions.java
index 2fd8f383..b7e5b260 100644
--- a/src/tethys/deployment/EffortFunctions.java
+++ b/src/tethys/deployment/EffortFunctions.java
@@ -41,36 +41,40 @@ public class EffortFunctions {
this.tethysControl = tethysControl;
}
- private DeploymentOverview createOverview(RecordingList tempPeriods) {
-
- DutyCycleInfo dutyCycleinfo = assessDutyCycle(tempPeriods);
- if (dutyCycleinfo == null) {
- return null;
- }
-
- // if it's duty cycles, then we only want a single entry.
- RecordingList deploymentPeriods;
- if (dutyCycleinfo.isDutyCycled == false) {
- deploymentPeriods = tempPeriods;
- }
- else {
- deploymentPeriods = new RecordingList();
- deploymentPeriods.add(new RecordingPeriod(tempPeriods.get(0).getRecordStart(), tempPeriods.get(tempPeriods.size()-1).getRecordStop()));
- }
- /*
- * do another sort of the deploymentPeriods. The start stops were in the order they went into the
- * database in the hope that pairs were the right way round. Now check all data are/
- */
- Collections.sort(deploymentPeriods, new Comparator() {
- @Override
- public int compare(RecordingPeriod o1, RecordingPeriod o2) {
- return (int) (o1.getRecordStart()-o2.getRecordStart());
- }
- });
-
- DeploymentOverview deploymentOverview = new DeploymentOverview(dutyCycleinfo, deploymentPeriods);
- return deploymentOverview;
- }
+// private DeploymentOverview createOverview(RecordingList tempPeriods) {
+//
+// tempPeriods.sort();
+//
+// DutyCycleInfo dutyCycleinfo = assessDutyCycle(tempPeriods);
+// if (dutyCycleinfo == null) {
+// return null;
+// }
+//
+//
+// // if it's duty cycles, then we only want a single entry.
+// RecordingList deploymentPeriods;
+// if (dutyCycleinfo.isDutyCycled == false) {
+// deploymentPeriods = tempPeriods;
+// }
+// else {
+// deploymentPeriods = new RecordingList(tempPeriods.getSourceName());
+// deploymentPeriods.add(new RecordingPeriod(tempPeriods.get(0).getRecordStart(), tempPeriods.get(tempPeriods.size()-1).getRecordStop()));
+// }
+// /*
+// * do another sort of the deploymentPeriods. The start stops were in the order they went into the
+// * database in the hope that pairs were the right way round. Now check all data are/
+// */
+// deploymentPeriods.sort();
+//// Collections.sort(deploymentPeriods, new Comparator() {
+//// @Override
+//// public int compare(RecordingPeriod o1, RecordingPeriod o2) {
+//// return (int) (o1.getRecordStart()-o2.getRecordStart());
+//// }
+//// });
+//
+// DeploymentOverview deploymentOverview = new DeploymentOverview(dutyCycleinfo, deploymentPeriods);
+// return deploymentOverview;
+// }
public DeploymentOverview makeRecordingOverview() {
@@ -79,13 +83,18 @@ public class EffortFunctions {
RecordingList binaryPeriods = listBinaryFiles();
- long l1 = listDuration(recordingPeriods);
- long l2 = listDuration(binaryPeriods);
- if (listDuration(binaryPeriods) > listDuration(recordingPeriods)) {
- recordingPeriods = binaryPeriods;
- }
+ // see what the similarity is between them
+// double sim = recordingPeriods.getSimilarity(binaryPeriods);
+// double testSim = recordingPeriods.getSimilarity(recordingPeriods);
- DeploymentOverview deploymentOverview = createOverview(recordingPeriods);
+// long l1 = listDuration(recordingPeriods);
+// long l2 = listDuration(binaryPeriods);
+// if (listDuration(binaryPeriods) > listDuration(recordingPeriods)) {
+// recordingPeriods = binaryPeriods;
+// }
+//
+// DeploymentOverview deploymentOverview = createOverview(recordingPeriods);
+ DeploymentOverview deploymentOverview = new DeploymentOverview(null, recordingPeriods, binaryPeriods);
return deploymentOverview;
}
@@ -128,7 +137,8 @@ public class EffortFunctions {
}
}
}
- bestList = mergeRecordings(bestList);
+ DeploymentExportOpts exportOptions = tethysControl.getDeploymentHandler().getDeploymentExportOptions();
+ bestList.mergeRecordingPeriods(exportOptions.maxRecordingGapSeconds*1000);
return bestList;
}
@@ -138,7 +148,7 @@ public class EffortFunctions {
if (mapPoints == null) {
return null;
}
- RecordingList periods = new RecordingList();
+ RecordingList periods = new RecordingList(dataMap.getDataMapName());
for (OfflineDataMapPoint mapPoint : mapPoints) {
periods.add(new RecordingPeriod(mapPoint.getStartTime(), mapPoint.getEndTime()));
}
@@ -219,117 +229,60 @@ public class EffortFunctions {
// PamCalendar.formatDBDateTime(aP.getRecordStop()));
// }
- tempPeriods = mergeRecordings(tempPeriods);
-
- return tempPeriods;
- }
-
- /**
- * Merge close recordings and discard ones that are too short.
- * @param tempPeriods all recording periods, may be from consecutive files.
- * @return merged list.
- */
- private RecordingList mergeRecordings(RecordingList tempPeriods) {
- // now go through those and merge into longer periods where there is no gap between files.
- if (tempPeriods == null) {
- return null;
- }
-
+ tempPeriods.sort();
DeploymentExportOpts exportOptions = tethysControl.getDeploymentHandler().getDeploymentExportOptions();
-
- ListIterator iterator = tempPeriods.listIterator();
- RecordingPeriod prevPeriod = null;
- while (iterator.hasNext()) {
- RecordingPeriod nextPeriod = iterator.next();
- long nextDur = nextPeriod.getRecordStop()-nextPeriod.getRecordStart();
- if (nextDur == 0) {
- continue;
- }
- if (prevPeriod != null) {
- long gap = nextPeriod.getRecordStart() - prevPeriod.getRecordStop();
- long prevDur = prevPeriod.getRecordStop()-prevPeriod.getRecordStart();
- if (gap < exportOptions.maxRecordingGapSeconds*1000) {
- // ignoring up to 3s gap or a sample error < 2%.Dunno if this is sensible or not.
- prevPeriod.setRecordStop(nextPeriod.getRecordStop());
- iterator.remove();
- nextPeriod = prevPeriod;
- }
- }
- prevPeriod = nextPeriod;
- }
- // now remove ones which are too short even after merging.
- iterator = tempPeriods.listIterator();
- while (iterator.hasNext()) {
- RecordingPeriod nextPeriod = iterator.next();
- long duration = nextPeriod.getDuration();
- if (duration < exportOptions.minRecordingLengthSeconds*1000L) {
- iterator.remove();
- }
- }
+ tempPeriods.mergeRecordingPeriods(exportOptions.maxRecordingGapSeconds*1000);
return tempPeriods;
}
- /**
- * Work out whether or not the data are evenly duty cycled by testing the
- * distributions of on and off times.
- * @param tempPeriods
- * @return
- */
- private DutyCycleInfo assessDutyCycle(RecordingList tempPeriods) {
- if (tempPeriods == null) {
- return null;
- }
- int n = tempPeriods.size();
- if (n < 2) {
- return new DutyCycleInfo(false, 0,0,n);
- }
- double[] ons = new double[n-1]; // ignore the last one since it may be artificially shortened which is OK
- double[] gaps = new double[n-1];
- for (int i = 0; i < n-1; i++) {
- ons[i] = tempPeriods.get(i).getDuration()/1000.;
- gaps[i] = (tempPeriods.get(i+1).getRecordStart()-tempPeriods.get(i).getRecordStop())/1000.;
- }
- /* now look at how consistent those values are
- * But some data gets messed by small gaps, so want to
- * remove outliers and concentrate on say 80% of the data.
- */
- ons = getDistributionCentre(ons, 80);
- gaps = getDistributionCentre(gaps, 80);
- Arrays.sort(gaps);
+// /**
+// * Merge close recordings and discard ones that are too short.
+// * @param tempPeriods all recording periods, may be from consecutive files.
+// * @return merged list.
+// */
+// private RecordingList mergeRecordings(RecordingList tempPeriods) {
+// // now go through those and merge into longer periods where there is no gap between files.
+// if (tempPeriods == null) {
+// return null;
+// }
+//
+// DeploymentExportOpts exportOptions = tethysControl.getDeploymentHandler().getDeploymentExportOptions();
+//
+// ListIterator iterator = tempPeriods.listIterator();
+// RecordingPeriod prevPeriod = null;
+// while (iterator.hasNext()) {
+// RecordingPeriod nextPeriod = iterator.next();
+// long nextDur = nextPeriod.getRecordStop()-nextPeriod.getRecordStart();
+// if (nextDur == 0) {
+// continue;
+// }
+// if (prevPeriod != null) {
+// long gap = nextPeriod.getRecordStart() - prevPeriod.getRecordStop();
+// long prevDur = prevPeriod.getRecordStop()-prevPeriod.getRecordStart();
+// if (gap < exportOptions.maxRecordingGapSeconds*1000) {
+// // ignoring up to 3s gap or a sample error < 2%.Dunno if this is sensible or not.
+// prevPeriod.setRecordStop(nextPeriod.getRecordStop());
+// iterator.remove();
+// nextPeriod = prevPeriod;
+// }
+// }
+// prevPeriod = nextPeriod;
+// }
+// // now remove ones which are too short even after merging.
+// iterator = tempPeriods.listIterator();
+// while (iterator.hasNext()) {
+// RecordingPeriod nextPeriod = iterator.next();
+// long duration = nextPeriod.getDuration();
+// if (duration < exportOptions.minRecordingLengthSeconds*1000L) {
+// iterator.remove();
+// }
+// }
+//
+// return tempPeriods;
+// }
- STD std = new STD();
- double onsMean = std.getMean(ons);
- double onsSTD = std.getSTD(ons);
- double gapsMean = std.getMean(gaps);
- double gapsSTD = std.getSTD(gaps);
- boolean dutyCycle = onsSTD/onsMean < .05 && gapsSTD/gapsMean < 0.05;
- DutyCycleInfo cycleInfo = new DutyCycleInfo(dutyCycle, onsMean, gapsMean, tempPeriods.size());
- return cycleInfo;
- }
-
- /**
- * Get the central part of a distribution without any outliers so
- * that we can get a better assessment of duty cycle.
- * @param data unsorted distribution data.
- * @param percent percentage to include (half this removed from top and bottom)
- * @return
- */
- private double[] getDistributionCentre(double[] data, double percent) {
- if (data == null) {
- return null;
- }
- Arrays.sort(data);
- int nRem = (int) Math.round(data.length * (100-percent)/200);
- int newLen = data.length-nRem*2;
- double[] subdata = Arrays.copyOfRange(data, nRem, data.length-2*nRem);
- if (subdata.length < 2) {
- return data;
- }
- return subdata;
- }
-
/**
* Get data times from any other datamap, since this will generally match the acquisition anyway
@@ -360,16 +313,17 @@ public class EffortFunctions {
return null;
}
// get the times out of it.
- RecordingList recPeriods = new RecordingList();
+ RecordingList recPeriods = new RecordingList(bestMap.getDataMapName());
List mapPoints = bestMap.getMapPoints();
for (OfflineDataMapPoint mapPoint : mapPoints) {
recPeriods.add(new RecordingPeriod(mapPoint.getStartTime(), mapPoint.getEndTime()));
+ recPeriods.add(mapPoint.getStartTime(), mapPoint.getEndTime());
}
return recPeriods;
}
private RecordingList extractTimesFromStatus(ArrayList allStatusData) {
- RecordingList tempPeriods = new RecordingList();
+ RecordingList tempPeriods = new RecordingList("Data acquisition status");
long dataStart = Long.MAX_VALUE;
long dataEnd = Long.MIN_VALUE;
Long lastStart = null;
diff --git a/src/tethys/deployment/RecordingList.java b/src/tethys/deployment/RecordingList.java
index 34b41c1d..038c0e69 100644
--- a/src/tethys/deployment/RecordingList.java
+++ b/src/tethys/deployment/RecordingList.java
@@ -1,12 +1,43 @@
package tethys.deployment;
+import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Iterator;
-public class RecordingList extends ArrayList {
+import PamUtils.PamCalendar;
+import pamMaths.STD;
+
+/**
+ * Information about periods of effort that might come from either the raw data recordings or
+ * an analysis of binary data maps.
+ * @author dg50
+ *
+ */
+public class RecordingList implements Serializable {
private static final long serialVersionUID = 1L;
+
+ private ArrayList effortPeriods = new ArrayList();
+
+ /**
+ * Name / source of this list.
+ */
+ private String sourceName;
+
+ /**
+ * @param sourceName
+ */
+ public RecordingList(String sourceName) {
+ this.sourceName = sourceName;
+ }
+
+ public RecordingList(String sourceName, ArrayList selectedDeployments) {
+ this.sourceName = sourceName;
+ this.effortPeriods = selectedDeployments;
+ }
/**
* Get the duration of the recording periods from start to end.
@@ -21,27 +52,27 @@ public class RecordingList extends ArrayList {
* @return
*/
public long getStart() {
- if (size() == 0) {
+ if (effortPeriods.size() == 0) {
return 0;
}
- return get(0).getRecordStart();
+ return effortPeriods.get(0).getRecordStart();
}
/**
* get the end of the last in the list.
*/
public long getEnd() {
- if (size() == 0) {
+ if (effortPeriods.size() == 0) {
return 0;
}
- return get(size()-1).getRecordStop();
+ return effortPeriods.get(effortPeriods.size()-1).getRecordStop();
}
/**
* Sort the list in ascending order.
*/
public void sort() {
- Collections.sort(this, new Comparator() {
+ Collections.sort(effortPeriods, new Comparator() {
@Override
public int compare(RecordingPeriod o1, RecordingPeriod o2) {
@@ -49,4 +80,172 @@ public class RecordingList extends ArrayList {
}
});
}
+
+ /**
+ * Get the coverage as a fraction. This is the sum of the individual periods divided
+ * by the start to end times
+ * @return
+ */
+ public double getCoverage() {
+ long cov = 0;
+ long durTot = 0;
+ if (effortPeriods.size() == 0) {
+ return 0;
+ }
+ Iterator it = effortPeriods.iterator();
+ while (it.hasNext()) {
+ RecordingPeriod rp = it.next();
+ cov += rp.getDuration();
+ }
+ durTot = getEnd()-getStart();
+ return (double) cov / (double) durTot;
+ }
+
+ /**
+ * Merge recording periods, with a max gap between periods in milliseconds.
+ * @param maxGap
+ * @return the number of periods removed.
+ */
+ public int mergeRecordingPeriods(long maxGap) {
+ if (effortPeriods.size() < 2) {
+ return 0;
+ }
+ Iterator it = effortPeriods.iterator();
+ RecordingPeriod prev = it.next();
+ int removed = 0;
+ while (it.hasNext()) {
+ RecordingPeriod curr = it.next();
+ if (curr.getRecordStart() - prev.getRecordStop() <= maxGap) {
+ prev.setRecordStop(curr.getRecordStop());
+ it.remove();
+ removed++;
+ }
+ else {
+ prev = curr;
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Work out whether or not the data are evenly duty cycled by testing the
+ * distributions of on and off times.
+ * @param tempPeriods
+ * @return
+ */
+ public DutyCycleInfo assessDutyCycle() {
+ if (effortPeriods == null) {
+ return null;
+ }
+ int n = effortPeriods.size();
+ if (n < 2) {
+ return new DutyCycleInfo(false, 0,0,n);
+ }
+ double[] ons = new double[n-1]; // ignore the last one since it may be artificially shortened which is OK
+ double[] gaps = new double[n-1];
+ for (int i = 0; i < n-1; i++) {
+ ons[i] = effortPeriods.get(i).getDuration()/1000.;
+ gaps[i] = (effortPeriods.get(i+1).getRecordStart()-effortPeriods.get(i).getRecordStop())/1000.;
+ }
+ /* now look at how consistent those values are
+ * But some data gets messed by small gaps, so want to
+ * remove outliers and concentrate on say 80% of the data.
+ */
+ ons = getDistributionCentre(ons, 80);
+ gaps = getDistributionCentre(gaps, 80);
+ Arrays.sort(gaps);
+
+ STD std = new STD();
+ double onsMean = std.getMean(ons);
+ double onsSTD = std.getSTD(ons);
+ double gapsMean = std.getMean(gaps);
+ double gapsSTD = std.getSTD(gaps);
+ boolean dutyCycle = onsSTD/onsMean < .05 && gapsSTD/gapsMean < 0.05;
+ DutyCycleInfo cycleInfo = new DutyCycleInfo(dutyCycle, onsMean, gapsMean, effortPeriods.size());
+ return cycleInfo;
+ }
+ /**
+ * Get the central part of a distribution without any outliers so
+ * that we can get a better assessment of duty cycle.
+ * @param data unsorted distribution data.
+ * @param percent percentage to include (half this removed from top and bottom)
+ * @return
+ */
+ private double[] getDistributionCentre(double[] data, double percent) {
+ if (data == null) {
+ return null;
+ }
+ Arrays.sort(data);
+ int nRem = (int) Math.round(data.length * (100-percent)/200);
+ int newLen = data.length-nRem*2;
+ double[] subdata = Arrays.copyOfRange(data, nRem, data.length-2*nRem);
+ if (subdata.length < 2) {
+ return data;
+ }
+ return subdata;
+ }
+
+ /**
+ * @return the sourceName
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ @Override
+ public String toString() {
+ if (effortPeriods.size() == 0) {
+ return "Empty recording list";
+ }
+ String str = String.format("%s: %s to %s, %3.1f%% coverage", getSourceName(),
+ PamCalendar.formatDBDateTime(getStart()),
+ PamCalendar.formatDBDateTime(getEnd()), getCoverage()*100);
+ return str;
+ }
+
+ /**
+ * Get similarity to another recording list. 1 = identical, 0 means not even overlapping.
+ * @param other other recording list.
+ * @return measure of similarity.
+ */
+ public double getSimilarity(RecordingList other) {
+ double sim1 = (double) other.duration() / (double) this.duration();
+ if (sim1 > 1) {
+ sim1 = 1./sim1;
+ }
+ long overlap = Math.min(other.getEnd(), this.getEnd()) - Math.max(other.getStart(), this.getStart());
+ overlap = Math.max(0, overlap);
+ long longest = Math.max(other.duration(), this.duration());
+ double sim2 = (double) overlap / (double) longest;
+
+ return Math.min(sim1, sim2);
+ }
+
+ /**
+ * Add a recording period to the list.
+ * @param recordingPeriod
+ */
+ public void add(RecordingPeriod recordingPeriod) {
+ effortPeriods.add(recordingPeriod);
+ }
+
+ /**
+ * Add a recording period to the list.
+ * @param startTime
+ * @param endTime
+ */
+ public void add(long startTime, long endTime) {
+ add (new RecordingPeriod(startTime, endTime));
+ }
+
+ public int size() {
+ return effortPeriods.size();
+ }
+
+ /**
+ * @return the effortPeriods
+ */
+ public ArrayList getEffortPeriods() {
+ return effortPeriods;
+ }
}
diff --git a/src/tethys/deployment/RecordingPeriod.java b/src/tethys/deployment/RecordingPeriod.java
index ad010827..ae985347 100644
--- a/src/tethys/deployment/RecordingPeriod.java
+++ b/src/tethys/deployment/RecordingPeriod.java
@@ -1,5 +1,6 @@
package tethys.deployment;
+import PamUtils.PamCalendar;
import tethys.niluswraps.PDeployment;
public class RecordingPeriod {
@@ -71,5 +72,11 @@ public class RecordingPeriod {
selected = !selected;
return selected;
}
+
+ @Override
+ public String toString() {
+ return String.format("%s to %s, %s", PamCalendar.formatDBDateTime(recordStart),
+ PamCalendar.formatDBDateTime(recordStop), PamCalendar.formatDuration(getDuration()));
+ }
}
diff --git a/src/tethys/deployment/swing/EffortProblemDialog.java b/src/tethys/deployment/swing/EffortProblemDialog.java
new file mode 100644
index 00000000..5fafa71b
--- /dev/null
+++ b/src/tethys/deployment/swing/EffortProblemDialog.java
@@ -0,0 +1,163 @@
+package tethys.deployment.swing;
+
+import java.awt.BorderLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Window;
+
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.border.TitledBorder;
+
+import PamUtils.PamCalendar;
+import PamView.dialog.PamDialog;
+import PamView.dialog.PamGridBagContraints;
+import tethys.deployment.DeploymentOverview;
+import tethys.deployment.RecordingList;
+
+/**
+ * Handle problems when binary and raw effort don't add up
+ * @author dg50
+ *
+ */
+public class EffortProblemDialog extends PamDialog {
+
+ private JRadioButton useRaw, useBinary, useNeither;
+
+ private JLabel generalInfo;
+
+ private InfoSet[] infoSets = new InfoSet[2];
+
+ private RecordingList chosenList;
+
+ private DeploymentOverview deploymentOverview;
+
+ private static EffortProblemDialog singleInstance;
+
+ private static final String[] setNames = {"Raw data", "Binary data"};
+
+ private EffortProblemDialog(Window parentFrame) {
+ super(parentFrame, "Deployment Effort", false);
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BorderLayout());
+ mainPanel.setBorder(new TitledBorder("Effort information"));
+ String info = "There is a mismatch between the time period covered by the raw
"
+ + "data recordings and the time covered in the binary data.
"
+ + "Select the one you wish to use, or Cancel and sort out your data
"
+ + "prior to restarting the Tethys export process";
+ generalInfo = new JLabel(info);
+// generalInfo.setBorder(new TitledBorder("General"));
+ mainPanel.add(generalInfo, BorderLayout.NORTH);
+ JPanel botPanel = new JPanel(new GridLayout(2, 1));
+ mainPanel.add(botPanel, BorderLayout.CENTER);
+ ButtonGroup bg = new ButtonGroup();
+ for (int i = 0; i < 2; i++) {
+ GridBagConstraints c = new PamGridBagContraints();
+ JPanel subPanel = new JPanel(new GridBagLayout());
+ botPanel.add(subPanel);
+ infoSets[i] = new InfoSet(setNames[i]);
+ c.gridwidth = 2;
+ subPanel.add(infoSets[i].name, c);
+ c.gridx += c.gridwidth;
+ subPanel.add(infoSets[i].select, c);
+ c.gridx = 0;
+ c.gridy++;
+ c.gridwidth = 1;
+ subPanel.add(new JLabel("Start: ", JLabel.RIGHT), c);
+ c.gridx++;
+ subPanel.add(infoSets[i].start, c);
+ c.gridx++;
+ subPanel.add(new JLabel("End: ", JLabel.RIGHT), c);
+ c.gridx++;
+ subPanel.add(infoSets[i].end, c);
+ c.gridy++;
+ c.gridx = 0;
+ subPanel.add(new JLabel("Duration: ", JLabel.RIGHT), c);
+ c.gridx++;
+ subPanel.add(infoSets[i].duration, c);
+ c.gridx++;
+ subPanel.add(new JLabel("Coverage: ", JLabel.RIGHT), c);
+ c.gridx++;
+ subPanel.add(infoSets[i].occupancy, c);
+
+ bg.add(infoSets[i].select);
+ }
+
+ setDialogComponent(mainPanel);
+ setResizable(true);
+ }
+
+ public static RecordingList showDialog(Window parentFrame, DeploymentOverview deploymentOverview) {
+ singleInstance = new EffortProblemDialog(parentFrame);
+ singleInstance.setData(deploymentOverview);
+ singleInstance.setVisible(true);
+ return singleInstance.chosenList;
+ }
+
+ private void setData(DeploymentOverview deploymentOverview) {
+ this.deploymentOverview = deploymentOverview;
+ RecordingList rl;
+ for (int i = 0; i < 2; i++) {
+ if (i == 0) {
+ rl = deploymentOverview.getRawDataList();
+ }
+ else {
+ rl = deploymentOverview.getBinaryDataList();
+ }
+ infoSets[i].start.setText(PamCalendar.formatDBDateTime(rl.getStart()));
+ infoSets[i].end.setText(PamCalendar.formatDBDateTime(rl.getEnd()));
+ infoSets[i].duration.setText(PamCalendar.formatDuration(rl.duration()));
+ infoSets[i].occupancy.setText(String.format("%3.0f%%", rl.getCoverage()*100.));
+ }
+ invalidate();
+ pack();
+ }
+
+ @Override
+ public boolean getParams() {
+ if (infoSets[0].select.isSelected()) {
+ chosenList = deploymentOverview.getRawDataList();
+ return true;
+ }
+ if (infoSets[1].select.isSelected()) {
+ chosenList = deploymentOverview.getBinaryDataList();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void cancelButtonPressed() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void restoreDefaultSettings() {
+ // TODO Auto-generated method stub
+
+ }
+
+ private class InfoSet {
+ JLabel name, start, end, duration, occupancy;
+ JCheckBox select;
+ /**
+ *
+ */
+ public InfoSet(String name) {
+ super();
+ this.name = new JLabel(name);
+ this.start = new JLabel(" ");
+ this.end = new JLabel(" ");
+ this.select = new JCheckBox("Select " + name);
+ duration = new JLabel(" ");
+ occupancy = new JLabel(" ");
+ }
+ }
+
+}
diff --git a/src/tethys/deployment/swing/ProjectInformationPanel.java b/src/tethys/deployment/swing/ProjectInformationPanel.java
index 1ced05b1..12a88a9a 100644
--- a/src/tethys/deployment/swing/ProjectInformationPanel.java
+++ b/src/tethys/deployment/swing/ProjectInformationPanel.java
@@ -23,6 +23,7 @@ import nilus.Deployment;
import tethys.TethysControl;
import tethys.swing.NewProjectDialog;
import tethys.swing.SelectProjectDialog;
+import tethys.tooltips.TethysTips;
/**
* Panel for entering project information
@@ -109,6 +110,12 @@ public class ProjectInformationPanel {
});
}
+ project.setToolTipText(TethysTips.findTip(Deployment.class, "Project"));
+ cruise.setToolTipText(TethysTips.findTip(Deployment.class, "Cruise"));
+ region.setToolTipText(TethysTips.findTip(Deployment.class, "Region"));
+ site.setToolTipText(TethysTips.findTip(Deployment.class, "Site"));
+
+
}
/**
diff --git a/src/tethys/deployment/swing/RecordingGapDialog.java b/src/tethys/deployment/swing/RecordingGapDialog.java
index 399eb3bc..9915a0e4 100644
--- a/src/tethys/deployment/swing/RecordingGapDialog.java
+++ b/src/tethys/deployment/swing/RecordingGapDialog.java
@@ -36,7 +36,7 @@ public class RecordingGapDialog extends PamDialog {
c.gridx++;
mainPanel.add(new JLabel(" seconds", JLabel.RIGHT), c);
- maxGap.setToolTipText("Maximum gap between recording periods. Periods with a gap less than this will be counted as one");
+ maxGap.setToolTipText("Maximum gap between recording periods. Sequential periods with a gap less than this will be counted as one");
minLength.setToolTipText("Minimum recording length. Recording sections shorter than this will be ignored");
setDialogComponent(mainPanel);
@@ -80,6 +80,7 @@ public class RecordingGapDialog extends PamDialog {
@Override
public void restoreDefaultSettings() {
DeploymentExportOpts defaults = new DeploymentExportOpts();
+ setParams(defaults);
}
}
diff --git a/src/tethys/detection/BinnedGranularityHandler.java b/src/tethys/detection/BinnedGranularityHandler.java
index db984b27..13b92d58 100644
--- a/src/tethys/detection/BinnedGranularityHandler.java
+++ b/src/tethys/detection/BinnedGranularityHandler.java
@@ -6,11 +6,16 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
+
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.XMLGregorianCalendar;
+
import java.util.Set;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import nilus.Detection;
+import nilus.Detections;
import nilus.SpeciesIDType;
import tethys.TethysControl;
import tethys.TethysTimeFuncs;
@@ -54,6 +59,8 @@ public class BinnedGranularityHandler extends GranularityHandler {
public void prepare(long timeMillis) {
// long binStart = DetectionsHandler.roundDownBinStart(timeMillis, binDurationMillis);
// startBin(binStart);
+// startBin(timeMillis);
+ currentDetections.clear();
}
// private void startBin(long timeMillis) {
@@ -169,4 +176,9 @@ public class BinnedGranularityHandler extends GranularityHandler {
return closeBins(timeMillis);
}
+ @Override
+ protected boolean autoEffortFix(Detections detections, Detection det) {
+ return contractDetection(detections, det);
+ }
+
}
diff --git a/src/tethys/detection/CallGranularityHandler.java b/src/tethys/detection/CallGranularityHandler.java
index 4ff9a888..03397596 100644
--- a/src/tethys/detection/CallGranularityHandler.java
+++ b/src/tethys/detection/CallGranularityHandler.java
@@ -3,6 +3,7 @@ package tethys.detection;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import nilus.Detection;
+import nilus.Detections;
import tethys.TethysControl;
import tethys.output.StreamExportParams;
import tethys.output.TethysExportParams;
@@ -37,4 +38,9 @@ public class CallGranularityHandler extends GranularityHandler {
return null;
}
+ @Override
+ protected boolean autoEffortFix(Detections detections, Detection det) {
+ return expandEffort(detections, det);
+ }
+
}
diff --git a/src/tethys/detection/DetectionExportProgress.java b/src/tethys/detection/DetectionExportProgress.java
index abfa74b8..24a982a5 100644
--- a/src/tethys/detection/DetectionExportProgress.java
+++ b/src/tethys/detection/DetectionExportProgress.java
@@ -6,10 +6,11 @@ import tethys.niluswraps.PDeployment;
public class DetectionExportProgress {
public static final int STATE_GATHERING = 1;
- public static final int STATE_CANCELED = 2;
- public static final int STATE_COMPLETE = 3;
- public static final int STATE_WRITING = 4;
- public static final int STATE_COUNTING = 5;
+ public static final int STATE_COUNTING = 2;
+ public static final int STATE_WRITING = 3;
+ public static final int STATE_CANCELED = 4;
+ public static final int STATE_COMPLETE = 5;
+
public PDeployment currentDeployment;
public Detections currentDetections;
public long lastUnitTime;
@@ -17,12 +18,18 @@ public class DetectionExportProgress {
public int exportCount;
public int skipCount;
public int state;
+ public int totalDeployments, deploymentsDone;
+ public int nMapPoints;
+ public int doneMapPoints;
- public DetectionExportProgress(PDeployment currentDeployment, Detections currentDetections, long lastUnitTime,
+ public DetectionExportProgress(PDeployment currentDeployment, Detections currentDetections, int nMapPoints, int doneMapPoints,
+ long lastUnitTime,
long totalUnits, int exportCount, int skipCount, int state) {
super();
this.currentDeployment = currentDeployment;
this.currentDetections = currentDetections;
+ this.nMapPoints = nMapPoints;
+ this.doneMapPoints = doneMapPoints;
this.lastUnitTime = lastUnitTime;
this.totalUnits = totalUnits;
this.exportCount = exportCount;
diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java
index f797c8ee..cd995bbb 100644
--- a/src/tethys/detection/DetectionsHandler.java
+++ b/src/tethys/detection/DetectionsHandler.java
@@ -1,9 +1,13 @@
package tethys.detection;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.ListIterator;
import javax.swing.SwingWorker;
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.XMLGregorianCalendar;
import PamController.PamControlledUnit;
import PamController.PamController;
@@ -32,6 +36,7 @@ import nilus.Detections;
import nilus.GranularityEnumType;
import nilus.Helper;
import tethys.Collection;
+import tethys.CollectionHandler;
import tethys.TethysControl;
import tethys.TethysTimeFuncs;
import tethys.dbxml.DBXMLConnect;
@@ -39,7 +44,6 @@ import tethys.dbxml.TethysException;
import tethys.deployment.DeploymentHandler;
import tethys.niluswraps.PDeployment;
import tethys.niluswraps.PDetections;
-import tethys.output.DatablockSynchInfo;
import tethys.output.StreamExportParams;
import tethys.output.TethysExportParams;
import tethys.pamdata.TethysDataProvider;
@@ -54,9 +58,7 @@ import tethys.swing.export.DetectionsExportWizard;
* @author dg50
*
*/
-public class DetectionsHandler {
-
- private TethysControl tethysControl;
+public class DetectionsHandler extends CollectionHandler {
public int uniqueDetectionsId=1;
public int uniqueDetectionId;
@@ -65,12 +67,14 @@ public class DetectionsHandler {
private ExportWorker exportWorker;
+ public static final String helpPoint = "utilities.tethys.docs.detect_localize";
+
/**
*
* @param tethysControl
*/
public DetectionsHandler(TethysControl tethysControl) {
- super();
+ super(tethysControl, Collection.Detections);
this.tethysControl = tethysControl;
}
@@ -296,9 +300,14 @@ public class DetectionsHandler {
viewerLoadPolicy = ViewerLoadPolicy.LOAD_UTCNORMAL;
}
GranularityHandler granularityHandler = GranularityHandler.getHandler(streamExportParams.granularity, tethysControl, dataBlock, exportParams, streamExportParams);
+ int totalMaps = 0;
+ int totalMappedPoints = 0;
+ int totalLoadedDatas = 0;
+ int totalMapPoints = dataMap.getNumMapPoints();
+ int doneMapPoints = 0;
for (PDeployment deployment : deployments) {
int documentCount = 0;
- prog = new DetectionExportProgress(deployment, null,
+ prog = new DetectionExportProgress(deployment, null, totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING);
exportObserver.update(prog);
granularityHandler.prepare(deployment.getAudioStart());
@@ -308,9 +317,10 @@ public class DetectionsHandler {
for (OfflineDataMapPoint mapPoint : mapPoints) {
if (!activeExport) {
- prog = new DetectionExportProgress(deployment, null,
+ prog = new DetectionExportProgress(deployment, null,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_CANCELED);
exportObserver.update(prog);
+ break;
}
if (mapPoint.getEndTime() < deployment.getAudioStart()) {
@@ -319,10 +329,13 @@ public class DetectionsHandler {
if (mapPoint.getStartTime() >= deployment.getAudioEnd()) {
break;
}
+ totalMaps ++;
+ totalMappedPoints += mapPoint.getNDatas();
dataBlock.loadViewerData(mapPoint.getStartTime(), mapPoint.getEndTime(), null);
ArrayList dataCopy = dataBlock.getDataCopy(deployment.getAudioStart(), deployment.getAudioEnd(), true, dataSelector);
-// System.out.printf("%d loaded from %s to %s %d kept\n", dataBlock.getUnitsCount(), PamCalendar.formatDateTime(mapPoint.getStartTime()),
-// PamCalendar.formatDateTime(mapPoint.getEndTime()), dataCopy.size());
+ totalLoadedDatas += dataCopy.size();
+ System.out.printf("%d loaded from %s to %s %d kept\n", dataBlock.getUnitsCount(), PamCalendar.formatDateTime(mapPoint.getStartTime()),
+ PamCalendar.formatDateTime(mapPoint.getEndTime()), dataCopy.size());
skipCount += dataBlock.getUnitsCount() - dataCopy.size();
for (PamDataUnit dataUnit : dataCopy) {
/*
@@ -334,7 +347,7 @@ public class DetectionsHandler {
documentCount+=dets.length;
if (exportCount % 100 == 0) {
- prog = new DetectionExportProgress(deployment, null,
+ prog = new DetectionExportProgress(deployment, null,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING);
exportObserver.update(prog);
}
@@ -345,15 +358,17 @@ public class DetectionsHandler {
// onEffort.getDetection().add(det);
lastUnitTime = dataUnit.getTimeMilliseconds();
}
-
- prog = new DetectionExportProgress(deployment, null,
+ doneMapPoints++;
+ prog = new DetectionExportProgress(deployment, null,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING);
exportObserver.update(prog);
if (viewerLoadPolicy == ViewerLoadPolicy.LOAD_ALWAYS_EVERYTHING) {
break;
}
-
+ if (!activeExport) {
+ return 0;
+ }
}
Detection dets[] = granularityHandler.cleanup(deployment.getAudioEnd());
if (dets != null) {
@@ -397,28 +412,33 @@ public class DetectionsHandler {
if (viewerLoadPolicy == null) {
viewerLoadPolicy = ViewerLoadPolicy.LOAD_UTCNORMAL;
}
+ int totalMapPoints = dataMap.getNumMapPoints();
+ int doneMapPoints = 0;
GranularityHandler granularityHandler = GranularityHandler.getHandler(streamExportParams.granularity, tethysControl, dataBlock, exportParams, streamExportParams);
for (PDeployment deployment : deployments) {
int documentCount = 0;
- prog = new DetectionExportProgress(deployment, null,
+ prog = new DetectionExportProgress(deployment, null,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING);
exportObserver.update(prog);
granularityHandler.prepare(deployment.getAudioStart());
- if (currentDetections == null) {
- currentDetections = startDetectionsDocument(deployment, dataBlock, streamExportParams);
- currentDetections.getEffort().setStart(TethysTimeFuncs.xmlGregCalFromMillis(deployment.getAudioStart()));
- }
// export everything in that deployment.
// need to loop through all map points in this interval.
List mapPoints = dataMap.getMapPoints();
for (OfflineDataMapPoint mapPoint : mapPoints) {
if (!activeExport) {
- prog = new DetectionExportProgress(deployment, currentDetections,
+ prog = new DetectionExportProgress(deployment, currentDetections,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_CANCELED);
exportObserver.update(prog);
+ break;
}
+ if (currentDetections == null) {
+ // needed in inner loop in case doc gets written at 500000.
+ currentDetections = startDetectionsDocument(deployment, dataBlock, streamExportParams);
+ currentDetections.getEffort().setStart(TethysTimeFuncs.xmlGregCalFromMillis(deployment.getAudioStart()));
+ }
+
if (mapPoint.getEndTime() < deployment.getAudioStart()) {
continue;
}
@@ -427,6 +447,7 @@ public class DetectionsHandler {
}
dataBlock.loadViewerData(mapPoint.getStartTime(), mapPoint.getEndTime(), null);
ArrayList dataCopy = dataBlock.getDataCopy(deployment.getAudioStart(), deployment.getAudioEnd(), true, dataSelector);
+ Collections.sort(dataCopy);
skipCount += dataBlock.getUnitsCount() - dataCopy.size();
DetectionGroup onEffort = currentDetections.getOnEffort();
for (PamDataUnit dataUnit : dataCopy) {
@@ -442,24 +463,27 @@ public class DetectionsHandler {
}
}
if (exportCount % 100 == 0) {
- prog = new DetectionExportProgress(deployment, null,
+ prog = new DetectionExportProgress(deployment, currentDetections, totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING);
exportObserver.update(prog);
}
lastUnitTime = dataUnit.getTimeMilliseconds();
}
- prog = new DetectionExportProgress(deployment, currentDetections,
+ doneMapPoints ++;
+ prog = new DetectionExportProgress(deployment, currentDetections,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING);
exportObserver.update(prog);
- if (documentCount > 500000 && mapPoint != dataMap.getLastMapPoint()) {
- prog = new DetectionExportProgress(deployment, currentDetections,
+ if (documentCount > 50000000 && mapPoint != dataMap.getLastMapPoint()) {
+ prog = new DetectionExportProgress(deployment, currentDetections,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING);
exportObserver.update(prog);
closeDetectionsDocument(currentDetections, mapPoint.getEndTime());
try {
- dbxmlConnect.postAndLog(currentDetections);
+ if (checkDetectionsDocument(currentDetections, granularityHandler)) {
+ dbxmlConnect.postAndLog(currentDetections);
+ }
} catch (TethysException e) {
tethysControl.showException(e);
}
@@ -469,8 +493,14 @@ public class DetectionsHandler {
if (viewerLoadPolicy == ViewerLoadPolicy.LOAD_ALWAYS_EVERYTHING) {
break;
}
+ if (!activeExport) {
+ break;
+ }
+ }
+
+ if (!activeExport) {
+ return DetectionExportProgress.STATE_CANCELED;
}
-
if (currentDetections != null) {
Detection dets[] = granularityHandler.cleanup(deployment.getAudioEnd());
@@ -481,11 +511,13 @@ public class DetectionsHandler {
currentDetections.getOnEffort().getDetection().add(dets[dd]);
}
}
- prog = new DetectionExportProgress(deployment, currentDetections,
+ prog = new DetectionExportProgress(deployment, currentDetections,totalMapPoints, doneMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING);
closeDetectionsDocument(currentDetections, deployment.getAudioEnd());
try {
- dbxmlConnect.postAndLog(currentDetections);
+ if (checkDetectionsDocument(currentDetections, granularityHandler)) {
+ dbxmlConnect.postAndLog(currentDetections);
+ }
} catch (TethysException e) {
tethysControl.showException(e);
}
@@ -493,7 +525,7 @@ public class DetectionsHandler {
}
}
- prog = new DetectionExportProgress(null, null,
+ prog = new DetectionExportProgress(null, null,totalMapPoints, totalMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COMPLETE);
exportObserver.update(prog);
return DetectionExportProgress.STATE_COMPLETE;
@@ -552,7 +584,7 @@ public class DetectionsHandler {
supportSoft.setVersion(getSupportSoftwareVersion(dataBlock));
supSoft.add(supportSoft);
detections.setAlgorithm(algorithm);
- detections.setUserId("Unknown user");
+ detections.setUserId("PAMGuard user");
detections.setEffort(getDetectorEffort(deployment, dataBlock, exportParams));
return detections;
@@ -567,6 +599,50 @@ public class DetectionsHandler {
private void closeDetectionsDocument(Detections detections, Long audioEnd) {
detections.getEffort().setEnd(TethysTimeFuncs.xmlGregCalFromMillis(audioEnd));
}
+
+ /**
+ * Run some checks on the Detections document prior to submission.
+ * Currently, is is just a check that the detections are within the effort times.
+ * @param detections Detections document
+ * @return false if there is an outstanding problem.
+ */
+ private boolean checkDetectionsDocument(Detections detections, GranularityHandler granularityHandler) {
+ XMLGregorianCalendar effStart = detections.getEffort().getStart();
+ XMLGregorianCalendar effEnd = detections.getEffort().getEnd();
+ DetectionGroup dets = detections.getOnEffort();
+ List detList = dets.getDetection();
+ ListIterator detIt = detList.listIterator();
+ while (detIt.hasNext()) {
+ Detection det = detIt.next();
+ XMLGregorianCalendar detS = det.getStart();
+ XMLGregorianCalendar detE = det.getEnd();
+ if (effStart.compare(detS) == DatatypeConstants.GREATER) {
+ if (granularityHandler.autoEffortFix(detections, det)) {
+ continue;
+ }
+ String str = String.format("A Detection at %s starts before the document effort start at %s
"
+ + "Do you want to adjust the effort start time or abort export ?", detS, effStart);
+ int ans = WarnOnce.showNamedWarning("TETHYSDETNOTINEFFORT", tethysControl.getGuiFrame(), "Detection Document Warning", str, WarnOnce.OK_CANCEL_OPTION);
+ if (ans == WarnOnce.CANCEL_OPTION) {
+ return false;
+ }
+ detections.getEffort().setStart(detS);
+ }
+ if (effEnd.compare(detE) == DatatypeConstants.LESSER) {
+ if (granularityHandler.autoEffortFix(detections, det)) {
+ continue;
+ }
+ String str = String.format("A Detection at %s-%s ends
after the document effort end at %s
"
+ + "Do you want to adjust the effort end time or abort export ?", detS, detE, effStart);
+ int ans = WarnOnce.showNamedWarning("TETHYSDETNOTINEFFORT", tethysControl.getGuiFrame(), "Detection Document Warning", str, WarnOnce.OK_CANCEL_OPTION);
+ if (ans == WarnOnce.CANCEL_OPTION) {
+ return false;
+ }
+ detections.getEffort().setEnd(detE);
+ }
+ }
+ return true;
+ }
/**
* Worker thread for exporting detections.
@@ -599,13 +675,16 @@ public class DetectionsHandler {
protected Integer doInBackground() throws Exception {
Integer ans = null;
try {
- int count = countDetections(dataBlock, exportParams, exportObserver);
- String msg = String.format("Do you want to go ahead and output %d %s detections to Tethys?",
- count, exportParams.granularity);
- int doit = WarnOnce.showWarning("Tethys Detections Export", msg, WarnOnce.OK_CANCEL_OPTION);
- if (doit == WarnOnce.OK_OPTION) {
+// int count = countDetections(dataBlock, exportParams, exportObserver);
+// if (activeExport == false) {
+// return 0;
+// }
+// String msg = String.format("Do you want to go ahead and output %d %s detections to Tethys?",
+// count, exportParams.granularity);
+// int doit = WarnOnce.showWarning("Tethys Detections Export", msg, WarnOnce.OK_CANCEL_OPTION);
+// if (doit == WarnOnce.OK_OPTION) {
ans = exportDetections(dataBlock, exportParams, this);
- }
+// }
}
catch (Exception e) {
e.printStackTrace();
@@ -616,7 +695,7 @@ public class DetectionsHandler {
@Override
protected void done() {
// this.
- DetectionExportProgress prog = new DetectionExportProgress(null, null, 0, 0, 0, 0, DetectionExportProgress.STATE_COMPLETE);
+ DetectionExportProgress prog = new DetectionExportProgress(null, null, 0, 0, 0, 0, 0, 0, DetectionExportProgress.STATE_COMPLETE);
tethysControl.exportedDetections(dataBlock);
exportObserver.update(prog);
TethysReporter.getTethysReporter().showReport(tethysControl.getGuiFrame(), true);
@@ -661,4 +740,10 @@ public class DetectionsHandler {
DetectionsExportWizard.showDialog(tethysControl.getGuiFrame(), tethysControl, dataBlock);
}
+
+
+ @Override
+ public String getHelpPoint() {
+ return helpPoint;
+ }
}
diff --git a/src/tethys/detection/EncounterGranularityHandler.java b/src/tethys/detection/EncounterGranularityHandler.java
index 57dd8757..54e70db5 100644
--- a/src/tethys/detection/EncounterGranularityHandler.java
+++ b/src/tethys/detection/EncounterGranularityHandler.java
@@ -11,6 +11,7 @@ import java.util.Map.Entry;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import nilus.Detection;
+import nilus.Detections;
import nilus.SpeciesIDType;
import tethys.TethysControl;
import tethys.TethysTimeFuncs;
@@ -48,7 +49,7 @@ public class EncounterGranularityHandler extends GranularityHandler {
@Override
public void prepare(long timeMillis) {
-
+ currentDetections.clear();
}
@Override
@@ -77,6 +78,7 @@ public class EncounterGranularityHandler extends GranularityHandler {
currentDetections.put(groupName, det);
}
else {
+
// add to current detection. Set new end time and increment count
det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getEndTimeInMilliseconds()));
int count = det.getCount().intValue() + 1;
@@ -122,34 +124,17 @@ public class EncounterGranularityHandler extends GranularityHandler {
}
}
- // private Detection[] checkCurrentEncounters(long timeMilliseconds) {
- // if (currentDetections == null || currentDetections.size() == 0) {
- // return null;
- // }
- // int nGood = 0;
- // Detection[] newDetections = new Detection[currentDetections.size()];
- // Iterator detIt = currentDetections.iterator();
- // while (detIt.hasNext()) {
- // Detection aDet = detIt.next();
- // Long detEnd = TethysTimeFuncs.millisFromGregorianXML(aDet.getEnd());
- // if (timeMilliseconds-detEnd > maxGapMillis) {
- // detIt.remove();
- // newDetections[nGood++] = aDet;
- // }
- // }
- //
- // if (nGood == 0) {
- // return null;
- // }
- // else {
- // return Arrays.copyOf(newDetections, nGood);
- // }
- // }
@Override
public Detection[] cleanup(long timeMillis) {
// get everything still on the go.
- return checkCurrentEncounters(timeMillis + maxGapMillis);
+ return checkCurrentEncounters(timeMillis + maxGapMillis*10);
}
+ @Override
+ protected boolean autoEffortFix(Detections detections, Detection det) {
+ return expandEffort(detections, det);
+ }
+
+
}
diff --git a/src/tethys/detection/GranularityHandler.java b/src/tethys/detection/GranularityHandler.java
index d172405e..47ef87e3 100644
--- a/src/tethys/detection/GranularityHandler.java
+++ b/src/tethys/detection/GranularityHandler.java
@@ -1,8 +1,16 @@
package tethys.detection;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.XMLGregorianCalendar;
+
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import nilus.Detection;
+import nilus.DetectionGroup;
+import nilus.Detections;
import nilus.GranularityEnumType;
import tethys.TethysControl;
import tethys.output.StreamExportParams;
@@ -118,4 +126,93 @@ public abstract class GranularityHandler {
}
return null;
}
+
+ /**
+ * Automatically fix mismatches between effort and detections. This will be called if a
+ * detection or part of a detection is outside of the start and end defined by the effort. If it's a
+ * small difference, i.e. if the detection at least overlaps the effort then it can be automatically
+ * fixed by truncating the detection (for binned types) or by a small extension to the effort (for encounter
+ * and call types).
+ * @param detections nilus Detections object
+ * @param det a single detection
+ * @return true if it was fixed automatically. False otherwise.
+ */
+ protected abstract boolean autoEffortFix(Detections detections, Detection det);
+
+ /**
+ * Check that the detection at least overlaps the effort period.
+ * @param detections nilus Detections object
+ * @param det a single detection
+ * @return true if the overlap
+ */
+ protected boolean effortOverlap(Detections detections, Detection det) {
+ XMLGregorianCalendar effStart = detections.getEffort().getStart();
+ XMLGregorianCalendar effEnd = detections.getEffort().getEnd();
+ XMLGregorianCalendar detStart = det.getStart();
+ XMLGregorianCalendar detEnd = det.getEnd();
+ if (effStart.compare(detEnd) == DatatypeConstants.GREATER) {
+ return false;
+ }
+ if (effEnd.compare(detStart) == DatatypeConstants.LESSER) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Fix effort / detection problem but contracting the start / end times of the detection
+ * @param detections nilus Detections object
+ * @param det a single detection
+ * @return true if fixed automatically
+ */
+ protected boolean contractDetection(Detections detections, Detection det) {
+ if (effortOverlap(detections, det) == false) {
+ return false;
+ }
+ // at least some overlap, so fix it.
+ // going to fix it my shortening the detection, and leave the effort alone.
+ XMLGregorianCalendar effStart = detections.getEffort().getStart();
+ XMLGregorianCalendar effEnd = detections.getEffort().getEnd();
+ XMLGregorianCalendar detStart = det.getStart();
+ XMLGregorianCalendar detEnd = det.getEnd();
+
+
+ if (effStart.compare(detStart) == DatatypeConstants.GREATER) {
+ System.out.printf("Fix Detections change detection start from %s to %s\n", detStart, effStart);
+ det.setStart(effStart);
+ }
+ if (effEnd.compare(detEnd) == DatatypeConstants.LESSER) {
+ System.out.printf("Fix Detections change detection end from %s to %s\n", detEnd, effEnd);
+ det.setEnd(effEnd);
+ }
+ return true;
+ }
+
+ /**
+ * Fix effort / detection problem but expanding the start / end times of the effort
+ * @param detections nilus Detections object
+ * @param det a single detection
+ * @return true if fixed automatically
+ */
+ protected boolean expandEffort(Detections detections, Detection det) {
+ if (effortOverlap(detections, det) == false) {
+ return false;
+ }
+ // at least some overlap, so fix it.
+ // going to fix it my shortening the detection, and leave the effort alone.
+ XMLGregorianCalendar effStart = detections.getEffort().getStart();
+ XMLGregorianCalendar effEnd = detections.getEffort().getEnd();
+ XMLGregorianCalendar detStart = det.getStart();
+ XMLGregorianCalendar detEnd = det.getEnd();
+
+ if (effStart.compare(detStart) == DatatypeConstants.GREATER) {
+ System.out.printf("Fix Detections change effort start from %s to %s\n", effStart, detStart);
+ detections.getEffort().setStart(detStart);
+ }
+ if (effEnd.compare(detEnd) == DatatypeConstants.LESSER) {
+ System.out.printf("Fix Detections change effort end from %s to %s\n", effEnd, detEnd);
+ detections.getEffort().setEnd(detEnd);
+ }
+ return true;
+ }
}
diff --git a/src/tethys/localization/LocalizationHandler.java b/src/tethys/localization/LocalizationHandler.java
new file mode 100644
index 00000000..69105b5e
--- /dev/null
+++ b/src/tethys/localization/LocalizationHandler.java
@@ -0,0 +1,17 @@
+package tethys.localization;
+
+import nilus.CylindricalCoordinateType;
+import nilus.LocalizationType;
+import nilus.Localize.Effort.CoordinateReferenceSystem;
+
+public class LocalizationHandler {
+
+
+ public LocalizationType getLoc() {
+ LocalizationType lt = new LocalizationType();
+ CylindricalCoordinateType cct = new CylindricalCoordinateType();
+// cct.set
+ CoordinateReferenceSystem cr;
+ return null;
+ }
+}
diff --git a/src/tethys/niluswraps/NilusChecker.java b/src/tethys/niluswraps/NilusChecker.java
index 43a53e97..e9107540 100644
--- a/src/tethys/niluswraps/NilusChecker.java
+++ b/src/tethys/niluswraps/NilusChecker.java
@@ -56,7 +56,7 @@ public class NilusChecker {
for (Field f : emptyFields) {
msg += String.format("
Field %s in object %s", f.getName(), f.getDeclaringClass().getName());
}
- msg += "
It is likely that this document will fail to write to the Tethys database.";
+ msg += "
It is possible that this document will fail to write to the Tethys database.