diff --git a/src/clickDetector/tethys/ClickEventSpeciesManager.java b/src/clickDetector/tethys/ClickEventSpeciesManager.java index 1e16a4b1..c0ee92fd 100644 --- a/src/clickDetector/tethys/ClickEventSpeciesManager.java +++ b/src/clickDetector/tethys/ClickEventSpeciesManager.java @@ -47,7 +47,11 @@ public class ClickEventSpeciesManager extends DataBlockSpeciesManager { @Override public String getSpeciesCode(PamDataUnit dataUnit) { OfflineEventDataUnit eventDataUnit = (OfflineEventDataUnit) dataUnit; - return eventDataUnit.getEventType(); + String eventType = eventDataUnit.getEventType(); + if (eventType == null) { + eventType = "Unknown"; + } + return eventType; } } diff --git a/src/metadata/PamguardMetaData.java b/src/metadata/PamguardMetaData.java index 45e7e051..a15b4373 100644 --- a/src/metadata/PamguardMetaData.java +++ b/src/metadata/PamguardMetaData.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamUtils.LatLong; import nilus.ContactInfo; import nilus.Deployment; +import nilus.DeploymentRecoveryDetails; import nilus.DescriptionType; import nilus.Helper; import nilus.MetadataInfo; @@ -24,6 +25,8 @@ public class PamguardMetaData implements Serializable { private NilusSettingsWrapper deploymentWrapper; + public boolean useAudioForDeploymentTimes = true; + // /** // * Deployment time (used if different // */ @@ -64,6 +67,13 @@ public class PamguardMetaData implements Serializable { if (deployment.getMetadataInfo().getContact().getContactInfo() == null) { deployment.getMetadataInfo().getContact().setContactInfo(new ContactInfo()); } + + if (deployment.getDeploymentDetails() == null) { + deployment.setDeploymentDetails(new DeploymentRecoveryDetails()); + } + if (deployment.getRecoveryDetails() == null) { + deployment.setRecoveryDetails(new DeploymentRecoveryDetails()); + } return deployment; } diff --git a/src/metadata/swing/MetaDataDialog.java b/src/metadata/swing/MetaDataDialog.java index cf2f2fc2..9f744391 100644 --- a/src/metadata/swing/MetaDataDialog.java +++ b/src/metadata/swing/MetaDataDialog.java @@ -2,27 +2,16 @@ package metadata.swing; import java.awt.BorderLayout; import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Point; import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JMenuItem; import javax.swing.JPanel; -import javax.swing.JPopupMenu; import javax.swing.JTabbedPane; -import javax.swing.JTextField; import javax.swing.border.TitledBorder; import PamController.PamController; import PamView.dialog.PamDialog; -import PamView.dialog.PamGridBagContraints; +import PamView.panel.PamNorthPanel; import PamView.panel.WestAlignedPanel; import metadata.PamguardMetaData; import nilus.Deployment; @@ -30,8 +19,7 @@ import tethys.TethysControl; import tethys.TethysState; import tethys.TethysState.StateType; import tethys.deployment.swing.ProjectInformationPanel; -import tethys.swing.NewProjectDialog; -import tethys.swing.SelectProjectDialog; +import tethys.swing.export.DeploymentPeriodPanel; import tethys.swing.export.DescriptionTypePanel; import tethys.swing.export.ResponsiblePartyPanel; @@ -44,6 +32,8 @@ public class MetaDataDialog extends PamDialog { private DescriptionTypePanel descriptionPanel; private ProjectInformationPanel projectInformationPanel; + + private DeploymentPeriodPanel deploymentPeriodPanel; private ResponsiblePartyPanel responsiblePanel; @@ -60,26 +50,26 @@ public class MetaDataDialog extends PamDialog { projectInformationPanel = new ProjectInformationPanel(parentFrame, null); descriptionPanel = new DescriptionTypePanel(null, false, false, false); + deploymentPeriodPanel = new DeploymentPeriodPanel(parentFrame); descriptionPanel.getMainPanel().setPreferredSize(new Dimension(400,300)); - -// JPanel projectPanel = new JPanel(new GridBagLayout()); - responsiblePanel = new ResponsiblePartyPanel(); JPanel northPanel = new JPanel(); WestAlignedPanel wp; northPanel.setLayout(new BoxLayout(northPanel, BoxLayout.Y_AXIS)); - + northPanel.add(wp = new WestAlignedPanel(projectInformationPanel.getMainPanel())); wp.setBorder(new TitledBorder("General project information")); northPanel.add(wp = new WestAlignedPanel(responsiblePanel.getMainPanel())); wp.setBorder(new TitledBorder("Contact information")); -// mainPanel.add(BorderLayout.CENTER, descriptionPanel.getMainPanel()); -// mainPanel.add(BorderLayout.NORTH, northPanel); + JPanel dpPanel = new WestAlignedPanel(deploymentPeriodPanel.getMainPanel()); + dpPanel.setBorder(new TitledBorder("Deployment period")); + mainPanel.add(tabbedPane, BorderLayout.CENTER); tabbedPane.add(northPanel, "General"); tabbedPane.add(descriptionPanel.getMainPanel(), "Description"); + tabbedPane.add(dpPanel, "Deployment"); setResizable(true); @@ -99,8 +89,10 @@ public class MetaDataDialog extends PamDialog { private void setParams(PamguardMetaData pamguardMetaData) { this.pamguardMetaData = pamguardMetaData; Deployment deployment = pamguardMetaData.getDeployment(); + projectInformationPanel.setParams(deployment); descriptionPanel.setParams(deployment.getDescription()); responsiblePanel.setParams(deployment.getMetadataInfo().getContact()); + deploymentPeriodPanel.setParams(pamguardMetaData); } @Override @@ -108,6 +100,7 @@ public class MetaDataDialog extends PamDialog { Deployment deployment = pamguardMetaData.getDeployment(); boolean ok = descriptionPanel.getParams(deployment.getDescription()); ok &= responsiblePanel.getParams(deployment.getMetadataInfo().getContact()); + ok &= deploymentPeriodPanel.getParams(pamguardMetaData); if (tethysControl != null) { tethysControl.sendStateUpdate(new TethysState(StateType.NEWPROJECTSELECTION)); diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 3352db15..446face1 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -49,10 +49,7 @@ import tethys.deployment.DeploymentHandler; import tethys.detection.DetectionsHandler; import tethys.niluswraps.PDeployment; import tethys.output.DatablockSynchInfo; -//import nilus.Deployment; import tethys.output.TethysExportParams; -import tethys.output.TethysExporter; -import tethys.output.swing.TethysExportDialog; import tethys.species.ITISFunctions; import tethys.species.SpeciesMapManager; import tethys.swing.ProjectDeploymentsDialog; @@ -520,6 +517,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet public void updateState(TethysState tethysState) { switch (tethysState.stateType) { case NEWPROJECTSELECTION: + case EXPORTRDATA: + case DELETEDATA: countProjectDetections(); break; } diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index b0ce438f..a7967f0f 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -22,6 +22,7 @@ import PamController.settings.output.xml.PamguardXMLWriter; import PamguardMVC.PamDataBlock; import dbxml.JerseyClient; import dbxml.Queries; +import nilus.DataSourceType; import nilus.Deployment; import nilus.Deployment.Instrument; import nilus.DeploymentRecoveryDetails; @@ -587,9 +588,9 @@ public class DBXMLQueries { ArrayList detectionDocs = new ArrayList<>(); - NodeList returns = doc.getElementsByTagName("Return"); + NodeList returns = doc.getElementsByTagName("Record"); if (returns.getLength() == 0) { - returns = doc.getElementsByTagName("Result"); + returns = doc.getElementsByTagName("Record"); } for (int i = 0; i < returns.getLength(); i++) { Node aNode = returns.item(i); @@ -925,7 +926,7 @@ public class DBXMLQueries { * @return */ public Detections getDetectionsDocInfo(String detectionsDocName) { - String oldqueryBase = "{\"species\":{\"query\":{\"op\":\"lib:abbrev2tsn\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]},\"return\":{\"op\":\"lib:tsn2abbrev\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; +// String oldqueryBase = "{\"species\":{\"query\":{\"op\":\"lib:abbrev2tsn\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]},\"return\":{\"op\":\"lib:tsn2abbrev\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; // updated May 23 String queryBase = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\",\"Detections/QualityAssurance\",\"Detections/UserId\",\"Detections/MetadataInfo\",\"Detections/Effort\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; String query = queryBase.replace("DetectionsDocName", detectionsDocName); @@ -971,6 +972,16 @@ public class DBXMLQueries { description.setMethod(getElementData(result, "Description.Method")); description.setObjectives(getElementData(result, "Description.Objectives")); + String deployment = getElementData(result, "DataSource.DeploymentId"); + if (deployment != null) { + DataSourceType dataSource = detections.getDataSource(); + if (dataSource == null) { + dataSource = new DataSourceType(); + detections.setDataSource(dataSource); + } + dataSource.setDeploymentId(deployment); + } + // get the effort start an end String effStart = getElementData(result, "Effort.Start"); String effEnd = getElementData(result, "Effort.End"); diff --git a/src/tethys/deployment/DeploymentExportOpts.java b/src/tethys/deployment/DeploymentExportOpts.java index 3563fb95..18fa461f 100644 --- a/src/tethys/deployment/DeploymentExportOpts.java +++ b/src/tethys/deployment/DeploymentExportOpts.java @@ -13,19 +13,22 @@ public class DeploymentExportOpts implements Serializable, Cloneable { public boolean separateDeployments; + /** + * Minimum number of seconds between GPS points in a track. + */ public double trackPointInterval; /** * Max gap before recording periods are separated, potentially into * separate Deployment documents */ - public int maxGapSeconds = 60; + public int maxRecordingGapSeconds = 60; /** * A recording section after joining with max gap parameter is too short * to be worth keeping. */ - public int minLengthSeconds = 10; + public int minRecordingLengthSeconds = 10; @Override protected DeploymentExportOpts clone() { diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 2eb0b185..85f834c3 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -65,6 +65,7 @@ import nilus.DeploymentRecoveryDetails; import nilus.DescriptionType; import nilus.GeometryTypeM; import nilus.Helper; +import nilus.MetadataInfo; import nilus.UnknownSensor; import pamMaths.PamVector; import pamMaths.STD; @@ -103,6 +104,8 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb public TethysControl getTethysControl() { return tethysControl; } + + private EffortFunctions effortFunctions; private DeploymentOverview deploymentOverview; @@ -110,17 +113,22 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb private Helper nilusHelper; - private DeploymentExportOpts exportOptions = new DeploymentExportOpts(); + private DeploymentExportOpts deploymentExportOptions = new DeploymentExportOpts(); public DeploymentHandler(TethysControl tethysControl) { super(); + this.tethysControl = tethysControl; + + this.effortFunctions = new EffortFunctions(tethysControl); + tethysControl.addStateObserver(this); try { nilusHelper = new Helper(); } catch (JAXBException e) { e.printStackTrace(); } + PamSettingManager.getInstance().registerSettings(new SettingsHandler()); } @@ -156,6 +164,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb updateProjectDeployments(); break; case EXPORTRDATA: + case DELETEDATA: updateProjectDeployments(); break; case UPDATESERVER: @@ -198,170 +207,175 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb return projectDeployments; } - /** - * Get an overview of all the deployments. - * @return - */ - public DeploymentOverview createPamguardOverview() { - // first find an acquisition module. - PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); - if (!(aModule instanceof AcquisitionControl)) { - // will return if it's null. Impossible for it to be the wrong type. - // but it's good practice to check anyway before casting. - return null; - } - // cast it to the right type. - AcquisitionControl daqControl = (AcquisitionControl) aModule; - AcquisitionParameters daqParams = daqControl.getAcquisitionParameters(); - /** - * The daqParams class has most of what we need about the set up in terms of sample rate, - * number of channels, instrument type, ADC input range (part of calibration), etc. - * It also has a hydrophone list, which maps the input channel numbers to the hydrophon numbers. - * Realistically, this list is always 0,1,2,etc or it goes horribly wrong ! - */ - // so write functions here to get information from the daqParams. -// System.out.printf("Sample regime: %s input with rate %3.1fHz, %d channels, gain %3.1fdB, ADCp-p %3.1fV\n", daqParams.getDaqSystemType(), -// daqParams.getSampleRate(), daqParams.getNChannels(), daqParams.preamplifier.getGain(), daqParams.voltsPeak2Peak); - /** - * then there is the actual sampling. This is a bit harder to find. I thought it would be in the data map - * but the datamap is a simple count of what's in the databasase which is not quite what we want. - * we're going to have to query the database to get more detailed informatoin I think. - * I'll do that here for now, but we may want to move this when we better organise the code. - * It also seems that there are 'bad' dates in the database when it starts new files, which are the date - * data were analysed at. So we really need to check the start and stop records only. - */ - PamDataBlock daqInfoDataBlock = daqControl.getAcquisitionProcess().getDaqStatusDataBlock(); - // just load everything. Probably OK for the acqusition, but will bring down - daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null); - ArrayList allStatusData = daqInfoDataBlock.getDataCopy(); - /** - * Due to seird file overlaps we need to resort this by id if we can. - * - */ - Collections.sort(allStatusData, new Comparator() { - - @Override - public int compare(DaqStatusDataUnit o1, DaqStatusDataUnit o2) { - if (o1.getDatabaseIndex() == 0) { - return (int) (o1.getTimeMilliseconds()-o2.getTimeMilliseconds()); - } - return o1.getDatabaseIndex()-o2.getDatabaseIndex(); - } - }); - - ArrayList tempPeriods = null; - - if (allStatusData == null || allStatusData.size() == 0) { - System.out.println("Data appear to have no logged recording periods. Try to extract from raw audio ..."); - tempPeriods = extractTimesFromFiles(daqControl); - } - else { - tempPeriods = extractTimesFromStatus(allStatusData); - } - if (tempPeriods == null || tempPeriods.size() == 0) { - System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); - tempPeriods = extractTimesFromOutputMaps(); - } - if (tempPeriods == null || tempPeriods.size() == 0) { - System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); - return null; - } - - int nPeriods = tempPeriods.size(); -// int i = 0; -// for (RecordingPeriod aP : tempPeriods) { -// System.out.printf("Pre merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), -// PamCalendar.formatDBDateTime(aP.getRecordStop())); +// /** +// * Get an overview of all the deployments. +// * @return +// */ +// public DeploymentOverview createPamguardOverview() { +// // first find an acquisition module. +// PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); +// if (!(aModule instanceof AcquisitionControl)) { +// // will return if it's null. Impossible for it to be the wrong type. +// // but it's good practice to check anyway before casting. +// return null; // } - // now go through those and merge into longer periods where there is no gap between files. - 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.maxGapSeconds*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.minLengthSeconds*1000L) { - iterator.remove(); - } - } -// i = 0; -// for (RecordingPeriod aP : tempPeriods) { -// System.out.printf("Post merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), -// PamCalendar.formatDBDateTime(aP.getRecordStop())); +// // cast it to the right type. +// AcquisitionControl daqControl = (AcquisitionControl) aModule; +// AcquisitionParameters daqParams = daqControl.getAcquisitionParameters(); +// /** +// * The daqParams class has most of what we need about the set up in terms of sample rate, +// * number of channels, instrument type, ADC input range (part of calibration), etc. +// * It also has a hydrophone list, which maps the input channel numbers to the hydrophon numbers. +// * Realistically, this list is always 0,1,2,etc or it goes horribly wrong ! +// */ +// // so write functions here to get information from the daqParams. +//// System.out.printf("Sample regime: %s input with rate %3.1fHz, %d channels, gain %3.1fdB, ADCp-p %3.1fV\n", daqParams.getDaqSystemType(), +//// daqParams.getSampleRate(), daqParams.getNChannels(), daqParams.preamplifier.getGain(), daqParams.voltsPeak2Peak); +// /** +// * then there is the actual sampling. This is a bit harder to find. I thought it would be in the data map +// * but the datamap is a simple count of what's in the databasase which is not quite what we want. +// * we're going to have to query the database to get more detailed informatoin I think. +// * I'll do that here for now, but we may want to move this when we better organise the code. +// * It also seems that there are 'bad' dates in the database when it starts new files, which are the date +// * data were analysed at. So we really need to check the start and stop records only. +// */ +// PamDataBlock daqInfoDataBlock = daqControl.getAcquisitionProcess().getDaqStatusDataBlock(); +// // just load everything. Probably OK for the acqusition, but will bring down +// daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null); +// ArrayList allStatusData = daqInfoDataBlock.getDataCopy(); +// /** +// * Due to seird file overlaps we need to resort this by id if we can. +// * +// */ +// Collections.sort(allStatusData, new Comparator() { +// +// @Override +// public int compare(DaqStatusDataUnit o1, DaqStatusDataUnit o2) { +// if (o1.getDatabaseIndex() == 0) { +// return (int) (o1.getTimeMilliseconds()-o2.getTimeMilliseconds()); +// } +// return o1.getDatabaseIndex()-o2.getDatabaseIndex(); +// } +// }); +// +// ArrayList tempPeriods = null; +// +// if (allStatusData == null || allStatusData.size() == 0) { +// System.out.println("Data appear to have no logged recording periods. Try to extract from raw audio ..."); +// tempPeriods = extractTimesFromFiles(daqControl); // } -// System.out.printf("Data have %d distinct files, but only %d distinct recording periods\n", nPeriods, tempPeriods.size()); - DutyCycleInfo dutyCycleinfo = assessDutyCycle(tempPeriods); - // if it's duty cycles, then we only want a single entry. - ArrayList deploymentPeriods; - if (dutyCycleinfo.isDutyCycled == false) { - deploymentPeriods = tempPeriods; - } - else { - deploymentPeriods = new ArrayList<>(); - 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); - matchPamguard2Tethys(deploymentOverview, projectDeployments); - this.deploymentOverview = deploymentOverview; - return deploymentOverview; - // find the number of times it started and stopped .... -// System.out.printf("Input map of sound data indicates data from %s to %s with %d starts and %d stops over %d files\n", -// PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), nStart, nStop, nFile+1); - // now work out where there are genuine gaps and make up a revised list of recording periods. - - - } +// else { +// tempPeriods = extractTimesFromStatus(allStatusData); +// } +// if (tempPeriods == null || tempPeriods.size() == 0) { +// System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); +// tempPeriods = extractTimesFromOutputMaps(); +// } +// if (tempPeriods == null || tempPeriods.size() == 0) { +// System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); +// return null; +// } +// +// int nPeriods = tempPeriods.size(); +//// int i = 0; +//// for (RecordingPeriod aP : tempPeriods) { +//// System.out.printf("Pre merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), +//// PamCalendar.formatDBDateTime(aP.getRecordStop())); +//// } +// // now go through those and merge into longer periods where there is no gap between files. +// 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.maxGapSeconds*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.minLengthSeconds*1000L) { +// iterator.remove(); +// } +// } +//// i = 0; +//// for (RecordingPeriod aP : tempPeriods) { +//// System.out.printf("Post merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), +//// PamCalendar.formatDBDateTime(aP.getRecordStop())); +//// } +//// System.out.printf("Data have %d distinct files, but only %d distinct recording periods\n", nPeriods, tempPeriods.size()); +// DutyCycleInfo dutyCycleinfo = assessDutyCycle(tempPeriods); +// // if it's duty cycles, then we only want a single entry. +// ArrayList deploymentPeriods; +// if (dutyCycleinfo.isDutyCycled == false) { +// deploymentPeriods = tempPeriods; +// } +// else { +// deploymentPeriods = new ArrayList<>(); +// 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); +// matchPamguard2Tethys(deploymentOverview, projectDeployments); +// this.deploymentOverview = deploymentOverview; +// return deploymentOverview; +// // find the number of times it started and stopped .... +//// System.out.printf("Input map of sound data indicates data from %s to %s with %d starts and %d stops over %d files\n", +//// PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), nStart, nStop, nFile+1); +// // now work out where there are genuine gaps and make up a revised list of recording periods. +// +// +// } public void showOptions(Window parent) { if (parent == null) { parent = tethysControl.getGuiFrame(); } - DeploymentExportOpts newOpts = RecordingGapDialog.showDiloag(parent, exportOptions); + DeploymentExportOpts newOpts = RecordingGapDialog.showDiloag(parent, deploymentExportOptions); if (newOpts != null) { - exportOptions = newOpts; - deploymentOverview = createPamguardOverview(); - updateProjectDeployments(); + deploymentExportOptions = newOpts; + createPamguardOverview(); } } - + + public void createPamguardOverview() { + deploymentOverview = effortFunctions.makeRecordingOverview(); + updateProjectDeployments(); + matchPamguard2Tethys(deploymentOverview, projectDeployments); + } + /** * Export button pressed on GUI. Run wizard.... */ public void exportDeployments() { Deployment deployment = MetaDataContol.getMetaDataControl().getMetaData().getDeployment(); - DeploymentExportOpts exportOptions = DeploymentWizard.showWizard(getTethysControl().getGuiFrame(), tethysControl, deployment, this.exportOptions); + DeploymentExportOpts exportOptions = DeploymentWizard.showWizard(getTethysControl().getGuiFrame(), tethysControl, deployment, this.deploymentExportOptions); if (exportOptions != null) { - this.exportOptions = exportOptions; + this.deploymentExportOptions = exportOptions; deploymentOverview = getDeploymentOverview(); ArrayList allPeriods = deploymentOverview.getRecordingPeriods(); exportDeployments(allPeriods); @@ -373,14 +387,14 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb * @param selectedDeployments */ public void exportDeployments(ArrayList selectedDeployments) { - if (exportOptions.separateDeployments) { + if (deploymentExportOptions.separateDeployments) { exportSeparateDeployments(selectedDeployments); } else { exportOneDeploymnet(selectedDeployments); } - } + /** * Make one big deployment document with all the recording periods in it. */ @@ -390,7 +404,9 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId(); RecordingPeriod onePeriod = new RecordingPeriod(selectedDeployments.get(0).getRecordStart(), selectedDeployments.get(selectedDeployments.size()-1).getRecordStop()); - Deployment deployment = createDeploymentDocument(freeId, onePeriod); + TethysExportParams exportParams = tethysControl.getTethysExportParams(); + String id = String.format("%s_%s", exportParams.getDatasetName(), "all"); + Deployment deployment = createDeploymentDocument(freeId, onePeriod, id); // fill in a few things from here Deployment globalMeta = getTethysControl().getGlobalDeplopymentData(); deployment.setCruise(globalMeta.getCruise()); @@ -428,16 +444,18 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb 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); PDeployment exDeploymnet = recordPeriod.getMatchedTethysDeployment(); Deployment deployment = null; + String id = String.format("%s_%d", exportParams.getDatasetName(), i); if (exDeploymnet != null) { - deployment = createDeploymentDocument(freeId, recordPeriod); + deployment = createDeploymentDocument(freeId, recordPeriod, id); deployment.setId(exDeploymnet.deployment.getId()); } if (deployment == null) { - deployment = createDeploymentDocument(freeId++, recordPeriod); + deployment = createDeploymentDocument(freeId++, recordPeriod, id); } deployment.setCruise(globalMeta.getCruise()); deployment.setSite(globalMeta.getSite()); @@ -458,42 +476,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); } - /** - * Get data times from any other datamap, since this will generally match the acquisition anyway - * @return - */ - private ArrayList extractTimesFromOutputMaps() { - OfflineDataMap bestMap = null; - PamDataBlock bestBlock = null; - long firstStart = Long.MAX_VALUE; - long lastEnd = Long.MIN_VALUE; - ArrayList dataBlocks = PamController.getInstance().getDetectorDataBlocks(); - for (PamDataBlock aBlock : dataBlocks) { - if (aBlock instanceof PamRawDataBlock) { - continue; // don't want acquisition ! - } - OfflineDataMap dataMap = aBlock.getPrimaryDataMap(); - if (dataMap == null) { - continue; - } - if (dataMap.getFirstDataTime() < firstStart && dataMap.getLastDataTime() > lastEnd) { - bestMap = dataMap; - bestBlock = aBlock; - firstStart = dataMap.getFirstDataTime(); - lastEnd = dataMap.getLastDataTime(); - } - } - if (bestMap == null) { - return null; - } - // get the times out of it. - ArrayList recPeriods = new ArrayList<>(); - List mapPoints = bestMap.getMapPoints(); - for (OfflineDataMapPoint mapPoint : mapPoints) { - recPeriods.add(new RecordingPeriod(mapPoint.getStartTime(), mapPoint.getEndTime())); - } - return recPeriods; - } + public DeploymentOverview getDeploymentOverview() { return deploymentOverview; @@ -556,108 +539,12 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb return overlap; } - /** - * 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(ArrayList tempPeriods) { - 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); - - - 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; - } - - private ArrayList extractTimesFromStatus(ArrayList allStatusData) { - ArrayList tempPeriods = new ArrayList<>(); - long dataStart = Long.MAX_VALUE; - long dataEnd = Long.MIN_VALUE; - Long lastStart = null; - int nStart = 0; - int nStop = 0; - int nFile = 0; - for (DaqStatusDataUnit daqStatus : allStatusData) { - switch (daqStatus.getStatus()) { - case "Start": - nStart++; - dataStart = Math.min(dataStart, daqStatus.getTimeMilliseconds()); - lastStart = daqStatus.getTimeMilliseconds(); -// System.out.println("Start at " + PamCalendar.formatDBDateTime(lastStart)); - break; - case "Stop": - nStop++; - dataEnd = Math.max(dataEnd, daqStatus.getEndTimeInMilliseconds()); - long lastEnd = daqStatus.getEndTimeInMilliseconds(); - if (lastStart != null) { -// System.out.printf("Adding period %s to %s\n", PamCalendar.formatDBDateTime(lastStart), -// PamCalendar.formatDBDateTime(lastEnd)); - tempPeriods.add(new RecordingPeriod(lastStart, lastEnd)); - } - else { -// System.out.println("Skipping stop at " + PamCalendar.formatDBDateTime(lastEnd)); - } - lastStart = null; - break; - case "NextFile": - nFile++; - break; - } - } - return tempPeriods; - } - - private ArrayList extractTimesFromFiles(AcquisitionControl daqControl) { - // TODO Auto-generated method stub - return null; - } - - /** - * Get a list of Tethys Deployment docs that match the current PAMGuard data. + * Get a list of Tethys Deployment docs that match the current PAMGuard data. Watch for repeats + * if a single deployment doc covers many perdiods. * @return */ public ArrayList getMatchedDeployments() { @@ -666,8 +553,11 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb return matched; } for (RecordingPeriod period : deploymentOverview.getRecordingPeriods()) { - if (period.getMatchedTethysDeployment() != null) { - matched.add(period.getMatchedTethysDeployment()); + PDeployment deployment = period.getMatchedTethysDeployment(); + if (deployment != null) { + if (matched.contains(deployment) == false) { + matched.add(period.getMatchedTethysDeployment()); + } } } return matched; @@ -808,7 +698,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb return firstFree; } - public Deployment createDeploymentDocument(int i, RecordingPeriod recordingPeriod) { + public Deployment createDeploymentDocument(int i, RecordingPeriod recordingPeriod, String deploymentId) { Deployment deployment = new Deployment(); try { nilus.Helper.createRequiredElements(deployment); @@ -822,10 +712,11 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb // TODO Auto-generated catch block e.printStackTrace(); } + PamguardMetaData pamguardMetaData = MetaDataContol.getMetaDataControl().getMetaData(); + Deployment templateDeployment = pamguardMetaData.getDeployment(); + // Deployment globalDeplData = tethysControl.getGlobalDeplopymentData(); - TethysExportParams exportParams = tethysControl.getTethysExportParams(); - String id = String.format("%s_%d", exportParams.getDatasetName(), i); - deployment.setId(id); + deployment.setId(deploymentId); deployment.setDeploymentId(i); DeploymentRecoveryDetails deploymentDetails = deployment.getDeploymentDetails(); @@ -836,11 +727,21 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb if (recoveryDetails == null) { recoveryDetails = new DeploymentRecoveryDetails(); } - deploymentDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); - recoveryDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); deploymentDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); recoveryDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); + // handle situation where deployment and recovery times are not the same as the audio times. + if (pamguardMetaData.useAudioForDeploymentTimes == false) { + if (templateDeployment.getDeploymentDetails().getAudioTimeStamp() != null) { + deploymentDetails.setTimeStamp(templateDeployment.getDeploymentDetails().getAudioTimeStamp()); + } + if (templateDeployment.getRecoveryDetails().getAudioTimeStamp() != null) { + recoveryDetails.setTimeStamp(templateDeployment.getRecoveryDetails().getAudioTimeStamp()); + } + } + + deploymentDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); + recoveryDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); deployment.setDeploymentDetails(deploymentDetails); deployment.setRecoveryDetails(recoveryDetails); @@ -849,16 +750,25 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb TethysLocationFuncs.getTrackAndPositionData(deployment); - getTrackDetails(deployment); + getTrackDetails(deployment); - DescriptionType description = deployment.getDescription(); - if (description == null ) { - description = new DescriptionType(); - deployment.setDescription(description); - description.setAbstract("No abstract"); - description.setMethod("no methods"); - description.setObjectives("No objectives"); - } + /** + * Get some of the meta data from the centralised source. + */ + MetadataInfo metaData = templateDeployment.getMetadataInfo(); + metaData.setDate(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); + metaData.setUpdateFrequency("as-needed"); + deployment.setMetadataInfo(metaData); + + deployment.setDescription(templateDeployment.getDescription()); +// DescriptionType description = deployment.getDescription(); +// if (description == null ) { +// description = new DescriptionType(); +// deployment.setDescription(description); +// description.setAbstract("No abstract"); +// description.setMethod("no methods"); +// description.setObjectives("No objectives"); +// } // description.set addSamplingDetails(deployment, recordingPeriod); @@ -900,7 +810,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb */ long trackStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getTimeStamp()); long trackEnd = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getTimeStamp()); - long dataWin =(long) (Math.max(1./trackInfo.getGPSDataRate(), exportOptions.trackPointInterval)); + long dataWin =(long) (Math.max(1./trackInfo.getGPSDataRate(), deploymentExportOptions.trackPointInterval)); // get the tracks object. Tracks tracks = deployment.getData().getTracks(); @@ -919,7 +829,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb ListIterator it = gpsDataBlock.getListIterator(0); while (it.hasNext()) { GpsDataUnit gpsDataUnit = it.next(); - if (gpsDataUnit.getTimeMilliseconds()-lastPointTime < exportOptions.trackPointInterval*1000) { + if (gpsDataUnit.getTimeMilliseconds()-lastPointTime < deploymentExportOptions.trackPointInterval*1000) { continue; } GpsData gpsData = gpsDataUnit.getGpsData(); @@ -1248,8 +1158,8 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb List channelInfos = samplingDetails.getChannel(); for (int i = 0; i < nChan; i++) { ChannelInfo channelInfo = new ChannelInfo(); - channelInfo.setStart(deployment.getDeploymentDetails().getAudioTimeStamp()); - channelInfo.setEnd(deployment.getRecoveryDetails().getAudioTimeStamp()); + channelInfo.setStart(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); + channelInfo.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); BigIntegerConverter biCon = new BigIntegerConverter(); BigInteger chanNum = BigInteger.valueOf(i); @@ -1273,7 +1183,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb Sampling sampling = new Sampling(); List regimens = sampling.getRegimen(); Sampling.Regimen regimen = new Sampling.Regimen(); - regimen.setTimeStamp(deployment.getDeploymentDetails().getAudioTimeStamp()); + regimen.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); regimen.setSampleRateKHz(fs/1000.); if (system != null) { regimen.setSampleBits(system.getSampleBits()); @@ -1288,10 +1198,15 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb nilus.ChannelInfo.DutyCycle.Regimen dsr = new nilus.ChannelInfo.DutyCycle.Regimen(); reg.add(dsr); RecordingDurationS ssss = new RecordingDurationS(); - ssss.setValue(dutyCycleInf.meanOnTimeS); + // round to a second ... or .1s if short duty cycle. + int dp = 1; + if (dutyCycleInf.meanOnTimeS > 59) { + dp = 0; + } + ssss.setValue(AutoTethysProvider.roundDecimalPlaces(dutyCycleInf.meanOnTimeS,dp)); dsr.setRecordingDurationS(ssss); RecordingIntervalS ris = new RecordingIntervalS(); - ris.setValue(dutyCycleInf.meanOnTimeS + dutyCycleInf.meanGapS); + ris.setValue(AutoTethysProvider.roundDecimalPlaces(dutyCycleInf.meanOnTimeS + dutyCycleInf.meanGapS,dp)); dsr.setRecordingIntervalS(ris); dsr.setTimeStamp(deployment.getDeploymentDetails().getAudioTimeStamp()); channelInfo.setDutyCycle(dutyCycle); @@ -1329,7 +1244,7 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb @Override public Serializable getSettingsReference() { - return exportOptions; + return deploymentExportOptions; } @Override @@ -1339,10 +1254,17 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb @Override public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { - exportOptions = (DeploymentExportOpts) pamControlledUnitSettings.getSettings(); + deploymentExportOptions = (DeploymentExportOpts) pamControlledUnitSettings.getSettings(); return true; } } + /** + * @return the deploymentExportOptions + */ + public DeploymentExportOpts getDeploymentExportOptions() { + return deploymentExportOptions; + } + } diff --git a/src/tethys/deployment/DeploymentOverview.java b/src/tethys/deployment/DeploymentOverview.java index 2dacab61..f296df8e 100644 --- a/src/tethys/deployment/DeploymentOverview.java +++ b/src/tethys/deployment/DeploymentOverview.java @@ -1,6 +1,16 @@ 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; /** * Class to give a general overview of all the effort in PAMGuard which will form the @@ -25,7 +35,8 @@ public class DeploymentOverview { this.dutyCycleInfo = dutyCycleInfo; this.recordingPeriods = tempPeriods; } - + + public void addRecordingPeriod(long start, long stop) { addRecordingPeriod(new RecordingPeriod(start, stop)); } diff --git a/src/tethys/deployment/EffortFunctions.java b/src/tethys/deployment/EffortFunctions.java new file mode 100644 index 00000000..75184c16 --- /dev/null +++ b/src/tethys/deployment/EffortFunctions.java @@ -0,0 +1,407 @@ +package tethys.deployment; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.ListIterator; + +import Acquisition.AcquisitionControl; +import Acquisition.AcquisitionParameters; +import Acquisition.DaqStatusDataUnit; +import PamController.PamControlledUnit; +import PamController.PamController; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamRawDataBlock; +import SoundRecorder.RecordingInfo; +import binaryFileStorage.BinaryStore; +import dataMap.OfflineDataMap; +import dataMap.OfflineDataMapPoint; +import pamMaths.STD; +import tethys.TethysControl; +import tethys.output.TethysExportParams; +import tethys.pamdata.TethysDataProvider; + +/** + * functions for working out total effort and periods of recording from a variety of sources, which may be + * the recordings database, binary files, etc. + * @author dg50 + * + */ +public class EffortFunctions { + + private TethysControl tethysControl; + + + /** + * @param tethysControl + */ + public EffortFunctions(TethysControl tethysControl) { + this.tethysControl = tethysControl; + } + + private DeploymentOverview createOverview(RecordingList tempPeriods) { + + DutyCycleInfo dutyCycleinfo = assessDutyCycle(tempPeriods); + + // 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; + } + + + public DeploymentOverview makeRecordingOverview() { + + RecordingList recordingPeriods = listSoundAcquisitionDatabase(); + + RecordingList binaryPeriods = listBinaryFiles(); + + long l1 = listDuration(recordingPeriods); + long l2 = listDuration(binaryPeriods); + if (listDuration(binaryPeriods) > listDuration(recordingPeriods)) { + recordingPeriods = binaryPeriods; + } + + DeploymentOverview deploymentOverview = createOverview(recordingPeriods); + + return deploymentOverview; + } + + private long listDuration(RecordingList recordingList) { + if (recordingList == null) { + return -1; + } + return recordingList.duration(); + } + + public RecordingList listBinaryFiles() { + BinaryStore binaryStore = BinaryStore.findBinaryStoreControl(); + if (binaryStore == null) { + return null; + } + RecordingList bestList = null; + ArrayList allBlocks = PamController.getInstance().getDataBlocks(); + for (PamDataBlock aBlock : allBlocks) { + OfflineDataMap dataMap = aBlock.getOfflineDataMap(binaryStore); + if (dataMap == null) { + continue; + } + TethysDataProvider tethysProvider = aBlock.getTethysDataProvider(tethysControl); + if (tethysProvider == null) { + continue; // do we really need this ? + } + RecordingList blockList = listMapPoints(dataMap); + if (blockList == null) { + continue; + } + if (bestList == null) { + bestList = blockList; + } + else { + long l1 = bestList.duration(); + long l2 = blockList.duration(); + if (l2>l1) { + bestList = blockList; + } + } + } + bestList = mergeRecordings(bestList); + return bestList; + } + + + public RecordingList listMapPoints(OfflineDataMap dataMap) { + List mapPoints = dataMap.getMapPoints(); + if (mapPoints == null) { + return null; + } + RecordingList periods = new RecordingList(); + for (OfflineDataMapPoint mapPoint : mapPoints) { + periods.add(new RecordingPeriod(mapPoint.getStartTime(), mapPoint.getEndTime())); + } + return periods; + } + + + + public RecordingList listSoundAcquisitionDatabase() { + // first find an acquisition module. + PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); + if (!(aModule instanceof AcquisitionControl)) { + // will return if it's null. Impossible for it to be the wrong type. + // but it's good practice to check anyway before casting. + return null; + } + // cast it to the right type. + AcquisitionControl daqControl = (AcquisitionControl) aModule; + AcquisitionParameters daqParams = daqControl.getAcquisitionParameters(); + /** + * The daqParams class has most of what we need about the set up in terms of sample rate, + * number of channels, instrument type, ADC input range (part of calibration), etc. + * It also has a hydrophone list, which maps the input channel numbers to the hydrophon numbers. + * Realistically, this list is always 0,1,2,etc or it goes horribly wrong ! + */ + // so write functions here to get information from the daqParams. + // System.out.printf("Sample regime: %s input with rate %3.1fHz, %d channels, gain %3.1fdB, ADCp-p %3.1fV\n", daqParams.getDaqSystemType(), + // daqParams.getSampleRate(), daqParams.getNChannels(), daqParams.preamplifier.getGain(), daqParams.voltsPeak2Peak); + /** + * then there is the actual sampling. This is a bit harder to find. I thought it would be in the data map + * but the datamap is a simple count of what's in the databasase which is not quite what we want. + * we're going to have to query the database to get more detailed informatoin I think. + * I'll do that here for now, but we may want to move this when we better organise the code. + * It also seems that there are 'bad' dates in the database when it starts new files, which are the date + * data were analysed at. So we really need to check the start and stop records only. + */ + PamDataBlock daqInfoDataBlock = daqControl.getAcquisitionProcess().getDaqStatusDataBlock(); + // just load everything. Probably OK for the acqusition, but will bring down + daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null); + ArrayList allStatusData = daqInfoDataBlock.getDataCopy(); + /** + * Due to weird file overlaps we need to resort this by id if we can. + * + */ + Collections.sort(allStatusData, new Comparator() { + + @Override + public int compare(DaqStatusDataUnit o1, DaqStatusDataUnit o2) { + if (o1.getDatabaseIndex() == 0) { + return (int) (o1.getTimeMilliseconds()-o2.getTimeMilliseconds()); + } + return o1.getDatabaseIndex()-o2.getDatabaseIndex(); + } + }); + + RecordingList tempPeriods = null; + + if (allStatusData == null || allStatusData.size() == 0) { + System.out.println("Data appear to have no logged recording periods. Try to extract from raw audio ..."); + tempPeriods = extractTimesFromFiles(daqControl); + } + else { + tempPeriods = extractTimesFromStatus(allStatusData); + } + if (tempPeriods == null || tempPeriods.size() == 0) { + System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); + tempPeriods = extractTimesFromOutputMaps(); + } + if (tempPeriods == null || tempPeriods.size() == 0) { + System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); + return null; + } + + int nPeriods = tempPeriods.size(); + // int i = 0; + // for (RecordingPeriod aP : tempPeriods) { + // System.out.printf("Pre merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), + // 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; + } + + 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; + } + + /** + * 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) { + 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); + + + 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 + * @return + */ + private RecordingList extractTimesFromOutputMaps() { + OfflineDataMap bestMap = null; + PamDataBlock bestBlock = null; + long firstStart = Long.MAX_VALUE; + long lastEnd = Long.MIN_VALUE; + ArrayList dataBlocks = PamController.getInstance().getDetectorDataBlocks(); + for (PamDataBlock aBlock : dataBlocks) { + if (aBlock instanceof PamRawDataBlock) { + continue; // don't want acquisition ! + } + OfflineDataMap dataMap = aBlock.getPrimaryDataMap(); + if (dataMap == null) { + continue; + } + if (dataMap.getFirstDataTime() < firstStart && dataMap.getLastDataTime() > lastEnd) { + bestMap = dataMap; + bestBlock = aBlock; + firstStart = dataMap.getFirstDataTime(); + lastEnd = dataMap.getLastDataTime(); + } + } + if (bestMap == null) { + return null; + } + // get the times out of it. + RecordingList recPeriods = new RecordingList(); + List mapPoints = bestMap.getMapPoints(); + for (OfflineDataMapPoint mapPoint : mapPoints) { + recPeriods.add(new RecordingPeriod(mapPoint.getStartTime(), mapPoint.getEndTime())); + } + return recPeriods; + } + + private RecordingList extractTimesFromStatus(ArrayList allStatusData) { + RecordingList tempPeriods = new RecordingList(); + long dataStart = Long.MAX_VALUE; + long dataEnd = Long.MIN_VALUE; + Long lastStart = null; + int nStart = 0; + int nStop = 0; + int nFile = 0; + for (DaqStatusDataUnit daqStatus : allStatusData) { + switch (daqStatus.getStatus()) { + case "Start": + nStart++; + dataStart = Math.min(dataStart, daqStatus.getTimeMilliseconds()); + lastStart = daqStatus.getTimeMilliseconds(); + // System.out.println("Start at " + PamCalendar.formatDBDateTime(lastStart)); + break; + case "Stop": + nStop++; + dataEnd = Math.max(dataEnd, daqStatus.getEndTimeInMilliseconds()); + long lastEnd = daqStatus.getEndTimeInMilliseconds(); + if (lastStart != null) { + // System.out.printf("Adding period %s to %s\n", PamCalendar.formatDBDateTime(lastStart), + // PamCalendar.formatDBDateTime(lastEnd)); + tempPeriods.add(new RecordingPeriod(lastStart, lastEnd)); + } + else { + // System.out.println("Skipping stop at " + PamCalendar.formatDBDateTime(lastEnd)); + } + lastStart = null; + break; + case "NextFile": + nFile++; + break; + } + } + return tempPeriods; + } + + private RecordingList extractTimesFromFiles(AcquisitionControl daqControl) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/tethys/deployment/RecordingList.java b/src/tethys/deployment/RecordingList.java new file mode 100644 index 00000000..34b41c1d --- /dev/null +++ b/src/tethys/deployment/RecordingList.java @@ -0,0 +1,52 @@ +package tethys.deployment; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +public class RecordingList extends ArrayList { + + private static final long serialVersionUID = 1L; + + /** + * Get the duration of the recording periods from start to end. + * @return + */ + public long duration() { + return getEnd()-getStart(); + } + + /** + * Get the start of the first in the list. + * @return + */ + public long getStart() { + if (size() == 0) { + return 0; + } + return get(0).getRecordStart(); + } + + /** + * get the end of the last in the list. + */ + public long getEnd() { + if (size() == 0) { + return 0; + } + return get(size()-1).getRecordStop(); + } + + /** + * Sort the list in ascending order. + */ + public void sort() { + Collections.sort(this, new Comparator() { + + @Override + public int compare(RecordingPeriod o1, RecordingPeriod o2) { + return (int) Math.signum(o1.getRecordStart()-o2.getRecordStart()); + } + }); + } +} diff --git a/src/tethys/deployment/swing/ProjectInformationPanel.java b/src/tethys/deployment/swing/ProjectInformationPanel.java index dea88590..1ced05b1 100644 --- a/src/tethys/deployment/swing/ProjectInformationPanel.java +++ b/src/tethys/deployment/swing/ProjectInformationPanel.java @@ -127,6 +127,9 @@ public class ProjectInformationPanel { } public boolean getParams(Deployment deployment) { + if (deployment == null) { + return false; + } deployment.setCruise(cruise.getText()); deployment.setRegion(region.getText()); deployment.setSite(site.getText()); diff --git a/src/tethys/deployment/swing/RecordingGapDialog.java b/src/tethys/deployment/swing/RecordingGapDialog.java index 6012d97d..399eb3bc 100644 --- a/src/tethys/deployment/swing/RecordingGapDialog.java +++ b/src/tethys/deployment/swing/RecordingGapDialog.java @@ -51,20 +51,20 @@ public class RecordingGapDialog extends PamDialog { private void setParams(DeploymentExportOpts exportOpts) { this.exportOpts = exportOpts; - maxGap.setText(String.format("%d", exportOpts.maxGapSeconds)); - minLength.setText(String.format("%d", exportOpts.minLengthSeconds)); + maxGap.setText(String.format("%d", exportOpts.maxRecordingGapSeconds)); + minLength.setText(String.format("%d", exportOpts.minRecordingLengthSeconds)); } @Override public boolean getParams() { try { - exportOpts.maxGapSeconds = Integer.valueOf(maxGap.getText()); + exportOpts.maxRecordingGapSeconds = Integer.valueOf(maxGap.getText()); } catch (NumberFormatException e) { return showWarning("Invalid inter recording interval"); } try { - exportOpts.minLengthSeconds = Integer.valueOf(minLength.getText()); + exportOpts.minRecordingLengthSeconds = Integer.valueOf(minLength.getText()); } catch (NumberFormatException e) { return showWarning("Invalid minimum recording length"); diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index b7c77add..b5740ea7 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -39,10 +39,12 @@ 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; import tethys.species.DataBlockSpeciesManager; +import tethys.swing.export.DetectionsExportWizard; /** * Functions for handling output of Detections documents. @@ -401,6 +403,11 @@ public class DetectionsHandler { 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(); @@ -411,10 +418,6 @@ public class DetectionsHandler { exportObserver.update(prog); } - if (currentDetections == null) { - currentDetections = startDetectionsDocument(deployment, dataBlock, streamExportParams); - currentDetections.getEffort().setStart(TethysTimeFuncs.xmlGregCalFromMillis(mapPoint.getStartTime())); - } if (mapPoint.getEndTime() < deployment.getAudioStart()) { continue; } @@ -629,4 +632,30 @@ public class DetectionsHandler { } } + + /** + * Export data from given block, using appropriate species checks and other dialogs. + * @param dataBlock + */ + public void exportDataBlock(PamDataBlock dataBlock) { + if (dataBlock == null) { + return; + } + + /** + * Check the species map is OK before doing anything. + */ + DataBlockSpeciesManager spManager = dataBlock.getDatablockSpeciesManager(); + if (spManager != null) { + String error = spManager.checkSpeciesMapError(); + if (error != null) { + PamDialog.showWarning(PamController.getMainFrame(), "Datablock species manager error", error); + spManager.showSpeciesDialog(); + return; + } + } + + DetectionsExportWizard.showDialog(tethysControl.getGuiFrame(), tethysControl, dataBlock); + + } } diff --git a/src/tethys/niluswraps/NilusSettingsWrapper.java b/src/tethys/niluswraps/NilusSettingsWrapper.java index 72616537..d5ed1401 100644 --- a/src/tethys/niluswraps/NilusSettingsWrapper.java +++ b/src/tethys/niluswraps/NilusSettingsWrapper.java @@ -169,48 +169,5 @@ public class NilusSettingsWrapper implements Serializable, Clo } return clone; } - - -// private Class getNilusClass() throws NoSuchMethodException, SecurityException { -// Method method = this.getClass().getMethod("getNilusObject", null); -// Class retClass = GenericTypeResolver.resolveReturnType(method, this.getClass()); -// -// return retClass; -// } - -// -// public static void main(String[] args) { -// -// Deployment deployment = new Deployment(); -// try { -// Helper.createRequiredElements(deployment); -// } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { -// e.printStackTrace(); -// } -// deployment.setCruise("Test cruise"); -// deployment.setDeploymentId(111); -// deployment.setProject("Tethys Development"); -// DescriptionType desc = deployment.getDescription(); -// if (desc == null) { -// desc = new DescriptionType(); -// deployment.setDescription(desc); -// } -// desc.setAbstract("Deployment Abstract"); -// -// NilusSettingsWrapper wrapper = new NilusSettingsWrapper<>(); -// -// wrapper.setNilusObject(deployment); -// -// System.out.println(wrapper.xmlString); -// -// Deployment newDeployment = wrapper.getNilusObject(Deployment.class); -// -// // now warp the new object again and print that. -// newDeployment.setDeploymentId(newDeployment.getDeploymentId()*2); -// wrapper.setNilusObject(newDeployment); -// System.out.println("********************************************"); -// System.out.println(wrapper.xmlString); -// -// } } diff --git a/src/tethys/niluswraps/WrappedDescriptionType.java b/src/tethys/niluswraps/WrappedDescriptionType.java index bc1a5ac4..cd27b1f1 100644 --- a/src/tethys/niluswraps/WrappedDescriptionType.java +++ b/src/tethys/niluswraps/WrappedDescriptionType.java @@ -3,6 +3,7 @@ package tethys.niluswraps; import java.io.Serializable; import nilus.DescriptionType; +import nilus.Helper; /** * Because we want to save DescriptionType objects in serialised @@ -32,7 +33,17 @@ public class WrappedDescriptionType extends NilusSettingsWrapper unique - //project => project in pamguard - //deploymentId == id - //deploymentAlias => blank - //site => UI addition in pamguard, not done, can be blank - //siteAlias => blank - //cruise => UI addition, optional - //Platform=> UI addition in pamguard - //region => UI addition - //Instrument/Type => UI, array manager details (hydrophone names area) - //Instrument/Id => UI, array manager details - //Instrument/Geometry => in pamguard array manager - //SamplingDetails/Channel - //ChannelNumber => in pamguard, hyrdrophone array - //SensorNumber => in pamguard, - //Start => same as timestamp deployment detail - //End => same as timestamp recovery detail - //Sampling/Regimen (change sample rate, pamgauard doesnt handle, only on, get channel info in that loop) - //TimeStamp => start time - //SampleRate_kHz => - //SampleBits => - //Gain (another func call to get gain info) - //DutyCycles => needs to be calculated, not fields in pamguard, have fun Douglas - //QualityAssurance => not in pamguard, UI, maybe deployment notes, optional - //Data/Audio (static) - //URI => folder where audio is saved - //Data/Tracks - //Track => GPS datatable (granularity filter) - //TrackId => not unique between deployments, - //TrackEffort - //OnPath => scattered throughout pamguard - //URI => option, check with Shannon on how they are doing deployments - //Sensors/Audio (per hydrophone not quad array) streamer info + individual hydrophone data together - //pamguard hydrophone data - //number => hydrophoneId - //sensorId => sensor serial number - //Geometry => array geometry field goes to - //Sensors/Depth - //optional - //Sensors/Sensor - //Number => hydrophoneId in pamguard - //SensorId => addition to UI - //Geometry => array geometry fields - //Type => Hydrophone Type - - - - - - //get list of deployment recovery details (start, stop times and lat/long) - //deployment details and recovery details are same structure - //per pair, go through a loop to fill in each deployment -// DeploymentHandler deploymentHandler = new DeploymentHandler(tethysControl); - DeploymentHandler deploymentHandler = tethysControl.getDeploymentHandler(); - -// ArrayList deployRecover = deploymentHandler.getDeployments(); -// if (deployRecover == null) { -// return false; -// } - - ArrayList deploymentDocs = new ArrayList<>(); - /* - * This will become the main loop over deployment documents - */ - DeploymentOverview deploymentOverview = deploymentHandler.getDeploymentOverview(); - int i = deploymentHandler.getFirstFreeDeploymentId(); - for (RecordingPeriod recordingPeriod : deploymentOverview.getRecordingPeriods()) { - - Deployment deployment = deploymentHandler.createDeploymentDocument(i++, recordingPeriod); -// System.out.println(deployment.toString()); - deploymentDocs.add(deployment); - try { - tethysControl.getDbxmlConnect().postAndLog(deployment); - } catch (TethysException e) { - tethysControl.showException(e); - } - - } - - - /* - * go through the export params and call something for every data block that's - * enabled. - */ - DetectionsHandler detectionsHandler = new DetectionsHandler(tethysControl); - ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); -// /** -// * Outer loop is through deployemnt documents. Will then export detections within each -// * deployment detector by detector -// */ -// for (Deployment aDeployment : deploymentDocs) { -// for (PamDataBlock aDataBlock : allDataBlocks) { -// StreamExportParams streamExportParams = tethysExportParams.getStreamParams(aDataBlock); -// if (streamExportParams == null || !streamExportParams.selected) { -// continue; // not interested in this one. -// } -// detectionsHandler.exportDetections(aDataBlock, aDeployment, -// new DetectionGranularity(GRANULARITY.TIME, 3600*12), tethysExportParams, streamExportParams); -// } -// } - /* - * Then do whatever else is needed to complete the document. - */ - - - return true; - } - -// /** -// * find Deployment data. This is stored in a separate PAMGuard module, which may -// * not be present. -// * -// * @return -// */ -// public DeploymentData findDeploymentData() { -// /** -// * What to do if this isn't present or is incomplete ? Should we be showing this -// * in the main export dialog at some point ? More a Q for when we make a nicer -// * UI later in the project. -// */ -// MetaDataContol metaControl = (MetaDataContol) PamController.getInstance() -// .findControlledUnit(MetaDataContol.unitType); -// if (metaControl == null) { -// return null; -// } else { -// return metaControl.getDeploymentData(); -// } -// } - - public SnapshotGeometry findArrayGeometrey() { - /* - * this should never be null, but again, we might want to put some warnings and - * exception handlers in here anyway. Really just an example to show how to find - * this. We'll need to dig a bit elsewhere to get more detailed hydrophone - * information. - */ - /* - * In PAMGuard hydrophones are assigned to streamers, which can have different - * methods for estimating their positions from GPS. The geometry is a sum of xyz - * in the streamer and xyz in the hydrophone object Within a streamer, - * hydrophones are considered rigid relative to each other. The stremer will - * floow a choice of modesl (rigid, threading, etc) to estimate it's position - * relative to the GPS track. Different errors are used when estimating - * localisation errors within and between streamers. The Snapshot geometry sorts - * a lot of this out for a point in time and will give back a single object - * which is most of what we'll be wanting. - */ - PamArray currentArray = ArrayManager.getArrayManager().getCurrentArray(); - SnapshotGeometry currentGeometry = currentArray.getSnapshotGeometry(PamCalendar.getTimeInMillis()); - /* - * The following lines of code show how to get more detailed calibration info - * for each hydrophone, but we'll have to think about the easiest way to - * repackage this for Tethys. e.g. this function could be modified to return the - * correct Tethys object in one go. - */ - ArrayList hydrophones = currentArray.getHydrophoneArray(); - /* - * each object in the list will have more detailed cal information for each - * phone. But for the full system calibration we'd need to go to the Acquisition - * module. - */ - AcquisitionControl daqControl = (AcquisitionControl) PamController.getInstance() - .findControlledUnit(AcquisitionControl.unitType); - if (daqControl != null) { - AcquisitionProcess daqProcess = daqControl.getAcquisitionProcess(); - for (int iPhone = 0; iPhone < hydrophones.size(); iPhone++) { - Hydrophone aPhone = hydrophones.get(iPhone); - double totalCal = -daqProcess.rawAmplitude2dB(1, iPhone, false); -// System.out.printf( -// "hydrophone %d has sensitivity %3.1fdB + gain %3.1fdB. Total calibration is %3.1fdB re1U/uPa\n", -// iPhone, aPhone.getSensitivity(), aPhone.getPreampGain(), totalCal); - } - } - - return currentGeometry; - } - - - -} diff --git a/src/tethys/output/swing/TethysExportDialog.java b/src/tethys/output/swing/TethysExportDialog.java deleted file mode 100644 index 91302405..00000000 --- a/src/tethys/output/swing/TethysExportDialog.java +++ /dev/null @@ -1,187 +0,0 @@ -package tethys.output.swing; - -import java.awt.BorderLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Window; -import java.util.ArrayList; - -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.border.TitledBorder; - -import PamController.PamController; -import PamView.dialog.PamDialog; -import PamView.dialog.PamGridBagContraints; -import PamguardMVC.PamDataBlock; -import PamguardMVC.dataSelector.DataSelector; -import tethys.TethysControl; -import tethys.output.StreamExportParams; -import tethys.output.TethysExportParams; - -/** - * Start of a dialog for controlling the export of Tethys data. For first iteration - * this will just be a list of output streams (PamDataBlocks) which have a database - * connection. Each will have a checkbox. On OK it will return back a class listing - * what to output and the calling function can do as it will. Future versions will - * probably want to push the functionality into a SwingWorker to show progress, etc. - * but that can come later. - * - * Normally, I use single instance dialogs for this sort of thing. - * @author dg50 - * - */ -public class TethysExportDialog extends PamDialog { - - private static TethysExportDialog singleInstance; - - private TethysControl tethysControl; - - private TethysExportParams exportParams; - - private JPanel streamsPanel; - - private ArrayList dataStreamSets = new ArrayList<>(); - - private TethysExportDialog(Window parentFrame, TethysControl tethysControl) { - super(parentFrame, "Tethys Export", false); - this.tethysControl = tethysControl; - - JPanel mainPanel = new JPanel(new BorderLayout()); - /* - * Expect to add at least one more panel at the top of this to have options - * for things like connection details to the database. If not another panel, - * then they can be arranged on tabs, as a wizard, etc. - */ - streamsPanel = new JPanel(); - streamsPanel.setBorder(new TitledBorder("Data Streams")); - mainPanel.add(BorderLayout.CENTER, streamsPanel); - - setDialogComponent(mainPanel); - setResizable(true); - - } - @Deprecated - private static TethysExportParams showDialog(Window parentFrame, TethysControl tethysControl) { - if (singleInstance == null || singleInstance.getOwner() != parentFrame || singleInstance.tethysControl != tethysControl) { - singleInstance = new TethysExportDialog(parentFrame, tethysControl); - } - singleInstance.makeStreamsPanel(); - singleInstance.setParams(); - singleInstance.setVisible(true); - return singleInstance.exportParams; - } - - - /** - * remake the panel. Gets rebuilt whenever dialog opens in case - * the list of available data has changed. - */ - private void makeStreamsPanel() { - streamsPanel.removeAll(); - streamsPanel.setLayout(new GridBagLayout()); - GridBagConstraints c = new PamGridBagContraints(); - dataStreamSets = findDataStreams(); - streamsPanel.add(new JLabel(" Data Stream ", JLabel.CENTER), c); - c.gridx++; - streamsPanel.add(new JLabel(" Data Select ", JLabel.CENTER), c); - for (DataStreamSet aSet : dataStreamSets) { - c.gridx = 0; - c.gridy++; - streamsPanel.add(aSet.checkBox, c); - // try to add a data selector - DataSelector dataSelector = aSet.dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); - if (dataSelector != null) { - c.gridx++; - JButton button = dataSelector.getDialogButton(this); - if (button != null) { - streamsPanel.add(button, c); - } - } - } - pack(); - } - - /** - * Get a set of data blocks that can provide Tethys data. - * @return datablocks which can provide Tethys data - */ - private ArrayList findDataStreams() { - ArrayList sets = new ArrayList<>(); - ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); - for (PamDataBlock aDataBlock : allDataBlocks) { - if (aDataBlock.getTethysDataProvider(tethysControl) != null) { - sets.add(new DataStreamSet(aDataBlock)); - } - } - return sets; - } - - private void setParams() { - this.exportParams = tethysControl.getTethysExportParams(); - if (exportParams == null) { - exportParams = new TethysExportParams(); - } - else { - exportParams = exportParams.clone(); - } - setParams(exportParams); - } - - private void setParams(TethysExportParams exportParams) { - if (exportParams == null || dataStreamSets == null) { - return; - } - for (DataStreamSet streamSet : dataStreamSets) { - StreamExportParams streamOpts = exportParams.getStreamParams(streamSet.dataBlock); - if (streamOpts == null) { - continue; - } - streamSet.checkBox.setSelected(streamOpts.selected); - } - - } - - @Override - public boolean getParams() { - if (exportParams == null || dataStreamSets == null) { - return false; - } - int nSel = 0; - for (DataStreamSet streamSet : dataStreamSets) { - StreamExportParams streamOpts = new StreamExportParams(tethysControl, streamSet.dataBlock, streamSet.checkBox.isSelected()); - exportParams.setStreamParams(streamSet.dataBlock, streamOpts); - nSel++; - } - return nSel > 0; - } - - @Override - public void cancelButtonPressed() { - exportParams = null; - } - - @Override - public void restoreDefaultSettings() { - // TODO Auto-generated method stub - - } - - private class DataStreamSet { - - private PamDataBlock dataBlock; - - private JCheckBox checkBox; - - public DataStreamSet(PamDataBlock dataBlock) { - super(); - this.dataBlock = dataBlock; - checkBox = new JCheckBox(dataBlock.getDataName()); - checkBox.setToolTipText(dataBlock.getLongDataName()); - } - - - } -} diff --git a/src/tethys/pamdata/TethysParameterPacker.java b/src/tethys/pamdata/TethysParameterPacker.java index 892859e1..e5cc05cc 100644 --- a/src/tethys/pamdata/TethysParameterPacker.java +++ b/src/tethys/pamdata/TethysParameterPacker.java @@ -30,6 +30,7 @@ import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; import nilus.MarshalXML; import tethys.TethysControl; +import tethys.output.TethysExportParams; /** * Functions to pack up a PAMGuard parameters object into the correct format @@ -64,6 +65,7 @@ public class TethysParameterPacker { * Get a list of elements of parameters for all modules feeding * the given datablock. These are given in reverse order. * @param pamDataBlock output datablock + * @param fullChain * @return parameters of all modules feeding that datablock. */ public List packParameters(PamDataBlock pamDataBlock) { @@ -72,11 +74,14 @@ public class TethysParameterPacker { if (pamControlledUnit == null || pamControlledUnit instanceof PamSettings == false) { return null; } + + int paramOption = tethysControl.getTethysExportParams().detectorParameterOutput; + if (paramOption == TethysExportParams.DETECTORE_PARAMS_NONE) { + return null; + } + PamSettings pamSettings = (PamSettings) pamControlledUnit; -// return null; -// } -// -// public List packParameters(Object data) { + List elList = new ArrayList(); Object data = pamSettings.getSettingsReference(); @@ -132,9 +137,9 @@ public class TethysParameterPacker { // } } } - - - + if (paramOption == TethysExportParams.DETECTOR_DATASELECTOR) { + return elList; + } QName qname = new QName(MarshalXML.schema, "parameters", "ty"); JAXBElement jaxel = new JAXBElement( @@ -165,6 +170,9 @@ public class TethysParameterPacker { el.appendChild(pgEl); // elList.add(pgEl); } + if (paramOption == TethysExportParams.DETECTORE_PARAMS_MODULE) { + break; + } } return elList; } diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java index 35947776..7817cf82 100644 --- a/src/tethys/species/DataBlockSpeciesManager.java +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -121,7 +121,7 @@ abstract public class DataBlockSpeciesManager { allCodes.add(defaultDefaultSpecies.getPamguardName()); } DataBlockSpeciesCodes codeList = getSpeciesCodes(); - if (codeList != null) { + if (codeList != null && codeList.getSpeciesNames() != null) { allCodes.addAll(codeList.getSpeciesNames()); } if (allCodes.size() == 0) { diff --git a/src/tethys/species/ITISTypes.java b/src/tethys/species/ITISTypes.java index 409f74dd..4681cac9 100644 --- a/src/tethys/species/ITISTypes.java +++ b/src/tethys/species/ITISTypes.java @@ -8,7 +8,7 @@ package tethys.species; public class ITISTypes { public static final int UNKNOWN = 0; - public static final int ANTHROPOGENIC = -10; + public static final int ANTHROPOGENIC = 1; public static final String getName(int code) { switch (code) { diff --git a/src/tethys/species/swing/SpeciesSubPanel.java b/src/tethys/species/swing/SpeciesSubPanel.java index 548b23a1..53495f2a 100644 --- a/src/tethys/species/swing/SpeciesSubPanel.java +++ b/src/tethys/species/swing/SpeciesSubPanel.java @@ -29,18 +29,30 @@ public class SpeciesSubPanel { private JButton searchButton; public SpeciesSubPanel(String aSpecies) { + + callType = new JTextField(15); + pamguardName = new JLabel(aSpecies); + itisCode = new JTextField(6); + searchButton = new JButton("Find"); + latinName = new JTextField(15); + commonName = new JTextField(15); + mainPanel = new JPanel(new GridBagLayout()); mainPanel.setBorder(new BevelBorder(BevelBorder.RAISED)); GridBagConstraints c = new PamGridBagContraints(); mainPanel.add(new JLabel("Name ", JLabel.RIGHT), c); c.gridx++; - mainPanel.add(pamguardName = new JLabel(aSpecies), c); + mainPanel.add(pamguardName, c); c.gridx++; mainPanel.add(new JLabel(" ITIS code ", JLabel.RIGHT), c); c.gridx++; - mainPanel.add(itisCode = new JTextField(6), c); + mainPanel.add(itisCode, c); c.gridx ++; - mainPanel.add(searchButton = new JButton("Find")); + mainPanel.add(searchButton, c); + + c.gridx ++; + c.gridwidth = 1; + mainPanel.add(latinName); int w1 = 2; int w2 = 3; @@ -50,21 +62,26 @@ public class SpeciesSubPanel { mainPanel.add(new JLabel("Call / sound type ", JLabel.RIGHT), c); c.gridx+= c.gridwidth; c.gridwidth = w2; - mainPanel.add(callType = new JTextField(15), c); - c.gridx = 0; - c.gridy++; - c.gridwidth = w1; - mainPanel.add(new JLabel("Scientific name ", JLabel.RIGHT), c); - c.gridx+= c.gridwidth; - c.gridwidth = w2; - mainPanel.add(latinName = new JTextField(15), c); - c.gridx = 0; - c.gridy++; - c.gridwidth = w1; - mainPanel.add(new JLabel("Common name ", JLabel.RIGHT), c); - c.gridx+= c.gridwidth; - c.gridwidth = w2; - mainPanel.add(commonName = new JTextField(15), c); + mainPanel.add(callType, c); + c.gridx += c.gridwidth; + c.gridwidth = 1; + mainPanel.add(commonName, c); +// c.gridx = 0; +// c.gridy++; +// c.gridwidth = w1; +// mainPanel.add(new JLabel("Scientific name ", JLabel.RIGHT), c); +// c.gridx+= c.gridwidth; +// c.gridwidth = w2; +// mainPanel.add(latinName, c); +// c.gridx = 0; +// c.gridy++; +// c.gridwidth = w1; +// mainPanel.add(new JLabel("Common name ", JLabel.RIGHT), c); +// c.gridx+= c.gridwidth; +// c.gridwidth = w2; +// mainPanel.add(commonName = new JTextField(15), c); + + callType.setText(aSpecies); // will get overwritten if the user choses something else. pamguardName.setToolTipText("Internal name within PAMGuard module"); itisCode.setToolTipText("ITIS species code"); @@ -72,6 +89,9 @@ public class SpeciesSubPanel { callType.setToolTipText("Descriptive name for call type or measurement"); latinName.setToolTipText("Scientific name"); commonName.setToolTipText("Common name"); + commonName.setEditable(false); +// commonName.setEnabled(false); + latinName.setEditable(false); searchButton.addActionListener(new ActionListener() { @Override @@ -119,14 +139,20 @@ public class SpeciesSubPanel { public void setParams(SpeciesMapItem speciesMapItem) { if (speciesMapItem == null) { itisCode.setText(null); - callType.setText(null); +// callType.setText(null); latinName.setText(null); commonName.setText(null); return; } pamguardName.setText("\"" + speciesMapItem.getPamguardName() + "\""); itisCode.setText(String.format("%d", speciesMapItem.getItisCode())); - callType.setText(speciesMapItem.getCallType()); + String callT = speciesMapItem.getCallType(); + if (callT != null && callT.length()>0) { + callType.setText(speciesMapItem.getCallType()); + } + else { + callType.setText(speciesMapItem.getPamguardName()); + } latinName.setText(speciesMapItem.getLatinName()); commonName.setText(speciesMapItem.getCommonName()); } diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index 30ce74be..b5b75397 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -23,6 +23,7 @@ import PamView.PamGui; import PamView.dialog.warn.WarnOnce; import PamView.tables.SwingTableColumnWidths; import PamguardMVC.PamDataBlock; +import nilus.DataSourceType; import nilus.DetectionEffortKind; import nilus.Detections; import nilus.GranularityType; @@ -42,25 +43,25 @@ import tethys.niluswraps.PDetections; * */ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTableObserver { - + private JPanel mainPanel; - + private JLabel dataBlockName; private TableModel tableModel; - + private JTable table; private PamDataBlock dataBlock; - + private StreamDetectionsSummary streamDetectionsSummary; - + public DatablockDetectionsPanel(TethysControl tethysControl) { super(tethysControl); mainPanel = new JPanel(new BorderLayout()); mainPanel.add(BorderLayout.NORTH, dataBlockName = new JLabel("PAMGUard data stream", JLabel.LEFT)); mainPanel.setBorder(new TitledBorder("Data stream Tethys Detections documents")); - + tableModel = new TableModel(); table = new JTable(tableModel) { @Override @@ -68,46 +69,46 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa return getToolTip(event); } - protected JTableHeader createDefaultTableHeader() { - return new JTableHeader(columnModel) { - public String getToolTipText(MouseEvent e) { - return getToolTip(e); - } - }; - } - + protected JTableHeader createDefaultTableHeader() { + return new JTableHeader(columnModel) { + public String getToolTipText(MouseEvent e) { + return getToolTip(e); + } + }; + } + }; JScrollPane scrollPane = new JScrollPane(table); mainPanel.add(BorderLayout.CENTER, scrollPane); - + new SwingTableColumnWidths(tethysControl.getUnitName() + getClass().getName(), table); - + table.addMouseListener(new MouseActions()); } protected String getToolTip(MouseEvent event) { - java.awt.Point p = event.getPoint(); - int rowIndex = table.rowAtPoint(p); -// if (rowIndex < 0) { -// return null; -// } - int colIndex = table.columnAtPoint(p); - switch (colIndex) { - case 0: - return "Tethys Detections document name"; - case 1: - return "Name of PAMGuard data stream"; - case 2: - return "Effort period"; - case 3: - return "Output granularity"; - case 4: - return "Number of detection elements in document"; - case 5: - return "Document abstract"; - - } - return "No tip"; + java.awt.Point p = event.getPoint(); + int rowIndex = table.rowAtPoint(p); + // if (rowIndex < 0) { + // return null; + // } + int colIndex = table.columnAtPoint(p); + switch (colIndex) { + case 0: + return "Tethys Detections document name"; + case 1: + return "Name of PAMGuard data stream"; + case 2: + return "Effort period"; + case 3: + return "Output granularity"; + case 4: + return "Number of detection elements in document"; + case 5: + return "Document abstract"; + + } + return "No tip"; } @Override @@ -122,7 +123,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa streamDetectionsSummary = getTethysControl().getDetectionsHandler().getStreamDetections(dataBlock); tableModel.fireTableDataChanged(); } - + @Override public void updateState(TethysState tethysState) { if (dataBlock != null) { @@ -146,55 +147,92 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } } - + } - + public void showPopupMenu(MouseEvent e) { int row = table.getSelectedRow(); if (row < 0) { return; } - + int[] rows = table.getSelectedRows(); + PDetections pDets = detectionsForRow(row); if (pDets == null) { return; } JPopupMenu popMenu = new JPopupMenu(); - - JMenuItem menuItem = new JMenuItem("Delete document " + pDets.detections.getId()); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - deleteDocument(pDets); - } - }); - popMenu.add(menuItem); - - menuItem = new JMenuItem("Display document " + pDets.detections.getId()); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - displayDocument(pDets); - } - }); - popMenu.add(menuItem); - - menuItem = new JMenuItem("Export document " + pDets.detections.getId()); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - exportDocument(pDets); - } - }); - popMenu.add(menuItem); - - - + + if (rows.length == 1) { + JMenuItem menuItem = new JMenuItem("Delete document " + pDets.detections.getId()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteDocument(pDets); + } + }); + popMenu.add(menuItem); + + menuItem = new JMenuItem("Display document " + pDets.detections.getId()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + displayDocument(pDets); + } + }); + popMenu.add(menuItem); + + menuItem = new JMenuItem("Export document " + pDets.detections.getId()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportDocument(pDets); + } + }); + popMenu.add(menuItem); + } + else if (rows.length > 0){ + JMenuItem menuItem = new JMenuItem("Delete multiple Detections documents"); + menuItem.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + deleteDocuments(rows); + } + + }); + popMenu.add(menuItem); + } + + popMenu.show(e.getComponent(), e.getX(), e.getY()); - + } - + + private void deleteDocuments(int[] rows) { + String msg = String.format("Are you sure you want to delete %d Detections documents ?", rows.length); + + int ans = WarnOnce.showWarning(PamGui.findComponentWindow(mainPanel), "Delete Document", msg, WarnOnce.OK_CANCEL_OPTION); + if (ans != WarnOnce.OK_OPTION) { + return; + } + for (int i = 0; i < rows.length; i++) { + int row = rows[i]; + PDetections pDets = detectionsForRow(row); + if (pDets == null) { + continue; + } + try { + getTethysControl().getDbxmlConnect().deleteDocument(pDets.detections); + } catch (TethysException e) { + getTethysControl().showException(e); + } + } + getTethysControl().exportedDetections(dataBlock); + selectDataBlock(dataBlock); // force table update. + } + protected void deleteDocument(PDetections pDets) { String msg = String.format("Are you sure you want to delete the Detections document %s ?", pDets.detections.getId()); int ans = WarnOnce.showWarning(PamGui.findComponentWindow(mainPanel), "Delete Document", msg, WarnOnce.OK_CANCEL_OPTION); @@ -212,12 +250,12 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa private void displayDocument(PDetections pDets) { getTethysControl().displayDocument(Collection.Detections.collectionName(), pDets.detections.getId()); - + } private void exportDocument(PDetections pDets) { getTethysControl().exportDocument(Collection.Detections.toString(), pDets.detections.getId()); - + } private PDetections detectionsForRow(int iRow) { @@ -231,8 +269,8 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } private class TableModel extends AbstractTableModel { - - private String[] colNames = {"Document", "Detector", "Effort", "Granularity", "Count", "Abstract"}; + + private String[] colNames = {"Document", "Detector", "Deployment", "Effort", "Granularity", "Count", "Abstract"}; @Override public int getRowCount() { @@ -275,10 +313,18 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } return pDets.dataBlock.getDataName(); case 2: + DataSourceType dataSource = dets.getDataSource(); + if (dataSource == null) { + return null; + } + else { + return dataSource.getDeploymentId(); + } + case 3: XMLGregorianCalendar start = dets.getEffort().getStart(); XMLGregorianCalendar stop = dets.getEffort().getEnd(); return start + " to " + stop; - case 3: + case 4: List kinds = dets.getEffort().getKind(); if (kinds == null) { return null; @@ -287,19 +333,19 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa if (kind.getGranularity() != null) { GranularityType granularity = kind.getGranularity(); return PDeployment.formatGranularity(granularity); -// if (granularity != null) { -// return granularity.getValue(); -// } + // if (granularity != null) { + // return granularity.getValue(); + // } } } break; - case 4: - return pDets.count; case 5: + return pDets.count; + case 6: return dets.getDescription().getAbstract(); } return null; } - + } } diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java index 8f1a220f..f7d1df4f 100644 --- a/src/tethys/swing/DatablockSynchPanel.java +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -11,6 +11,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; +import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JPanel; @@ -22,13 +23,16 @@ import javax.swing.table.AbstractTableModel; import PamUtils.PamCalendar; +import PamView.dialog.warn.WarnOnce; import PamView.panel.PamPanel; +import PamView.panel.WestAlignedPanel; import PamView.tables.SwingTableColumnWidths; import PamguardMVC.PamDataBlock; import dataMap.OfflineDataMap; import tethys.TethysControl; import tethys.TethysState; import tethys.TethysStateObserver; +import tethys.niluswraps.PDeployment; import tethys.output.DatablockSynchInfo; import tethys.species.DataBlockSpeciesManager; @@ -43,6 +47,8 @@ public class DatablockSynchPanel extends TethysGUIPanel { private ArrayList dataBlockSynchInfo; private ArrayList tableObservers = new ArrayList<>(); + + private JButton exportButton; public DatablockSynchPanel(TethysControl tethysControl) { super(tethysControl); @@ -53,8 +59,22 @@ public class DatablockSynchPanel extends TethysGUIPanel { new SwingTableColumnWidths(tethysControl.getUnitName()+"SynchTable", synchTable); JScrollPane scrollPane = new JScrollPane(synchTable); mainPanel.add(BorderLayout.CENTER, scrollPane); + PamPanel ctrlPanel = new PamPanel(new BorderLayout()); + exportButton = new JButton("Export ..."); + ctrlPanel.add(BorderLayout.WEST, exportButton); + mainPanel.add(BorderLayout.NORTH, ctrlPanel); + + synchTable.addMouseListener(new MouseActions()); synchTable.addKeyListener(new KeyActions()); + + exportButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportData(); + } + }); + enableExportButton(); } @Override @@ -106,9 +126,30 @@ public class DatablockSynchPanel extends TethysGUIPanel { DatablockSynchInfo synchInfo = dataBlockSynchInfo.get(row); // datablockDetectionsPanel.setDataBlock(synchInfo.getDataBlock()); notifyObservers(synchInfo.getDataBlock()); + enableExportButton(); return row; } + protected void exportData() { + int[] rows = synchTable.getSelectedRows(); + if (rows == null || rows.length != 1) { + WarnOnce.showWarning("Data selection", "you must select a single data block for export", WarnOnce.WARNING_MESSAGE); + return; + } + PamDataBlock dataBlock = dataBlockSynchInfo.get(rows[0]).getDataBlock(); + getTethysControl().getDetectionsHandler().exportDataBlock(dataBlock); + } + + private void enableExportButton() { + int[] rows = synchTable.getSelectedRows(); + boolean en = rows != null && rows.length == 1; + ArrayList deployments = getTethysControl().getDeploymentHandler().getMatchedDeployments(); + if (deployments == null || deployments.size() == 0) { + en = false; + } + exportButton.setEnabled(en); + } + public void showPopup(MouseEvent e, int row) { DatablockSynchInfo synchInfo = dataBlockSynchInfo.get(row); if (synchInfo == null) { @@ -133,6 +174,15 @@ public class DatablockSynchPanel extends TethysGUIPanel { @Override public void updateState(TethysState tethysState) { + switch (tethysState.stateType) { + case DELETEDATA: + case EXPORTRDATA: + case NEWPROJECTSELECTION: +// dataBlockSynchInfo = null; +// getSychInfos(); +// getTethysControl().coun + } + synchTableModel.fireTableDataChanged(); selectRow(); } diff --git a/src/tethys/swing/DetectionsExportPanel.java b/src/tethys/swing/DetectionsExportPanel.java index 6598f68e..5dbbf941 100644 --- a/src/tethys/swing/DetectionsExportPanel.java +++ b/src/tethys/swing/DetectionsExportPanel.java @@ -20,6 +20,7 @@ import tethys.TethysControl; import tethys.species.DataBlockSpeciesManager; import tethys.swing.export.DetectionsExportWizard; +@Deprecated public class DetectionsExportPanel extends TethysGUIPanel implements StreamTableObserver { private JPanel mainPanel; @@ -28,7 +29,7 @@ public class DetectionsExportPanel extends TethysGUIPanel implements StreamTable private PamDataBlock selectedDataBlock; - public DetectionsExportPanel(TethysControl tethysControl) { + private DetectionsExportPanel(TethysControl tethysControl) { super(tethysControl); mainPanel = new PamAlignmentPanel(BorderLayout.NORTH); mainPanel.setLayout(new GridBagLayout()); diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 2aec93d2..6d7209b5 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -222,20 +222,49 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { continue; } try { + if (checkDetections(depl.deployment) == false) { + continue; + } boolean gone = getTethysControl().getDbxmlConnect().deleteDocument(depl.deployment); } catch (TethysException e) { getTethysControl().showException(e); } } - getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); + getTethysControl().sendStateUpdate(new TethysState(StateType.DELETEDATA, Collection.Deployments)); } - protected void exportDeployment(PDeployment pDeployment) { - getTethysControl().exportDocument(Collection.Deployments.collectionName(), pDeployment.deployment.getId()); - } - - protected void displayDeployment(PDeployment pDeployment) { - getTethysControl().displayDocument(Collection.Deployments.collectionName(), pDeployment.deployment.getId()); + /** + * Check for detections associated with this deployment. they must be deleted first. + * @param deployment + * @return true if there are no Detections or if they are sucessfully removed as well. + */ + private boolean checkDetections(Deployment deployment) { + // get any deployment documents that associate with this deployment. + ArrayList detectionDocs = getTethysControl().getDbxmlQueries().getDetectionsDocuments(deployment.getId()); + if (detectionDocs == null || detectionDocs.size() == 0) { + return true; + } + String msg = String.format("One or more Detections documents are associated with Deployment %s
", deployment.getId()); + for (String str : detectionDocs) { + msg += String.format("
%s", str); + } + msg += String.format("

You must delete these prior to deleting the Deploymen. Go ahead and delete ?"); + int ans = WarnOnce.showWarning(getTethysControl().getGuiFrame(), "Existing Detections documents !" , msg, WarnOnce.OK_CANCEL_OPTION); + if (ans == WarnOnce.CANCEL_OPTION) { + return false; + } + // OK, so delete all the Detections too !!! + boolean errors = false; + for (String str : detectionDocs) { + try { + boolean gone = getTethysControl().getDbxmlConnect().removeDocument(Collection.Detections, str); + } catch (TethysException e) { + getTethysControl().showException(e); + errors = true; + } + } + + return !errors; } protected void deleteDeployment(PDeployment pDeployment) { @@ -248,12 +277,23 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { if (ans == WarnOnce.CANCEL_OPTION) { return; } + if (checkDetections(dep) == false) { + return; + } try { boolean gone = getTethysControl().getDbxmlConnect().deleteDocument(dep); } catch (TethysException e) { getTethysControl().showException(e); } - getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); + getTethysControl().sendStateUpdate(new TethysState(StateType.DELETEDATA, Collection.Deployments)); + } + + protected void exportDeployment(PDeployment pDeployment) { + getTethysControl().exportDocument(Collection.Deployments.collectionName(), pDeployment.deployment.getId()); + } + + protected void displayDeployment(PDeployment pDeployment) { + getTethysControl().displayDocument(Collection.Deployments.collectionName(), pDeployment.deployment.getId()); } @Override @@ -320,7 +360,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { private class TableModel extends AbstractTableModel { - private String[] columnNames = {"Id", "Select", "Start", "Stop", "Gap", "Duration", "Cycle", "Tethys Deployment"}; + private String[] columnNames = {"Id", "Select", "Start", "Stop", "Gap", "Duration", "Cycle", "Tethys Deployment", "Deployment Effort"}; private static final int SELECTCOLUMN = 1; @@ -369,6 +409,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } private Object getValueAt(RecordingPeriod period, int rowIndex, int columnIndex) { + PDeployment deployment = period.getMatchedTethysDeployment(); switch (columnIndex) { case 0: return rowIndex; @@ -383,8 +424,18 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { // long t2 = TethysTimeFuncs.millisFromGregorianXML(deplInfo.recoveryDetails.getAudioTimeStamp()); return PamCalendar.formatDuration(period.getRecordStop()-period.getRecordStart()); case 7: - PDeployment deployment = period.getMatchedTethysDeployment(); - return makeDeplString(period, deployment); + if (deployment == null) { + return null; + } + return deployment.deployment.getId(); +// return makeDeplString(period, deployment); + case 8: + if (deployment == null) { + return null; + } + return String.format("%s to %s", PamCalendar.formatDBDateTime(deployment.getAudioStart()), + PamCalendar.formatDBDateTime(deployment.getAudioEnd())); + case SELECTCOLUMN: // return selectBoxes[rowIndex]; return period.isSelected(); diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index c7f0ada0..e86efef1 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -25,7 +25,7 @@ public class TethysMainPanel extends TethysGUIPanel { private DatablockDetectionsPanel datablockDetectionsPanel; - private DetectionsExportPanel detectionsExportPanel; +// private DetectionsExportPanel detectionsExportPanel; private CalibrationsMainPanel calibrationPanel; @@ -37,8 +37,8 @@ public class TethysMainPanel extends TethysGUIPanel { datablockDetectionsPanel = new DatablockDetectionsPanel(tethysControl); datablockSynchPanel = new DatablockSynchPanel(tethysControl); deploymentsPanel = new DeploymentsPanel(tethysControl); - detectionsExportPanel = new DetectionsExportPanel(tethysControl); - datablockSynchPanel.addTableObserver(detectionsExportPanel); +// detectionsExportPanel = new DetectionsExportPanel(tethysControl); +// datablockSynchPanel.addTableObserver(detectionsExportPanel); datablockSynchPanel.addTableObserver(datablockDetectionsPanel); calibrationPanel = new CalibrationsMainPanel(tethysControl, tethysControl.getCalibrationHandler()); @@ -62,7 +62,7 @@ public class TethysMainPanel extends TethysGUIPanel { southwestSplit.add(datablockSynchPanel.getComponent()); southwestSplit.add(southEastPanel); southEastPanel.add(datablockDetectionsPanel.getComponent(), BorderLayout.CENTER); - southEastPanel.add(detectionsExportPanel.getComponent(), BorderLayout.WEST); +// southEastPanel.add(detectionsExportPanel.getComponent(), BorderLayout.WEST); splitPane.add(southwestSplit); SwingUtilities.invokeLater(new Runnable() { // these only work if called after display is visible diff --git a/src/tethys/swing/XMLStringView.java b/src/tethys/swing/XMLStringView.java index be476f91..26098fe3 100644 --- a/src/tethys/swing/XMLStringView.java +++ b/src/tethys/swing/XMLStringView.java @@ -27,6 +27,7 @@ public class XMLStringView extends PamDialog { textArea.setCaretPosition(0); getCancelButton().setVisible(false); + setModal(false); } public static void showDialog(Window parent, String collection, String documentId, String xmlString) { diff --git a/src/tethys/swing/export/DeploymentPeriodPanel.java b/src/tethys/swing/export/DeploymentPeriodPanel.java new file mode 100644 index 00000000..26b3e9b3 --- /dev/null +++ b/src/tethys/swing/export/DeploymentPeriodPanel.java @@ -0,0 +1,128 @@ +package tethys.swing.export; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ButtonGroup; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import PamUtils.PamCalendar; +import PamView.dialog.PamDialog; +import PamView.dialog.PamGridBagContraints; +import metadata.PamguardMetaData; +import nilus.Deployment; +import nilus.DeploymentRecoveryDetails; +import tethys.TethysTimeFuncs; + +public class DeploymentPeriodPanel { + + private JPanel mainPanel; + private JTextField deploymentStart, deploymentEnd; + private JRadioButton useThese, useAudio; + private Window parentFrame; + + public DeploymentPeriodPanel(Window parentFrame) { + super(); + this.parentFrame = parentFrame; + mainPanel = new JPanel(new GridBagLayout()); + ButtonGroup bg = new ButtonGroup(); + useThese = new JRadioButton("Use fixed deployment and recovery times below"); + useAudio = new JRadioButton("Use start and end times of collected audio data"); + bg.add(useThese); + bg.add(useAudio); + useThese.setToolTipText("This is useful if recording started before a device was first deployed as is often the case for moored systems"); + deploymentStart = new JTextField(15); + deploymentEnd = new JTextField(15); + + GridBagConstraints c = new PamGridBagContraints(); + c.gridwidth = 2; + mainPanel.add(useAudio, c); + c.gridy++; + mainPanel.add(useThese, c); + c.gridy++; + c.gridwidth = 1; + mainPanel.add(new JLabel("Deployment start ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(deploymentStart, c); + c.gridy++; + c.gridx = 0; + mainPanel.add(new JLabel("Deployment end ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(deploymentEnd, c); + + useThese.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + enableControls(); + } + }); + useAudio.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + enableControls(); + } + }); + } + + protected void enableControls() { + deploymentStart.setEditable(useThese.isSelected()); + deploymentEnd.setEditable(useThese.isSelected()); + } + + /** + * @return the mainPanel + */ + public JPanel getMainPanel() { + return mainPanel; + } + + public void setParams(PamguardMetaData metaData) { + useThese.setSelected(metaData.useAudioForDeploymentTimes == false); + useAudio.setSelected(metaData.useAudioForDeploymentTimes); + + enableControls(); + + Deployment deployment = metaData.getDeployment(); + if (deployment == null) { + return; + } + DeploymentRecoveryDetails drl = deployment.getDeploymentDetails(); + Long millis = TethysTimeFuncs.millisFromGregorianXML(drl.getTimeStamp()); + if (millis != null) { + deploymentStart.setText(PamCalendar.formatDBDateTime(millis)); + } + drl = deployment.getRecoveryDetails(); + millis = TethysTimeFuncs.millisFromGregorianXML(drl.getTimeStamp()); + if (millis != null) { + deploymentEnd.setText(PamCalendar.formatDBDateTime(millis)); + } + } + + public boolean getParams(PamguardMetaData metaData) { + Deployment deployment = metaData.getDeployment(); + metaData.useAudioForDeploymentTimes = useAudio.isSelected(); + if (metaData.useAudioForDeploymentTimes) { + return true; + } + Long millis = PamCalendar.millisFromDateString(deploymentStart.getText(), true); + if (millis == null) { + return PamDialog.showWarning(parentFrame, "Bad data string", "unable to read date strin for deployment start"); + } + deployment.getDeploymentDetails().setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(millis)); + + millis = PamCalendar.millisFromDateString(deploymentEnd.getText(), true); + if (millis == null) { + return PamDialog.showWarning(parentFrame, "Bad data string", "unable to read date strin for deployment end"); + } + deployment.getRecoveryDetails().setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(millis)); + + return true; + } + +} diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index 32d70607..edfe80fd 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -24,6 +24,7 @@ import tethys.pamdata.TethysDataProvider; public class DetectionsExportWizard extends PamWizard { + private static final long serialVersionUID = 1L; private PamDataBlock dataBlock; private CardLayout cardLayout; private GranularityCard granularityCard; @@ -33,6 +34,7 @@ public class DetectionsExportWizard extends PamWizard { private AlgorithmCard algorithmCard; private ExportWorkerCard exportWorkerCard; private TethysDataProvider tethysDataProvider; + private ParameterCard parameterCard; private DetectionsExportWizard(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { super(parentFrame, "Detections Export"); @@ -48,6 +50,7 @@ public class DetectionsExportWizard extends PamWizard { addCard(algorithmCard = new AlgorithmCard(this, tethysControl, dataBlock)); addCard(granularityCard = new GranularityCard(this, tethysControl, dataBlock)); addCard(descriptionCard = new DescriptionCard(this, tethysControl)); + addCard(parameterCard = new ParameterCard(tethysControl, this, dataBlock)); addCard(exportWorkerCard = new ExportWorkerCard(this, tethysControl, dataBlock)); moveFirst(); @@ -73,15 +76,47 @@ public class DetectionsExportWizard extends PamWizard { @Override public void setCardParams(PamWizardCard wizardCard) { - wizardCard.setParams(streamExportParams); +// return wizardCard.getParams(streamExportParams); + if (wizardCard == granularityCard) { + granularityCard.setParams(streamExportParams); + } + if (wizardCard == descriptionCard) { + descriptionCard.setParams(streamExportParams.getNilusDetectionDescription()); + } + if (wizardCard == algorithmCard) { + algorithmCard.setParams(streamExportParams); + } + if (wizardCard == exportWorkerCard) { + exportWorkerCard.setParams(streamExportParams); + } + try { + wizardCard.setParams(streamExportParams); + } + catch (Exception e) { + + } } @Override public boolean getCardParams(PamWizardCard wizardCard) { +// return wizardCard.getParams(streamExportParams); + if (wizardCard == granularityCard) { + return granularityCard.getParams(streamExportParams); + } + if (wizardCard == descriptionCard) { + return descriptionCard.getParams(streamExportParams.getNilusDetectionDescription()); + } + if (wizardCard == algorithmCard) { + return algorithmCard.getParams(streamExportParams); + } + if (wizardCard == exportWorkerCard) { + return exportWorkerCard.getParams(streamExportParams); + } return wizardCard.getParams(streamExportParams); } + @Override public void cancelButtonPressed() { // TODO Auto-generated method stub diff --git a/src/tethys/swing/export/ExportWorkerCard.java b/src/tethys/swing/export/ExportWorkerCard.java index ea07875e..6c512c24 100644 --- a/src/tethys/swing/export/ExportWorkerCard.java +++ b/src/tethys/swing/export/ExportWorkerCard.java @@ -133,7 +133,10 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor itemCount.setText("0"); projectedCount.setText(String.format("%d", progress.exportCount)); skipCount.setText(String.format("%d", progress.skipCount)); - long perc = (progress.exportCount+progress.skipCount) * 100 / progress.totalUnits; + long perc = 100; + if (progress.totalUnits>0) { + perc = (progress.exportCount+progress.skipCount) * 100 / progress.totalUnits; + } progressBar.setValue((int) perc); } else if (progress.totalUnits > 0) { diff --git a/src/tethys/swing/export/ParameterCard.java b/src/tethys/swing/export/ParameterCard.java new file mode 100644 index 00000000..ddca33b4 --- /dev/null +++ b/src/tethys/swing/export/ParameterCard.java @@ -0,0 +1,67 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.ButtonGroup; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamGridBagContraints; +import PamView.panel.WestAlignedPanel; +import PamView.wizard.PamWizard; +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.deployment.DeploymentHandler; +import tethys.detection.DetectionsHandler; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; + +public class ParameterCard extends ExportWizardCard { + + private DetectionsHandler detectionsHandler; + + private JRadioButton[] optButtons; + + public ParameterCard(TethysControl tethysControl, PamWizard pamWizard, PamDataBlock dataBlock) { + super(tethysControl, pamWizard, "Algorithm Parameters", dataBlock); + detectionsHandler = tethysControl.getDetectionsHandler(); + String[] optionStrings = TethysExportParams.paramsOptNames; + optButtons = new JRadioButton[optionStrings.length]; + JPanel buttonPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + ButtonGroup bg = new ButtonGroup(); + for (int i = 0; i < optButtons.length; i++) { + optButtons[i] = new JRadioButton(optionStrings[i]); + bg.add(optButtons[i]); + buttonPanel.add(optButtons[i], c); + c.gridy++; + } + this.setLayout(new BorderLayout()); + this.add(BorderLayout.NORTH, new WestAlignedPanel(buttonPanel)); + this.setBorder(new TitledBorder("Algorithm parameters export")); + } + + @Override + public boolean getParams(StreamExportParams cardParams) { + TethysExportParams exportParams = getTethysControl().getTethysExportParams(); + for (int i = 0; i < optButtons.length; i++) { + if (optButtons[i].isSelected()) { + exportParams.detectorParameterOutput = i; + return true; + } + } + return getPamWizard().showWarning("Select a parameters export option"); + } + + @Override + public void setParams(StreamExportParams cardParams) { + TethysExportParams exportParams = getTethysControl().getTethysExportParams(); + for (int i = 0; i < optButtons.length; i++) { + optButtons[i].setSelected(i == exportParams.detectorParameterOutput); + } + } + +}