diff --git a/src/Array/ArrayDialog.java b/src/Array/ArrayDialog.java index 912d0934..578f9218 100644 --- a/src/Array/ArrayDialog.java +++ b/src/Array/ArrayDialog.java @@ -45,6 +45,8 @@ public class ArrayDialog extends PamDialog implements ActionListener { private EnvironmentPanel environmentPanel; private HydrophoneDiagram hydrophoneDiagram; + + private InstrumentIdentityPanel instrumentIdentityPanel; private JButton okButton, cancelButton; @@ -73,6 +75,7 @@ public class ArrayDialog extends PamDialog implements ActionListener { eastPanel.add(channelPanel.getChannelPanel()); environmentPanel = new EnvironmentPanel(this); + instrumentIdentityPanel = new InstrumentIdentityPanel(); // eastPanel.add(environmentPanel.getEnvironmentPanel()); @@ -80,6 +83,7 @@ public class ArrayDialog extends PamDialog implements ActionListener { JPanel westPanel = new JPanel(new BorderLayout()); westPanel.add(BorderLayout.CENTER, hydrophoneDiagram.getPlotPanel()); westPanel.add(BorderLayout.SOUTH, environmentPanel.getEnvironmentPanel()); + westPanel.add(BorderLayout.NORTH, instrumentIdentityPanel.getComponent()); splitPanel.add(westPanel); @@ -150,6 +154,7 @@ public class ArrayDialog extends PamDialog implements ActionListener { hydrophoneDialogPanel.setParams(selArray); channelPanel.setParams(); hydrophoneDiagram.rePaint(); + instrumentIdentityPanel.setParams(selArray); if (selArray != null) { environmentPanel.setNewSpeed(selArray.getSpeedOfSound()); } @@ -177,6 +182,7 @@ public class ArrayDialog extends PamDialog implements ActionListener { array.setSpeedOfSound(environmentPanel.getNewSpeed()); array.setSpeedOfSoundError(environmentPanel.getNewError()); hydrophoneDialogPanel.getParams(); + instrumentIdentityPanel.getParams(array); if (checkDaqChannels(array) == false) { return false; @@ -256,6 +262,7 @@ public class ArrayDialog extends PamDialog implements ActionListener { environmentPanel.setNewSpeed(currentArray.getSpeedOfSound()); environmentPanel.setNewError(currentArray.getSpeedOfSoundError()); } + instrumentIdentityPanel.setParams(currentArray); } void newChannelSelection() { diff --git a/src/Array/InstrumentIdentityPanel.java b/src/Array/InstrumentIdentityPanel.java new file mode 100644 index 00000000..b8f21137 --- /dev/null +++ b/src/Array/InstrumentIdentityPanel.java @@ -0,0 +1,72 @@ +package Array; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamGridBagContraints; +import PamView.panel.WestAlignedPanel; + +/** + * Instrument identity panel, contrians additional fields required by Tethys. + * @author dg50 + * + */ +public class InstrumentIdentityPanel { + + private JPanel mainPanel; + + private JTextField instrumentId; + + private JTextField instrumentType; + + public InstrumentIdentityPanel() { + mainPanel = new WestAlignedPanel(); + mainPanel.setBorder(new TitledBorder("Instrument information")); + mainPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(new JLabel("Instrument Type ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(instrumentType = new JTextField(20), c); + c.gridx = 0; + c.gridy++; + mainPanel.add(new JLabel("Instrument Id ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(instrumentId = new JTextField(20), c); + + instrumentType.setToolTipText("Instrument type, e.g. Towed array, HARP, EAR, Popup, DMON, Rock Hopper, etc."); + instrumentId.setToolTipText("Instrument identifier, e.g. serial number"); + + } + + public JComponent getComponent() { + return mainPanel; + } + + public void setParams(PamArray currentArray) { + if (currentArray == null) { + currentArray = ArrayManager.getArrayManager().getCurrentArray(); + } + if (currentArray == null) { + return; + } + instrumentType.setText(currentArray.getInstrumentType()); + instrumentId.setText(currentArray.getInstrumentId()); + } + + public void getParams(PamArray currentArray) { + if (currentArray == null) { + currentArray = ArrayManager.getArrayManager().getCurrentArray(); + } + if (currentArray == null) { + return; + } + currentArray.setInstrumentType(instrumentType.getText()); + currentArray.setInstrumentId(instrumentId.getText()); + } +} diff --git a/src/Array/PamArray.java b/src/Array/PamArray.java index a351e47d..b91f3f50 100644 --- a/src/Array/PamArray.java +++ b/src/Array/PamArray.java @@ -90,6 +90,32 @@ public class PamArray implements Serializable, Cloneable, ManagedParameters { private String arrayName; private String arrayFile; + + /** + * Type, used for Tethys and other meta data control + */ + private String instrumentType; + + public String getInstrumentType() { + return instrumentType; + } + + public void setInstrumentType(String instrumentType) { + this.instrumentType = instrumentType; + } + + public String getInstrumentId() { + return instrumentId; + } + + public void setInstrumentId(String instrumentId) { + this.instrumentId = instrumentId; + } + + /** + * Array Id. Can be anything. Compulsory for Tethys. + */ + private String instrumentId; // private int originInterpolation = ORIGIN_USE_LATEST; private int originInterpolation = ORIGIN_USE_PRECEEDING; diff --git a/src/metadata/deployment/DeploymentData.java b/src/metadata/deployment/DeploymentData.java index d42df884..3f7e351b 100644 --- a/src/metadata/deployment/DeploymentData.java +++ b/src/metadata/deployment/DeploymentData.java @@ -56,25 +56,25 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter */ private String cruise; - /** - * On what platform is the instrument deployed? (e.g. mooring, tag) - */ - private String platform = "Unknown"; +// /** +// * On what platform is the instrument deployed? (e.g. mooring, tag) +// */ +// private String platform = "Unknown"; /** * Name of geographic region. */ private String region; - /** - * Instrument type, e.g. HARP, EAR, Popup, DMON, etc. - */ - private String instrumentType; - - /** - * Instrument identifier, e.g. serial number - */ - private String instrumentId; +// /** +// * Instrument type, e.g. HARP, EAR, Popup, DMON, etc. +// */ +// private String instrumentType; +// +// /** +// * Instrument identifier, e.g. serial number +// */ +// private String instrumentId; public DeploymentData() { } @@ -207,19 +207,19 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter this.cruise = cruise; } - /** - * @return the platform - */ - public String getPlatform() { - return platform; - } - - /** - * @param platform the platform to set - */ - public void setPlatform(String platform) { - this.platform = platform; - } +// /** +// * @return the platform +// */ +// public String getPlatform() { +// return platform; +// } +// +// /** +// * @param platform the platform to set +// */ +// public void setPlatform(String platform) { +// this.platform = platform; +// } /** * @return the region @@ -235,32 +235,32 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter this.region = region; } - /** - * @return the instrumentType - */ - public String getInstrumentType() { - return instrumentType; - } - - /** - * @param instrumentType the instrumentType to set - */ - public void setInstrumentType(String instrumentType) { - this.instrumentType = instrumentType; - } - - /** - * @return the instrumentId - */ - public String getInstrumentId() { - return instrumentId; - } - - /** - * @param instrumentId the instrumentId to set - */ - public void setInstrumentId(String instrumentId) { - this.instrumentId = instrumentId; - } +// /** +// * @return the instrumentType +// */ +// public String getInstrumentType() { +// return instrumentType; +// } +// +// /** +// * @param instrumentType the instrumentType to set +// */ +// public void setInstrumentType(String instrumentType) { +// this.instrumentType = instrumentType; +// } +// +// /** +// * @return the instrumentId +// */ +// public String getInstrumentId() { +// return instrumentId; +// } +// +// /** +// * @param instrumentId the instrumentId to set +// */ +// public void setInstrumentId(String instrumentId) { +// this.instrumentId = instrumentId; +// } } diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index de485f53..b5ef662d 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -4,6 +4,7 @@ import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; +import java.io.Serializable; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; @@ -12,19 +13,28 @@ import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.Timer; import PamController.PamControlledUnit; +import PamController.PamControlledUnitSettings; import PamController.PamController; +import PamController.PamSettingManager; +import PamController.PamSettings; import PamView.PamTabPanel; import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; -import nilus.Deployment.Instrument; +import pamViewFX.PamSettingsMenuPane; +import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.DBXMLQueries; +import tethys.dbxml.ServerStatus; +import tethys.deployment.DeploymentHandler; +import tethys.detection.DetectionsHandler; +import tethys.niluswraps.PDeployment; +import tethys.output.DatablockSynchInfo; //import nilus.Deployment; -//import nilus.Deployment.Instrument; -import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.output.TethysExporter; import tethys.output.swing.TethysExportDialog; @@ -37,13 +47,12 @@ import tethys.swing.TethysTabPanel; * @author dg50 * */ -public class TethysControl extends PamControlledUnit { +public class TethysControl extends PamControlledUnit implements PamSettings, TethysStateObserver { public static final String unitType = "Tethys Interface"; public static String defaultName = "Tethys"; public static String xmlNameSpace = "http://tethys.sdsu.edu/schema/1.0"; - private TethysExportParams tethysExportParams = new TethysExportParams(); private DBXMLConnect dbxmlConnect; @@ -53,12 +62,40 @@ public class TethysControl extends PamControlledUnit { private DBXMLQueries dbxmlQueries; private ArrayList stateObservers; + + private Timer serverCheckTimer; + + private ServerStatus lastServerStatus; + + private ArrayList dataBlockSynchInfos; + + private DeploymentHandler deploymentHandler; public TethysControl(String unitName) { super(unitType, unitName); stateObservers = new ArrayList(); dbxmlConnect = new DBXMLConnect(this); dbxmlQueries = new DBXMLQueries(this); + deploymentHandler = new DeploymentHandler(this); + serverCheckTimer = new Timer(10000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + checkServer(); + } + }); + serverCheckTimer.setInitialDelay(0); + PamSettingManager.getInstance().registerSettings(this); + addStateObserver(this); + + if (PamController.getInstance().isInitializationComplete()) { + // must be adding module later on ... + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + initializationStuff(); + } + }); + } } /** @@ -91,7 +128,60 @@ public class TethysControl extends PamControlledUnit { tethysMenu.add(openClient); return tethysMenu; } + + public ArrayList getExportableDataBlocks() { + ArrayList sets = new ArrayList<>(); + ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); + for (PamDataBlock aDataBlock : allDataBlocks) { + if (aDataBlock.getTethysDataProvider() != null) { + sets.add(aDataBlock); + } + } + return sets; + } + + /** + * Get the synchronisation info for all datablocks. + * This list should be static, but check it in case something has been + * added or removed. + * @return + */ + public ArrayList getSynchronisationInfos() { + if (dataBlockSynchInfos == null) { + dataBlockSynchInfos = new ArrayList<>(); + } + ArrayList dataBlocks = getExportableDataBlocks(); + // check all datablocks are in there ... + for (PamDataBlock aBlock : dataBlocks) { + if (findDatablockSynchInfo(aBlock) == null) { + dataBlockSynchInfos.add(new DatablockSynchInfo(this, aBlock)); + } + } + // and remove any which are no longer there. + for (DatablockSynchInfo synchInfo : dataBlockSynchInfos) { + if (dataBlocks.contains(synchInfo.getDataBlock()) == false) { + dataBlockSynchInfos.remove(synchInfo); + } + } + + return dataBlockSynchInfos; + } + public DatablockSynchInfo findDatablockSynchInfo(PamDataBlock dataBlock) { + if (dataBlockSynchInfos == null) { + return null; + } + for (DatablockSynchInfo synchInfo : dataBlockSynchInfos) { + if (synchInfo.getDataBlock() == dataBlock) { + return synchInfo; + } + } + return null; + } + + /** + * open client in the default web browser + */ protected void openTethysClient() { String urlString = tethysExportParams.getFullServerName() + "/Client"; System.out.println("Opening url " + urlString); @@ -148,8 +238,17 @@ public class TethysControl extends PamControlledUnit { private void exportTethysData(TethysExportParams tethysExportParams) { TethysExporter tethysExporter = new TethysExporter(this, tethysExportParams); tethysExporter.doExport(); + + sendStateUpdate(new TethysState(StateType.TRANSFERDATA)); + countProjectDetections(); + sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION)); } + /** + * Get global deployment data. This is a bit of a mess, trying to use a separate module + * so that the rest of PAMGuard can use it, but creating the + * @return + */ public DeploymentData getGlobalDeplopymentData() { PamControlledUnit aUnit = PamController.getInstance().findControlledUnit(MetaDataContol.class, null); // if (aUnit instanceof MetaDataContol == false || true) { @@ -163,20 +262,29 @@ public class TethysControl extends PamControlledUnit { // } MetaDataContol metaControl = (MetaDataContol) aUnit; - DeploymentData deploymentData = metaControl != null ? metaControl.getDeploymentData() : new DeploymentData(); + DeploymentData deploymentData = metaControl != null ? metaControl.getDeploymentData() : getTethysProjectData(); - deploymentData.setProject("thisIsAProject"); - deploymentData.setPlatform("Yay a platform"); - deploymentData.setCruise("cruisey"); - deploymentData.setDeploymentId(142536); - deploymentData.setInstrumentId("super instrument"); - deploymentData.setSite("in the ocean somewhere"); - deploymentData.setRegion("ocean water"); - deploymentData.setInstrumentType("sensor of sorts"); +// deploymentData.setProject("thisIsAProject"); +//// deploymentData.setPlatform("Yay a platform"); +// deploymentData.setCruise("cruisey"); +// deploymentData.setDeploymentId(142536); +//// deploymentData.setInstrumentId("super instrument"); +// deploymentData.setSite("in the ocean somewhere"); +// deploymentData.setRegion("ocean water"); +//// deploymentData.setInstrumentType("sensor of sorts"); return deploymentData; } + /** + * Gets a copy of Deployment data stored with other Tethys params when the more + * general meta data provider is not present. + * @return + */ + private DeploymentData getTethysProjectData() { + return tethysExportParams.getProjectData(); + } + /** * Add a new state observer. * @param stateObserver @@ -215,4 +323,98 @@ public class TethysControl extends PamControlledUnit { return dbxmlQueries; } + @Override + public void notifyModelChanged(int changeType) { + super.notifyModelChanged(changeType); + switch (changeType) { + case PamController.INITIALIZATION_COMPLETE: + initializationStuff(); + break; + } + } + + /** + * Stuff to do on initial load (initialization complete or addition of + * a Tethys module after initialisation). + */ + private void initializationStuff() { + deploymentHandler.createPamguardOverview(); + serverCheckTimer.start(); + updateState(new TethysState(StateType.NEWPAMGUARDSELECTION)); + } + + /** + * Check the server. This will send around a notification if the state + * has changed since the last call to this function, so it's unlikely you'll + * need to use the return value + * @return server status. + */ + public ServerStatus checkServer() { + ServerStatus serverState = dbxmlConnect.pingServer(); + if (lastServerStatus == null || lastServerStatus.ok != serverState.ok) { + sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + } + lastServerStatus = serverState; + return serverState; + } + + @Override + public Serializable getSettingsReference() { + return tethysExportParams; + } + + @Override + public long getSettingsVersion() { + return TethysExportParams.serialVersionUID; + } + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + tethysExportParams = (TethysExportParams) pamControlledUnitSettings.getSettings(); + return true; + } + + @Override + public void updateState(TethysState tethysState) { + switch (tethysState.stateType) { + case NEWPROJECTSELECTION: + countProjectDetections(); + break; + } + } + + private void countProjectDetections() { + if (dataBlockSynchInfos == null) { + return; + } + DeploymentData deplData = getGlobalDeplopymentData(); + String[] dataPrefixes = new String[dataBlockSynchInfos.size()]; + int i = 0; + ArrayList matchedDeployments = deploymentHandler.getMatchedDeployments(); + for (DatablockSynchInfo synchInfo : dataBlockSynchInfos) { +// dataPrefixes[i] = DetectionsHandler.getDetectionsDocIdPrefix(deplData.getProject(), synchInfo.getDataBlock()); + int count = 0; + for (PDeployment pDepl : matchedDeployments) { + count += dbxmlQueries.countData(synchInfo.getDataBlock(), pDepl.deployment.getId()); + } + synchInfo.setDataCount(count); + i++; + } +// int[] counts = dbxmlQueries.countDataForProject(deplData.getProject(), dataPrefixes); +// if (counts != null) { +// for ( i = 0; i < counts.length; i++ ) { +// dataBlockSynchInfos.get(i).setDataCount(counts[i]); +// } +// } + } + + /** + * One stop place to get Deployment information. Will provide + * both information on record periods in PAMGuard and also Deployment docs in Tethys. + * @return set of functions for handling deployments. + */ + public DeploymentHandler getDeploymentHandler() { + return deploymentHandler; + } + } diff --git a/src/tethys/TethysMenuActions.java b/src/tethys/TethysMenuActions.java new file mode 100644 index 00000000..480399b0 --- /dev/null +++ b/src/tethys/TethysMenuActions.java @@ -0,0 +1,61 @@ +package tethys; + + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.util.ArrayList; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +import tethys.niluswraps.PDeployment; + +/* + * Some standard meny dirven functions which we may want to call from + * a few different places. + */ +public class TethysMenuActions { + + private TethysControl tethysControl; + + public TethysMenuActions(TethysControl tethysControl) { + super(); + this.tethysControl = tethysControl; + } + + public void deploymentMouseActions(MouseEvent e, PDeployment pDeployment) { + ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocsIds(pDeployment.deployment.getId()); +// System.out.println("Detections for deployment " + pDeployment.deployment.getId()); +// for (String detName : detDocNames) { +// System.out.println(detName); +// } + JPopupMenu menu = new JPopupMenu(); + if (detDocNames.size() == 0) { + JMenuItem menuItem = new JMenuItem("Delete deployment " + pDeployment.deployment.getId()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteDeployment(pDeployment); + } + }); + menu.add(menuItem); + } + else { + String str = String.format("Delete deployment %s and %d Detections documents", pDeployment.deployment.getId(), detDocNames.size()); + JMenuItem menuItem = new JMenuItem(str); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteDeployment(pDeployment); + } + }); + menu.add(menuItem); + } + menu.show(e.getComponent(), e.getX(), e.getY()); + } + + protected void deleteDeployment(PDeployment pDeployment) { + tethysControl.getDbxmlConnect().deleteDeployment(pDeployment.deployment.getId()); + } +} diff --git a/src/tethys/TethysState.java b/src/tethys/TethysState.java index 1fa7d4b4..35e3b933 100644 --- a/src/tethys/TethysState.java +++ b/src/tethys/TethysState.java @@ -8,7 +8,12 @@ package tethys; */ public class TethysState { - public enum StateType {UPDATESERVER, TRANSFERDATA}; + public enum StateType {UPDATESERVER, // Server connection or status has changed + TRANSFERDATA, // data have been transferred from PAMGuard to Tethys + NEWPROJECTSELECTION, // a new Tethys project has been selected in the GUI + NEWPAMGUARDSELECTION, // new PAMGuard data are available (called once on first load) + UPDATEMETADATA // META Data being prepared for output have changed (so may be able to enable output!) + }; public StateType stateType; diff --git a/src/tethys/TethysTimeFuncs.java b/src/tethys/TethysTimeFuncs.java index 53f6a666..922ebd1f 100644 --- a/src/tethys/TethysTimeFuncs.java +++ b/src/tethys/TethysTimeFuncs.java @@ -1,11 +1,18 @@ package tethys; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.GregorianCalendar; +import java.util.TimeZone; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; +import PamUtils.PamCalendar; + public class TethysTimeFuncs { /* @@ -36,4 +43,43 @@ public class TethysTimeFuncs { GregorianCalendar gc2 = xmlGregorian.toGregorianCalendar(); return gc2.getTimeInMillis(); } + + /** + * Make a Gregorian calendar object from a returned XML string. + * @param gregorianString + * @return + */ + public static XMLGregorianCalendar fromGregorianXML(String gregorianString) { + // typical string is 2018-10-20T00:00:00Z + if (gregorianString == null) { + return null; + } +// GregorianCalendar gCal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + gregorianString = gregorianString.replace("T", " "); + gregorianString = gregorianString.replace("Z", ""); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = null; + try { + date = df.parse(gregorianString); + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + return xmlGregCalFromMillis(date.getTime()); +// gCal.setTimeInMillis(date.getTime()); +//// gCal.se +// return gCal; + } + + public static String formatGregorianTime(XMLGregorianCalendar gregCal) { + if (gregCal == null) { + return null; + } + Long millis = millisFromGregorianXML(gregCal); + if (millis == null) { + return gregCal.toString(); + } + return PamCalendar.formatDBDateTime(millis); + } } diff --git a/src/tethys/dbxml/DBQueryResult.java b/src/tethys/dbxml/DBQueryResult.java new file mode 100644 index 00000000..4af1a245 --- /dev/null +++ b/src/tethys/dbxml/DBQueryResult.java @@ -0,0 +1,26 @@ +package tethys.dbxml; + +public class DBQueryResult { + + public long queryTimeMillis; + + public String queryResult; + + public String schemaPlan; + + public Exception queryException; + + public DBQueryResult(long queryTimeMillis, String queryResult, String schemaPlan) { + super(); + this.queryTimeMillis = queryTimeMillis; + this.queryResult = queryResult; + this.schemaPlan = schemaPlan; + } + + public DBQueryResult(long queryTimeMillis, Exception queryException) { + super(); + this.queryTimeMillis = queryTimeMillis; + this.queryException = queryException; + } + +} diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index ba061e9a..4508ebec 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -2,6 +2,7 @@ package tethys.dbxml; import java.io.IOException; import java.nio.file.Files; +import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBException; @@ -58,7 +59,7 @@ public class DBXMLConnect { fileError = Importer.ImportFiles(params.getFullServerName(), collection, new String[] { tempFile.toString() }, "", "", false); - System.out.println(fileError); +// System.out.println(fileError); tempFile.toFile().deleteOnExit(); } @@ -77,11 +78,11 @@ public class DBXMLConnect { } /** - * get tethys collection name from nilus collection objects + * get Tethys collection name from nilus collection objects * @param className nilus object Class Name * @return name of Tethys collection */ - private String getTethysCollection(String className) { + public String getTethysCollection(String className) { switch(className) { case "nilus.Deployment": return "Deployments"; @@ -106,6 +107,44 @@ public class DBXMLConnect { } } + /** + * Delete a Deploymnet and any contained Detections document. Doesn't work ! + * @param deploymentId + * @return + */ + public boolean deleteDeployment(String deploymentId) { + ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocsIds(deploymentId); + JerseyClient jerseyClient = null; + try { + jerseyClient = new JerseyClient(tethysControl.getTethysExportParams().getFullServerName()); + } + catch (Exception e) { + e.printStackTrace(); + return false; + } + String result; + for (int i = 0; i < detDocNames.size(); i++) { + try { + System.out.println("Delete " + detDocNames.get(i)); + result = jerseyClient.removeDocument("Detections", detDocNames.get(i)); + } + catch (Exception e) { + e.printStackTrace(); +// return false; +// break; + } + } + try { + result = jerseyClient.removeDocument("Deployments", deploymentId); + } + catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + public boolean openDatabase() { return true; @@ -117,7 +156,7 @@ public class DBXMLConnect { /** * Get the server state via a ping ? - * @return String descritption of state ? + * @return Server state ? */ public ServerStatus pingServer() { JerseyClient jerseyClient = new JerseyClient(tethysControl.getTethysExportParams().getFullServerName()); diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index cf3c17c0..440763d2 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -4,6 +4,7 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; +import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -13,12 +14,20 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import PamController.settings.output.xml.PamguardXMLWriter; +import PamguardMVC.PamDataBlock; import dbxml.JerseyClient; +import nilus.Deployment; +import nilus.Deployment.Instrument; +import nilus.DeploymentRecoveryDetails; +import nilus.Helper; import tethys.TethysControl; +import tethys.TethysTimeFuncs; +import tethys.niluswraps.PDeployment; import tethys.output.TethysExportParams; /** - * Some standard queries we're going to want to make from various + * Some standard queries we're going to want to make from various * parts of the system as the user interracts with the GUI. * @author dg50 * @@ -32,37 +41,57 @@ public class DBXMLQueries { this.tethysControl = tethysControl; } - public ArrayList getProjectNames() { + /** + * Execute a DBXML query. Returns an object which included the time + * taken to execute the query and either a returned Document or an Exception. + * Or will return null if the server is not connected + * @param jsonQueryString + * @return query result + */ + private DBQueryResult executeQuery(String jsonQueryString) { + + long t1 = System.currentTimeMillis(); + DBXMLConnect dbxmlConnect = tethysControl.getDbxmlConnect(); ServerStatus serverStatus = dbxmlConnect.pingServer(); - if (serverStatus.ok == false) { + if (!serverStatus.ok) { return null; } - Document doc = null; + String queryResult = null; + String schemaPlan = null; TethysExportParams params = tethysControl.getTethysExportParams(); try { - JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); + JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); - String testJson = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; - // web browse to http://localhost:9779/Client + queryResult = jerseyClient.queryJSON(jsonQueryString, 0); + schemaPlan = jerseyClient.queryJSON(jsonQueryString, 1); - String testResult = jerseyClient.queryJSON(testJson); - - doc = convertStringToXMLDocument(testResult); } catch (Exception e) { - e.printStackTrace(); + return new DBQueryResult(System.currentTimeMillis()-t1, e); + } - if (doc == null) { + return new DBQueryResult(System.currentTimeMillis()-t1, queryResult, schemaPlan); + } + + public ArrayList getProjectNames() { + + String projectQuery = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; + + DBQueryResult result = executeQuery(projectQuery); + + if (result == null || result.queryResult == null) { return null; } +// System.out.println("Project query execution time millis = " + result.queryTimeMillis); + ArrayList projectNames = new ArrayList<>(); - // iterate through the document and make a list of names, then make them unique. + // iterate through the document and make a list of names, then make them unique. /* looking for elements like this: - * + * * check out the jaxb unmarshaller ... @@ -70,8 +99,12 @@ public class DBXMLQueries { */ + Document doc = convertStringToXMLDocument(result.queryResult); + if (doc == null) { + return null; + } NodeList returns = doc.getElementsByTagName("Return"); -// System.out.println("N projects = " + returns.getLength()); + // System.out.println("N projects = " + returns.getLength()); int n = returns.getLength(); for (int i = 0; i < n; i++) { Node aNode = returns.item(i); @@ -84,19 +117,316 @@ public class DBXMLQueries { Element projEl = (Element) ((Element) depEl).getFirstChild(); String projName = projEl.getTextContent(); if (projName != null) { - if (projectNames.contains(projName) == false) { + if (!projectNames.contains(projName)) { projectNames.add(projName); } } } } } - + Collections.sort(projectNames); return projectNames; } + /** + * Get some basic (not all) data for deployments associated with a project. + * @param projectName + * @return + */ + public ArrayList getProjectDeployments(String projectName) { + String qBase = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"%s\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String qStr = String.format(qBase, projectName); + + DBQueryResult result = executeQuery(qStr); + if (result == null) { + return null; + } +// System.out.println("Deployment query execution time millis = " + result.queryTimeMillis); + + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + + Document doc = convertStringToXMLDocument(result.queryResult); + if (doc == null) { + return null; + } + +// System.out.println(pamXMLWriter.getAsString(doc)); + + ArrayList deployments = new ArrayList<>(); + + NodeList returns = doc.getElementsByTagName("Return"); + // System.out.println("N projects = " + returns.getLength()); + int n = returns.getLength(); + for (int i = 0; i < n; i++) { + Node aNode = returns.item(i); + if (aNode instanceof Element) { + Element returnedEl = (Element) aNode; + String Id = getElementData(returnedEl, "Id"); + String project = getElementData(returnedEl, "Project"); + String DeploymentId = getElementData(returnedEl, "DeploymentId"); + String instrType = getElementData(returnedEl, "Instrument.Type"); + String instrId = getElementData(returnedEl, "Instrument.InstrumentId"); + String geometry = getElementData(returnedEl, "Instrument.GeometryType"); + String audioStart = getElementData(returnedEl, "DeploymentDetails.AudioTimeStamp"); + String audioEnd = getElementData(returnedEl, "RecoveryDetails.AudioTimeStamp"); + String region = getElementData(returnedEl, "Region"); + Deployment deployment = new Deployment(); + try { + Helper.createRequiredElements(deployment); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + e.printStackTrace(); + } + deployment.setId(Id); + deployment.setProject(projectName); + deployment.setDeploymentId(Integer.valueOf(DeploymentId)); + XMLGregorianCalendar gcStart = TethysTimeFuncs.fromGregorianXML(audioStart); + XMLGregorianCalendar gcEnd = TethysTimeFuncs.fromGregorianXML(audioEnd); +// System.out.printf("Converted %s to %s\n", audioStart, +// PamCalendar.formatDBDateTime(TethysTimeFuncs.millisFromGregorianXML(gcStart), true)); + deployment.getDeploymentDetails().setAudioTimeStamp(gcStart); + if (deployment.getRecoveryDetails() == null) { + deployment.setRecoveryDetails(new DeploymentRecoveryDetails()); + } + deployment.getRecoveryDetails().setAudioTimeStamp(gcEnd); + if (instrType != null || instrId != null) { + Instrument instrument = new Instrument(); + instrument.setType(instrType); + instrument.setInstrumentId(instrId); + instrument.setGeometryType(geometry); + deployment.setInstrument(instrument); + } + deployment.setRegion(region); + deployments.add(deployment); + } + } + return deployments; + } + + public int countData(PamDataBlock dataBlock, String deploymentId) { + String queryNoDepl = "{\"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\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Description/Method\",\"LongDataName\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String queryWithDepl = "{\"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\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Description/Method\",\"LongDataName\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"TheDeploymentId\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String query; + if (deploymentId == null) { + query = queryNoDepl; + } + else { + query = queryWithDepl.replace("TheDeploymentId", deploymentId); + } + query = query.replace("LongDataName", dataBlock.getLongDataName()); + DBQueryResult queryResult = executeQuery(query); + if (queryResult ==null) { + return 0; + } + Document doc = convertStringToXMLDocument(queryResult.queryResult); + if (doc == null) { + return 0; + } + + int count = 0; + NodeList returns = doc.getElementsByTagName("Return"); + for (int i = 0; i < returns.getLength(); i++) { + Node aNode = returns.item(i); + String docName = aNode.getTextContent(); +// System.out.println(aNode.getTextContent()); + count += countDetecionsData(docName); + } + return count; + } + + /** + * Count the data in a detections document. + * @param detectionDocId + * @return count of on effort detections in document. + */ + private int countDetecionsData(String detectionDocId) { + String queryBase = "{\"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/OnEffort/Detection/Start\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String query = queryBase.replace("DetectionsDocName", detectionDocId); + DBQueryResult queryResult = executeQuery(query); + Document doc = convertStringToXMLDocument(queryResult.queryResult); + if (doc == null) { + return 0; + } + + NodeList returns = doc.getElementsByTagName("Start"); + return returns.getLength(); + } + + /** + * Get the names of all detection documents for a given deployment + * @param deploymentId + * @return + */ + public ArrayList getDetectionsDocsIds(String deploymentId) { + String queryBase = "{\"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\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"SomeDeploymentId\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String queryStr = queryBase.replace("SomeDeploymentId", deploymentId); + DBQueryResult queryResult = executeQuery(queryStr); + if (queryResult == null || queryResult.queryException != null) { + return null; + } + +// PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + + Document doc = convertStringToXMLDocument(queryResult.queryResult); + if (doc == null) { + return null; + } + + ArrayList detectionDocs = new ArrayList<>(); + + NodeList returns = doc.getElementsByTagName("Return"); + for (int i = 0; i < returns.getLength(); i++) { + Node aNode = returns.item(i); + detectionDocs.add(aNode.getTextContent()); + } + return detectionDocs; + } + + /** + * Get a count of the detections in a detections document. + * Only looking in onEffort so far. + * @param deploymentId + * @param detectionDocId + * @param dataBlock + * @return + */ + public int getDetectionsDetectionCount(String deploymentId, String detectionDocId, PamDataBlock dataBlock) { + String queryBase = "{\"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/OnEffort/Detection/Start\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"SomeDetectionsId\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"SomeDeploymentId\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String queryStr = queryBase.replace("SomeDetectionsId", detectionDocId); + queryStr = queryStr.replace("SomeDeploymentId", deploymentId); + DBQueryResult queryResult = executeQuery(queryStr); + if (queryResult == null || queryResult.queryException != null) { + return 0; + } +// System.out.println("Detections query time ms = " + queryResult.queryTimeMillis); + + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + + Document doc = convertStringToXMLDocument(queryResult.queryResult); + if (doc == null) { + return 0; + } + +// System.out.println(pamXMLWriter.getAsString(doc)); + +// ArrayList detectionDocs = new ArrayList<>(); + + NodeList returns = doc.getElementsByTagName("Start"); + int n = returns.getLength(); + return n; + } + + /** + * This is the quickest way of counting data in a project, but it will load the start + * times for every detection in a project at once, so might use a lot of memory. Also + * it wll probably get data for all deployments in a project, which may not be what we want. + * @param projectName + * @param dataPrefixes + * @return + */ + public int[] countDataForProject(String projectName, String[] dataPrefixes) { + int[] n = new int[dataPrefixes.length]; + ArrayList matchedDeployments = tethysControl.getDeploymentHandler().getMatchedDeployments(); +// ArrayList deployments = getProjectDeployments(projectName); + if (matchedDeployments == null) { + return null; + } + for (PDeployment aDeployment : matchedDeployments) { +// ArrayList detectionsIds = getDetectionsDocsIds(aDeployment.getId()); +// for (String detId : detectionsIds) { +// n += getDetectionsDetectionCount(aDeployment.getId(), detId, dataBlock); +// } + int[] newN = countDataForDeployment(projectName, aDeployment.deployment.getId(), dataPrefixes); + for (int i = 0; i < n.length; i++) { + n[i] += newN[i]; + } + } + return n; + } + + /** + * Count data within a deployment document which is associated with a set of datablocks + * Since the detections all come back in one query, it's easier to count all datablocks at once so + * that it can all happen off a single query. + * @param id + * @param dataBlockPrefixes + * @return + */ + private int[] countDataForDeployment(String projectId, String deploymentId, String[] dataPrefixes) { + String queryBase = "{\"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/OnEffort/Detection/Start\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"ReplaceDeploymentIdString\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String queryString = queryBase.replace("ReplaceDeploymentIdString", deploymentId); + DBQueryResult result = executeQuery(queryString); + if (result == null || result.queryResult == null) { + return null; + } + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + + Document doc = convertStringToXMLDocument(result.queryResult); + if (doc == null) { + return null; + } + +// System.out.println(pamXMLWriter.getAsString(doc)); + + NodeList detsDocs = doc.getElementsByTagName("Detections"); + int[] blockCounts = new int[dataPrefixes.length]; + +// String detDocPrefix = projectId + "_" + dataBlock.getDataName(); + +// int totalCalls = 0; + int detCount = 0; + int dataIndex; + for (int i = 0; i < detsDocs.getLength(); i++) { + Node detNode = detsDocs.item(i); + + NodeList childNodes = detNode.getChildNodes(); + detCount = childNodes.getLength()-1; + dataIndex = -1; + for (int n = 0; n < childNodes.getLength(); n++) { + Node aNode = childNodes.item(n); + if (aNode instanceof Element) { + Element el = (Element) aNode; + String nodeName = el.getNodeName(); + if (nodeName.equals("Id")) { + String id = el.getTextContent(); + for (int j = 0; j < dataPrefixes.length; j++) { + if (id != null && id.startsWith(dataPrefixes[j])) { + dataIndex = j; + } + } +// if (id != null && id.startsWith(detDocPrefix) == false) { +// detCount = 0; +// break; +// } + } + } + } + if (dataIndex >= 0) { + blockCounts[dataIndex] += detCount; + } +// System.out.printf("%d Added %d for new total %d\n",i, detCount, totalCalls); + } + + return blockCounts; + } + + private String getElementData(Element root, String elName) { + String[] tree = elName.split("\\."); + for (String element : tree) { + NodeList nodeList = root.getElementsByTagName(element); + // should only be one node for what we're unpacking. + if (nodeList == null || nodeList.getLength() == 0) { + return null; + } + Node firstNode = nodeList.item(0); + if (firstNode instanceof Element) { + root = (Element) firstNode; + } + } + return root.getTextContent(); + } + private Document convertStringToXMLDocument(String xmlString) { //Parser that produces DOM object trees from XML content DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 2bcdf6b2..578a3c99 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -21,12 +21,17 @@ import Array.Streamer; import Array.ThreadingHydrophoneLocator; import PamController.PamControlledUnit; import PamController.PamController; +import PamUtils.PamUtils; import PamguardMVC.PamDataBlock; +import SoundRecorder.RecordingInfo; import javafx.scene.chart.PieChart.Data; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; import nilus.Audio; import nilus.ChannelInfo; +import nilus.ChannelInfo.DutyCycle; +import nilus.ChannelInfo.DutyCycle.Regimen.RecordingDurationS; +import nilus.ChannelInfo.DutyCycle.Regimen.RecordingIntervalS; import nilus.ChannelInfo.Sampling; import nilus.ChannelInfo.Sampling.Regimen; import nilus.Deployment; @@ -37,30 +42,89 @@ import nilus.DeploymentRecoveryDetails; import nilus.GeometryTypeM; import nilus.Helper; import pamMaths.PamVector; +import pamMaths.STD; import tethys.TethysControl; import tethys.TethysLocationFuncs; +import tethys.TethysState; +import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; +import tethys.niluswraps.PDeployment; /** * Functions to gather data for the deployment document from all around PAMGuard. + * There should be just one of these, available from TethysControl and it will try + * to sensible handle when and how it updates it's list of PAMGuard and Tethys information + *
Any part of PAMGuard wanting information on Deployments should come here. * @author dg50 * */ -public class DeploymentHandler { +public class DeploymentHandler implements TethysStateObserver { private TethysControl tethysControl; - + + private DeploymentOverview deploymentOverview; + + private ArrayList projectDeployments; public DeploymentHandler(TethysControl tethysControl) { super(); this.tethysControl = tethysControl; + tethysControl.addStateObserver(this); } + @Override + public void updateState(TethysState tethysState) { + switch (tethysState.stateType) { + case NEWPROJECTSELECTION: + updateProjectDeployments(); + break; + case TRANSFERDATA: + updateProjectDeployments(); + break; + case UPDATESERVER: + updateProjectDeployments(); + break; + default: + break; + } + } + + /** + * Update the list of Tethys deployments + * @return true if OK + */ + public boolean updateProjectDeployments() { + DeploymentData projData = tethysControl.getGlobalDeplopymentData(); + ArrayList tethysDocs = tethysControl.getDbxmlQueries().getProjectDeployments(projData.getProject()); + if (tethysDocs == null) { + return false; + } + projectDeployments = new ArrayList<>(); + for (Deployment deployment : tethysDocs) { + projectDeployments.add(new PDeployment(deployment)); + } + matchPamguard2Tethys(deploymentOverview, projectDeployments); + return true; + } + + /** + * Get a list of Tethys deployment docs. Note that this + * doesn't update the list, but uses the one currently in memory + * so call updateTethysDeployments() first if necessary. + * @return list of (wrapped) nilus Deployment objects. + */ + public ArrayList getProjectDeployments() { + if (projectDeployments == null) { + updateProjectDeployments(); + } + return projectDeployments; + } + /** * Get an overview of all the deployments. * @return */ - public DeploymentOverview createOverview() { + public DeploymentOverview createPamguardOverview() { // first find an acquisition module. PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); if (!(aModule instanceof AcquisitionControl)) { @@ -118,7 +182,7 @@ public class DeploymentHandler { if (prevPeriod != null) { long gap = nextPeriod.getRecordStart() - prevPeriod.getRecordStop(); long prevDur = prevPeriod.getRecordStop()-prevPeriod.getRecordStart(); - if (gap < 3 || gap < prevDur/50) { + if (gap < 3000 || gap < prevDur/50) { // ignoring up to 3s gap or a sample error < 2%.Dunno if this is sensible or not. prevPeriod.setRecordStop(nextPeriod.getRecordStop()); iterator.remove(); @@ -127,9 +191,20 @@ public class DeploymentHandler { } prevPeriod = nextPeriod; } - System.out.printf("Data have %d distinct files, but only %d distinct recording periods\n", nPeriods, tempPeriods.size()); +// System.out.printf("Data have %d distinct files, but only %d distinct recording periods\n", nPeriods, tempPeriods.size()); DutyCycleInfo dutyCycleinfo = assessDutyCycle(tempPeriods); - DeploymentOverview deploymentOverview = new DeploymentOverview(false, 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())); + } + 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", @@ -138,6 +213,64 @@ public class DeploymentHandler { } + + public DeploymentOverview getDeploymentOverview() { + return deploymentOverview; + } + + /** + * Match what we think the PAMGuard deployment times are with Tethys Deployments read back + * from the database. + * @param deploymentOverview + * @param deployments + */ + private void matchPamguard2Tethys(DeploymentOverview deploymentOverview, ArrayList deployments) { + if (deployments == null || deploymentOverview == null) { + return; + } + ArrayList recordingPeriods = deploymentOverview.getRecordingPeriods(); + for (RecordingPeriod aPeriod : recordingPeriods) { + PDeployment closestDeployment = findClosestDeployment(aPeriod, deployments); + aPeriod.setMatchedTethysDeployment(closestDeployment); + if (closestDeployment != null) { + closestDeployment.setMatchedPAMGaurdPeriod(aPeriod); + } + } + } + + /** + * find the Tethys deployment that most closely matches the PAMGuard recording period. + * @param aPeriod + * @param deployments + * @return + */ + private PDeployment findClosestDeployment(RecordingPeriod aPeriod, ArrayList deployments) { + double overlap = -1; + PDeployment bestDeployment = null; + for (PDeployment aDeployment : deployments) { + double newOverlap = getDeploymentOverlap(aDeployment, aPeriod); + if (newOverlap > overlap) { + bestDeployment = aDeployment; + overlap = newOverlap; + } + } + return bestDeployment; + } + + /** + * Get the overlap in mills between a nilus Deployment and a PAMGuard recording period + * @param aDeployment nilus Deployment from Tethys + * @param aPeriod PAMGuard recording period + * @return overlap in milliseconds + */ + public long getDeploymentOverlap(PDeployment aDeployment, RecordingPeriod aPeriod) { + long start = aPeriod.getRecordStart(); + long stop = aPeriod.getRecordStop(); + long depStart = aDeployment.getAudioStart(); + long depStop = aDeployment.getAudioEnd(); + long overlap = (Math.min(stop, depStop)-Math.max(start, depStart)); + return overlap; + } /** * Work out whether or not the data are evenly duty cycled by testing the @@ -148,14 +281,23 @@ public class DeploymentHandler { private DutyCycleInfo assessDutyCycle(ArrayList tempPeriods) { int n = tempPeriods.size(); if (n < 2) { - return null; + 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(); + ons[i] = tempPeriods.get(i).getDuration()/1000.; + gaps[i] = (tempPeriods.get(i+1).getRecordStart()-tempPeriods.get(i).getRecordStop())/1000.; } - return null; + // now look at how consistent those values are + 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; } @@ -195,102 +337,160 @@ public class DeploymentHandler { // TODO Auto-generated method stub return null; } - - //in each channel - public ArrayList getDeployments() { - - DeploymentOverview recordingOverview = createOverview(); - - // 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; + + /** + * Get a list of Tethys Deployment docs that match the current PAMGuard data. + * @return + */ + public ArrayList getMatchedDeployments() { + ArrayList matched = new ArrayList<>(); + if (deploymentOverview == null) { + return matched; } - // 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(); - long dataStart = Long.MAX_VALUE; - long dataEnd = Long.MIN_VALUE; - if (allStatusData != null && allStatusData.size() > 0) { - // find the number of times it started and stopped .... - int nStart = 0, nStop = 0, nFile=0; - for (DaqStatusDataUnit daqStatus : allStatusData) { - switch (daqStatus.getStatus()) { - case "Start": - nStart++; - dataStart = Math.min(dataStart, daqStatus.getTimeMilliseconds()); - break; - case "Stop": - nStop++; - dataEnd = Math.max(dataEnd, daqStatus.getEndTimeInMilliseconds()); - break; - case "NextFile": - nFile++; - break; - } + for (RecordingPeriod period : deploymentOverview.getRecordingPeriods()) { + if (period.getMatchedTethysDeployment() != null) { + matched.add(period.getMatchedTethysDeployment()); } - -// 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); - } - -// // and we find the datamap within that ... -// OfflineDataMap daqMap = daqInfoDataBlock.getOfflineDataMap(DBControlUnit.findDatabaseControl()); -// if (daqMap != null) { -// // iterate through it. -// long dataStart = daqMap.getFirstDataTime(); -// long dataEnd = daqMap.getLastDataTime(); -// List mapPoints = daqMap.getMapPoints(); -// System.out.printf("Input map of sound data indicates data from %s to %s with %d individual files\n", -// PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), mapPoints.size()); -// /* -// * clearly in the first database I've been looking at of Tinas data, this is NOT getting sensible start and -// * end times. Print them out to see what's going on. -// */ -//// for () -// } - DeploymentRecoveryPair pair = new DeploymentRecoveryPair(); - DeploymentRecoveryDetails deployment = new DeploymentRecoveryDetails(); - DeploymentRecoveryDetails recovery = new DeploymentRecoveryDetails(); - pair.deploymentDetails = deployment; - pair.recoveryDetails = recovery; - - deployment.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataStart)); - deployment.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataStart)); - recovery.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataEnd)); - recovery.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataEnd)); - - ArrayList drPairs = new ArrayList<>(); - drPairs.add(pair); - return drPairs; - + return matched; } - public Deployment createDeploymentDocument(int i, DeploymentRecoveryPair drd) { + /** + * Get a list of instruments from the current project deployments. + * This may be a shorter list than the list of deployments. + * @return + */ + public ArrayList getProjectInstruments() { + if (projectDeployments == null) { + return null; + } + ArrayList instruments = new ArrayList<>(); + for (PDeployment aDepl : projectDeployments) { + Instrument intr = aDepl.deployment.getInstrument(); + if (intr == null) { + continue; + } + PInstrument pInstr = new PInstrument(intr.getType(), intr.getInstrumentId()); + if (instruments.contains(pInstr) == false) { + instruments.add(pInstr); + } + } + return instruments; + } + //in each channel +// public ArrayList getDeployments() { +// +// DeploymentOverview recordingOverview = this.deploymentOverview; +// +// // 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(); +// long dataStart = Long.MAX_VALUE; +// long dataEnd = Long.MIN_VALUE; +// if (allStatusData != null && allStatusData.size() > 0) { +// // find the number of times it started and stopped .... +// int nStart = 0, nStop = 0, nFile=0; +// for (DaqStatusDataUnit daqStatus : allStatusData) { +// switch (daqStatus.getStatus()) { +// case "Start": +// nStart++; +// dataStart = Math.min(dataStart, daqStatus.getTimeMilliseconds()); +// break; +// case "Stop": +// nStop++; +// dataEnd = Math.max(dataEnd, daqStatus.getEndTimeInMilliseconds()); +// break; +// case "NextFile": +// nFile++; +// break; +// } +// } +// +//// 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); +// +// } +// +//// // and we find the datamap within that ... +//// OfflineDataMap daqMap = daqInfoDataBlock.getOfflineDataMap(DBControlUnit.findDatabaseControl()); +//// if (daqMap != null) { +//// // iterate through it. +//// long dataStart = daqMap.getFirstDataTime(); +//// long dataEnd = daqMap.getLastDataTime(); +//// List mapPoints = daqMap.getMapPoints(); +//// System.out.printf("Input map of sound data indicates data from %s to %s with %d individual files\n", +//// PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), mapPoints.size()); +//// /* +//// * clearly in the first database I've been looking at of Tinas data, this is NOT getting sensible start and +//// * end times. Print them out to see what's going on. +//// */ +////// for () +//// } +// DeploymentRecoveryPair pair = new DeploymentRecoveryPair(); +// DeploymentRecoveryDetails deployment = new DeploymentRecoveryDetails(); +// DeploymentRecoveryDetails recovery = new DeploymentRecoveryDetails(); +// pair.deploymentDetails = deployment; +// pair.recoveryDetails = recovery; +// +// deployment.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataStart)); +// deployment.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataStart)); +// recovery.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataEnd)); +// recovery.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataEnd)); +// +// ArrayList drPairs = new ArrayList<>(); +// drPairs.add(pair); +// return drPairs; +// +// } + + /** + * Get the first free deploymendId. This will get appended to + * the ProjectName to make and id for each Deployment document + * @return + */ + public int getFirstFreeDeploymentId() { + /** + * This is an integer used for the DeploymentId. Note that the String Id (currentl9) is just the Project name + * appended with this number. + */ + int firstFree = 0; + if (projectDeployments != null) { + for (PDeployment dep : projectDeployments) { + firstFree = Math.max(firstFree, dep.deployment.getDeploymentId()+1); + } + } + return firstFree; + } + + public Deployment createDeploymentDocument(int i, RecordingPeriod recordingPeriod) { Deployment deployment = new Deployment(); try { nilus.Helper.createRequiredElements(deployment); @@ -304,17 +504,26 @@ public class DeploymentHandler { // TODO Auto-generated catch block e.printStackTrace(); } - String id = String.format("%d", i); + DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); + String id = String.format("%s%d", globalDeplData.getProject(), i); deployment.setId(id); deployment.setDeploymentId(i); - deployment.setDeploymentDetails(drd.deploymentDetails); - deployment.setRecoveryDetails(drd.recoveryDetails); + + DeploymentRecoveryDetails deploymentDetails = new DeploymentRecoveryDetails(); + DeploymentRecoveryDetails recoveryDetails = new DeploymentRecoveryDetails(); + deploymentDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); + deploymentDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); + recoveryDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); + recoveryDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); + + deployment.setDeploymentDetails(deploymentDetails); + deployment.setRecoveryDetails(recoveryDetails); TethysLocationFuncs.getTrackAndPositionData(deployment); getProjectData(deployment); - getSamplingDetails(deployment); + getSamplingDetails(deployment, recordingPeriod); getSensorDetails(deployment); @@ -354,11 +563,11 @@ public class DeploymentHandler { deployment.setDeploymentAlias(deploymentData.getDeploymentAlias()); deployment.setSite(deploymentData.getSite()); deployment.setCruise(deploymentData.getCruise()); - deployment.setPlatform(deploymentData.getPlatform()); + deployment.setPlatform(getPlatform()); deployment.setRegion(deploymentData.getRegion()); Instrument instrument = new Instrument(); - instrument.setType(deploymentData.getInstrumentType()); - instrument.setInstrumentId(deploymentData.getInstrumentId()); + instrument.setType(getInstrumentType()); + instrument.setInstrumentId(getInstrumentId()); // get the geometry type from the array manager. String geomType = getGeometryType(); instrument.setGeometryType(geomType); @@ -366,6 +575,62 @@ public class DeploymentHandler { return true; } + /** + * Instrument identifier, e.g. serial number + * @return + */ + private String getInstrumentId() { + return ArrayManager.getArrayManager().getCurrentArray().getInstrumentId(); + } + + /** + * Test to see if it's possible to export Deployment documents. This is basically a test of + * various metadata fields that are required, such as instrument id's. + * @return null if OK, or a string describing the first encountered error + */ + public String canExportDeployments() { + + DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); + if (globalDeplData.getProject() == null) { + return "You must set a project name"; + } + + PInstrument arrayInstrument = getCurrentArrayInstrument(); + if (arrayInstrument == null) { + return "No 'Instrument' set. Goto array manager"; + } + return null; + } + + /** + * Get the Instrument info for the current array. + * @return + */ + public PInstrument getCurrentArrayInstrument() { + PamArray currentArray = ArrayManager.getArrayManager().getCurrentArray(); + String currType = currentArray.getInstrumentType(); + String currId = currentArray.getInstrumentId(); + PInstrument currentInstrument = null; + if (currType != null || currId != null) { + currentInstrument = new PInstrument(currType, currId); + } + return currentInstrument; + } + + /** + * On what platform is the instrument deployed? (e.g. mooring, tag) + * @return + */ + private String getPlatform() { + return getGeometryType(); + } + /** + * Instrument type, e.g. HARP, EAR, Popup, DMON, Rock Hopper, etc. + * @return + */ + private String getInstrumentType() { + return ArrayManager.getArrayManager().getCurrentArray().getInstrumentType(); + } /** * Get a geometry type string for Tethys based on information in the array manager. * @return @@ -445,8 +710,9 @@ public class DeploymentHandler { /** * Fill in the sampling details in a Deployment document. * @param deployment + * @param recordingPeriod */ - private boolean getSamplingDetails(Deployment deployment) { + private boolean getSamplingDetails(Deployment deployment, RecordingPeriod recordingPeriod) { SamplingDetails samplingDetails = new SamplingDetails(); // this is basically going to be a list of almost identical channel information // currently just for the first acquisition. May extend to more. @@ -496,6 +762,23 @@ public class DeploymentHandler { regimen.setSampleBits(system.getSampleBits()); } regimens.add(regimen); + + DutyCycleInfo dutyCycleInf = deploymentOverview.getDutyCycleInfo(); + boolean isDS = dutyCycleInf != null && dutyCycleInf.isDutyCycled; + if (isDS) { + DutyCycle dutyCycle = new DutyCycle(); + List reg = dutyCycle.getRegimen(); + nilus.ChannelInfo.DutyCycle.Regimen dsr = new nilus.ChannelInfo.DutyCycle.Regimen(); + reg.add(dsr); + RecordingDurationS ssss = new RecordingDurationS(); + ssss.setValue(dutyCycleInf.meanOnTimeS); + dsr.setRecordingDurationS(ssss); + RecordingIntervalS ris = new RecordingIntervalS(); + ris.setValue(dutyCycleInf.meanOnTimeS + dutyCycleInf.meanGapS); + dsr.setRecordingIntervalS(ris); + dsr.setTimeStamp(deployment.getDeploymentDetails().getAudioTimeStamp()); + channelInfo.setDutyCycle(dutyCycle); + } channelInfo.setSampling(sampling); diff --git a/src/tethys/deployment/DeploymentOverview.java b/src/tethys/deployment/DeploymentOverview.java index 468aa1e2..2dacab61 100644 --- a/src/tethys/deployment/DeploymentOverview.java +++ b/src/tethys/deployment/DeploymentOverview.java @@ -14,14 +14,15 @@ public class DeploymentOverview { private ArrayList recordingPeriods = new ArrayList<>(); - private boolean dutyCycled; + private DutyCycleInfo dutyCycleInfo; - public DeploymentOverview(boolean dutyCycled) { + public DeploymentOverview(DutyCycleInfo dutyCycleInfo) { super(); - this.dutyCycled = dutyCycled; + this.dutyCycleInfo = dutyCycleInfo; } - public DeploymentOverview(boolean b, ArrayList tempPeriods) { + public DeploymentOverview(DutyCycleInfo dutyCycleInfo, ArrayList tempPeriods) { + this.dutyCycleInfo = dutyCycleInfo; this.recordingPeriods = tempPeriods; } @@ -33,17 +34,35 @@ public class DeploymentOverview { recordingPeriods.add(recordingPeriod); } - public boolean isDutyCycled() { - return dutyCycled; - } - - public void setDutyCycled(boolean dutyCycled) { - this.dutyCycled = dutyCycled; - } - public ArrayList getRecordingPeriods() { return recordingPeriods; } + + public DutyCycleInfo getDutyCycleInfo() { + return dutyCycleInfo; + } + + /** + * Get the start time of the first recording + * @return + */ + public Long getFirstStart() { + if (recordingPeriods.size() > 0) { + return recordingPeriods.get(0).getRecordStart(); + } + return null; + } + + /** + * Get the end time of the last recording + * @return + */ + public Long getLastEnd() { + if (recordingPeriods.size() > 0) { + return recordingPeriods.get(recordingPeriods.size()-1).getRecordStop(); + } + return null; + } diff --git a/src/tethys/deployment/DeploymentRecoveryPair.java b/src/tethys/deployment/DeploymentRecoveryPair.java index a32e0b88..8e168a2a 100644 --- a/src/tethys/deployment/DeploymentRecoveryPair.java +++ b/src/tethys/deployment/DeploymentRecoveryPair.java @@ -4,8 +4,8 @@ import nilus.DeploymentRecoveryDetails; public class DeploymentRecoveryPair { - public DeploymentRecoveryDetails deploymentDetails; - - public DeploymentRecoveryDetails recoveryDetails; +// public DeploymentRecoveryDetails deploymentDetails; +// +// public DeploymentRecoveryDetails recoveryDetails; } diff --git a/src/tethys/deployment/DutyCycleInfo.java b/src/tethys/deployment/DutyCycleInfo.java index 85aa39bf..740b63c9 100644 --- a/src/tethys/deployment/DutyCycleInfo.java +++ b/src/tethys/deployment/DutyCycleInfo.java @@ -2,4 +2,32 @@ package tethys.deployment; public class DutyCycleInfo { + public boolean isDutyCycled; + + public double meanOnTimeS; + + public double meanGapS; + + int nCycles; + + public DutyCycleInfo(boolean isDutyCycled, double meanOnTimeS, double meanGapS, int nCycles) { + super(); + this.isDutyCycled = isDutyCycled; + this.meanOnTimeS = meanOnTimeS; + this.meanGapS = meanGapS; + this.nCycles = nCycles; + } + + @Override + public String toString() { + if (isDutyCycled == false) { + return "No duty cycle"; + } + else { + return String.format("%3.1fs on, %3.1fs off, for %d cycles", meanOnTimeS, meanGapS, nCycles); + } + } + + + } diff --git a/src/tethys/deployment/PInstrument.java b/src/tethys/deployment/PInstrument.java new file mode 100644 index 00000000..15fd3c31 --- /dev/null +++ b/src/tethys/deployment/PInstrument.java @@ -0,0 +1,49 @@ +package tethys.deployment; + +/** + * Class to handle instrument information + * @author dg50 + * + */ +public class PInstrument { + + public String instrumentType; + + public String instrumentId; + + public PInstrument(String instrumentType, String instrumentId) { + super(); + this.instrumentType = instrumentType; + this.instrumentId = instrumentId; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PInstrument == false) { + return false; + } + PInstrument other = (PInstrument) obj; + boolean eq = true; + if (this.instrumentType != null) { + eq &= this.instrumentType.equals(other.instrumentType); + } + if (this.instrumentId != null) { + eq &= this.instrumentId.equals(other.instrumentId); + } + if (other.instrumentType != null) { + eq &= other.instrumentType.equals(this.instrumentType); + } + if (other.instrumentId != null) { + eq &= other.instrumentId.equals(this.instrumentId); + } + + return eq; + + } + + @Override + public String toString() { + return String.format("%s : %s", instrumentType == null ? "Undefined" : instrumentType, instrumentId); + } + +} diff --git a/src/tethys/deployment/RecordingPeriod.java b/src/tethys/deployment/RecordingPeriod.java index c770c2aa..b7830080 100644 --- a/src/tethys/deployment/RecordingPeriod.java +++ b/src/tethys/deployment/RecordingPeriod.java @@ -1,10 +1,18 @@ package tethys.deployment; +import tethys.niluswraps.PDeployment; + public class RecordingPeriod { private long recordStart; private long recordStop; + + /** + * Reference to a matched nilus Deployment document retrieved + * from the database. + */ + private PDeployment matchedTethysDeployment; public RecordingPeriod(long recordStart, long recordStop) { super(); @@ -31,6 +39,14 @@ public class RecordingPeriod { public long getDuration() { return recordStop-recordStart; } + + public PDeployment getMatchedTethysDeployment() { + return matchedTethysDeployment; + } + + public void setMatchedTethysDeployment(PDeployment closestDeployment) { + this.matchedTethysDeployment = closestDeployment; + } } diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 0b6d117b..eda7c061 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -10,6 +10,7 @@ import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelector; import dataMap.OfflineDataMap; import dataMap.OfflineDataMapPoint; +import metadata.deployment.DeploymentData; import nilus.DataSourceType; import nilus.Deployment; import nilus.Detection; @@ -143,9 +144,12 @@ public class DetectionsHandler { /* * Here, make Detection object and add the DetectionEffort data. */ + DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); Detections detections = new Detections(); - detections.setId(String.format("%d", uniqueDetectionsId++)); +// String prefix = getDetectionsDocIdPrefix(globalDeplData.getProject(), dataBlock); + String prefix = deployment.getId(); + detections.setId(String.format("%s_%d", prefix, uniqueDetectionsId++)); detections.setDescription(dataProvider.getDescription(deployment, tethysExportParams)); DataSourceType dataSource = new DataSourceType(); dataSource.setDeploymentId(deployment.getId()); @@ -214,4 +218,15 @@ public class DetectionsHandler { return effort; } +// /** +// * Get a prefix for a id for a Detections document. This is just the project name +// * and the datablock name. Something may need to be added to allow for multiple +// * analysis going into one database. +// * @param project +// * @param dataBlock +// * @return Detections document prefix. +// */ +// public static final String getDetectionsDocIdPrefix(String project, PamDataBlock dataBlock) { +// return project + "_" + dataBlock.getDataName(); +// } } diff --git a/src/tethys/niluswraps/PDeployment.java b/src/tethys/niluswraps/PDeployment.java new file mode 100644 index 00000000..364a9a46 --- /dev/null +++ b/src/tethys/niluswraps/PDeployment.java @@ -0,0 +1,55 @@ +package tethys.niluswraps; + +import PamUtils.PamCalendar; +import nilus.Deployment; +import nilus.DeploymentRecoveryDetails; +import tethys.TethysTimeFuncs; +import tethys.deployment.RecordingPeriod; +/** + * Wrapper around a nilus Deployment object to provide a bit of extra bookkeeping + * and functionality for PAMGuard. + * @author dg50 + * + */ +public class PDeployment { + + public Deployment deployment; + private RecordingPeriod matchedPAMGaurdPeriod; + + public PDeployment(Deployment deployment) { + super(); + this.deployment = deployment; + } + + public Long getAudioStart() { + DeploymentRecoveryDetails detail = deployment.getDeploymentDetails(); + if (detail == null) { + return null; + } + return TethysTimeFuncs.millisFromGregorianXML(detail.getAudioTimeStamp()); + } + + public Long getAudioEnd() { + DeploymentRecoveryDetails detail = deployment.getRecoveryDetails(); + if (detail == null) { + return null; + } + return TethysTimeFuncs.millisFromGregorianXML(detail.getAudioTimeStamp()); + } + + @Override + public String toString() { + return String.format("%s:%d; %s - %s", deployment.getId(), deployment.getDeploymentId(), + PamCalendar.formatDBDateTime(getAudioStart()), PamCalendar.formatDBDateTime(getAudioEnd())); + } + + public RecordingPeriod getMatchedPAMGaurdPeriod() { + return matchedPAMGaurdPeriod; + } + + public void setMatchedPAMGaurdPeriod(RecordingPeriod matchedPAMGaurdPeriod) { + this.matchedPAMGaurdPeriod = matchedPAMGaurdPeriod; + } + + +} diff --git a/src/tethys/output/DatablockSynchInfo.java b/src/tethys/output/DatablockSynchInfo.java new file mode 100644 index 00000000..7664637b --- /dev/null +++ b/src/tethys/output/DatablockSynchInfo.java @@ -0,0 +1,48 @@ +package tethys.output; + +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; + +/** + * Data about a PAMDataBlock.
+ * All the information needed to populate a table row in the synchronisation table. + * some will need to be set as rarely as possible since it may + * be slow to update.
+ * This needs to sit alongside the StreamExportParams objects since those others are serialisable wheras + * there is a lot of stuff in here which isn't. + * @author dg50 + * + */ +public class DatablockSynchInfo { + + private PamDataBlock dataBlock; + public PamDataBlock getDataBlock() { + return dataBlock; + } + + private TethysControl tethysControl; + private int setDataCount; + + public DatablockSynchInfo(TethysControl tethysControl, PamDataBlock dataBlock) { + super(); + this.tethysControl = tethysControl; + this.dataBlock = dataBlock; + } + + /** + * Get the stored export params for this data block + * @return + */ + public StreamExportParams getExportParams() { + return tethysControl.getTethysExportParams().getStreamParams(dataBlock); + } + + public void setDataCount(int n) { + this.setDataCount = n; + } + + public int getDataCount() { + return setDataCount; + } + +} diff --git a/src/tethys/output/TethysExportParams.java b/src/tethys/output/TethysExportParams.java index b8ddfd40..7172b6cc 100644 --- a/src/tethys/output/TethysExportParams.java +++ b/src/tethys/output/TethysExportParams.java @@ -3,6 +3,7 @@ package tethys.output; import java.io.Serializable; import java.util.HashMap; import PamguardMVC.PamDataBlock; +import metadata.deployment.DeploymentData; /** @@ -28,6 +29,8 @@ public class TethysExportParams implements Serializable, Cloneable{ private HashMap streamParamsMap = new HashMap(); + private DeploymentData deploymentData; + @Override public TethysExportParams clone() { try { @@ -63,4 +66,11 @@ public class TethysExportParams implements Serializable, Cloneable{ return streamParamsMap.get(longDataName); } + public DeploymentData getProjectData() { + if (deploymentData == null) { + deploymentData = new DeploymentData(); + } + return deploymentData; + } + } diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 2760099c..f944c9cc 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -33,7 +33,9 @@ import nilus.Deployment; import tethys.TethysControl; import tethys.dbxml.DBXMLConnect; import tethys.deployment.DeploymentHandler; +import tethys.deployment.DeploymentOverview; import tethys.deployment.DeploymentRecoveryPair; +import tethys.deployment.RecordingPeriod; import tethys.detection.DetectionGranularity; import tethys.detection.DetectionGranularity.GRANULARITY; import tethys.detection.DetectionsHandler; @@ -168,21 +170,23 @@ public class TethysExporter { //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 = new DeploymentHandler(tethysControl); + DeploymentHandler deploymentHandler = tethysControl.getDeploymentHandler(); - ArrayList deployRecover = deploymentHandler.getDeployments(); - if (deployRecover == null) { - return false; - } +// ArrayList deployRecover = deploymentHandler.getDeployments(); +// if (deployRecover == null) { +// return false; +// } ArrayList deploymentDocs = new ArrayList<>(); /* * This will become the main loop over deployment documents */ - int i = 0; - for (DeploymentRecoveryPair drd : deployRecover) { + DeploymentOverview deploymentOverview = deploymentHandler.getDeploymentOverview(); + int i = deploymentHandler.getFirstFreeDeploymentId(); + for (RecordingPeriod recordingPeriod : deploymentOverview.getRecordingPeriods()) { - Deployment deployment = deploymentHandler.createDeploymentDocument(i++, drd); + Deployment deployment = deploymentHandler.createDeploymentDocument(i++, recordingPeriod); // System.out.println(deployment.toString()); deploymentDocs.add(deployment); @@ -196,10 +200,10 @@ public class TethysExporter { */ 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 - */ +// /** +// * 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); @@ -207,7 +211,7 @@ public class TethysExporter { continue; // not interested in this one. } detectionsHandler.exportDetections(aDataBlock, aDeployment, - new DetectionGranularity(GRANULARITY.TIME, 3600), tethysExportParams, streamExportParams); + new DetectionGranularity(GRANULARITY.TIME, 3600*12), tethysExportParams, streamExportParams); } } /* diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 653c4e4d..0be8daab 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -84,7 +84,7 @@ public class AutoTethysProvider implements TethysDataProvider { String fullUnitName = pamControlledUnit.getUnitType() + " " + pamControlledUnit.getUnitName(); description.setAbstract(fullUnitName); description.setObjectives(fullUnitName); - description.setMethod(pamControlledUnit.getUnitType()); + description.setMethod(pamDataBlock.getLongDataName()); return description; } diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java new file mode 100644 index 00000000..0049587b --- /dev/null +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -0,0 +1,112 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.util.ArrayList; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.border.TitledBorder; +import javax.swing.table.AbstractTableModel; + +import PamUtils.PamCalendar; +import PamView.panel.PamPanel; +import PamView.tables.SwingTableColumnWidths; +import dataMap.OfflineDataMap; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.output.DatablockSynchInfo; + +public class DatablockSynchPanel extends TethysGUIPanel { + + public JPanel mainPanel; + + private JTable synchTable; + + private SynchTableModel synchTableModel; + + private ArrayList dataBlockSynchInfo; + + public DatablockSynchPanel(TethysControl tethysControl) { + super(tethysControl); + mainPanel = new PamPanel(new BorderLayout()); + mainPanel.setBorder(new TitledBorder("PAMGuard data blocks")); + synchTableModel = new SynchTableModel(); + synchTable = new JTable(synchTableModel); + new SwingTableColumnWidths(tethysControl.getUnitName()+"SynchTable", synchTable); + JScrollPane scrollPane = new JScrollPane(synchTable); + mainPanel.add(BorderLayout.CENTER, scrollPane); + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + + + @Override + public void updateState(TethysState tethysState) { + synchTableModel.fireTableDataChanged(); + } + + private ArrayList getSychInfos() { + if (dataBlockSynchInfo == null) { + dataBlockSynchInfo = getTethysControl().getSynchronisationInfos(); + } + return dataBlockSynchInfo; + } + + private class SynchTableModel extends AbstractTableModel { + + String[] columnNames = {"Data Stream", "N PAM Datas", "PAMGuard Time", "N Tethys Datas", "Tethys Time", "Options"}; + + @Override + public int getRowCount() { + return getSychInfos().size(); + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public String getColumnName(int column) { + return columnNames[column]; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + DatablockSynchInfo synchInfo = getSychInfos().get(rowIndex); + return getValueAt(synchInfo, columnIndex); + } + + private Object getValueAt(DatablockSynchInfo synchInfo, int columnIndex) { + OfflineDataMap dataMap = synchInfo.getDataBlock().getPrimaryDataMap(); + switch (columnIndex) { + case 0: + return synchInfo.getDataBlock().getDataName(); + case 1: + if (dataMap == null) { + return null; + } + return synchInfo.getDataBlock().getPrimaryDataMap().getDataCount(); + case 2: + if (dataMap == null) { + return null; + } + if (dataMap.getDataCount() == 0) { + return "No data"; + } + long start = synchInfo.getDataBlock().getPrimaryDataMap().getFirstDataTime(); + long stop = synchInfo.getDataBlock().getPrimaryDataMap().getLastDataTime(); + return String.format("%s - %s", PamCalendar.formatDBDateTime(start), PamCalendar.formatDBDateTime(stop)); + case 3: + return synchInfo.getDataCount(); + } + return null; + } + + } +} diff --git a/src/tethys/swing/DeploymentsPanel.java b/src/tethys/swing/DeploymentsPanel.java new file mode 100644 index 00000000..73e238fd --- /dev/null +++ b/src/tethys/swing/DeploymentsPanel.java @@ -0,0 +1,48 @@ +package tethys.swing; + +import java.awt.BorderLayout; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.SwingUtilities; +import javax.swing.border.TitledBorder; + +import PamView.panel.PamPanel; +import tethys.TethysControl; + +public class DeploymentsPanel extends TethysGUIPanel { + + private JPanel mainPanel; + + private PAMGuardDeploymentsTable pamDeploymentsTable; + + private TethysDeploymentsTable tethysDeploymentsTable; + + public DeploymentsPanel(TethysControl tethysControl) { + super(tethysControl); + pamDeploymentsTable = new PAMGuardDeploymentsTable(tethysControl); + tethysDeploymentsTable = new TethysDeploymentsTable(tethysControl); + mainPanel = new PamPanel(new BorderLayout()); + mainPanel.setBorder(new TitledBorder("Deployment information")); + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + splitPane.add(pamDeploymentsTable.getComponent()); + splitPane.add(tethysDeploymentsTable.getComponent()); + mainPanel.add(splitPane,BorderLayout.CENTER); + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + splitPane.setDividerLocation(0.6); + } + }); + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + + + +} diff --git a/src/tethys/swing/NewProjectDialog.java b/src/tethys/swing/NewProjectDialog.java new file mode 100644 index 00000000..353ad732 --- /dev/null +++ b/src/tethys/swing/NewProjectDialog.java @@ -0,0 +1,91 @@ +package tethys.swing; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Window; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamGridBagContraints; +import metadata.deployment.DeploymentData; +import tethys.TethysControl; + +public class NewProjectDialog extends PamView.dialog.PamDialog { + + private static final long serialVersionUID = 1L; + + private static NewProjectDialog singleInstance; + + private JTextField projectName; + + private JTextField projectRegion; + + private DeploymentData deploymentData; + + private NewProjectDialog(Window parentFrame, TethysControl tethysControl) { + super(parentFrame, "New Project", false); + JPanel mainPanel = new JPanel(new GridBagLayout()); + mainPanel.setBorder(new TitledBorder("Project details")); + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(new JLabel("Project name ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(projectName = new JTextField(30), c); + c.gridx = 0; + c.gridy++; + mainPanel.add(new JLabel("Geographic region ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(projectRegion = new JTextField(30), c); + + projectName.setToolTipText("Name of project associated with this deployment. Can be related to a geographic region, funding source, etc"); + projectRegion.setToolTipText("Name of geographic region (optional)"); + + setDialogComponent(mainPanel); + } + + public static DeploymentData showDialog(Window parent, TethysControl tethysControl, DeploymentData deploymentData) { + if (singleInstance == null) { + singleInstance = new NewProjectDialog(parent, tethysControl); + } + singleInstance.setParams(deploymentData); + singleInstance.setVisible(true); + return singleInstance.deploymentData; + } + + private void setParams(DeploymentData deploymentData) { + if (deploymentData == null) { + return; + } + this.deploymentData = deploymentData; + projectName.setText(deploymentData.getProject()); + projectRegion.setText(deploymentData.getRegion()); + } + + @Override + public boolean getParams() { + if (deploymentData == null) { + return false; + } + deploymentData.setProject(projectName.getText()); + deploymentData.setRegion(projectRegion.getText()); + if (deploymentData.getProject() == null || deploymentData.getProject().length() == 0) { + return showWarning("you must specify a project name"); + } + + return true; + } + + @Override + public void cancelButtonPressed() { + deploymentData = null; + } + + @Override + public void restoreDefaultSettings() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java new file mode 100644 index 00000000..25e93601 --- /dev/null +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -0,0 +1,215 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.border.TitledBorder; +import javax.swing.table.AbstractTableModel; + +import PamUtils.PamCalendar; +import PamView.panel.PamPanel; +import PamView.tables.SwingTableColumnWidths; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.deployment.DeploymentHandler; +import tethys.deployment.DeploymentOverview; +import tethys.deployment.RecordingPeriod; +import tethys.niluswraps.PDeployment; + +/** + * Table view of PAMGuard deployments. For a really simple deployment, this may have only + * one line. For towed surveys where we stop and start a lot, it may have a LOT of lines. + * @author dg50 + * + */ +public class PAMGuardDeploymentsTable extends TethysGUIPanel { + + private TableModel tableModel; + + private JTable table; + + private JPanel mainPanel; + + private DeploymentOverview deploymentOverview; + + public PAMGuardDeploymentsTable(TethysControl tethysControl) { + super(tethysControl); +// deploymentHandler = new DeploymentHandler(getTethysControl()); + mainPanel = new PamPanel(new BorderLayout()); + mainPanel.setBorder(new TitledBorder("PAMGuard recording periods")); + tableModel = new TableModel(); + table = new JTable(tableModel); + table.setRowSelectionAllowed(true); + table.addMouseListener(new TableMouse()); + JScrollPane scrollPane = new JScrollPane(table); + mainPanel.add(BorderLayout.CENTER, scrollPane); + new SwingTableColumnWidths(tethysControl.getUnitName()+"PAMDeploymensTable", table); + table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + + private class TableMouse extends MouseAdapter { + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(); + } + } + + } + + public void showPopup() { + int aRow = table.getSelectedRow(); + int[] selRows = table.getSelectedRows(); + if (selRows == null || selRows.length == 0) { + if (aRow >= 0) { + selRows = new int[1]; + selRows[0] = aRow; + } + else { + return; + } + } + // make a list of RecordingPeriods which don't currently have a Deployment document + ArrayList newPeriods = new ArrayList<>(); + ArrayList allPeriods = deploymentOverview.getRecordingPeriods(); + for (int i = 0; i < selRows.length; i++) { + if (allPeriods.get(selRows[i]).getMatchedTethysDeployment() == null) { + newPeriods.add(allPeriods.get(i)); + } + } + if (newPeriods.size() == 0) { + return; + } + /* + * if we get here, we've one or more rows without a Tethys output, so can have + * a menu to create them. + */ + + } + + @Override + public void updateState(TethysState tethysState) { + switch(tethysState.stateType) { + case NEWPROJECTSELECTION: + case NEWPAMGUARDSELECTION: + updateDeployments(); + break; + case UPDATEMETADATA: + checkExportMeta(); + } + + tableModel.fireTableDataChanged(); + } + + private void checkExportMeta() { + String metaErr = getTethysControl().getDeploymentHandler().canExportDeployments(); + if (metaErr != null) { + mainPanel.setBackground(Color.RED); + } + else { + JPanel anyPanel = new JPanel(); + mainPanel.setBackground(anyPanel.getBackground()); + } + } + + private void updateDeployments() { + DeploymentHandler deploymentHandler = getTethysControl().getDeploymentHandler(); + deploymentOverview = deploymentHandler.getDeploymentOverview(); + tableModel.fireTableDataChanged(); +// DeploymentData deplData = getTethysControl().getGlobalDeplopymentData(); +// ArrayList projectDeployments = getTethysControl().getDbxmlQueries().getProjectDeployments(deplData.getProject()); +// deploymentHandler.matchPamguard2Tethys(deploymentOverview, projectDeployments); + } + + private class TableModel extends AbstractTableModel { + + private String[] columnNames = {"Id", "Start", "Stop", "Duration", "Cycle", "Tethys Deployment"}; + + @Override + public int getRowCount() { + if (deploymentOverview == null) { + return 0; + } + else { + return deploymentOverview.getRecordingPeriods().size(); + } + } + + @Override + public String getColumnName(int column) { + return columnNames[column]; + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + RecordingPeriod period = deploymentOverview.getRecordingPeriods().get(rowIndex); +// DeploymentRecoveryPair deplInfo = deploymentInfo.get(rowIndex); + if (columnIndex == 4) { + return deploymentOverview.getDutyCycleInfo(); + } + return getValueAt(period, rowIndex, columnIndex); + } + + private Object getValueAt(RecordingPeriod period, int rowIndex, int columnIndex) { + switch (columnIndex) { + case 0: + return rowIndex; + case 1: + return PamCalendar.formatDBDateTime(period.getRecordStart()); +// return TethysTimeFuncs.formatGregorianTime(deplInfo.deploymentDetails.getAudioTimeStamp()); + case 2: + return PamCalendar.formatDBDateTime(period.getRecordStop()); +// return TethysTimeFuncs.formatGregorianTime(deplInfo.recoveryDetails.getAudioTimeStamp()); + case 3: +// long t1 = TethysTimeFuncs.millisFromGregorianXML(deplInfo.deploymentDetails.getAudioTimeStamp()); +// long t2 = TethysTimeFuncs.millisFromGregorianXML(deplInfo.recoveryDetails.getAudioTimeStamp()); + return PamCalendar.formatDuration(period.getRecordStop()-period.getRecordStart()); + case 5: + PDeployment deployment = period.getMatchedTethysDeployment(); + return makeDeplString(period, deployment); + } + + return null; + } + + private String makeDeplString(RecordingPeriod period, PDeployment deployment) { + if (deployment == null) { + return "no match"; + } + DeploymentHandler deploymentHandler = getTethysControl().getDeploymentHandler(); + long overlap = deploymentHandler.getDeploymentOverlap(deployment, period); + + long start = period.getRecordStart(); + long stop = period.getRecordStop(); + double percOverlap = (overlap*100.) / (stop-start); + return String.format("%s : %3.1f%% overlap", deployment.toString(), percOverlap); + } + + } +} diff --git a/src/tethys/swing/TethysConnectionPanel.java b/src/tethys/swing/TethysConnectionPanel.java index 5b0d2dc6..c13efd54 100644 --- a/src/tethys/swing/TethysConnectionPanel.java +++ b/src/tethys/swing/TethysConnectionPanel.java @@ -6,6 +6,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; +import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; @@ -13,16 +14,24 @@ import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.TitledBorder; +import Array.ArrayDialog; +import Array.ArrayManager; +import Array.PamArray; import PamView.dialog.PamGridBagContraints; import PamView.dialog.ScrollingPamLabel; import PamView.dialog.SettingsButton; import PamView.panel.PamPanel; import PamView.panel.WestAlignedPanel; +import metadata.deployment.DeploymentData; +import nilus.Deployment; import pamViewFX.fxNodes.PamComboBox; import tethys.TethysControl; import tethys.TethysState; import tethys.TethysState.StateType; +import tethys.TethysTimeFuncs; import tethys.dbxml.ServerStatus; +import tethys.deployment.PInstrument; +import tethys.niluswraps.PDeployment; import tethys.output.TethysExportParams; /** @@ -45,6 +54,14 @@ public class TethysConnectionPanel extends TethysGUIPanel { private JComboBox projectList; +// private JComboBox deploymentList; + + private JButton newProjectButton; + + private JComboBox projectInstruments; + + private JButton newInstrument; + public TethysConnectionPanel(TethysControl tethysControl) { super(tethysControl); mainPanel = new WestAlignedPanel(new GridBagLayout()); @@ -61,6 +78,39 @@ public class TethysConnectionPanel extends TethysGUIPanel { selectServer(); } }); + newProjectButton = new JButton("New project"); + newProjectButton.setToolTipText("Create new project information"); + newProjectButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + createNewProject(); + } + }); + projectList = new JComboBox<>(); + projectList.setToolTipText("All projects present in the current Tethys database"); + projectList.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + newProjectSelect(); + } + }); + projectInstruments = new JComboBox(); + newInstrument = new JButton("New / Edit"); + projectInstruments.setToolTipText("Instruments currently listed within all deployments of the current project"); + newInstrument.setToolTipText("Edit or create a new instrument (uses PAMGuard Array dialog)"); + projectInstruments.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + newInstrumentSelect(); + } + }); + newInstrument.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + createNewInstrument(); + } + }); +// deploymentList = new JComboBox(); GridBagConstraints c = new PamGridBagContraints(); mainPanel.add(new JLabel("Tethys Server "), c); @@ -69,23 +119,110 @@ public class TethysConnectionPanel extends TethysGUIPanel { c.gridx++; mainPanel.add(serverSelButton, c); c.gridx++; + c.gridwidth = 2; mainPanel.add(serverStatus, c); - c.gridx++; c.gridx =0; c.gridy++; - mainPanel.add(new JLabel("Projects "), c); + c.gridwidth = 1; + mainPanel.add(new JLabel("Projects ", JLabel.RIGHT), c); c.gridx++; - mainPanel.add(projectList = new JComboBox<>(), c); + mainPanel.add(projectList, c); + c.gridx++; + mainPanel.add(newProjectButton, c); - fillServerControl(); + // instrument section + c.gridx++; + mainPanel.add(new JLabel(" Instruments ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(projectInstruments, c); + c.gridx++; + mainPanel.add(newInstrument, c); + + +// c.gridx = 0; +// c.gridy++; +// mainPanel.add(new JLabel("Deployments ", JLabel.RIGHT), c); +// c.gridx += c.gridwidth; +// c.gridwidth = 2; +// mainPanel.add(deploymentList, c); + +// fillServerControl(); // no need Will get set from TethysControl as soon as all initialised. } + protected void newInstrumentSelect() { + PInstrument pInstr = (PInstrument) projectInstruments.getSelectedItem(); + if (pInstr == null) { + return; + } + PamArray currentArray = ArrayManager.getArrayManager().getCurrentArray(); + currentArray.setInstrumentType(pInstr.instrumentType); + currentArray.setInstrumentId(pInstr.instrumentId); + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATEMETADATA)); + } + + protected void createNewInstrument() { + PamArray updatedArray = ArrayDialog.showDialog(getTethysControl().getGuiFrame(), ArrayManager.getArrayManager()); + if (updatedArray != null) { + updateInstrumentsList(); + } + } + + /** + * Action from new project button + */ + protected void createNewProject() { + DeploymentData pamDeploymentData = getTethysControl().getGlobalDeplopymentData(); + pamDeploymentData = NewProjectDialog.showDialog(getTethysControl().getGuiFrame(), getTethysControl(), pamDeploymentData); + if (pamDeploymentData != null) { + updateProjectList(); + } + } + + protected void newProjectSelect() { + String project = (String) projectList.getSelectedItem(); + if (project == null) { + return; + } + DeploymentData globData = getTethysControl().getGlobalDeplopymentData(); + globData.setProject(project); + getTethysControl().getDeploymentHandler().updateProjectDeployments(); + /* + * if there are existing deployment data, then copy the info to the + * internal project information + */ + ArrayList projectDeployments = getTethysControl().getDeploymentHandler().getProjectDeployments(); + if (projectDeployments != null && projectDeployments.size() > 0) { + Deployment dep = projectDeployments.get(0).deployment; + globData.setProject(dep.getProject()); + globData.setRegion(dep.getRegion()); + getTethysControl().sendStateUpdate(new TethysState(TethysState.StateType.NEWPROJECTSELECTION)); + } + + updateInstrumentsList(); +// fillDeploymentsList(project); + } + + +// private void fillDeploymentsList(String project) { +// ArrayList projectDeployments = getTethysControl().getDeploymentHandler().getProjectDeployments(); +// deploymentList.removeAllItems(); +// if (projectDeployments == null) { +// return; +// } +// for (PDeployment dep : projectDeployments) { +//// String str = String.format("%s:%d, %s to %s", dep.getId(), dep.getDeploymentId(), +//// TethysTimeFuncs.formatGregorianTime(dep.getDeploymentDetails().getAudioTimeStamp()), +//// TethysTimeFuncs.formatGregorianTime(dep.getRecoveryDetails().getAudioTimeStamp())); +// deploymentList.addItem(dep); +// } +// } + protected void selectServer() { // will return the same object at the moment, so no need to do anything. TethysExportParams newParams = SelectServerdDialog.showDialog(getTethysControl(), getTethysControl().getGuiFrame(), getTethysControl().getTethysExportParams()); if (newParams != null) { - getTethysControl().sendStateUpdate(new TethysState(TethysState.StateType.UPDATESERVER)); + getTethysControl().checkServer();// sendStateUpdate(new TethysState(TethysState.StateType.UPDATESERVER)); } } @@ -94,8 +231,11 @@ public class TethysConnectionPanel extends TethysGUIPanel { serverName.setText(exportParams.getFullServerName()); ServerStatus status = getTethysControl().getDbxmlConnect().pingServer(); serverStatus.setText(status.toString()); + + colourBackground(status.ok ? 0 : 1); } + @Override public JComponent getComponent() { return mainPanel; @@ -113,13 +253,53 @@ public class TethysConnectionPanel extends TethysGUIPanel { private void updateProjectList() { projectList.removeAllItems(); - ArrayList dbNames = getTethysControl().getDbxmlQueries().getProjectNames(); - if (dbNames == null || dbNames.size() == 0) { - System.out.println("No existing projects"); + /* + * put the project name assigned within this PAMGuard config at the top of the + * list. + */ + String localProjName = null; + DeploymentData pamDeploymentData = getTethysControl().getGlobalDeplopymentData(); + if (pamDeploymentData != null && pamDeploymentData.getProject() != null) { + localProjName = pamDeploymentData.getProject(); + if (localProjName.length() == 0) { + localProjName = null; + } + } + if (localProjName != null) { + projectList.addItem(localProjName); + } + + ArrayList projectNames = getTethysControl().getDbxmlQueries().getProjectNames(); + if (projectNames == null || projectNames.size() == 0) { +// System.out.println("No existing projects"); return; } - for (int i = 0; i < dbNames.size(); i++) { - projectList.addItem(dbNames.get(i)); + for (int i = 0; i < projectNames.size(); i++) { + String projName = projectNames.get(i); + if (projName.equals(localProjName)) { + continue; + } + projectList.addItem(projectNames.get(i)); + } + } + + /** + * Update displayed list of instruments + */ + private void updateInstrumentsList() { + projectInstruments.removeAllItems(); + PInstrument currentInstrument = getTethysControl().getDeploymentHandler().getCurrentArrayInstrument(); + if (currentInstrument != null) { + projectInstruments.addItem(currentInstrument); + } + ArrayList projectInst = getTethysControl().getDeploymentHandler().getProjectInstruments(); + if (projectInst == null) { + return; + } + for (int i = 0; i < projectInst.size(); i++) { + if (projectInst.get(i).equals(currentInstrument) == false) { + projectInstruments.addItem(projectInst.get(i)); + } } } diff --git a/src/tethys/swing/TethysDeploymentsTable.java b/src/tethys/swing/TethysDeploymentsTable.java new file mode 100644 index 00000000..08781605 --- /dev/null +++ b/src/tethys/swing/TethysDeploymentsTable.java @@ -0,0 +1,151 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; + +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.border.TitledBorder; +import javax.swing.table.AbstractTableModel; + +import PamView.panel.PamPanel; +import PamView.tables.SwingTableColumnWidths; +import tethys.TethysControl; +import tethys.TethysMenuActions; +import tethys.TethysState; +import tethys.deployment.DeploymentOverview; +import tethys.niluswraps.PDeployment; + +public class TethysDeploymentsTable extends TethysGUIPanel { + + private JPanel mainPanel; + + private TableModel tableModel; + + private JTable table; + + private ArrayList projectDeployments; + + private DeploymentOverview deploymentOverview; + + public TethysDeploymentsTable(TethysControl tethysControl) { + super(tethysControl); + mainPanel = new PamPanel(new BorderLayout()); + mainPanel.setBorder(new TitledBorder("All project deployments")); + tableModel = new TableModel(); + table = new JTable(tableModel); + table.setRowSelectionAllowed(true); + table.addMouseListener(new TableMouse()); + JScrollPane scrollPane = new JScrollPane(table); + mainPanel.add(BorderLayout.CENTER, scrollPane); + table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + new SwingTableColumnWidths(tethysControl.getUnitName()+"AllProjectDeploymentsTable", table); + } + + private class TableMouse extends MouseAdapter { + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + } + + protected void showPopupMenu(MouseEvent e) { + int row = table.getSelectedRow(); + if (row < 0) { + return; + } + PDeployment pDeployment = projectDeployments.get(row); + TethysMenuActions menuActions = new TethysMenuActions(getTethysControl()); + menuActions.deploymentMouseActions(e, pDeployment); + + } + + + + @Override + public JComponent getComponent() { + return mainPanel; + } + + @Override + public void updateState(TethysState tethysState) { + projectDeployments = getTethysControl().getDeploymentHandler().getProjectDeployments(); + deploymentOverview = getTethysControl().getDeploymentHandler().getDeploymentOverview(); + tableModel.fireTableDataChanged(); + } + + private class TableModel extends AbstractTableModel { + + private String[] columnNames = {"Deployment document", "PAMGuard Match"}; + + @Override + public int getRowCount() { + return projectDeployments == null ? 0 : projectDeployments.size(); + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + PDeployment deployment = projectDeployments.get(rowIndex); + switch (columnIndex) { + case 0: + return deployment.toString(); + case 1: + return getMatchText(deployment); + } + return null; + } + + @Override + public String getColumnName(int column) { + return columnNames[column]; + } + + + public String getMatchText(PDeployment deployment) { + // TODO Auto-generated method stub + if (deployment.getMatchedPAMGaurdPeriod() != null) { + return "Matched to PAMGaurd data"; + }; + if (deploymentOverview == null) { + return "No PAMGuard data"; + } + Long depStart = deploymentOverview.getFirstStart(); + Long depEnd = deploymentOverview.getLastEnd(); + if (depStart == null) { + return "No PAMGuard recordings"; + } + if (deployment.getAudioEnd() < depStart) { + return "Earlier than PAMGuard data"; + } + if (deployment.getAudioStart() > depEnd) { + return "Later than PAMGuard data"; + } + return "Partial overlap with PAMGuard data, but no match"; + } + } + + +} diff --git a/src/tethys/swing/TethysGUIPanel.java b/src/tethys/swing/TethysGUIPanel.java index a9263b85..f865aee7 100644 --- a/src/tethys/swing/TethysGUIPanel.java +++ b/src/tethys/swing/TethysGUIPanel.java @@ -1,6 +1,10 @@ package tethys.swing; +import java.awt.Color; +import java.awt.Component; + import javax.swing.JComponent; +import javax.swing.JPanel; import tethys.TethysControl; import tethys.TethysState; @@ -27,5 +31,28 @@ public abstract class TethysGUIPanel implements TethysStateObserver { } + public Color getNormalColour() { + return new JPanel().getBackground(); + } + public Color getErrorColour() { + return Color.ORANGE; + } + + public void colourBackground(int iCol) { + Color col = iCol == 0 ? getNormalColour() : getErrorColour(); + colourPanels(getComponent(), col); + } + + private void colourPanels(JComponent component, Color col) { + component.setBackground(col); + int nChild = component.getComponentCount(); + for (int i = 0; i < nChild; i++) { + Component aChild = component.getComponent(i); + if (aChild instanceof JPanel) { + colourPanels((JComponent) aChild, col); + } + } + + } } diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index d03929eb..72e07590 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -4,6 +4,8 @@ import java.awt.BorderLayout; import javax.swing.JComponent; import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.SwingUtilities; import tethys.TethysControl; @@ -15,12 +17,31 @@ public class TethysMainPanel extends TethysGUIPanel { private TethysConnectionPanel connectionPanel; + private DatablockSynchPanel datablockSynchPanel; + + private DeploymentsPanel deploymentsPanel; + public TethysMainPanel(TethysControl tethysControl) { super(tethysControl); this.tethysControl = tethysControl; mainPanel = new JPanel(new BorderLayout()); connectionPanel = new TethysConnectionPanel(tethysControl); + datablockSynchPanel = new DatablockSynchPanel(tethysControl); + deploymentsPanel = new DeploymentsPanel(tethysControl); + mainPanel.add(BorderLayout.NORTH, connectionPanel.getComponent()); + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); +// splitPane.set + mainPanel.add(BorderLayout.CENTER, splitPane); +// mainPanel.add(BorderLayout.CENTER, datablockSynchPanel.getComponent()); + splitPane.add(deploymentsPanel.getComponent()); + splitPane.add(datablockSynchPanel.getComponent()); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + splitPane.setDividerLocation(0.5); + } + }); } public JPanel getMainPanel() {